import { useEffect, useMemo, useState } from 'react'; import PageHeader from '../../components/PageHeader'; import TicketTable from '../../components/TicketTable'; import TicketDetailModal from '../../components/TicketDetailModal'; import { getTickets } from '../../services/ticket.service'; import { formatCost, formatMinutes } from '../../utils/format.utils'; import type { Ticket } from '../../types/ticket.types'; import styles from './Closed.module.scss'; export default function Closed({ search, refreshKey }: { search: string; refreshKey: number }) { const [tickets, setTickets] = useState([]); const [loading, setLoading] = useState(true); const [error, setError] = useState(false); const [selected, setSelected] = useState(null); const [brandFilter, setBrandFilter] = useState(''); useEffect(() => { let alive = true; setLoading(true); setError(false); getTickets({ status: 'closed', q: search || undefined }) .then(t => { if (alive) setTickets(t); }) .catch(() => { if (alive) setError(true); }) .finally(() => { if (alive) setLoading(false); }); return () => { alive = false; }; }, [search, refreshKey]); const brands = useMemo( () => [...new Set(tickets.map(t => t.brand).filter((b): b is string => !!b))].sort(), [tickets], ); const filtered = useMemo( () => tickets.filter(t => !brandFilter || t.brand === brandFilter), [tickets, brandFilter], ); const totalCost = useMemo( () => filtered.reduce((sum, t) => sum + (t.finalCost ?? 0), 0), [filtered], ); const avgTtfr = useMemo(() => { const vals = filtered.map(t => t.ttfrMinutes).filter((v): v is number => v != null); return vals.length ? Math.round(vals.reduce((a, b) => a + b, 0) / vals.length) : null; }, [filtered]); return (
{!loading && !error && filtered.length > 0 && (
)} {loading &&
Loading tickets…
} {error &&
Failed to load tickets. Is the server running?
} {!loading && !error && filtered.length === 0 &&
No closed tickets found.
} {!loading && !error && filtered.length > 0 && ( )} {selected && setSelected(null)} />}
); } function Stat({ label, value }: { label: string; value: string }) { return (
{value}
{label}
); }