import { Link } from 'react-router-dom'; import { Plus } from 'lucide-react'; import { extractApiError } from '@/api/client'; import type { SimulationTemplate } from '@/api/types'; import { useDeleteTemplate, useTemplates } from '@/hooks/useTemplates'; import { useToast } from '@/hooks/useToast'; import { LoadingState } from '@/components/LoadingState'; import { ErrorState } from '@/components/ErrorState'; import { EmptyState } from '@/components/EmptyState'; function mitreCount(t: SimulationTemplate): number { return t.techniques.length + t.tactics.length; } function formatDate(value: string | null): string { if (!value) return '—'; return value.slice(0, 10); } export function TemplatesListPage(): JSX.Element { const { data, isLoading, isError, error, refetch } = useTemplates(); const deleteMutation = useDeleteTemplate(); const { push } = useToast(); const onDelete = async (t: SimulationTemplate) => { if (!window.confirm(`Delete template "${t.name}"? This cannot be undone.`)) return; try { await deleteMutation.mutateAsync(t.id); push('Template deleted', 'success'); } catch (err) { push(extractApiError(err, 'Could not delete template'), 'error'); } }; return (

Templates

Reusable simulation blueprints for red team operations.

New
{isLoading ? : null} {isError ? ( refetch()} /> ) : null} {!isLoading && !isError && data && data.length === 0 ? ( New template } /> ) : null} {!isLoading && !isError && data && data.length > 0 ? (
{data.map((t) => ( ))}
Name MITRE Created by Updated Actions
{t.name} {mitreCount(t) === 0 ? '—' : mitreCount(t)} {t.created_by.username} {formatDate(t.updated_at)}
Edit
) : null}
); }