deckgl_tracksolid/web/src/components/Sidebar/DateControls.tsx
David Kiania 47c86c9d7a Add deckgl trips visualization stack
Initial implementation of the public trips dashboard:

- db/migrations/001..005: read-only viz_anon role + thin trips_viz_v1
  view + three SECURITY DEFINER RPCs (trips_for_day, trips_for_range,
  list_cost_centres). Builds path on demand from position_history;
  coalesces missing cost_centre to 'Unassigned'. Smoke-tested against
  staging: 982 trips / 13 cost centres for 2026-04-29.

- compose/: PostgREST v12 service + trips_web Caddy service. CORS
  allow-listed to the web FQDN; viz_anon role is the only authorization.

- web/: Vite + React + TS SPA. deck.gl TripsLayer animated over
  PathLayer (whole route in low opacity), Mapbox GL dark base map,
  Zustand store, TanStack Query for fetching. Sidebar = date controls
  + cost-centre multi-select + vehicle drilldown. Timebar = scrubber
  with 1x/10x/60x/600x speeds. tsc + vite build clean.

- README + design doc updated to match the verified schema (path lives
  in tracksolid.position_history, vehicle key is imei, no down-sampling
  needed at observed volume).
2026-05-01 01:13:57 +03:00

47 lines
1.5 KiB
TypeScript

import { useVizStore } from '../../store/useVizStore';
export function DateControls() {
const mode = useVizStore((s) => s.mode);
const setMode = useVizStore((s) => s.setMode);
const date = useVizStore((s) => s.date);
const setDate = useVizStore((s) => s.setDate);
const rangeStart = useVizStore((s) => s.rangeStart);
const rangeEnd = useVizStore((s) => s.rangeEnd);
const setRange = useVizStore((s) => s.setRange);
return (
<div className="section">
<h2>Time window</h2>
<div className="toggle">
<button className={mode === 'day' ? 'active' : ''} onClick={() => setMode('day')}>
Single day
</button>
<button className={mode === 'range' ? 'active' : ''} onClick={() => setMode('range')}>
Range
</button>
</div>
{mode === 'day' ? (
<div className="input-row">
<input type="date" value={date} onChange={(e) => setDate(e.target.value)} />
</div>
) : (
<>
<div className="input-row">
<input
type="date"
value={rangeStart}
onChange={(e) => setRange(e.target.value, rangeEnd)}
/>
<span></span>
<input
type="date"
value={rangeEnd}
onChange={(e) => setRange(rangeStart, e.target.value)}
/>
</div>
<p className="legend-note">Server caps range at 14 days.</p>
</>
)}
</div>
);
}