import { useQuery } from '@tanstack/react-query'; import { useMemo, useState } from 'react'; import { Link } from 'react-router-dom'; import { Alert } from '@/components/ui/Alert'; import { Button } from '@/components/ui/Button'; import { Card } from '@/components/ui/Card'; import { SectionHeader } from '@/components/ui/SectionHeader'; import { Tag } from '@/components/ui/Tag'; import { TextField } from '@/components/ui/TextField'; import { ApiError, apiGet } from '@/lib/api'; import { useAuth } from '@/lib/auth'; import { MISSION_STATUS_ACCENT, MISSION_STATUS_LABEL, buildMissionQueryString, missionKeys, type MissionFilters, type MissionListResponse, type MissionStatus, } from '@/lib/missions'; const STATUS_OPTIONS: Array<{ value: '' | MissionStatus; label: string }> = [ { value: '', label: 'All statuses' }, { value: 'draft', label: MISSION_STATUS_LABEL.draft }, { value: 'in_progress', label: MISSION_STATUS_LABEL.in_progress }, { value: 'completed', label: MISSION_STATUS_LABEL.completed }, { value: 'archived', label: MISSION_STATUS_LABEL.archived }, ]; function useMissions(filters: MissionFilters) { return useQuery({ queryKey: missionKeys.list(filters), queryFn: () => apiGet(`/missions${buildMissionQueryString(filters)}`), }); } function formatDateRange(start: string | null, end: string | null): string { if (!start && !end) return '—'; if (start && end) return `${start} → ${end}`; return start ?? end ?? '—'; } export function MissionsListPage() { const { state } = useAuth(); const canCreate = state.user?.is_admin || state.user?.permissions.includes('mission.create'); const [q, setQ] = useState(''); const [status, setStatus] = useState<'' | MissionStatus>(''); const [client, setClient] = useState(''); const filters = useMemo( () => ({ q: q.trim() || undefined, status: status || undefined, client: client.trim() || undefined, }), [q, status, client], ); const { data, error, isLoading } = useMissions(filters); const apiErr = error instanceof ApiError ? error : null; return (
{canCreate && ( )}
setQ(e.target.value)} data-testid="missions-filter-q" /> setClient(e.target.value)} data-testid="missions-filter-client" />
{apiErr && (
{apiErr.message}
)} {isLoading && (

Loading missions…

)} {data && data.items.length === 0 && !isLoading && (

No missions match the filters. {canCreate ? 'Create one to get started.' : ''}

)}
{data?.items.map((m) => { const accent = MISSION_STATUS_ACCENT[m.status]; return (
{MISSION_STATUS_LABEL[m.status]} {m.scenarios_count} scenarios {m.tests_count} tests {m.members_count} members

{formatDateRange(m.date_start, m.date_end)}

); })}
{data && (

{data.total} mission{data.total === 1 ? '' : 's'} total

)}
); }