feat(phase-37): timer pause/resume + professional reporting engine
Some checks failed
Aegis CI / lint-and-test (push) Has been cancelled
Some checks failed
Aegis CI / lint-and-test (push) Has been cancelled
Pause/Resume timer:
- Add paused_at, red_paused_seconds, blue_paused_seconds fields to Test model
- Add pause_timer/resume_timer workflow functions with accumulated pause tracking
- Auto-resume on phase submit; subtract paused time from worklog duration
- Add POST /tests/{id}/pause-timer and resume-timer endpoints
- Update LiveTimer component with pause/resume button and paused visual state
- Wire pause/resume mutations through TestDetailPage and TestDetailHeader
Professional Reporting Engine - Fase 2:
- Add ReportEngine service with Jinja2 HTML rendering, WeasyPrint PDF, and docxtpl DOCX
- Add corporate CSS stylesheet with cover page, data tables, stats grid, findings
- Create purple_campaign, coverage_report, and executive_summary HTML templates
- Add report_generation_service collecting domain data for each report type
- Add professional_reports router: GET /reports/generate/purple-campaign/{id}, coverage-summary, executive-summary
- Add analytics router with flat JSON endpoints for PowerBI: /coverage, /tests, /trends, /operators
- Add advanced_metrics router: /coverage-by-tactic, /never-tested, /avg-validation-time, /detection-rate-trend
- Add weasyprint and docxtpl to requirements.txt
- Add REPORT_TEMPLATES_DIR, REPORT_OUTPUT_DIR, COMPANY_NAME, COMPANY_LOGO_PATH to config
This commit is contained in:
@@ -53,6 +53,9 @@ interface TestDetailHeaderProps {
|
||||
onSubmitBlue: () => void;
|
||||
onOpenValidateModal: (side: "red" | "blue") => void;
|
||||
onReopen: () => void;
|
||||
onPauseTimer: () => void;
|
||||
onResumeTimer: () => void;
|
||||
isTogglingTimer: boolean;
|
||||
}
|
||||
|
||||
// ── Component ──────────────────────────────────────────────────────
|
||||
@@ -66,6 +69,9 @@ export default function TestDetailHeader({
|
||||
onSubmitBlue,
|
||||
onOpenValidateModal,
|
||||
onReopen,
|
||||
onPauseTimer,
|
||||
onResumeTimer,
|
||||
isTogglingTimer,
|
||||
}: TestDetailHeaderProps) {
|
||||
const role = user?.role ?? "";
|
||||
const currentIdx = STATE_INDEX[test.state];
|
||||
@@ -238,13 +244,23 @@ export default function TestDetailHeader({
|
||||
|
||||
// ── Live timer ───────────────────────────────────────────────────
|
||||
|
||||
const canControlTimer =
|
||||
(test.state === "red_executing" && (role === "red_tech" || role === "admin")) ||
|
||||
(test.state === "blue_evaluating" && (role === "blue_tech" || role === "admin"));
|
||||
|
||||
const renderLiveTimer = () => {
|
||||
if (test.state === "red_executing" && test.red_started_at) {
|
||||
return (
|
||||
<LiveTimer
|
||||
startedAt={test.red_started_at}
|
||||
pausedAt={test.paused_at}
|
||||
pausedSeconds={test.red_paused_seconds}
|
||||
label="Red Team Timer"
|
||||
variant="red"
|
||||
onPause={onPauseTimer}
|
||||
onResume={onResumeTimer}
|
||||
canControl={canControlTimer}
|
||||
isToggling={isTogglingTimer}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@@ -252,8 +268,14 @@ export default function TestDetailHeader({
|
||||
return (
|
||||
<LiveTimer
|
||||
startedAt={test.blue_started_at}
|
||||
pausedAt={test.paused_at}
|
||||
pausedSeconds={test.blue_paused_seconds}
|
||||
label="Blue Team Timer"
|
||||
variant="blue"
|
||||
onPause={onPauseTimer}
|
||||
onResume={onResumeTimer}
|
||||
canControl={canControlTimer}
|
||||
isToggling={isTogglingTimer}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user