2026-05-23 04:26:48 +02:00
|
|
|
import { useState, type ReactNode } from 'react';
|
feat(frontend): wireframes for 5 MVP screens + audit (F0.3)
Mock-data wireframes covering spec §5 / §9 surface area. All read from
src/mocks/fixtures.ts — no backend wiring yet. Each screen is built from
the design-system primitives (Panel, Pill, Button, label-system, status-dot)
and adheres to the instrumentation-grade visual grammar.
Screens:
- /login LoginPage — RT vs SOC mode switch (segmented), role-tinted.
RT form picks rt_operator / rt_lead at sign-in (mock only).
SOC form takes a session token (out-of-band, D-006).
Left rail carries mission brief + platform telemetry.
- /engagements EngagementsPage — mission roster table (codename, client,
status, c2_type, operators, SOC count, window).
- /runs LiveCockpitPage — the cornerstone screen. 3-column layout:
steps timeline | step detail (resolved command,
output, evidence, detection) | side rail
(DetectionPanel for SOC; EvidencePanel +
DetectionPanel readonly + CleanupPanel for RT).
Control bar (F6 pause/skip/retry/abort) is lead-RT-only.
Stats header: steps done, detected/partial/missed counts.
- /scenarios ScenarioComposerPage — 3-column composer:
filterable TTP library | ordered steps with delays
| inspector (params from params_schema_json, target
host list, jinja2 cleanup template preview).
c2_type locked at scenario level (D-F3 / H33).
- /library TtpLibraryPage — catalog table with stealth-variant
flagging, source provenance (custom/atr/mission),
payload_type chip, tags. Import journal / ATR buttons.
- /reports ReportPage — restricted MITRE matrix (techniques
played only, H29), narration timeline, integrity
hash footer (SHA-256, H19/H24/F9). PDF/JSON/MD
export buttons.
- /audit AuditPage — append-only journal viewer (lead RT only,
F13). Tabular timestamp/actor/role/action/resource.
UX guardrails baked in:
- SOC analysts never see RT-only controls (conditional rendering, not just
disabled state). UI layer mirrors backend RBAC but does not replace it.
- Layout density and dark-first palette tuned for long purple sessions
(sober contrast, no flash, status colors carry information without
being shouted).
- Live cockpit reserves a clear visual slot for cleanup-failed alerts
(R-T5) — currently a Pill, real alert UX lands when the WebSocket is
wired in sprint 1+.
2026-05-21 20:31:24 +02:00
|
|
|
import { Link } from 'react-router-dom';
|
2026-05-23 04:26:48 +02:00
|
|
|
import { useQuery } from '@tanstack/react-query';
|
feat(frontend): wireframes for 5 MVP screens + audit (F0.3)
Mock-data wireframes covering spec §5 / §9 surface area. All read from
src/mocks/fixtures.ts — no backend wiring yet. Each screen is built from
the design-system primitives (Panel, Pill, Button, label-system, status-dot)
and adheres to the instrumentation-grade visual grammar.
Screens:
- /login LoginPage — RT vs SOC mode switch (segmented), role-tinted.
RT form picks rt_operator / rt_lead at sign-in (mock only).
SOC form takes a session token (out-of-band, D-006).
Left rail carries mission brief + platform telemetry.
- /engagements EngagementsPage — mission roster table (codename, client,
status, c2_type, operators, SOC count, window).
- /runs LiveCockpitPage — the cornerstone screen. 3-column layout:
steps timeline | step detail (resolved command,
output, evidence, detection) | side rail
(DetectionPanel for SOC; EvidencePanel +
DetectionPanel readonly + CleanupPanel for RT).
Control bar (F6 pause/skip/retry/abort) is lead-RT-only.
Stats header: steps done, detected/partial/missed counts.
- /scenarios ScenarioComposerPage — 3-column composer:
filterable TTP library | ordered steps with delays
| inspector (params from params_schema_json, target
host list, jinja2 cleanup template preview).
c2_type locked at scenario level (D-F3 / H33).
- /library TtpLibraryPage — catalog table with stealth-variant
flagging, source provenance (custom/atr/mission),
payload_type chip, tags. Import journal / ATR buttons.
- /reports ReportPage — restricted MITRE matrix (techniques
played only, H29), narration timeline, integrity
hash footer (SHA-256, H19/H24/F9). PDF/JSON/MD
export buttons.
- /audit AuditPage — append-only journal viewer (lead RT only,
F13). Tabular timestamp/actor/role/action/resource.
UX guardrails baked in:
- SOC analysts never see RT-only controls (conditional rendering, not just
disabled state). UI layer mirrors backend RBAC but does not replace it.
- Layout density and dark-first palette tuned for long purple sessions
(sober contrast, no flash, status colors carry information without
being shouted).
- Live cockpit reserves a clear visual slot for cleanup-failed alerts
(R-T5) — currently a Pill, real alert UX lands when the WebSocket is
wired in sprint 1+.
2026-05-21 20:31:24 +02:00
|
|
|
import { Panel } from '@/components/ui/Panel';
|
|
|
|
|
import { Pill } from '@/components/ui/Pill';
|
|
|
|
|
import { Button } from '@/components/ui/Button';
|
2026-05-23 04:26:48 +02:00
|
|
|
import { ApiClientError } from '@/lib/api';
|
|
|
|
|
import type { Engagement, EngagementStatus } from '@/types/api';
|
|
|
|
|
import { ENGAGEMENTS_QUERY_KEY, fetchEngagements } from './engagementsApi';
|
|
|
|
|
import { EngagementCreateDialog } from './EngagementCreateDialog';
|
feat(frontend): wireframes for 5 MVP screens + audit (F0.3)
Mock-data wireframes covering spec §5 / §9 surface area. All read from
src/mocks/fixtures.ts — no backend wiring yet. Each screen is built from
the design-system primitives (Panel, Pill, Button, label-system, status-dot)
and adheres to the instrumentation-grade visual grammar.
Screens:
- /login LoginPage — RT vs SOC mode switch (segmented), role-tinted.
RT form picks rt_operator / rt_lead at sign-in (mock only).
SOC form takes a session token (out-of-band, D-006).
Left rail carries mission brief + platform telemetry.
- /engagements EngagementsPage — mission roster table (codename, client,
status, c2_type, operators, SOC count, window).
- /runs LiveCockpitPage — the cornerstone screen. 3-column layout:
steps timeline | step detail (resolved command,
output, evidence, detection) | side rail
(DetectionPanel for SOC; EvidencePanel +
DetectionPanel readonly + CleanupPanel for RT).
Control bar (F6 pause/skip/retry/abort) is lead-RT-only.
Stats header: steps done, detected/partial/missed counts.
- /scenarios ScenarioComposerPage — 3-column composer:
filterable TTP library | ordered steps with delays
| inspector (params from params_schema_json, target
host list, jinja2 cleanup template preview).
c2_type locked at scenario level (D-F3 / H33).
- /library TtpLibraryPage — catalog table with stealth-variant
flagging, source provenance (custom/atr/mission),
payload_type chip, tags. Import journal / ATR buttons.
- /reports ReportPage — restricted MITRE matrix (techniques
played only, H29), narration timeline, integrity
hash footer (SHA-256, H19/H24/F9). PDF/JSON/MD
export buttons.
- /audit AuditPage — append-only journal viewer (lead RT only,
F13). Tabular timestamp/actor/role/action/resource.
UX guardrails baked in:
- SOC analysts never see RT-only controls (conditional rendering, not just
disabled state). UI layer mirrors backend RBAC but does not replace it.
- Layout density and dark-first palette tuned for long purple sessions
(sober contrast, no flash, status colors carry information without
being shouted).
- Live cockpit reserves a clear visual slot for cleanup-failed alerts
(R-T5) — currently a Pill, real alert UX lands when the WebSocket is
wired in sprint 1+.
2026-05-21 20:31:24 +02:00
|
|
|
|
2026-05-23 04:26:48 +02:00
|
|
|
const STATUS_TONE: Record<EngagementStatus, 'running' | 'soc' | 'success' | 'pending'> = {
|
feat(frontend): wireframes for 5 MVP screens + audit (F0.3)
Mock-data wireframes covering spec §5 / §9 surface area. All read from
src/mocks/fixtures.ts — no backend wiring yet. Each screen is built from
the design-system primitives (Panel, Pill, Button, label-system, status-dot)
and adheres to the instrumentation-grade visual grammar.
Screens:
- /login LoginPage — RT vs SOC mode switch (segmented), role-tinted.
RT form picks rt_operator / rt_lead at sign-in (mock only).
SOC form takes a session token (out-of-band, D-006).
Left rail carries mission brief + platform telemetry.
- /engagements EngagementsPage — mission roster table (codename, client,
status, c2_type, operators, SOC count, window).
- /runs LiveCockpitPage — the cornerstone screen. 3-column layout:
steps timeline | step detail (resolved command,
output, evidence, detection) | side rail
(DetectionPanel for SOC; EvidencePanel +
DetectionPanel readonly + CleanupPanel for RT).
Control bar (F6 pause/skip/retry/abort) is lead-RT-only.
Stats header: steps done, detected/partial/missed counts.
- /scenarios ScenarioComposerPage — 3-column composer:
filterable TTP library | ordered steps with delays
| inspector (params from params_schema_json, target
host list, jinja2 cleanup template preview).
c2_type locked at scenario level (D-F3 / H33).
- /library TtpLibraryPage — catalog table with stealth-variant
flagging, source provenance (custom/atr/mission),
payload_type chip, tags. Import journal / ATR buttons.
- /reports ReportPage — restricted MITRE matrix (techniques
played only, H29), narration timeline, integrity
hash footer (SHA-256, H19/H24/F9). PDF/JSON/MD
export buttons.
- /audit AuditPage — append-only journal viewer (lead RT only,
F13). Tabular timestamp/actor/role/action/resource.
UX guardrails baked in:
- SOC analysts never see RT-only controls (conditional rendering, not just
disabled state). UI layer mirrors backend RBAC but does not replace it.
- Layout density and dark-first palette tuned for long purple sessions
(sober contrast, no flash, status colors carry information without
being shouted).
- Live cockpit reserves a clear visual slot for cleanup-failed alerts
(R-T5) — currently a Pill, real alert UX lands when the WebSocket is
wired in sprint 1+.
2026-05-21 20:31:24 +02:00
|
|
|
active: 'running',
|
|
|
|
|
reporting: 'soc',
|
|
|
|
|
archived: 'pending',
|
|
|
|
|
planning: 'success',
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
export function EngagementsPage() {
|
2026-05-23 04:26:48 +02:00
|
|
|
const [createOpen, setCreateOpen] = useState(false);
|
|
|
|
|
|
|
|
|
|
const query = useQuery<Engagement[]>({
|
|
|
|
|
queryKey: ENGAGEMENTS_QUERY_KEY,
|
|
|
|
|
queryFn: ({ signal }) => fetchEngagements(signal),
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
const engagements = query.data ?? [];
|
|
|
|
|
|
feat(frontend): wireframes for 5 MVP screens + audit (F0.3)
Mock-data wireframes covering spec §5 / §9 surface area. All read from
src/mocks/fixtures.ts — no backend wiring yet. Each screen is built from
the design-system primitives (Panel, Pill, Button, label-system, status-dot)
and adheres to the instrumentation-grade visual grammar.
Screens:
- /login LoginPage — RT vs SOC mode switch (segmented), role-tinted.
RT form picks rt_operator / rt_lead at sign-in (mock only).
SOC form takes a session token (out-of-band, D-006).
Left rail carries mission brief + platform telemetry.
- /engagements EngagementsPage — mission roster table (codename, client,
status, c2_type, operators, SOC count, window).
- /runs LiveCockpitPage — the cornerstone screen. 3-column layout:
steps timeline | step detail (resolved command,
output, evidence, detection) | side rail
(DetectionPanel for SOC; EvidencePanel +
DetectionPanel readonly + CleanupPanel for RT).
Control bar (F6 pause/skip/retry/abort) is lead-RT-only.
Stats header: steps done, detected/partial/missed counts.
- /scenarios ScenarioComposerPage — 3-column composer:
filterable TTP library | ordered steps with delays
| inspector (params from params_schema_json, target
host list, jinja2 cleanup template preview).
c2_type locked at scenario level (D-F3 / H33).
- /library TtpLibraryPage — catalog table with stealth-variant
flagging, source provenance (custom/atr/mission),
payload_type chip, tags. Import journal / ATR buttons.
- /reports ReportPage — restricted MITRE matrix (techniques
played only, H29), narration timeline, integrity
hash footer (SHA-256, H19/H24/F9). PDF/JSON/MD
export buttons.
- /audit AuditPage — append-only journal viewer (lead RT only,
F13). Tabular timestamp/actor/role/action/resource.
UX guardrails baked in:
- SOC analysts never see RT-only controls (conditional rendering, not just
disabled state). UI layer mirrors backend RBAC but does not replace it.
- Layout density and dark-first palette tuned for long purple sessions
(sober contrast, no flash, status colors carry information without
being shouted).
- Live cockpit reserves a clear visual slot for cleanup-failed alerts
(R-T5) — currently a Pill, real alert UX lands when the WebSocket is
wired in sprint 1+.
2026-05-21 20:31:24 +02:00
|
|
|
return (
|
|
|
|
|
<div className="px-8 py-6 space-y-6 max-w-[1400px] mx-auto">
|
|
|
|
|
<header className="flex items-end justify-between">
|
|
|
|
|
<div>
|
|
|
|
|
<div className="label-system mb-1">// Engagements</div>
|
2026-05-23 04:26:48 +02:00
|
|
|
<h1
|
|
|
|
|
className="font-display text-fg-default"
|
|
|
|
|
style={{ fontSize: '22px', letterSpacing: '0.02em' }}
|
|
|
|
|
>
|
feat(frontend): wireframes for 5 MVP screens + audit (F0.3)
Mock-data wireframes covering spec §5 / §9 surface area. All read from
src/mocks/fixtures.ts — no backend wiring yet. Each screen is built from
the design-system primitives (Panel, Pill, Button, label-system, status-dot)
and adheres to the instrumentation-grade visual grammar.
Screens:
- /login LoginPage — RT vs SOC mode switch (segmented), role-tinted.
RT form picks rt_operator / rt_lead at sign-in (mock only).
SOC form takes a session token (out-of-band, D-006).
Left rail carries mission brief + platform telemetry.
- /engagements EngagementsPage — mission roster table (codename, client,
status, c2_type, operators, SOC count, window).
- /runs LiveCockpitPage — the cornerstone screen. 3-column layout:
steps timeline | step detail (resolved command,
output, evidence, detection) | side rail
(DetectionPanel for SOC; EvidencePanel +
DetectionPanel readonly + CleanupPanel for RT).
Control bar (F6 pause/skip/retry/abort) is lead-RT-only.
Stats header: steps done, detected/partial/missed counts.
- /scenarios ScenarioComposerPage — 3-column composer:
filterable TTP library | ordered steps with delays
| inspector (params from params_schema_json, target
host list, jinja2 cleanup template preview).
c2_type locked at scenario level (D-F3 / H33).
- /library TtpLibraryPage — catalog table with stealth-variant
flagging, source provenance (custom/atr/mission),
payload_type chip, tags. Import journal / ATR buttons.
- /reports ReportPage — restricted MITRE matrix (techniques
played only, H29), narration timeline, integrity
hash footer (SHA-256, H19/H24/F9). PDF/JSON/MD
export buttons.
- /audit AuditPage — append-only journal viewer (lead RT only,
F13). Tabular timestamp/actor/role/action/resource.
UX guardrails baked in:
- SOC analysts never see RT-only controls (conditional rendering, not just
disabled state). UI layer mirrors backend RBAC but does not replace it.
- Layout density and dark-first palette tuned for long purple sessions
(sober contrast, no flash, status colors carry information without
being shouted).
- Live cockpit reserves a clear visual slot for cleanup-failed alerts
(R-T5) — currently a Pill, real alert UX lands when the WebSocket is
wired in sprint 1+.
2026-05-21 20:31:24 +02:00
|
|
|
Mission roster
|
|
|
|
|
</h1>
|
|
|
|
|
<p className="text-fg-muted mt-1" style={{ fontSize: '12.5px' }}>
|
|
|
|
|
Each engagement is a multi-tenant container. Pick one to access its hosts, scenarios,
|
|
|
|
|
runs, and reports.
|
|
|
|
|
</p>
|
|
|
|
|
</div>
|
2026-05-23 04:26:48 +02:00
|
|
|
<Button variant="primary" onClick={() => setCreateOpen(true)}>
|
|
|
|
|
+ New engagement
|
|
|
|
|
</Button>
|
feat(frontend): wireframes for 5 MVP screens + audit (F0.3)
Mock-data wireframes covering spec §5 / §9 surface area. All read from
src/mocks/fixtures.ts — no backend wiring yet. Each screen is built from
the design-system primitives (Panel, Pill, Button, label-system, status-dot)
and adheres to the instrumentation-grade visual grammar.
Screens:
- /login LoginPage — RT vs SOC mode switch (segmented), role-tinted.
RT form picks rt_operator / rt_lead at sign-in (mock only).
SOC form takes a session token (out-of-band, D-006).
Left rail carries mission brief + platform telemetry.
- /engagements EngagementsPage — mission roster table (codename, client,
status, c2_type, operators, SOC count, window).
- /runs LiveCockpitPage — the cornerstone screen. 3-column layout:
steps timeline | step detail (resolved command,
output, evidence, detection) | side rail
(DetectionPanel for SOC; EvidencePanel +
DetectionPanel readonly + CleanupPanel for RT).
Control bar (F6 pause/skip/retry/abort) is lead-RT-only.
Stats header: steps done, detected/partial/missed counts.
- /scenarios ScenarioComposerPage — 3-column composer:
filterable TTP library | ordered steps with delays
| inspector (params from params_schema_json, target
host list, jinja2 cleanup template preview).
c2_type locked at scenario level (D-F3 / H33).
- /library TtpLibraryPage — catalog table with stealth-variant
flagging, source provenance (custom/atr/mission),
payload_type chip, tags. Import journal / ATR buttons.
- /reports ReportPage — restricted MITRE matrix (techniques
played only, H29), narration timeline, integrity
hash footer (SHA-256, H19/H24/F9). PDF/JSON/MD
export buttons.
- /audit AuditPage — append-only journal viewer (lead RT only,
F13). Tabular timestamp/actor/role/action/resource.
UX guardrails baked in:
- SOC analysts never see RT-only controls (conditional rendering, not just
disabled state). UI layer mirrors backend RBAC but does not replace it.
- Layout density and dark-first palette tuned for long purple sessions
(sober contrast, no flash, status colors carry information without
being shouted).
- Live cockpit reserves a clear visual slot for cleanup-failed alerts
(R-T5) — currently a Pill, real alert UX lands when the WebSocket is
wired in sprint 1+.
2026-05-21 20:31:24 +02:00
|
|
|
</header>
|
|
|
|
|
|
|
|
|
|
<Panel
|
|
|
|
|
title="Active and recent"
|
|
|
|
|
meta={
|
|
|
|
|
<span className="tabular">
|
2026-05-23 04:26:48 +02:00
|
|
|
{query.isLoading
|
|
|
|
|
? 'loading …'
|
|
|
|
|
: query.isError
|
|
|
|
|
? 'error'
|
|
|
|
|
: `${String(engagements.length)} entries`}
|
feat(frontend): wireframes for 5 MVP screens + audit (F0.3)
Mock-data wireframes covering spec §5 / §9 surface area. All read from
src/mocks/fixtures.ts — no backend wiring yet. Each screen is built from
the design-system primitives (Panel, Pill, Button, label-system, status-dot)
and adheres to the instrumentation-grade visual grammar.
Screens:
- /login LoginPage — RT vs SOC mode switch (segmented), role-tinted.
RT form picks rt_operator / rt_lead at sign-in (mock only).
SOC form takes a session token (out-of-band, D-006).
Left rail carries mission brief + platform telemetry.
- /engagements EngagementsPage — mission roster table (codename, client,
status, c2_type, operators, SOC count, window).
- /runs LiveCockpitPage — the cornerstone screen. 3-column layout:
steps timeline | step detail (resolved command,
output, evidence, detection) | side rail
(DetectionPanel for SOC; EvidencePanel +
DetectionPanel readonly + CleanupPanel for RT).
Control bar (F6 pause/skip/retry/abort) is lead-RT-only.
Stats header: steps done, detected/partial/missed counts.
- /scenarios ScenarioComposerPage — 3-column composer:
filterable TTP library | ordered steps with delays
| inspector (params from params_schema_json, target
host list, jinja2 cleanup template preview).
c2_type locked at scenario level (D-F3 / H33).
- /library TtpLibraryPage — catalog table with stealth-variant
flagging, source provenance (custom/atr/mission),
payload_type chip, tags. Import journal / ATR buttons.
- /reports ReportPage — restricted MITRE matrix (techniques
played only, H29), narration timeline, integrity
hash footer (SHA-256, H19/H24/F9). PDF/JSON/MD
export buttons.
- /audit AuditPage — append-only journal viewer (lead RT only,
F13). Tabular timestamp/actor/role/action/resource.
UX guardrails baked in:
- SOC analysts never see RT-only controls (conditional rendering, not just
disabled state). UI layer mirrors backend RBAC but does not replace it.
- Layout density and dark-first palette tuned for long purple sessions
(sober contrast, no flash, status colors carry information without
being shouted).
- Live cockpit reserves a clear visual slot for cleanup-failed alerts
(R-T5) — currently a Pill, real alert UX lands when the WebSocket is
wired in sprint 1+.
2026-05-21 20:31:24 +02:00
|
|
|
</span>
|
|
|
|
|
}
|
|
|
|
|
>
|
2026-05-23 04:26:48 +02:00
|
|
|
{query.isLoading ? (
|
|
|
|
|
<LoadingRow />
|
|
|
|
|
) : query.isError ? (
|
|
|
|
|
<ErrorRow error={query.error} onRetry={() => void query.refetch()} />
|
|
|
|
|
) : engagements.length === 0 ? (
|
|
|
|
|
<EmptyRow onCreate={() => setCreateOpen(true)} />
|
|
|
|
|
) : (
|
|
|
|
|
<EngagementsTable engagements={engagements} />
|
|
|
|
|
)}
|
feat(frontend): wireframes for 5 MVP screens + audit (F0.3)
Mock-data wireframes covering spec §5 / §9 surface area. All read from
src/mocks/fixtures.ts — no backend wiring yet. Each screen is built from
the design-system primitives (Panel, Pill, Button, label-system, status-dot)
and adheres to the instrumentation-grade visual grammar.
Screens:
- /login LoginPage — RT vs SOC mode switch (segmented), role-tinted.
RT form picks rt_operator / rt_lead at sign-in (mock only).
SOC form takes a session token (out-of-band, D-006).
Left rail carries mission brief + platform telemetry.
- /engagements EngagementsPage — mission roster table (codename, client,
status, c2_type, operators, SOC count, window).
- /runs LiveCockpitPage — the cornerstone screen. 3-column layout:
steps timeline | step detail (resolved command,
output, evidence, detection) | side rail
(DetectionPanel for SOC; EvidencePanel +
DetectionPanel readonly + CleanupPanel for RT).
Control bar (F6 pause/skip/retry/abort) is lead-RT-only.
Stats header: steps done, detected/partial/missed counts.
- /scenarios ScenarioComposerPage — 3-column composer:
filterable TTP library | ordered steps with delays
| inspector (params from params_schema_json, target
host list, jinja2 cleanup template preview).
c2_type locked at scenario level (D-F3 / H33).
- /library TtpLibraryPage — catalog table with stealth-variant
flagging, source provenance (custom/atr/mission),
payload_type chip, tags. Import journal / ATR buttons.
- /reports ReportPage — restricted MITRE matrix (techniques
played only, H29), narration timeline, integrity
hash footer (SHA-256, H19/H24/F9). PDF/JSON/MD
export buttons.
- /audit AuditPage — append-only journal viewer (lead RT only,
F13). Tabular timestamp/actor/role/action/resource.
UX guardrails baked in:
- SOC analysts never see RT-only controls (conditional rendering, not just
disabled state). UI layer mirrors backend RBAC but does not replace it.
- Layout density and dark-first palette tuned for long purple sessions
(sober contrast, no flash, status colors carry information without
being shouted).
- Live cockpit reserves a clear visual slot for cleanup-failed alerts
(R-T5) — currently a Pill, real alert UX lands when the WebSocket is
wired in sprint 1+.
2026-05-21 20:31:24 +02:00
|
|
|
</Panel>
|
2026-05-23 04:26:48 +02:00
|
|
|
|
|
|
|
|
{createOpen && <EngagementCreateDialog onClose={() => setCreateOpen(false)} />}
|
|
|
|
|
</div>
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function EngagementsTable({ engagements }: { engagements: Engagement[] }) {
|
|
|
|
|
return (
|
|
|
|
|
<table className="w-full" style={{ fontSize: 12.5 }}>
|
|
|
|
|
<thead>
|
|
|
|
|
<tr className="text-fg-subtle">
|
|
|
|
|
<Th>Name</Th>
|
|
|
|
|
<Th>Client</Th>
|
|
|
|
|
<Th>Status</Th>
|
|
|
|
|
<Th>C2</Th>
|
|
|
|
|
<Th>Window</Th>
|
|
|
|
|
<Th />
|
|
|
|
|
</tr>
|
|
|
|
|
</thead>
|
|
|
|
|
<tbody>
|
|
|
|
|
{engagements.map((eng, idx) => (
|
|
|
|
|
<tr
|
|
|
|
|
key={eng.id}
|
|
|
|
|
style={{
|
|
|
|
|
borderTop:
|
|
|
|
|
idx === 0 ? '1px solid var(--line-default)' : '1px solid var(--line-faint)',
|
|
|
|
|
}}
|
|
|
|
|
>
|
|
|
|
|
<Td>
|
|
|
|
|
<div
|
|
|
|
|
className="font-display text-fg-default"
|
|
|
|
|
style={{ letterSpacing: '0.06em' }}
|
|
|
|
|
>
|
|
|
|
|
{eng.name}
|
|
|
|
|
</div>
|
|
|
|
|
<div className="font-mono text-fg-faint" style={{ fontSize: '10.5px' }}>
|
|
|
|
|
{eng.id}
|
|
|
|
|
</div>
|
|
|
|
|
</Td>
|
|
|
|
|
<Td>{eng.client_name ?? <span className="text-fg-faint">—</span>}</Td>
|
|
|
|
|
<Td>
|
|
|
|
|
<Pill tone={STATUS_TONE[eng.status]}>
|
|
|
|
|
<span className="status-dot" style={{ color: 'currentColor' }} />
|
|
|
|
|
{eng.status}
|
|
|
|
|
</Pill>
|
|
|
|
|
</Td>
|
|
|
|
|
<Td>
|
|
|
|
|
{eng.c2_type ? (
|
|
|
|
|
<span className="font-mono tabular">{eng.c2_type.toUpperCase()}</span>
|
|
|
|
|
) : (
|
|
|
|
|
<span className="text-fg-faint">—</span>
|
|
|
|
|
)}
|
|
|
|
|
</Td>
|
|
|
|
|
<Td>
|
|
|
|
|
{eng.start_date || eng.end_date ? (
|
|
|
|
|
<span className="font-mono tabular text-fg-muted">
|
|
|
|
|
{eng.start_date ?? '—'} → {eng.end_date ?? '—'}
|
|
|
|
|
</span>
|
|
|
|
|
) : (
|
|
|
|
|
<span className="text-fg-faint">—</span>
|
|
|
|
|
)}
|
|
|
|
|
</Td>
|
|
|
|
|
<Td align="right">
|
|
|
|
|
<Link to="/runs">
|
|
|
|
|
<Button variant="ghost" size="sm">
|
|
|
|
|
Enter →
|
|
|
|
|
</Button>
|
|
|
|
|
</Link>
|
|
|
|
|
</Td>
|
|
|
|
|
</tr>
|
|
|
|
|
))}
|
|
|
|
|
</tbody>
|
|
|
|
|
</table>
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function LoadingRow() {
|
|
|
|
|
return (
|
|
|
|
|
<div className="px-4 py-12 flex items-center gap-3 text-fg-faint label-system">
|
|
|
|
|
<span className="status-dot text-fg-faint pulsing" />
|
|
|
|
|
<span>fetching engagements …</span>
|
|
|
|
|
</div>
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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 (
|
|
|
|
|
<div className="px-4 py-8 flex items-center justify-between gap-4">
|
|
|
|
|
<div>
|
|
|
|
|
<div className="label-system" style={{ color: 'var(--state-failed)' }}>
|
|
|
|
|
// Fetch failed
|
|
|
|
|
</div>
|
|
|
|
|
<div className="text-fg-muted mt-1" style={{ fontSize: 12.5 }}>
|
|
|
|
|
{message}
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
<Button variant="ghost" size="sm" onClick={onRetry}>
|
|
|
|
|
↻ Retry
|
|
|
|
|
</Button>
|
|
|
|
|
</div>
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function EmptyRow({ onCreate }: { onCreate: () => void }) {
|
|
|
|
|
return (
|
|
|
|
|
<div className="px-4 py-12 flex flex-col items-start gap-3">
|
|
|
|
|
<div className="label-system">// No engagements yet</div>
|
|
|
|
|
<p className="text-fg-muted" style={{ fontSize: 12.5 }}>
|
|
|
|
|
Create your first engagement to start composing scenarios and running them against client
|
|
|
|
|
infrastructure.
|
|
|
|
|
</p>
|
|
|
|
|
<Button variant="primary" size="sm" onClick={onCreate}>
|
|
|
|
|
+ New engagement
|
|
|
|
|
</Button>
|
feat(frontend): wireframes for 5 MVP screens + audit (F0.3)
Mock-data wireframes covering spec §5 / §9 surface area. All read from
src/mocks/fixtures.ts — no backend wiring yet. Each screen is built from
the design-system primitives (Panel, Pill, Button, label-system, status-dot)
and adheres to the instrumentation-grade visual grammar.
Screens:
- /login LoginPage — RT vs SOC mode switch (segmented), role-tinted.
RT form picks rt_operator / rt_lead at sign-in (mock only).
SOC form takes a session token (out-of-band, D-006).
Left rail carries mission brief + platform telemetry.
- /engagements EngagementsPage — mission roster table (codename, client,
status, c2_type, operators, SOC count, window).
- /runs LiveCockpitPage — the cornerstone screen. 3-column layout:
steps timeline | step detail (resolved command,
output, evidence, detection) | side rail
(DetectionPanel for SOC; EvidencePanel +
DetectionPanel readonly + CleanupPanel for RT).
Control bar (F6 pause/skip/retry/abort) is lead-RT-only.
Stats header: steps done, detected/partial/missed counts.
- /scenarios ScenarioComposerPage — 3-column composer:
filterable TTP library | ordered steps with delays
| inspector (params from params_schema_json, target
host list, jinja2 cleanup template preview).
c2_type locked at scenario level (D-F3 / H33).
- /library TtpLibraryPage — catalog table with stealth-variant
flagging, source provenance (custom/atr/mission),
payload_type chip, tags. Import journal / ATR buttons.
- /reports ReportPage — restricted MITRE matrix (techniques
played only, H29), narration timeline, integrity
hash footer (SHA-256, H19/H24/F9). PDF/JSON/MD
export buttons.
- /audit AuditPage — append-only journal viewer (lead RT only,
F13). Tabular timestamp/actor/role/action/resource.
UX guardrails baked in:
- SOC analysts never see RT-only controls (conditional rendering, not just
disabled state). UI layer mirrors backend RBAC but does not replace it.
- Layout density and dark-first palette tuned for long purple sessions
(sober contrast, no flash, status colors carry information without
being shouted).
- Live cockpit reserves a clear visual slot for cleanup-failed alerts
(R-T5) — currently a Pill, real alert UX lands when the WebSocket is
wired in sprint 1+.
2026-05-21 20:31:24 +02:00
|
|
|
</div>
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function Th({ children, align = 'left' }: { children?: ReactNode; align?: 'left' | 'right' }) {
|
|
|
|
|
return (
|
|
|
|
|
<th
|
|
|
|
|
className="label-system px-3 py-2"
|
|
|
|
|
style={{ textAlign: align, fontWeight: 500 }}
|
|
|
|
|
>
|
|
|
|
|
{children}
|
|
|
|
|
</th>
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function Td({ children, align = 'left' }: { children?: ReactNode; align?: 'left' | 'right' }) {
|
|
|
|
|
return (
|
|
|
|
|
<td className="px-3 py-3 align-middle" style={{ textAlign: align }}>
|
|
|
|
|
{children}
|
|
|
|
|
</td>
|
|
|
|
|
);
|
|
|
|
|
}
|