feat(iace-ui): component presence/CE review + machine-type dropdown
- Components view: three presence sections (Vorhanden / Nicht vorhanden / Geloescht) with bidirectional move + soft-delete (audit-visible, restorable), so the expert corrects the engine's best-effort negation in both directions. - CE marking per component (bought robot/actuator/SPS) with a clear "validate the integrated safety function (PL/SIL)" note when also safety-relevant. Safe semantics: hazards are not suppressed, only provenance is surfaced. - Project-create form: machine type is now a grouped dropdown from the engine's controlled vocabulary (GET /machine-types) instead of free text. - Knowledge graph: component→hazard edges use the real component_id. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -2,17 +2,25 @@
|
||||
|
||||
import { useState } from 'react'
|
||||
import { useParams } from 'next/navigation'
|
||||
import { Component } from './_components/types'
|
||||
import { Component, buildTree } from './_components/types'
|
||||
import { ComponentTreeNode } from './_components/ComponentTreeNode'
|
||||
import { ComponentForm } from './_components/ComponentForm'
|
||||
import { ComponentLibraryModal } from './_components/ComponentLibraryModal'
|
||||
import { PresenceSection } from './_components/PresenceSection'
|
||||
import { useComponents } from './_hooks/useComponents'
|
||||
|
||||
export default function ComponentsPage() {
|
||||
const params = useParams()
|
||||
const projectId = params.projectId as string
|
||||
|
||||
const { loading, tree, handleSubmit, handleDelete, handleAddFromLibrary } = useComponents(projectId)
|
||||
const { loading, components, handleSubmit, handleDelete, handleAddFromLibrary, setPresence, setCEMarked } = useComponents(projectId)
|
||||
|
||||
// Split auto-detected components by presence so the expert can review the
|
||||
// engine's best-effort negation verdicts and move items in both directions.
|
||||
const present = components.filter((c) => !c.presence_status || c.presence_status === 'vorhanden')
|
||||
const negated = components.filter((c) => c.presence_status === 'nicht_vorhanden')
|
||||
const deleted = components.filter((c) => c.presence_status === 'geloescht')
|
||||
const tree = buildTree(present)
|
||||
|
||||
const [showForm, setShowForm] = useState(false)
|
||||
const [editingComponent, setEditingComponent] = useState<Component | null>(null)
|
||||
@@ -110,7 +118,9 @@ export default function ComponentsPage() {
|
||||
<div className="py-1">
|
||||
{tree.map((component) => (
|
||||
<ComponentTreeNode key={component.id} component={component} depth={0}
|
||||
onEdit={handleEdit} onDelete={handleDelete} onAddChild={handleAddChild} />
|
||||
onEdit={handleEdit} onDelete={handleDelete} onAddChild={handleAddChild}
|
||||
onMarkAbsent={(id) => setPresence(id, 'nicht_vorhanden')}
|
||||
onToggleCE={setCEMarked} />
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
@@ -140,6 +150,27 @@ export default function ComponentsPage() {
|
||||
</div>
|
||||
)
|
||||
)}
|
||||
|
||||
<PresenceSection
|
||||
title="Nicht vorhanden"
|
||||
hint={'Vom System als verneint erkannt (z. B. „keine Pneumatik"). Pruefen Sie und verschieben Sie bei Bedarf zu Vorhanden.'}
|
||||
accent="border-amber-400"
|
||||
components={negated}
|
||||
actions={[
|
||||
{ label: '→ Vorhanden', variant: 'primary', onClick: (id) => setPresence(id, 'vorhanden') },
|
||||
{ label: 'Loeschen', variant: 'danger', onClick: handleDelete },
|
||||
]}
|
||||
/>
|
||||
|
||||
<PresenceSection
|
||||
title="Geloescht"
|
||||
hint="Entfernte Komponenten bleiben zur Nachvollziehbarkeit sichtbar und koennen wiederhergestellt werden."
|
||||
accent="border-gray-400"
|
||||
components={deleted}
|
||||
actions={[
|
||||
{ label: 'Wiederherstellen', variant: 'neutral', onClick: (id) => setPresence(id, 'vorhanden') },
|
||||
]}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user