Thanks to visit codestin.com
Credit goes to github.com

Skip to content

feat(site): add shared DateTimeRangeFilter component - #28255

Merged
johnstcn merged 6 commits into
mainfrom
cian/datetime-range-filter-component
Aug 19, 2026
Merged

feat(site): add shared DateTimeRangeFilter component#28255
johnstcn merged 6 commits into
mainfrom
cian/datetime-range-filter-component

Conversation

@johnstcn

@johnstcn johnstcn commented Aug 18, 2026

Copy link
Copy Markdown
Member

A text-expression datetime range picker with From/To inputs accepting now, a clock time (current day), a date (midnight), or a date with a clock time. Invalid text gets inline errors, underspecified expressions resolve on blur, and out-of-order boundaries clamp against the other one. The trigger derives a concise label ("Last 24 hours", "Apr 10", "Aug 11 - Today", "Apr 17 - 19").

Placed in site/src/components/DateTimeRangeFilter/ so other pages can reuse it. Expression parsing and trigger-label derivation live in timeRange.ts next to the component; both are generic over Date pairs. Depends on #28254 for the shared filterQuery serialization helpers.

Part of AIGOV-580


Generated by Coder Agents on behalf of @johnstcn.$


Stack: #28254 (filterQuery fix) \u2192 #28255 (component) \u2192 #28256 (sessions page)

stringifyFilter only quoted values containing spaces, so filter
values like RFC 3339 timestamps (2026-08-16T20:42:00Z) were emitted
unquoted and the backend query parser rejected their colons. Quote
values containing colons too; they were never valid unquoted.
Extract parseFilterQuery/stringifyFilter into filterQuery.ts with
unit tests.
A text-expression datetime range picker with From/To inputs accepting
now, clock times, dates, or date and time pairs, with inline errors,
blur normalization, and clamping. Moved to site/src/components so
other pages can reuse it.
@linear-code

linear-code Bot commented Aug 18, 2026

Copy link
Copy Markdown

AIGOV-580

Comment on lines +72 to +87
export const formatTriggerLabel = (range: TimeRange, now: Date): string => {
const from = dayjs(range.startedAfter);
if (sameDay(range.startedAfter, range.startedBefore)) {
return from.format(MONTH_DAY);
}
if (sameDay(range.startedBefore, now)) {
return `${from.format(MONTH_DAY)} - Today`;
}
if (
range.startedAfter.getFullYear() === range.startedBefore.getFullYear() &&
range.startedAfter.getMonth() === range.startedBefore.getMonth()
) {
return `${from.format(MONTH_DAY)} - ${range.startedBefore.getDate()}`;
}
return `${from.format(MONTH_DAY)} - ${dayjs(range.startedBefore).format(MONTH_DAY)}`;
};

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm open to suggestions on the actual copy here.

Comment thread site/src/components/DateTimeRangeFilter/timeRange.ts
The shared component's parser and trigger-label logic lived in
timeRange.ts with no unit tests; coverage was only via story play
functions. Add vitest coverage for the expression grammar and the
label derivation cases.
@johnstcn
johnstcn marked this pull request as ready for review August 18, 2026 11:07
Copilot AI lite review requested due to automatic review settings August 18, 2026 11:07

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a reusable, text-expression based datetime range filter UI to the frontend so multiple pages (notably the AI Gateway sessions page in the PR stack) can apply consistent started-after / started-before filtering with concise trigger labels.

Changes:

  • Introduces DateTimeRangeFilter popover component with From/To expression inputs, inline validation, and an Apply action.
  • Adds parsing/labeling utilities (parseTimeExpression, formatTriggerLabel) plus unit tests for these pure functions.
  • Adds Storybook stories with interaction coverage for the component’s key behaviors (prefill, blur normalization, clamping, validation, apply/cancel).

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 1 comment.

File Description
site/src/components/DateTimeRangeFilter/timeRange.ts Implements strict parsing for supported time expressions and derives trigger labels from resolved ranges.
site/src/components/DateTimeRangeFilter/timeRange.test.ts Unit tests for expression parsing and trigger label formatting.
site/src/components/DateTimeRangeFilter/DateTimeRangeFilter.tsx New shared UI component: popover trigger, inputs, validation, normalization/clamping, and apply behavior.
site/src/components/DateTimeRangeFilter/DateTimeRangeFilter.stories.tsx Storybook stories and interaction tests validating the UX and state transitions.
Suppressed comments (2)

site/src/components/DateTimeRangeFilter/DateTimeRangeFilter.tsx:125

  • Empty input is treated as non-error (fromError/toError stay null), so the Apply button can become enabled even though parsedFrom/parsedTo are null. Clicking Apply then closes the popover without applying anything. Also, Apply is enabled based only on "touched" (not actual value changes), which contradicts the comment and allows no-op applies after reverting edits.
	const fromError =
		fromField.text !== "" && parsedFrom === null ? INVALID_TIME_MESSAGE : null;
	const toError =
		toField.text !== "" && parsedTo === null ? INVALID_TIME_MESSAGE : null;

site/src/components/DateTimeRangeFilter/DateTimeRangeFilter.tsx:109

  • normalize() currently skips clamping/normalizing whenever the current text is "now", so an out-of-order "now" boundary (for example, To="now" when From is in the future, or From="now" when To is in the past) will not clamp against its sibling as described. This leaves the user stuck with a range error until they manually edit the field away from "now".
			setField((current) => {
				if (isNowExpression(current.text) || parsed === null) {
					return current;
				}

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread site/src/components/DateTimeRangeFilter/DateTimeRangeFilter.tsx
@matifali
matifali requested review from jakehwll and removed request for jakehwll August 18, 2026 11:16
@johnstcn
johnstcn requested a review from jeremyruppel August 18, 2026 15:40
johnstcn added a commit that referenced this pull request Aug 18, 2026
)

`stringifyFilter` in the shared `Filter` component only quoted values
containing spaces. Filter values like RFC 3339 timestamps
(`2026-08-16T20:42:00Z`) contain colons but no spaces, so when any
filter was edited and the whole query re-serialized, the timestamp went
out unquoted and the backend `searchTerms` parser rejected it (`Query
element ... can only contain 1 ':'`).

Quote values containing colons as well; they were never valid unquoted
because the backend parser already rejects them. Extract
`parseFilterQuery`/`stringifyFilter` into `filterQuery.ts` with unit
tests, including a round-trip of quoted timestamps.

Part of
[AIGOV-580](https://linear.app/codercom/issue/AIGOV-580/ai-gateway-sessions-page-takes-5-10-seconds-to-load)

---
_Generated by Coder Agents on behalf of @johnstcn._$

---

**Stack:** #28254 (filterQuery fix) \u2192 #28255 (component) \u2192
#28256 (sessions page)
Base automatically changed from cian/filter-colon-quoting to main August 18, 2026 16:01
Comment thread site/src/components/DateTimeRangeFilter/DateTimeRangeFilter.tsx
Comment thread site/src/components/DateTimeRangeFilter/DateTimeRangeFilter.tsx Outdated
Comment thread site/src/components/DateTimeRangeFilter/DateTimeRangeFilter.tsx Outdated
Comment thread site/src/components/DateTimeRangeFilter/DateTimeRangeFilter.tsx
Comment thread site/src/components/DateTimeRangeFilter/timeRange.ts Outdated
Split the shared blur normalizer into normalizeFrom and normalizeTo,
each reading its own parsed boundary from render scope. Source the
parse formats from the shared DATE_FORMAT constants and drop the
single-digit-hour variants so input is padded-only. Add a comment
pointing future maintainers at formik and yup if the control grows.
…lter

The component's isNowExpression duplicated the /^now$/i regex in
parseTimeExpression. Export one predicate from timeRange.ts and use
it in both the parser and the blur normalizers.

@jeremyruppel jeremyruppel left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

letsgoooooooooo 👍

@johnstcn
johnstcn merged commit da99941 into main Aug 19, 2026
26 checks passed
@johnstcn
johnstcn deleted the cian/datetime-range-filter-component branch August 19, 2026 12:47
@github-actions github-actions Bot locked and limited conversation to collaborators Aug 19, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants