06bfbd1dca
Build + Deploy / build-admin-compliance (push) Successful in 2m46s
Build + Deploy / build-backend-compliance (push) Successful in 26s
Build + Deploy / build-ai-sdk (push) Successful in 52s
Build + Deploy / build-developer-portal (push) Successful in 22s
Build + Deploy / build-tts (push) Successful in 16s
Build + Deploy / build-document-crawler (push) Successful in 12s
Build + Deploy / build-dsms-gateway (push) Successful in 20s
Build + Deploy / build-dsms-node (push) Successful in 16s
CI / branch-name (push) Has been skipped
CI / guardrail-integrity (push) Has been skipped
CI / loc-budget (push) Failing after 18s
CI / secret-scan (push) Has been skipped
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 3m16s
CI / dep-audit (push) Has been skipped
CI / sbom-scan (push) Has been skipped
CI / test-go (push) Successful in 1m0s
CI / test-python-backend (push) Successful in 41s
CI / test-python-document-crawler (push) Successful in 29s
CI / test-python-dsms-gateway (push) Successful in 23s
CI / validate-canonical-controls (push) Successful in 16s
Build + Deploy / trigger-orca (push) Successful in 2m36s
Implements the Use-Case Compiler that turns Master Controls into interactive compliance audits. 5 templates (Vendor Check, SAST/DAST, DSGVO, NIS2, CRA), deterministic + LLM question generation, scoring engine with regulation/severity breakdown, and gap detection. - Backend: 9 API endpoints, 22 unit tests (all pass) - Frontend: Template selector, questionnaire, result dashboard - Migration 027: usecase_audits + usecase_answers tables Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
139 lines
4.8 KiB
TypeScript
139 lines
4.8 KiB
TypeScript
'use client'
|
|
|
|
import React, { useState } from 'react'
|
|
|
|
interface Template {
|
|
id: string
|
|
name: string
|
|
description: string
|
|
mc_filters: string[]
|
|
regulations: string[]
|
|
}
|
|
|
|
interface Props {
|
|
templates: Template[]
|
|
onCreateAudit: (templateId: string, name: string, targetName: string) => void
|
|
loading: boolean
|
|
}
|
|
|
|
const REG_COLORS: Record<string, string> = {
|
|
dsgvo: 'bg-purple-100 text-purple-700',
|
|
nis2: 'bg-orange-100 text-orange-700',
|
|
cra: 'bg-red-100 text-red-700',
|
|
owasp: 'bg-yellow-100 text-yellow-700',
|
|
}
|
|
|
|
export function UseCaseSelector({ templates, onCreateAudit, loading }: Props) {
|
|
const [selected, setSelected] = useState<Template | null>(null)
|
|
const [auditName, setAuditName] = useState('')
|
|
const [targetName, setTargetName] = useState('')
|
|
|
|
const handleSelect = (t: Template) => {
|
|
setSelected(t)
|
|
setAuditName(t.name)
|
|
setTargetName('')
|
|
}
|
|
|
|
const handleSubmit = (e: React.FormEvent) => {
|
|
e.preventDefault()
|
|
if (!selected || !auditName.trim()) return
|
|
onCreateAudit(selected.id, auditName.trim(), targetName.trim())
|
|
}
|
|
|
|
return (
|
|
<div>
|
|
{!selected ? (
|
|
<div>
|
|
<h2 className="text-xl font-semibold text-gray-800 mb-4">Template auswaehlen</h2>
|
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
|
{templates.map(t => (
|
|
<button
|
|
key={t.id}
|
|
onClick={() => handleSelect(t)}
|
|
className="text-left bg-white rounded-xl shadow-sm border border-gray-200 p-5 hover:shadow-md hover:border-blue-300 transition-all"
|
|
>
|
|
<h3 className="font-semibold text-gray-900 mb-2">{t.name}</h3>
|
|
<p className="text-sm text-gray-600 mb-3 line-clamp-2">{t.description}</p>
|
|
<div className="flex flex-wrap gap-1.5">
|
|
{t.regulations.map(r => (
|
|
<span
|
|
key={r}
|
|
className={`px-2 py-0.5 rounded text-xs font-medium ${REG_COLORS[r] || 'bg-gray-100 text-gray-600'}`}
|
|
>
|
|
{r.toUpperCase()}
|
|
</span>
|
|
))}
|
|
<span className="px-2 py-0.5 bg-blue-50 text-blue-600 rounded text-xs">
|
|
{t.mc_filters.length} Filter
|
|
</span>
|
|
</div>
|
|
</button>
|
|
))}
|
|
</div>
|
|
</div>
|
|
) : (
|
|
<form onSubmit={handleSubmit} className="bg-white rounded-xl shadow-sm border border-gray-200 p-6">
|
|
<div className="flex items-center gap-3 mb-6">
|
|
<button
|
|
type="button"
|
|
onClick={() => setSelected(null)}
|
|
className="text-gray-400 hover:text-gray-600"
|
|
>
|
|
←
|
|
</button>
|
|
<div>
|
|
<h2 className="text-xl font-semibold text-gray-800">{selected.name}</h2>
|
|
<p className="text-sm text-gray-500">{selected.description}</p>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="space-y-4">
|
|
<div>
|
|
<label className="block text-sm font-medium text-gray-700 mb-1">
|
|
Audit-Name *
|
|
</label>
|
|
<input
|
|
type="text"
|
|
value={auditName}
|
|
onChange={e => setAuditName(e.target.value)}
|
|
placeholder={`z.B. "${selected.name}: Firma XY"`}
|
|
className="w-full px-4 py-2.5 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent text-sm"
|
|
required
|
|
/>
|
|
</div>
|
|
<div>
|
|
<label className="block text-sm font-medium text-gray-700 mb-1">
|
|
Zielobjekt (optional)
|
|
</label>
|
|
<input
|
|
type="text"
|
|
value={targetName}
|
|
onChange={e => setTargetName(e.target.value)}
|
|
placeholder="z.B. AWS, SAP, Produkt XY"
|
|
className="w-full px-4 py-2.5 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent text-sm"
|
|
/>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="mt-6 flex items-center gap-3">
|
|
<button
|
|
type="submit"
|
|
disabled={loading || !auditName.trim()}
|
|
className="px-6 py-2.5 bg-blue-600 text-white rounded-lg hover:bg-blue-700 font-medium text-sm disabled:opacity-50"
|
|
>
|
|
{loading ? 'Wird erstellt...' : 'Audit starten'}
|
|
</button>
|
|
<button
|
|
type="button"
|
|
onClick={() => setSelected(null)}
|
|
className="px-4 py-2.5 text-gray-600 hover:text-gray-800 text-sm"
|
|
>
|
|
Abbrechen
|
|
</button>
|
|
</div>
|
|
</form>
|
|
)}
|
|
</div>
|
|
)
|
|
}
|