Files
breakpilot-compliance/admin-compliance/app/sdk/cra/_components/StatusStepper.tsx
T
Benjamin Admin 1cf5de1d45 feat(cra): CRA Compliance module Phase 1+2+3 (intake, scope, path, requirements, backlog, sbom, checks)
Phase 1 — Intake + Scope + Path:
- Migration 119: compliance_cra_projects table (intake + classification + path + status state machine)
- Backend service cra_routes.py: CRUD + scope-check + path-select
- Deterministic Annex III/IV classifier (verbatim mapping from migration 059 wiki)
- Path validation per classification (CRITICAL → notified_body mandatory)
- Frontend: project list, dashboard, 3-step wizard (intake/scope/path)
- Sidebar entry under "CRA Compliance" (red)

Phase 2 — Annex I Requirements + Priorisierungs-Backlog:
- cra_annex_i_data.py: 40 Annex-I requirements (8 categories), 9 measures (M540-M548), 3 CRA deadlines
- Endpoints: /requirements (40 items), /backlog (priority-sorted with deadline pressure)
- Frontend: requirements table with filters + expandable details, backlog with deadline banner + score-ranked table
- Dashboard KPI cards (Critical count, days to CE deadline, etc.) + top-10 backlog snippet

Phase 3 — SBOM Upload + Automated Checks:
- Migration 120: compliance_cra_sboms (versioned uploads, CycloneDX + SPDX)
- SBOM endpoints: POST /sbom/upload (format detection, summary extraction), GET /sboms
- Checks reuse compliance_evidence_checks: init creates 6 default CRA checks, run executes
- Real implementations: cra_security_txt (HTTP + Contact: line) and cra_tls_cert_check (TLS handshake)
- Frontend: SBOM file upload + version list, Checks page with per-check URL input + Run button

Backend-Reuse: gap_projects (intake pre-population), compliance_evidence_checks/_check_results.
Tenant scoping via existing X-Tenant-ID header pattern.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-18 17:56:52 +02:00

41 lines
1.5 KiB
TypeScript

'use client'
const STEPS = [
{ id: 'draft', label: 'Entwurf' },
{ id: 'scoped', label: 'Intake' },
{ id: 'classified', label: 'Klassifiziert' },
{ id: 'path_selected', label: 'Pfad' },
{ id: 'requirements_mapped', label: 'Requirements' },
{ id: 'evidence_pending', label: 'Evidence' },
{ id: 'ready_for_review', label: 'Review' },
{ id: 'declaration_ready', label: 'DoC' },
{ id: 'post_market', label: 'Post-Market' },
]
export function StatusStepper({ current }: { current: string }) {
const currentIdx = STEPS.findIndex(s => s.id === current)
return (
<div className="flex items-center gap-1 overflow-x-auto py-2">
{STEPS.map((step, idx) => {
const isPast = idx < currentIdx
const isCurrent = idx === currentIdx
return (
<div key={step.id} className="flex items-center gap-1 flex-shrink-0">
<div className={`w-7 h-7 rounded-full flex items-center justify-center text-xs font-medium ${
isCurrent ? 'bg-blue-600 text-white' :
isPast ? 'bg-green-500 text-white' :
'bg-gray-200 text-gray-500'
}`}>{idx + 1}</div>
<span className={`text-xs ${isCurrent ? 'font-semibold text-blue-700' : isPast ? 'text-gray-700' : 'text-gray-400'}`}>
{step.label}
</span>
{idx < STEPS.length - 1 && (
<span className={`mx-1 ${isPast ? 'text-green-500' : 'text-gray-300'}`}></span>
)}
</div>
)
})}
</div>
)
}