'use client' import { useState, useEffect, useCallback, useRef } from 'react' import { useParams, useRouter } from 'next/navigation' import { LimitsFormSections } from './_components/LimitsFormSections' import { EMPTY_LIMITS_FORM, type LimitsFormData } from './_types' type SaveStatus = 'idle' | 'saving' | 'saved' | 'error' interface ProjectData { machine_name: string machine_type: string manufacturer: string metadata?: { limits_form?: LimitsFormData [key: string]: unknown } } export default function IACEInterviewPage() { const { projectId } = useParams<{ projectId: string }>() const router = useRouter() const [formData, setFormData] = useState(EMPTY_LIMITS_FORM) const [projectData, setProjectData] = useState(null) const [loading, setLoading] = useState(true) const [saveStatus, setSaveStatus] = useState('idle') const saveTimerRef = useRef | null>(null) const latestFormRef = useRef(EMPTY_LIMITS_FORM) // Load project data and existing form data useEffect(() => { loadProject() }, [projectId]) // eslint-disable-line react-hooks/exhaustive-deps async function loadProject() { try { const res = await fetch(`/api/sdk/v1/iace/projects/${projectId}`) if (!res.ok) return const json = await res.json() const proj: ProjectData = { machine_name: json.machine_name || '', machine_type: json.machine_type || '', manufacturer: json.manufacturer || '', metadata: json.metadata || {}, } setProjectData(proj) // Restore saved form data from metadata if (proj.metadata?.limits_form) { const saved = proj.metadata.limits_form const merged = { ...EMPTY_LIMITS_FORM, ...saved } setFormData(merged) latestFormRef.current = merged } else { // Pre-fill from project fields const prefilled: LimitsFormData = { ...EMPTY_LIMITS_FORM, machine_designation: proj.machine_name, machine_type: proj.machine_type, manufacturer: proj.manufacturer, } setFormData(prefilled) latestFormRef.current = prefilled } } catch (err) { console.error('Failed to load project:', err) } finally { setLoading(false) } } // Debounced auto-save const saveToBackend = useCallback(async (data: LimitsFormData) => { setSaveStatus('saving') try { // Merge limits_form into existing metadata const existingMetadata = projectData?.metadata || {} const newMetadata = { ...existingMetadata, limits_form: data } const res = await fetch(`/api/sdk/v1/iace/projects/${projectId}`, { method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ metadata: newMetadata }), }) if (res.ok) { setSaveStatus('saved') // Also update local projectData metadata so next save merges correctly setProjectData((prev) => prev ? { ...prev, metadata: newMetadata } : prev) setTimeout(() => setSaveStatus((s) => s === 'saved' ? 'idle' : s), 2000) } else { setSaveStatus('error') } } catch { setSaveStatus('error') } }, [projectId, projectData?.metadata]) // eslint-disable-line react-hooks/exhaustive-deps const handleFieldChange = useCallback((field: keyof LimitsFormData, value: string | string[]) => { setFormData((prev) => { const next = { ...prev, [field]: value } latestFormRef.current = next return next }) // Debounce save: wait 1.5s after last change if (saveTimerRef.current) clearTimeout(saveTimerRef.current) saveTimerRef.current = setTimeout(() => { saveToBackend(latestFormRef.current) }, 1500) }, [saveToBackend]) // Cleanup timer on unmount useEffect(() => { return () => { if (saveTimerRef.current) clearTimeout(saveTimerRef.current) } }, []) // Calculate completion percentage const completionPct = calculateCompletion(formData) if (loading) { return (
) } return (
{/* Header */}

Grenzen & Verwendung

Schritt 3 — Bestimmungsgemasse Verwendung, Fehlanwendung und Maschinengrenzen definieren (ISO 12100)

{/* Form Sections */} {/* Navigation */}
) } // ──────────────────────────────────────── // Sub-components (small, page-local) // ──────────────────────────────────────── function SaveIndicator({ status }: { status: SaveStatus }) { if (status === 'idle') return null return (
{status === 'saving' && ( <>
Speichert... )} {status === 'saved' && ( <> Gespeichert )} {status === 'error' && ( <>
Fehler beim Speichern )}
) } function CompletionBadge({ pct }: { pct: number }) { const color = pct >= 80 ? 'text-green-600 bg-green-50 border-green-200' : pct >= 40 ? 'text-yellow-600 bg-yellow-50 border-yellow-200' : 'text-gray-500 bg-gray-50 border-gray-200' return ( {pct}% ausgefuellt ) } /** Calculate how many required-ish fields are filled */ function calculateCompletion(data: LimitsFormData): number { const checks = [ !!data.machine_designation, !!data.machine_type, !!data.manufacturer, !!data.general_description, !!data.intended_purpose, !!data.area_of_use, data.operating_modes.length > 0, !!data.foreseeable_misuses, !!data.spatial_limits, !!data.operating_conditions, !!data.energy_supply, data.person_groups.length > 0, !!data.qualification_requirements, ] const filled = checks.filter(Boolean).length return Math.round((filled / checks.length) * 100) }