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
239 lines
5.4 KiB
CSS
239 lines
5.4 KiB
CSS
/* ── Aegis Professional Report CSS ─────────────────────────────── */
|
|
|
|
@page {
|
|
size: A4;
|
|
margin: 2cm 2.5cm;
|
|
@bottom-center {
|
|
content: "Page " counter(page) " of " counter(pages);
|
|
font-size: 9px;
|
|
color: #6b7280;
|
|
}
|
|
}
|
|
|
|
* { box-sizing: border-box; }
|
|
|
|
body {
|
|
font-family: "Segoe UI", -apple-system, "Helvetica Neue", Arial, sans-serif;
|
|
font-size: 11pt;
|
|
color: #1f2937;
|
|
line-height: 1.6;
|
|
}
|
|
|
|
/* ── Cover Page ─────────────────────────────────────────────────── */
|
|
|
|
.cover-page {
|
|
page-break-after: always;
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
justify-content: center;
|
|
min-height: 80vh;
|
|
text-align: center;
|
|
}
|
|
|
|
.cover-page .logo {
|
|
max-width: 180px;
|
|
margin-bottom: 3rem;
|
|
}
|
|
|
|
.cover-page h1 {
|
|
font-size: 28pt;
|
|
color: #0e7490;
|
|
font-weight: 700;
|
|
margin: 0 0 0.5rem;
|
|
}
|
|
|
|
.cover-page h2 {
|
|
font-size: 18pt;
|
|
color: #374151;
|
|
font-weight: 400;
|
|
margin: 0 0 2rem;
|
|
}
|
|
|
|
.cover-page .date {
|
|
font-size: 12pt;
|
|
color: #6b7280;
|
|
}
|
|
|
|
.cover-page .classification {
|
|
margin-top: 2rem;
|
|
padding: 0.4rem 1.5rem;
|
|
border: 2px solid #ef4444;
|
|
color: #ef4444;
|
|
font-weight: 700;
|
|
font-size: 10pt;
|
|
text-transform: uppercase;
|
|
letter-spacing: 0.1em;
|
|
}
|
|
|
|
/* ── Section headings ───────────────────────────────────────────── */
|
|
|
|
section {
|
|
page-break-inside: avoid;
|
|
margin-bottom: 1.5rem;
|
|
}
|
|
|
|
h2 {
|
|
font-size: 16pt;
|
|
color: #0e7490;
|
|
border-bottom: 2px solid #0e7490;
|
|
padding-bottom: 0.3rem;
|
|
margin-top: 2rem;
|
|
page-break-after: avoid;
|
|
}
|
|
|
|
h3 {
|
|
font-size: 13pt;
|
|
color: #1f2937;
|
|
margin-top: 1.2rem;
|
|
}
|
|
|
|
/* ── Stats grid ─────────────────────────────────────────────────── */
|
|
|
|
.stats-grid {
|
|
display: flex;
|
|
gap: 1.5rem;
|
|
margin: 1.5rem 0;
|
|
}
|
|
|
|
.stat {
|
|
flex: 1;
|
|
text-align: center;
|
|
padding: 1rem;
|
|
border: 1px solid #d1d5db;
|
|
border-radius: 8px;
|
|
background: #f9fafb;
|
|
}
|
|
|
|
.stat .number {
|
|
display: block;
|
|
font-size: 28pt;
|
|
font-weight: 700;
|
|
color: #0e7490;
|
|
}
|
|
|
|
.stat .label {
|
|
display: block;
|
|
font-size: 10pt;
|
|
color: #6b7280;
|
|
text-transform: uppercase;
|
|
letter-spacing: 0.05em;
|
|
}
|
|
|
|
/* ── Data table ─────────────────────────────────────────────────── */
|
|
|
|
.data-table {
|
|
width: 100%;
|
|
border-collapse: collapse;
|
|
margin: 1rem 0;
|
|
font-size: 10pt;
|
|
}
|
|
|
|
.data-table th {
|
|
background: #0e7490;
|
|
color: white;
|
|
padding: 0.5rem 0.75rem;
|
|
text-align: left;
|
|
font-weight: 600;
|
|
}
|
|
|
|
.data-table td {
|
|
padding: 0.4rem 0.75rem;
|
|
border-bottom: 1px solid #e5e7eb;
|
|
}
|
|
|
|
.data-table tbody tr:nth-child(even) {
|
|
background: #f9fafb;
|
|
}
|
|
|
|
/* ── Detection result row colors ───────────────────────────────── */
|
|
|
|
tr.result-detected td:last-child { color: #059669; font-weight: 600; }
|
|
tr.result-not_detected td:last-child { color: #dc2626; font-weight: 600; }
|
|
tr.result-partially_detected td:last-child { color: #d97706; font-weight: 600; }
|
|
tr.result-pending td:last-child { color: #6b7280; }
|
|
|
|
/* ── Findings ───────────────────────────────────────────────────── */
|
|
|
|
.finding {
|
|
padding: 1rem;
|
|
border-left: 4px solid #d1d5db;
|
|
margin-bottom: 1rem;
|
|
background: #f9fafb;
|
|
border-radius: 0 6px 6px 0;
|
|
}
|
|
|
|
.finding.critical { border-left-color: #dc2626; }
|
|
.finding.high { border-left-color: #ea580c; }
|
|
.finding.medium { border-left-color: #d97706; }
|
|
.finding.low { border-left-color: #059669; }
|
|
|
|
.finding h3 {
|
|
margin-top: 0;
|
|
color: #1f2937;
|
|
}
|
|
|
|
/* ── Footer ─────────────────────────────────────────────────────── */
|
|
|
|
footer {
|
|
position: fixed;
|
|
bottom: 0;
|
|
left: 0;
|
|
right: 0;
|
|
text-align: center;
|
|
font-size: 9pt;
|
|
color: #9ca3af;
|
|
border-top: 1px solid #e5e7eb;
|
|
padding-top: 0.5rem;
|
|
}
|
|
|
|
/* ── Table of Contents ──────────────────────────────────────────── */
|
|
|
|
.toc {
|
|
page-break-after: always;
|
|
}
|
|
|
|
.toc h2 {
|
|
border-bottom: none;
|
|
}
|
|
|
|
.toc ul {
|
|
list-style: none;
|
|
padding: 0;
|
|
}
|
|
|
|
.toc li {
|
|
padding: 0.3rem 0;
|
|
border-bottom: 1px dotted #d1d5db;
|
|
}
|
|
|
|
/* ── Metric cards (for coverage reports) ────────────────────────── */
|
|
|
|
.metric-cards {
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
gap: 1rem;
|
|
margin: 1rem 0;
|
|
}
|
|
|
|
.metric-card {
|
|
flex: 1 1 calc(33% - 1rem);
|
|
min-width: 140px;
|
|
padding: 1rem;
|
|
border: 1px solid #e5e7eb;
|
|
border-radius: 8px;
|
|
text-align: center;
|
|
}
|
|
|
|
.metric-card .value {
|
|
font-size: 24pt;
|
|
font-weight: 700;
|
|
color: #0e7490;
|
|
}
|
|
|
|
.metric-card .label {
|
|
font-size: 9pt;
|
|
color: #6b7280;
|
|
text-transform: uppercase;
|
|
}
|