feat(iace): KI-Vorschlag Button im FMEA-Tab
Build + Deploy / build-admin-compliance (push) Successful in 16s
Build + Deploy / build-backend-compliance (push) Successful in 24s
Build + Deploy / build-ai-sdk (push) Successful in 12s
Build + Deploy / build-developer-portal (push) Successful in 12s
Build + Deploy / build-tts (push) Successful in 34s
Build + Deploy / build-document-crawler (push) Successful in 11s
Build + Deploy / build-dsms-gateway (push) Successful in 11s
Build + Deploy / build-dsms-node (push) Successful in 14s
CI / branch-name (push) Has been skipped
CI / guardrail-integrity (push) Has been skipped
CI / loc-budget (push) Failing after 16s
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 2m49s
CI / dep-audit (push) Has been skipped
CI / sbom-scan (push) Has been skipped
CI / test-go (push) Successful in 43s
CI / test-python-backend (push) Successful in 37s
CI / test-python-document-crawler (push) Successful in 26s
CI / test-python-dsms-gateway (push) Successful in 23s
CI / validate-canonical-controls (push) Successful in 13s
Build + Deploy / trigger-orca (push) Successful in 2m25s

- Dropdown: Komponente waehlen → "KI-Vorschlag" klicken
- Ruft POST /projects/:id/components/:cid/suggest-fms auf
- Zeigt LLM-generierte oder Bibliotheks-FMs als Overlay
- Jeder Vorschlag mit Name, Auswirkung, S/O/D, RPZ

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Benjamin Admin
2026-05-12 10:07:10 +02:00
parent 93028b443e
commit 65b4857be5
2 changed files with 87 additions and 4 deletions
@@ -130,5 +130,31 @@ export function useFMEA(projectId: string) {
acceptable: rows.filter((r) => r.rpz <= 100).length,
}
return { rows, loading, stats }
const [suggesting, setSuggesting] = useState(false)
const [suggestions, setSuggestions] = useState<FailureMode[]>([])
const [suggestSource, setSuggestSource] = useState<string>('')
async function suggestFMs(componentId: string) {
setSuggesting(true)
setSuggestions([])
try {
const res = await fetch(`/api/sdk/v1/iace/projects/${projectId}/components/${componentId}/suggest-fms`, {
method: 'POST',
})
if (res.ok) {
const json = await res.json()
setSuggestions(json.suggestions || [])
setSuggestSource(json.source || 'unknown')
}
} catch (err) {
console.error('FM suggest failed:', err)
} finally {
setSuggesting(false)
}
}
// Get unique components for the suggest button
const components = [...new Map(rows.map((r) => [r.component.id, r.component])).values()]
return { rows, loading, stats, components, suggestFMs, suggesting, suggestions, suggestSource, setSuggestions }
}
@@ -27,7 +27,8 @@ function rpzLabel(rpz: number): string {
export default function FMEAPage() {
const { projectId } = useParams<{ projectId: string }>()
const { rows, loading, stats } = useFMEA(projectId)
const { rows, loading, stats, components, suggestFMs, suggesting, suggestions, suggestSource, setSuggestions } = useFMEA(projectId)
const [suggestComp, setSuggestComp] = useState<string | null>(null)
if (loading) {
return (
@@ -50,7 +51,34 @@ export default function FMEAPage() {
{/* Info Box */}
<FMEAInfoBox />
{/* Export Button */}
{/* KI-Vorschlag + Export */}
<div className="flex items-center justify-between gap-3">
<div className="flex items-center gap-2">
<select
value={suggestComp || ''}
onChange={(e) => setSuggestComp(e.target.value || null)}
className="px-3 py-2 border border-gray-300 rounded-lg text-sm dark:bg-gray-700 dark:border-gray-600 dark:text-white"
>
<option value="">Komponente waehlen...</option>
{components.map((c) => (
<option key={c.id} value={c.id}>{c.name}</option>
))}
</select>
<button
onClick={() => suggestComp && suggestFMs(suggestComp)}
disabled={!suggestComp || suggesting}
className="flex items-center gap-2 px-4 py-2 bg-purple-600 text-white rounded-lg hover:bg-purple-700 text-sm font-medium transition-colors disabled:opacity-50"
>
{suggesting ? (
<span className="animate-spin inline-block w-4 h-4 border-2 border-white border-t-transparent rounded-full" />
) : (
<svg className="w-4 h-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9.663 17h4.673M12 3v1m6.364 1.636l-.707.707M21 12h-1M4 12H3m3.343-5.657l-.707-.707m2.828 9.9a5 5 0 117.072 0l-.548.547A3.374 3.374 0 0014 18.469V19a2 2 0 11-4 0v-.531c0-.895-.356-1.754-.988-2.386l-.548-.547z" />
</svg>
)}
KI-Vorschlag
</button>
</div>
<div className="flex justify-end">
<a
href={`/api/sdk/v1/iace/projects/${projectId}/fmea/export`}
@@ -63,6 +91,35 @@ export default function FMEAPage() {
VDA Excel exportieren
</a>
</div>
</div>
{/* Suggest Results */}
{suggestions.length > 0 && (
<div className="bg-purple-50 dark:bg-purple-900/20 border border-purple-200 dark:border-purple-800 rounded-xl p-4">
<div className="flex items-center justify-between mb-3">
<h3 className="text-sm font-semibold text-purple-800 dark:text-purple-300">
KI-Vorschlaege ({suggestions.length}) {suggestSource === 'llm' ? 'LLM-generiert' : 'Bibliothek'}
</h3>
<button onClick={() => setSuggestions([])} className="text-xs text-purple-600 hover:text-purple-800">Schliessen</button>
</div>
<div className="space-y-2">
{suggestions.map((fm, i) => (
<div key={i} className="flex items-center justify-between bg-white dark:bg-gray-800 rounded-lg p-3 border border-purple-100 dark:border-purple-800">
<div className="flex-1 min-w-0">
<div className="text-sm font-medium text-gray-900 dark:text-white">{fm.name_de}</div>
<div className="text-xs text-gray-500 mt-0.5">{fm.effect}</div>
<div className="flex gap-3 mt-1 text-xs text-gray-400">
<span>S={fm.default_severity}</span>
<span>O={fm.default_occurrence}</span>
<span>D={fm.default_detection}</span>
<span className="font-bold">RPZ={fm.default_severity * fm.default_occurrence * fm.default_detection}</span>
</div>
</div>
</div>
))}
</div>
</div>
)}
{/* Stats */}
<div className="grid grid-cols-4 gap-3">