Files
breakpilot-compliance/admin-compliance/app/sdk/cra-meldewesen/_components/NewIncidentForm.tsx
T
Benjamin Bönisch 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
feat(sdk): Kunden-Dokumente + CRA-Meldewesen, Screening aus Frontend genommen
- /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>
2026-06-17 21:21:28 +02:00

67 lines
3.4 KiB
TypeScript

'use client'
import { useState } from 'react'
import { Meta } from '../_hooks/useMeldewesen'
const KIND_LABEL: Record<string, string> = {
exploited_vulnerability: 'Aktiv ausgenutzte Schwachstelle',
severe_incident: 'Schwerwiegender Sicherheitsvorfall',
}
const SEV_LABEL: Record<string, string> = {
critical: 'kritisch', high: 'hoch', medium: 'mittel', low: 'niedrig',
}
export function NewIncidentForm({ meta, onCreate, onCancel }: {
meta: Meta | null
onCreate: (body: Record<string, unknown>) => Promise<boolean>
onCancel: () => void
}) {
const [f, setF] = useState<Record<string, string>>({
summary: '', product_name: '', product_version: '', manufacturer: '',
kind: 'exploited_vulnerability', severity: 'high', contact: '', impact: '',
})
const [busy, setBusy] = useState(false)
const set = (k: string, v: string) => setF((p) => ({ ...p, [k]: v }))
const field = 'w-full text-sm rounded border border-gray-200 dark:border-gray-600 bg-white dark:bg-gray-700 p-2'
const submit = async () => {
setBusy(true)
try { if (await onCreate(f)) onCancel() } finally { setBusy(false) }
}
return (
<div className="rounded-xl border border-indigo-200 dark:border-indigo-800 bg-indigo-50/40 dark:bg-indigo-900/20 p-4 space-y-3">
<h3 className="text-sm font-semibold text-gray-900 dark:text-gray-100">Neue CRA-Meldung erfassen</h3>
<p className="text-xs text-gray-600 dark:text-gray-300">
Die 24h/72h/14-Tage-Fristen laufen ab dem Zeitpunkt, an dem Sie Kenntnis erlangt haben.
</p>
<input className={field} placeholder="Kurzbeschreibung des Vorfalls *"
value={f.summary} onChange={(e) => set('summary', e.target.value)} />
<div className="grid grid-cols-2 gap-2">
<input className={field} placeholder="Produkt" value={f.product_name} onChange={(e) => set('product_name', e.target.value)} />
<input className={field} placeholder="Version" value={f.product_version} onChange={(e) => set('product_version', e.target.value)} />
<input className={field} placeholder="Hersteller" value={f.manufacturer} onChange={(e) => set('manufacturer', e.target.value)} />
<input className={field} placeholder="Kontakt (PSIRT-E-Mail)" value={f.contact} onChange={(e) => set('contact', e.target.value)} />
<select className={field} value={f.kind} onChange={(e) => set('kind', e.target.value)}>
{(meta?.kinds || ['exploited_vulnerability', 'severe_incident']).map((k) => (
<option key={k} value={k}>{KIND_LABEL[k] || k}</option>
))}
</select>
<select className={field} value={f.severity} onChange={(e) => set('severity', e.target.value)}>
{(meta?.severities || ['low', 'medium', 'high', 'critical']).map((s) => (
<option key={s} value={s}>{SEV_LABEL[s] || s}</option>
))}
</select>
</div>
<textarea className={field} rows={2} placeholder="Auswirkung (kurz)" value={f.impact} onChange={(e) => set('impact', e.target.value)} />
<div className="flex items-center gap-2">
<button onClick={submit} disabled={busy || f.summary.trim().length < 3}
className="rounded bg-indigo-600 hover:bg-indigo-700 disabled:opacity-50 text-white text-sm px-4 py-2">
{busy ? 'Lege an …' : 'Meldung anlegen'}
</button>
<button onClick={onCancel} className="text-sm text-gray-500 hover:underline">Abbrechen</button>
</div>
</div>
)
}