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 (
Reusable simulation blueprints for red team operations.
| Name | MITRE | Created by | Updated | Actions |
|---|---|---|---|---|
| {t.name} | {mitreCount(t) === 0 ? '—' : mitreCount(t)} | {t.created_by.username} | {formatDate(t.updated_at)} |
Edit
|