'use client' import { useEffect, useRef, useState, useTransition } from 'react' import { de } from '@/lib/i18n/de' import type { SaveSection } from './types' type Props = { /** Wohin gespeichert wird — gebundene Server Action der Akte oder des Dokuments. */ save: SaveSection /** Eindeutig je Besitzer, damit Entwürfe zweier Akten sich nicht mischen. */ scope: string fieldKey: string label: string type: string options?: string[] help?: string requiredToClose?: boolean initialValue: unknown initialVersion: number readOnly: boolean } const AUTOSAVE_MS = 2000 /** * Ein Abschnitt zum Bearbeiten. Trägt einen Fallabschnitt genauso wie ein * Feld eines Dokuments — wohin gespeichert wird, kommt von außen. * * Entwürfe werden alle zwei Sekunden **lokal** gesichert, damit ein * versehentlicher Tab-Wechsel keinen Text kostet. Gespeichert wird erst auf * Knopfdruck: ein Abschnitt ist der Sachstand der Akte, nicht ein Chatbeitrag. * Siehe docs/09-performance-und-ux.md. * * Beim Speichern geht die geladene Version mit. Weicht sie ab, gibt es kein * stilles Überschreiben, sondern beide Fassungen nebeneinander. */ export function SectionEditor(props: Props) { const speicherSchluessel = `afis:abschnitt:${props.scope}:${props.fieldKey}` const [value, setValue] = useState(() => normalise(props.initialValue, props.type)) const [version, setVersion] = useState(props.initialVersion) const [conflict, setConflict] = useState<{ value: unknown; version: number } | null>(null) const [error, setError] = useState(null) const [saved, setSaved] = useState(false) const [draftKept, setDraftKept] = useState(false) const [pending, startTransition] = useTransition() const timer = useRef | null>(null) // Entwurf aus dem letzten Besuch übernehmen, falls er neuer ist. useEffect(() => { try { const stored = localStorage.getItem(speicherSchluessel) if (stored !== null && stored !== normalise(props.initialValue, props.type)) { setValue(stored) setDraftKept(true) } } catch { // Privates Fenster oder gesperrter Speicher: dann eben ohne Entwurf. } }, [speicherSchluessel, props.initialValue, props.type]) function change(next: string) { setValue(next) setSaved(false) if (timer.current) clearTimeout(timer.current) timer.current = setTimeout(() => { try { localStorage.setItem(speicherSchluessel, next) setDraftKept(true) } catch { // still ignorieren, der Entwurf ist eine Bequemlichkeit } }, AUTOSAVE_MS) } function submit(override?: unknown, baseVersion = version) { setError(null) startTransition(async () => { const result = await props.save({ fieldKey: props.fieldKey, value: override ?? parse(value, props.type), baseVersion, }) if (result.ok) { setVersion(result.version) setConflict(null) setSaved(true) setDraftKept(false) try { localStorage.removeItem(speicherSchluessel) } catch { // egal } return } if (result.conflict) setConflict(result.current) else setError(result.error) }) } if (props.readOnly) { return (

{String(value || '—')}

) } return (
{props.type === 'select' ? ( ) : props.type === 'longtext' ? (