import { useState, useCallback } from 'react'; import { Slider } from '../ui/Slider'; import { PillToggle } from '../ui/PillToggle'; import { PillGroup } from '../ui/PillGroup'; import { IconButton } from '../ui/IconButton'; import { CloseIcon } from '../ui/icons/CloseIcon'; import { MapPinIcon } from '../ui/icons/MapPinIcon'; import { RouteIcon } from '../ui/icons/RouteIcon'; import { formatFilterValue } from '../../lib/format'; import { authHeaders } from '../../lib/api'; import type { TransportMode } from '../../hooks/useTravelTime'; const MODES: { value: TransportMode; label: string }[] = [ { value: 'transit', label: 'Transit' }, { value: 'car', label: 'Car' }, { value: 'bicycle', label: 'Bicycle' }, ]; interface TravelTimeCardProps { destination: [number, number] | null; destinationLabel: string; mode: TransportMode; timeRange: [number, number] | null; dataRange: [number, number] | null; onSetDestination: (lat: number, lon: number, label: string) => void; onModeChange: (mode: TransportMode) => void; onTimeRangeChange: (range: [number, number]) => void; onRemove: () => void; } export function TravelTimeCard({ destination, destinationLabel, mode, timeRange, dataRange, onSetDestination, onModeChange, onTimeRangeChange, onRemove, }: TravelTimeCardProps) { const [query, setQuery] = useState(''); const [loading, setLoading] = useState(false); const [error, setError] = useState(null); const handleSearch = useCallback( async (e: React.FormEvent) => { e.preventDefault(); const trimmed = query.trim(); if (!trimmed) return; setError(null); setLoading(true); try { const res = await fetch( `/api/postcode/${encodeURIComponent(trimmed)}`, authHeaders() ); if (!res.ok) { setError('Postcode not found'); return; } const json: { postcode: string; latitude: number; longitude: number } = await res.json(); onSetDestination(json.latitude, json.longitude, json.postcode); setQuery(''); } catch { setError('Lookup failed'); } finally { setLoading(false); } }, [query, onSetDestination] ); const sliderMin = dataRange ? Math.floor(dataRange[0]) : 0; const sliderMax = dataRange ? Math.ceil(dataRange[1]) : 120; const displayRange = timeRange ?? [sliderMin, sliderMax]; return (
{/* Header */}
Travel Time
onRemove()} title="Remove travel time">
{/* Destination search */}
{ setQuery(e.target.value); setError(null); }} placeholder={destination ? 'Change destination...' : 'Enter postcode...'} className="flex-1 min-w-0 px-2 py-1 text-xs rounded border border-warm-200 dark:border-warm-600 bg-white dark:bg-warm-800 text-navy-950 dark:text-warm-200 placeholder-warm-400 dark:placeholder-warm-500 outline-none focus:ring-1 focus:ring-teal-400" />
{error && (

{error}

)} {destination && destinationLabel && (
{destinationLabel}
)}
{/* Mode selector */}
Mode {MODES.map((m) => ( onModeChange(m.value)} size="xs" /> ))}
{/* Time range slider — only show when we have data */} {destination && dataRange && (
Max time onTimeRangeChange([min, max])} />
{formatFilterValue(displayRange[0])} min {formatFilterValue(displayRange[1])} min
)}
); }