'use client' import { useEffect, useRef, useState, useTransition } from 'react' import { sectionText } from '@/lib/i18n/cases' import { useSectionsForm } from './SectionsForm' 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. * * Der Knopf steht **am Blatt, nicht am Feld**: dieses Feld meldet sich beim * umgebenden Formular an und wird mit allen anderen zusammen gespeichert. Nur * wenn kein Formular da ist, trägt es seinen eigenen Knopf. * * 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) const formular = useSectionsForm() // Was zuletzt gespeichert wurde. Daran hängt, ob dieses Feld beim nächsten // Speichern überhaupt mitgeschickt wird. const gespeichert = useRef(normalise(props.initialValue, props.type)) const aktuell = useRef(value) aktuell.current = value const stand = useRef(version) stand.current = version useEffect(() => { if (!formular) return return formular.register({ fieldKey: props.fieldKey, collect: () => aktuell.current === gespeichert.current ? null : { fieldKey: props.fieldKey, value: parse(aktuell.current, props.type), baseVersion: stand.current, }, apply: (ergebnis) => { if (ergebnis.ok) { gespeichert.current = aktuell.current setVersion(ergebnis.version) setConflict(null) setError(null) setSaved(true) setDraftKept(false) formular.changed(props.fieldKey, false) try { localStorage.removeItem(speicherSchluessel) } catch { // egal } return } if (ergebnis.conflict) setConflict(ergebnis.current) else setError(ergebnis.error) }, }) }, [formular, props.fieldKey, props.type, speicherSchluessel]) // 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) formular?.changed(props.fieldKey, next !== gespeichert.current) 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) { gespeichert.current = override === undefined ? value : normalise(override, props.type) setVersion(result.version) setConflict(null) setSaved(true) setDraftKept(false) formular?.changed(props.fieldKey, 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' ? (