8f21650d74
CI / detect-changes (push) Successful in 16s
CI / branch-name (push) Has been skipped
CI / guardrail-integrity (push) Has been skipped
CI / secret-scan (push) Has been skipped
CI / dep-audit (push) Has been skipped
CI / sbom-scan (push) Has been skipped
CI / build-sha-integrity (push) Successful in 15s
CI / validate-canonical-controls (push) Successful in 13s
CI / loc-budget (push) Successful in 25s
CI / go-lint (push) Has been skipped
CI / python-lint (push) Has been skipped
CI / nodejs-lint (push) Has been skipped
CI / nodejs-build (push) Successful in 3m9s
CI / test-go (push) Has been skipped
CI / iace-gt-coverage (push) Has been skipped
CI / test-python-backend (push) Successful in 31s
CI / test-python-document-crawler (push) Has been skipped
CI / test-python-dsms-gateway (push) Has been skipped
- /sdk/dokumente: Kundensicht nur auf veroeffentlichte Rechtsdokumente (Ansehen + Download); Proxy mit Allow-List nur /public — Templates/Drafts/ Generator bleiben unerreichbar. - /sdk/cra-meldewesen: CRA Art. 14 Meldewesen (24h/72h/14d-Kaskade) mit Fristen-Tracking + ENISA-SRP-Export-Entwurf (kein Live-API). Backend: cra_meldewesen (pure, getestet) + cra_incident_store (schema-neutral ueber compliance_cra_documents) + /api/v1/cra/incidents (additiv, contract-safe). - Screening (Self-Scan) aus dem Frontend genommen: Flow-Stepper-Eintrag ausgeblendet (visibleWhen), Dashboard-Kachel + Import-Button entfernt. Repo-Scanning laeuft extern im Compliance-Scanner; Backend-Router bleibt vorerst gemountet (Contract-Stabilitaet). Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
86 lines
3.9 KiB
TypeScript
86 lines
3.9 KiB
TypeScript
'use client'
|
|
|
|
import { useState } from 'react'
|
|
import { useMeldewesen } from './_hooks/useMeldewesen'
|
|
import { IncidentCard } from './_components/IncidentCard'
|
|
import { NewIncidentForm } from './_components/NewIncidentForm'
|
|
|
|
// CRA Article 14 Meldewesen: the 24h/72h/14d incident-reporting cascade to ENISA.
|
|
// Customer-facing; deadlines + report drafts. No live ENISA API (manual export).
|
|
|
|
export default function MeldewesenPage() {
|
|
const m = useMeldewesen()
|
|
const [showForm, setShowForm] = useState(false)
|
|
|
|
return (
|
|
<div className="space-y-6">
|
|
<header className="flex flex-wrap items-start justify-between gap-3">
|
|
<div>
|
|
<h1 className="text-2xl font-bold text-gray-900 dark:text-gray-100">CRA-Meldewesen</h1>
|
|
<p className="text-sm text-gray-600 dark:text-gray-300 mt-1 max-w-2xl">
|
|
Meldepflichten nach CRA Artikel 14: Frühwarnung (24 h), Meldung (72 h) und Abschlussbericht
|
|
(14 Tage) an die ENISA Single Reporting Platform. Wir behalten die Fristen im Blick und
|
|
erstellen die Berichtsentwürfe — die Übermittlung bestätigen Sie selbst.
|
|
</p>
|
|
</div>
|
|
{m.meta?.reporting_active_from && (
|
|
<span className="text-xs rounded-full bg-blue-50 dark:bg-blue-900/30 text-blue-700 dark:text-blue-300 px-3 py-1">
|
|
Meldepflicht aktiv ab {new Date(m.meta.reporting_active_from).toLocaleDateString('de-DE')}
|
|
</span>
|
|
)}
|
|
</header>
|
|
|
|
<div className="flex flex-wrap items-center gap-3">
|
|
<label className="text-sm text-gray-600 dark:text-gray-300">Projekt:</label>
|
|
<select
|
|
value={m.projectId}
|
|
onChange={(e) => m.setProjectId(e.target.value)}
|
|
className="text-sm rounded border border-gray-200 dark:border-gray-600 bg-white dark:bg-gray-700 p-2 min-w-[16rem]"
|
|
>
|
|
{m.projects.length === 0 && <option value="">— kein CRA-Projekt —</option>}
|
|
{m.projects.map((p) => <option key={p.id} value={p.id}>{p.name}</option>)}
|
|
</select>
|
|
{m.projectId && !showForm && (
|
|
<button onClick={() => setShowForm(true)}
|
|
className="ml-auto rounded bg-indigo-600 hover:bg-indigo-700 text-white text-sm px-4 py-2">
|
|
+ Neue Meldung
|
|
</button>
|
|
)}
|
|
</div>
|
|
|
|
{showForm && m.projectId && (
|
|
<NewIncidentForm meta={m.meta} onCreate={m.createIncident} onCancel={() => setShowForm(false)} />
|
|
)}
|
|
|
|
{!m.projectId && (
|
|
<div className="rounded-xl border border-dashed border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-800 p-8 text-center text-sm text-gray-600 dark:text-gray-300">
|
|
Legen Sie zuerst ein CRA-Projekt an, um Vorfälle zu erfassen.
|
|
</div>
|
|
)}
|
|
|
|
{m.loading && <div className="text-sm text-gray-500">Lade Meldungen …</div>}
|
|
{m.error && !m.loading && (
|
|
<div className="rounded-xl border border-amber-300 bg-amber-50 dark:bg-amber-900/20 text-amber-900 dark:text-amber-200 p-4 text-sm">
|
|
Meldungen konnten nicht geladen werden ({m.error}).{' '}
|
|
<button onClick={m.reload} className="underline font-medium">Erneut versuchen</button>
|
|
</div>
|
|
)}
|
|
|
|
{m.projectId && !m.loading && !m.error && m.incidents.length === 0 && (
|
|
<div className="rounded-xl border border-dashed border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-800 p-8 text-center">
|
|
<p className="text-sm text-gray-700 dark:text-gray-200 font-medium">Keine offenen Meldungen</p>
|
|
<p className="text-xs text-gray-500 mt-1">Im Ernstfall erfassen Sie hier den Vorfall — die Fristen laufen dann automatisch mit.</p>
|
|
</div>
|
|
)}
|
|
|
|
{m.incidents.length > 0 && (
|
|
<div className="grid gap-3">
|
|
{m.incidents.map((inc) => (
|
|
<IncidentCard key={inc.id} inc={inc} onSubmit={m.submitStage} />
|
|
))}
|
|
</div>
|
|
)}
|
|
</div>
|
|
)
|
|
}
|