import { useState, type ReactNode } from 'react'; import { Link } from 'react-router-dom'; import { useQuery } from '@tanstack/react-query'; import { Panel } from '@/components/ui/Panel'; import { Pill } from '@/components/ui/Pill'; import { Button } from '@/components/ui/Button'; import { ApiClientError } from '@/lib/api'; import type { Engagement, EngagementStatus } from '@/types/api'; import { ENGAGEMENTS_QUERY_KEY, fetchEngagements } from './engagementsApi'; import { EngagementCreateDialog } from './EngagementCreateDialog'; const STATUS_TONE: Record = { active: 'running', reporting: 'soc', archived: 'pending', planning: 'success', }; export function EngagementsPage() { const [createOpen, setCreateOpen] = useState(false); const query = useQuery({ queryKey: ENGAGEMENTS_QUERY_KEY, queryFn: ({ signal }) => fetchEngagements(signal), }); const engagements = query.data ?? []; return (
// Engagements

Mission roster

Each engagement is a multi-tenant container. Pick one to access its hosts, scenarios, runs, and reports.

{query.isLoading ? 'loading …' : query.isError ? 'error' : `${String(engagements.length)} entries`} } > {query.isLoading ? ( ) : query.isError ? ( void query.refetch()} /> ) : engagements.length === 0 ? ( setCreateOpen(true)} /> ) : ( )} {createOpen && setCreateOpen(false)} />}
); } function EngagementsTable({ engagements }: { engagements: Engagement[] }) { return ( {engagements.map((eng, idx) => ( ))}
Name Client Status C2 Window
{eng.name}
{eng.id}
{eng.client_name ?? } {eng.status} {eng.c2_type ? ( {eng.c2_type.toUpperCase()} ) : ( )} {eng.start_date || eng.end_date ? ( {eng.start_date ?? '—'} → {eng.end_date ?? '—'} ) : ( )}
); } function LoadingRow() { return (
fetching engagements …
); } function ErrorRow({ error, onRetry }: { error: unknown; onRetry: () => void }) { const message = error instanceof ApiClientError ? `HTTP ${String(error.status)} · ${error.message}` : 'Unable to reach the backend.'; return (
// Fetch failed
{message}
); } function EmptyRow({ onCreate }: { onCreate: () => void }) { return (
// No engagements yet

Create your first engagement to start composing scenarios and running them against client infrastructure.

); } function Th({ children, align = 'left' }: { children?: ReactNode; align?: 'left' | 'right' }) { return ( {children} ); } function Td({ children, align = 'left' }: { children?: ReactNode; align?: 'left' | 'right' }) { return ( {children} ); }