feat: Variantenmanagement — Sub-Projekte mit GAP-Analyse
Backend: - parent_project_id auf iace_projects (DB + Go Struct) - POST/GET /variants + GET /variant-gap Endpoints - GAP-Analyse: Differenz Hazards/Massnahmen/Kategorien Frontend: - VariantPanel auf Projekt-Uebersicht - Variante erstellen Dialog - Sidebar-Anzeige (Variantenanzahl / Basis-Link) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -108,12 +108,40 @@ export default function IACELayout({ children }: { children: React.ReactNode })
|
||||
const params = useParams()
|
||||
const projectId = params?.projectId as string | undefined
|
||||
const [projectName, setProjectName] = React.useState('')
|
||||
const [variantInfo, setVariantInfo] = React.useState<{
|
||||
parentProjectId?: string; parentName?: string; variantCount?: number
|
||||
}>({})
|
||||
|
||||
React.useEffect(() => {
|
||||
if (!projectId) return
|
||||
fetch(`/api/sdk/v1/iace/projects/${projectId}`)
|
||||
.then(r => r.ok ? r.json() : null)
|
||||
.then(d => { if (d?.machine_name) setProjectName(d.machine_name) })
|
||||
.then(async (d) => {
|
||||
if (!d?.machine_name) return
|
||||
setProjectName(d.machine_name)
|
||||
// Resolve variant info
|
||||
if (d.parent_project_id) {
|
||||
try {
|
||||
const pRes = await fetch(`/api/sdk/v1/iace/projects/${d.parent_project_id}`)
|
||||
if (pRes.ok) {
|
||||
const pj = await pRes.json()
|
||||
setVariantInfo({ parentProjectId: d.parent_project_id, parentName: pj.machine_name })
|
||||
} else {
|
||||
setVariantInfo({ parentProjectId: d.parent_project_id })
|
||||
}
|
||||
} catch { setVariantInfo({ parentProjectId: d.parent_project_id }) }
|
||||
} else {
|
||||
// Check if this project has variants
|
||||
try {
|
||||
const vRes = await fetch(`/api/sdk/v1/iace/projects/${projectId}/variants`)
|
||||
if (vRes.ok) {
|
||||
const vj = await vRes.json()
|
||||
const list = vj.variants || vj.projects || []
|
||||
if (list.length > 0) setVariantInfo({ variantCount: list.length })
|
||||
}
|
||||
} catch { /* no variants endpoint yet */ }
|
||||
}
|
||||
})
|
||||
.catch(() => {})
|
||||
}, [projectId])
|
||||
|
||||
@@ -148,6 +176,21 @@ export default function IACELayout({ children }: { children: React.ReactNode })
|
||||
{projectName}
|
||||
</p>
|
||||
)}
|
||||
{variantInfo.parentProjectId && (
|
||||
<Link
|
||||
href={`/sdk/iace/${variantInfo.parentProjectId}`}
|
||||
className="mt-1 flex items-center gap-1 text-[10px] text-orange-600 hover:text-orange-700 dark:text-orange-400 truncate"
|
||||
title={variantInfo.parentName ? `Variante von: ${variantInfo.parentName}` : 'Variante'}
|
||||
>
|
||||
<svg className="w-3 h-3 flex-shrink-0" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M7 16V4m0 0L3 8m4-4l4 4" />
|
||||
</svg>
|
||||
Variante von: {variantInfo.parentName || 'Basis'}
|
||||
</Link>
|
||||
)}
|
||||
{variantInfo.variantCount != null && variantInfo.variantCount > 0 && (
|
||||
<p className="mt-1 text-[10px] text-gray-400">({variantInfo.variantCount} Varianten)</p>
|
||||
)}
|
||||
<p className="text-[10px] text-gray-400 mt-0.5">CE-Compliance</p>
|
||||
<Link
|
||||
href="/sdk/iace/lines"
|
||||
|
||||
Reference in New Issue
Block a user