"use client"; import * as React from "react"; import { CalendarRange, X } from "lucide-react"; import { Segmented, ChipRow } from "@/components/ui/segmented"; import { ResponsiveDialog } from "@/components/ui/sheet"; import { Button } from "@/components/ui/button"; import { Input, Field } from "@/components/ui/input"; import { ProviderIcon } from "@/components/brand/provider-icon"; import { providerName } from "@/lib/client/providers"; import { cn } from "@/lib/utils"; import type { ProviderId } from "@/lib/client/types"; import type { UsageRangeKey } from "./types"; export interface UsageQueryState { range: UsageRangeKey; from: string; // YYYY-MM-DD when range === "custom" to: string; provider: ProviderId | ""; modelKey: string; projectId: string; } export const DEFAULT_QUERY: UsageQueryState = { range: "30d", from: "", to: "", provider: "", modelKey: "", projectId: "" }; const RANGE_OPTIONS: { value: UsageRangeKey; label: string; short: string }[] = [ { value: "today", label: "Today", short: "Today" }, { value: "7d", label: "7 days", short: "7d" }, { value: "30d", label: "30 days", short: "30d" }, { value: "90d", label: "90 days", short: "90d" }, { value: "custom", label: "Custom", short: "Custom" }, ]; export function rangeLabel(q: UsageQueryState): string { if (q.range === "custom" && q.from && q.to) return `${fmt(q.from)} – ${fmt(q.to)}`; if (q.range === "all") return "All time"; return RANGE_OPTIONS.find((r) => r.value === q.range)?.label ?? "30 days"; } function fmt(ymd: string): string { const m = /^(\d{4})-(\d{2})-(\d{2})$/.exec(ymd); if (!m) return ymd; return new Date(Number(m[1]), Number(m[2]) - 1, Number(m[3])).toLocaleDateString("en-US", { month: "short", day: "numeric" }); } function today(): string { const d = new Date(); return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, "0")}-${String(d.getDate()).padStart(2, "0")}`; } /** Range segmented control + custom-dates sheet. One filter row scopes every chart below it. */ export function RangePicker({ value, onChange, compact }: { value: UsageQueryState; onChange: (patch: Partial) => void; compact?: boolean }) { const [open, setOpen] = React.useState(false); const [from, setFrom] = React.useState(value.from); const [to, setTo] = React.useState(value.to); const max = today(); const valid = /^\d{4}-\d{2}-\d{2}$/.test(from) && /^\d{4}-\d{2}-\d{2}$/.test(to) && from <= to; const openCustom = () => { setFrom(value.from || defaultFrom()); setTo(value.to || max); setOpen(true); }; const apply = () => { if (!valid) return; onChange({ range: "custom", from, to }); setOpen(false); }; return ( <> ariaLabel="Time range" size={compact ? "sm" : "md"} value={value.range} onChange={(v) => (v === "custom" ? openCustom() : onChange({ range: v, from: "", to: "" }))} options={RANGE_OPTIONS.map((r) => ({ value: r.value, label: r.value === "custom" && value.range === "custom" && value.from ? rangeLabel(value) : compact ? r.short : r.label, icon: r.value === "custom" ? : undefined }))} /> } >
setFrom(e.target.value)} className="h-11 text-[16px] md:h-9" /> to ? "End before start." : null}> setTo(e.target.value)} className="h-11 text-[16px] md:h-9" />
{[ { label: "This month", get: () => [max.slice(0, 8) + "01", max] }, { label: "Last month", get: () => lastMonth() }, { label: "Last 6 months", get: () => [shiftDays(-182), max] }, { label: "This year", get: () => [`${max.slice(0, 4)}-01-01`, max] }, ].map((p) => ( ))}
); } function defaultFrom(): string { return shiftDays(-29); } function shiftDays(n: number): string { const d = new Date(); d.setDate(d.getDate() + n); return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, "0")}-${String(d.getDate()).padStart(2, "0")}`; } function lastMonth(): [string, string] { const d = new Date(); const first = new Date(d.getFullYear(), d.getMonth() - 1, 1); const last = new Date(d.getFullYear(), d.getMonth(), 0); const f = (x: Date) => `${x.getFullYear()}-${String(x.getMonth() + 1).padStart(2, "0")}-${String(x.getDate()).padStart(2, "0")}`; return [f(first), f(last)]; } export interface FacetData { providers: ProviderId[]; models: { modelKey: string; provider: ProviderId; requests: number }[]; projects: { id: string; name: string; icon: string | null; color: string | null }[]; } /** Provider / model / project chips. Chips scroll horizontally on phones; "All" clears each dimension. */ export function FilterChips({ value, onChange, facets, modelLabel }: { value: UsageQueryState; onChange: (patch: Partial) => void; facets: FacetData | null; modelLabel: (key: string) => string }) { const providers = facets?.providers ?? []; const models = (facets?.models ?? []).filter((m) => !value.provider || m.provider === value.provider).slice(0, 10); const projects = facets?.projects ?? []; const showProviders = providers.length > 1 || value.provider; const showModels = models.length > 1 || value.modelKey; const showProjects = projects.length > 0; if (!showProviders && !showModels && !showProjects) return null; return (
{showProviders ? ( value={value.provider || "all"} onChange={(v) => onChange({ provider: v === "all" ? "" : (v as ProviderId), modelKey: "" })} options={[{ value: "all", label: "All providers" }, ...providers.map((p) => ({ value: p, label: providerName(p), icon: }))]} className="-mx-4 px-4 md:mx-0 md:px-0" /> ) : null} {showModels ? ( value={value.modelKey || "all"} onChange={(v) => onChange({ modelKey: v === "all" ? "" : v })} options={[{ value: "all", label: "All models" }, ...models.map((m) => ({ value: m.modelKey, label: modelLabel(m.modelKey), icon: , count: m.requests }))]} className="-mx-4 px-4 md:mx-0 md:px-0" /> ) : null} {showProjects ? ( value={value.projectId || "all"} onChange={(v) => onChange({ projectId: v === "all" ? "" : v })} options={[{ value: "all", label: "All projects" }, ...projects.map((p) => ({ value: p.id, label: p.name, icon: p.icon && /^\p{Extended_Pictographic}/u.test(p.icon) ? {p.icon} : }))]} className="-mx-4 px-4 md:mx-0 md:px-0" /> ) : null}
); } /** Summary of active filters with one-tap clear (shown above the KPIs when anything is narrowed). */ export function ActiveFilters({ value, onChange, modelLabel, projects }: { value: UsageQueryState; onChange: (patch: Partial) => void; modelLabel: (key: string) => string; projects: FacetData["projects"] }) { const items: { key: keyof UsageQueryState; label: string }[] = []; if (value.provider) items.push({ key: "provider", label: providerName(value.provider) }); if (value.modelKey) items.push({ key: "modelKey", label: modelLabel(value.modelKey) }); if (value.projectId) items.push({ key: "projectId", label: projects.find((p) => p.id === value.projectId)?.name ?? "Project" }); if (!items.length) return null; return (
Filtered by {items.map((i) => ( ))}
); }