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
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:
@@ -130,5 +130,31 @@ export function useFMEA(projectId: string) {
|
|||||||
acceptable: rows.filter((r) => r.rpz <= 100).length,
|
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() {
|
export default function FMEAPage() {
|
||||||
const { projectId } = useParams<{ projectId: string }>()
|
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) {
|
if (loading) {
|
||||||
return (
|
return (
|
||||||
@@ -50,8 +51,35 @@ export default function FMEAPage() {
|
|||||||
{/* Info Box */}
|
{/* Info Box */}
|
||||||
<FMEAInfoBox />
|
<FMEAInfoBox />
|
||||||
|
|
||||||
{/* Export Button */}
|
{/* KI-Vorschlag + Export */}
|
||||||
<div className="flex justify-end">
|
<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
|
<a
|
||||||
href={`/api/sdk/v1/iace/projects/${projectId}/fmea/export`}
|
href={`/api/sdk/v1/iace/projects/${projectId}/fmea/export`}
|
||||||
className="flex items-center gap-2 px-4 py-2 bg-green-600 text-white rounded-lg hover:bg-green-700 text-sm font-medium transition-colors"
|
className="flex items-center gap-2 px-4 py-2 bg-green-600 text-white rounded-lg hover:bg-green-700 text-sm font-medium transition-colors"
|
||||||
@@ -62,8 +90,37 @@ export default function FMEAPage() {
|
|||||||
</svg>
|
</svg>
|
||||||
VDA Excel exportieren
|
VDA Excel exportieren
|
||||||
</a>
|
</a>
|
||||||
|
</div>
|
||||||
</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 */}
|
{/* Stats */}
|
||||||
<div className="grid grid-cols-4 gap-3">
|
<div className="grid grid-cols-4 gap-3">
|
||||||
<StatCard label="Gesamt" value={stats.total} color="gray" />
|
<StatCard label="Gesamt" value={stats.total} color="gray" />
|
||||||
|
|||||||
Reference in New Issue
Block a user