Incidents and Archive get a from/to date range (native date inputs, local-day bounds). Incidents filters in the Firestore query; Archive passes date_from/date_to to GET /calls/search, which applies them as a started_at range — both ride the existing org_id/started_at index. Also fixes /calls/search and /calls/eval-queue paging: the cursor went to Firestore as a raw ISO string against a timestamp field, which compares by type rather than time, so "Load more" re-read the first page. Cursor and range bounds are now parsed to datetimes (400 on garbage). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
58 lines
1.5 KiB
TypeScript
58 lines
1.5 KiB
TypeScript
"use client";
|
|
|
|
// A from/to pair of native date inputs. Values are the inputs' own
|
|
// "YYYY-MM-DD" strings; dayStart/dayEnd turn them into the local-midnight
|
|
// bounds a started_at range query needs, so "to" includes the whole day.
|
|
|
|
export function dayStart(ymd: string): Date | undefined {
|
|
if (!ymd) return undefined;
|
|
const [y, m, d] = ymd.split("-").map(Number);
|
|
return new Date(y, m - 1, d, 0, 0, 0, 0);
|
|
}
|
|
|
|
export function dayEnd(ymd: string): Date | undefined {
|
|
if (!ymd) return undefined;
|
|
const [y, m, d] = ymd.split("-").map(Number);
|
|
return new Date(y, m - 1, d, 23, 59, 59, 999);
|
|
}
|
|
|
|
const inputClass =
|
|
"bg-surface border border-line rounded-lg px-2 py-1.5 text-sm text-ink-2 focus:outline-none focus:border-accent";
|
|
|
|
export function DateRange({
|
|
from,
|
|
to,
|
|
onChange,
|
|
}: {
|
|
from: string;
|
|
to: string;
|
|
onChange: (from: string, to: string) => void;
|
|
}) {
|
|
return (
|
|
<div className="flex items-center gap-2 text-xs text-ink-muted">
|
|
<input
|
|
type="date"
|
|
aria-label="From date"
|
|
value={from}
|
|
max={to || undefined}
|
|
onChange={(e) => onChange(e.target.value, to)}
|
|
className={inputClass}
|
|
/>
|
|
<span>to</span>
|
|
<input
|
|
type="date"
|
|
aria-label="To date"
|
|
value={to}
|
|
min={from || undefined}
|
|
onChange={(e) => onChange(from, e.target.value)}
|
|
className={inputClass}
|
|
/>
|
|
{(from || to) && (
|
|
<button onClick={() => onChange("", "")} className="text-ink-muted hover:text-ink-2">
|
|
clear
|
|
</button>
|
|
)}
|
|
</div>
|
|
);
|
|
}
|