'use client' import { useState, useEffect, useCallback } from 'react' import { useParams } from 'next/navigation' type Suggestion = { name: string reduction_type: 'design' | 'protection' | 'information' | string description: string source_project_count: number source_project_names: string[] is_customer_standard: boolean has_verified_instances: boolean } type ProjectInfo = { customer_name?: string; machine_name?: string } // /sdk/iace/[projectId]/customer-standards // // Surfaces mitigations that the expert flagged as "Kundenstandard" (or // successfully verified) in earlier projects of the SAME customer. Picking // one and clicking "Übernehmen" applies it to all matching hazards in the // current project — every match is set to is_relevant=true, // is_customer_standard=true, status='verified'. Saves the round-trip // through Massnahmen + Verifikation for the cases where the safety expert // already knows the answer from a prior plant at the same site. // // Filter "Auch verifizierte einbeziehen" widens the pool beyond strictly // is_customer_standard=true to also include status='verified' rows — useful // when the customer-standard habit is not yet established in the corpus. export default function CustomerStandardsPage() { const params = useParams() const projectId = params.projectId as string const [suggestions, setSuggestions] = useState([]) const [project, setProject] = useState(null) const [loading, setLoading] = useState(true) const [includeVerified, setIncludeVerified] = useState(false) const [importing, setImporting] = useState(null) const [importedNames, setImportedNames] = useState>(new Set()) const [selected, setSelected] = useState>(new Set()) const [error, setError] = useState(null) const load = useCallback(async () => { setLoading(true) setError(null) try { const [sgRes, prRes] = await Promise.all([ fetch(`/api/sdk/v1/iace/projects/${projectId}/customer-standards?include_verified=${includeVerified}`), fetch(`/api/sdk/v1/iace/projects/${projectId}`), ]) if (sgRes.ok) { const j = await sgRes.json() setSuggestions(j.suggestions || []) } if (prRes.ok) { const j = await prRes.json() const p = j.project || j setProject({ customer_name: p.customer_name, machine_name: p.machine_name }) } } catch (e) { setError(e instanceof Error ? e.message : String(e)) } finally { setLoading(false) } }, [projectId, includeVerified]) useEffect(() => { load() }, [load]) function toggleSelect(name: string) { setSelected((prev) => { const next = new Set(prev) if (next.has(name)) next.delete(name); else next.add(name) return next }) } async function importOne(name: string) { setImporting(name) try { const r = await fetch(`/api/sdk/v1/iace/projects/${projectId}/customer-standards/import`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ name }), }) if (r.ok) { setImportedNames((prev) => new Set(prev).add(name)) setSelected((prev) => { const n = new Set(prev); n.delete(name); return n }) } else { const j = await r.json().catch(() => null) setError(j?.error || `HTTP ${r.status}`) } } finally { setImporting(null) } } async function importSelected() { const names = Array.from(selected) for (const n of names) { await importOne(n) } } if (loading) return (
) // No customer set → guide the user to set it first const hasCustomer = !!(project?.customer_name && project.customer_name.trim() !== '') if (!hasCustomer) { return (

Kundenstandards

Dieses Projekt hat noch keinen Kundennamen. Damit Massnahmen aus früheren Anlagen desselben Kunden wiederverwendet werden können, trage den Kundennamen unter Auftrag → Kunde ein. Sobald der Kundenname gesetzt ist, erscheint hier die Liste der wiederverwendbaren Maßnahmen aus seinen Vorprojekten.
) } return (

Kundenstandards

Übernimm Maßnahmen, die der Kunde {project?.customer_name} in anderen Anlagen bereits als Standard etabliert hat. Übernehmen setzt sie für alle passenden Gefährdungen relevant und verifiziert ohne Nachweis.

{selected.size > 0 && ( )}
{error &&
Fehler: {error}
} {suggestions.length === 0 && (
Keine wiederverwendbaren Maßnahmen für {project?.customer_name} gefunden. {!includeVerified && ' Aktiviere „Auch verifizierte einbeziehen" oben rechts, um den Pool zu erweitern.'}
)} {suggestions.length > 0 && (
Massnahme
Vorprojekte
Status
Aktion
{suggestions.map((s) => { const imported = importedNames.has(s.name) return (
toggleSelect(s.name)} disabled={imported} className="accent-purple-600" />
{s.name}
{s.description &&
{s.description}
} {s.source_project_names.length > 0 && (
aus: {s.source_project_names.slice(0,3).join(', ')}{s.source_project_names.length > 3 ? ` (+${s.source_project_names.length - 3})` : ''}
)}
{s.source_project_count}×
{s.is_customer_standard && Kundenstandard} {s.has_verified_instances && !s.is_customer_standard && Verifiziert}
{imported ? ( ✓ Übernommen ) : ( )}
) })}
)}
) }