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+.
61 lines
1.5 KiB
TypeScript
61 lines
1.5 KiB
TypeScript
import type { SessionUser } from '@/types/roles';
|
|
|
|
/**
|
|
* Sprint 0 mock — no backend yet. The session is selected from /login
|
|
* and persisted in sessionStorage so route navigations preserve role.
|
|
* Real auth lands later (D-003: local user/password v1, Keycloak OIDC v2).
|
|
*/
|
|
|
|
const STORAGE_KEY = 'mimic.mock.session';
|
|
|
|
export const MOCK_SESSIONS: Record<string, SessionUser> = {
|
|
rt_operator: {
|
|
id: 'usr_001',
|
|
displayName: 'M. Dubreuil',
|
|
role: 'rt_operator',
|
|
engagementId: 'eng_42',
|
|
engagementName: 'Démo Client X',
|
|
},
|
|
rt_lead: {
|
|
id: 'usr_002',
|
|
displayName: 'A. Verlhac',
|
|
role: 'rt_lead',
|
|
engagementId: 'eng_42',
|
|
engagementName: 'Démo Client X',
|
|
},
|
|
soc_analyst: {
|
|
id: 'usr_soc_07',
|
|
displayName: 'SOC · session #07',
|
|
role: 'soc_analyst',
|
|
engagementId: 'eng_42',
|
|
engagementName: 'Démo Client X',
|
|
},
|
|
};
|
|
|
|
export function readMockSession(): SessionUser | null {
|
|
try {
|
|
const raw = sessionStorage.getItem(STORAGE_KEY);
|
|
if (!raw) return null;
|
|
const parsed: unknown = JSON.parse(raw);
|
|
if (
|
|
typeof parsed === 'object' &&
|
|
parsed !== null &&
|
|
'role' in parsed &&
|
|
typeof (parsed as SessionUser).role === 'string'
|
|
) {
|
|
return parsed as SessionUser;
|
|
}
|
|
return null;
|
|
} catch {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
export function writeMockSession(user: SessionUser): void {
|
|
sessionStorage.setItem(STORAGE_KEY, JSON.stringify(user));
|
|
}
|
|
|
|
export function clearMockSession(): void {
|
|
sessionStorage.removeItem(STORAGE_KEY);
|
|
}
|