Skip to content

Commit 0ea3c5c

Browse files
committed
When editing the time period it does the query again
1 parent c64d32d commit 0ea3c5c

File tree

2 files changed

+78
-16
lines changed
  • apps/webapp/app

2 files changed

+78
-16
lines changed

apps/webapp/app/components/runs/v3/SharedFilters.tsx

Lines changed: 61 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ import {
1616
isSunday,
1717
} from "date-fns";
1818
import parse from "parse-duration";
19-
import { startTransition, useCallback, useEffect, useState, type ReactNode } from "react";
19+
import { startTransition, useCallback, useEffect, useRef, useState, type ReactNode } from "react";
2020
import { AppliedFilter } from "~/components/primitives/AppliedFilter";
2121
import { DateTimePicker } from "~/components/primitives/DateTimePicker";
2222
import { DateTime } from "~/components/primitives/DateTime";
@@ -30,6 +30,7 @@ import { filterIcon } from "./RunFilters";
3030
import { Paragraph } from "~/components/primitives/Paragraph";
3131
import { ShortcutDefinition } from "~/hooks/useShortcutKeys";
3232
import { SpanPresenter } from "~/presenters/v3/SpanPresenter.server";
33+
import { useLocation } from "@remix-run/react";
3334

3435
export type DisplayableEnvironment = Pick<RuntimeEnvironment, "type" | "id"> & {
3536
userName?: string;
@@ -270,23 +271,65 @@ export function timeFilterRenderValues({
270271
return { label, valueLabel, rangeType };
271272
}
272273

274+
/** Values passed to onApply callback when a time filter is applied */
275+
export interface TimeFilterApplyValues {
276+
period?: string;
277+
from?: string;
278+
to?: string;
279+
}
280+
273281
export interface TimeFilterProps {
274282
defaultPeriod?: string;
275283
/** Label name used in the filter display, defaults to "Created" */
276284
labelName?: string;
277285
applyShortcut?: ShortcutDefinition | undefined;
286+
/** Callback when the user applies a time filter selection, receives the applied values */
287+
onApply?: (values: TimeFilterApplyValues) => void;
278288
}
279289

280-
export function TimeFilter({ defaultPeriod, labelName = "Created", applyShortcut }: TimeFilterProps = {}) {
281-
const { value, del } = useSearchParams();
282-
290+
export function TimeFilter({
291+
defaultPeriod,
292+
labelName = "Created",
293+
applyShortcut,
294+
onApply,
295+
}: TimeFilterProps = {}) {
296+
const { value } = useSearchParams();
297+
const periodValue = value("period");
298+
const fromValue = value("from");
299+
const toValue = value("to");
300+
301+
//non-optimistic location
302+
const location = useLocation();
303+
283304
const { period, from, to, label, valueLabel } = timeFilters({
284-
period: value("period"),
285-
from: value("from"),
286-
to: value("to"),
305+
period: periodValue,
306+
from: fromValue,
307+
to: toValue,
287308
defaultPeriod,
288309
labelName,
289310
});
311+
312+
// Track the search string, not individual values
313+
const previousSearch = useRef<string | null>(null);
314+
315+
useEffect(() => {
316+
// Skip first render - set initial value and return
317+
if (previousSearch.current === null) {
318+
previousSearch.current = location.search;
319+
return;
320+
}
321+
322+
// Only call onApply if the URL actually changed
323+
if (previousSearch.current !== location.search) {
324+
const currentSearch = new URLSearchParams(location.search);
325+
onApply?.({
326+
period: currentSearch.get("period") ?? undefined,
327+
from: currentSearch.get("from") ?? undefined,
328+
to: currentSearch.get("to") ?? undefined
329+
});
330+
previousSearch.current = location.search;
331+
}
332+
}, [location.search, onApply]);
290333

291334
return (
292335
<FilterMenuProvider>
@@ -309,6 +352,7 @@ export function TimeFilter({ defaultPeriod, labelName = "Created", applyShortcut
309352
defaultPeriod={defaultPeriod}
310353
labelName={labelName}
311354
applyShortcut={applyShortcut}
355+
onApply={onApply}
312356
/>
313357
)}
314358
</FilterMenuProvider>
@@ -334,6 +378,7 @@ export function TimeDropdown({
334378
defaultPeriod = DEFAULT_PERIOD,
335379
labelName = "Created",
336380
applyShortcut,
381+
onApply,
337382
}: {
338383
trigger: ReactNode;
339384
period?: string;
@@ -342,6 +387,7 @@ export function TimeDropdown({
342387
defaultPeriod?: string;
343388
labelName?: string;
344389
applyShortcut?: ShortcutDefinition | undefined;
390+
onApply?: (values: TimeFilterApplyValues) => void;
345391
}) {
346392
const [open, setOpen] = useState<boolean | undefined>();
347393
const { replace } = useSearchParams();
@@ -409,6 +455,7 @@ export function TimeDropdown({
409455
setFromValue(undefined);
410456
setToValue(undefined);
411457
setOpen(false);
458+
onApply?.({ period: periodToApply, from: undefined, to: undefined });
412459
} else {
413460
// Validate date range
414461
if (!fromValue && !toValue) {
@@ -421,15 +468,19 @@ export function TimeDropdown({
421468
return;
422469
}
423470

471+
const fromStr = fromValue?.getTime().toString();
472+
const toStr = toValue?.getTime().toString();
473+
424474
replace({
425475
period: undefined,
426476
cursor: undefined,
427477
direction: undefined,
428-
from: fromValue?.getTime().toString(),
429-
to: toValue?.getTime().toString(),
478+
from: fromStr,
479+
to: toStr,
430480
});
431481

432482
setOpen(false);
483+
onApply?.({ period: undefined, from: fromStr, to: toStr });
433484
}
434485
}, [
435486
activeSection,
@@ -440,6 +491,7 @@ export function TimeDropdown({
440491
fromValue,
441492
toValue,
442493
replace,
494+
onApply,
443495
]);
444496

445497
return (

apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.query/route.tsx

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { ArrowDownTrayIcon, ClipboardIcon } from "@heroicons/react/20/solid";
22
import type { OutputColumnMetadata, WhereClauseFallback } from "@internal/clickhouse";
3-
import { Form, useNavigation } from "@remix-run/react";
3+
import { Form, useNavigation, useSubmit } from "@remix-run/react";
44
import {
55
redirect,
66
type ActionFunctionArgs,
@@ -358,6 +358,7 @@ const QueryEditorForm = forwardRef<
358358
const [query, setQuery] = useState(defaultQuery);
359359
const [scope, setScope] = useState<QueryScope>(defaultScope);
360360
const { value: searchParamValue } = useSearchParams();
361+
const formRef = useRef<HTMLFormElement>(null);
361362

362363
// Get time filter values from URL search params
363364
const period = searchParamValue("period");
@@ -368,6 +369,8 @@ const QueryEditorForm = forwardRef<
368369
// This disables the time filter UI since the user is filtering in their query
369370
const queryHasTriggeredAt = /\bWHERE\b[\s\S]*\btriggered_at\b/i.test(query);
370371

372+
373+
371374
// Expose methods to parent for external query setting (history, AI, examples)
372375
useImperativeHandle(
373376
ref,
@@ -396,7 +399,7 @@ const QueryEditorForm = forwardRef<
396399
minHeight="200px"
397400
className="min-h-[200px]"
398401
/>
399-
<Form method="post" className="flex items-center justify-between gap-2 px-2">
402+
<Form ref={formRef} method="post" className="flex items-center justify-between gap-2 px-2">
400403
<input type="hidden" name="query" value={query} />
401404
<input type="hidden" name="scope" value={scope} />
402405
{/* Pass time filter values to action */}
@@ -434,11 +437,18 @@ const QueryEditorForm = forwardRef<
434437
))
435438
}
436439
</Select>
437-
{queryHasTriggeredAt ? <Button variant="tertiary/small" disabled={true} type="button">Set in query</Button> : <TimeFilter
438-
defaultPeriod={DEFAULT_PERIOD}
439-
labelName="Triggered"
440-
applyShortcut={{ key: "enter", enabledOnInputElements: true }}
441-
/>}
440+
{queryHasTriggeredAt ? (
441+
<Button variant="tertiary/small" disabled={true} type="button">
442+
Set in query
443+
</Button>
444+
) : (
445+
<TimeFilter
446+
defaultPeriod={DEFAULT_PERIOD}
447+
labelName="Triggered"
448+
applyShortcut={{ key: "enter", enabledOnInputElements: true }}
449+
onApply={() => formRef.current?.submit()}
450+
/>
451+
)}
442452
<Button
443453
type="submit"
444454
variant="primary/small"

0 commit comments

Comments
 (0)