/* App shell — routing, persistence, Tweaks */ const { useState: useStateApp, useEffect: useEffectApp, useCallback: useCallbackApp } = React; const TWEAK_DEFAULTS = /*EDITMODE-BEGIN*/{ "specialty": "Kinderchirurgie", "defaultFormat": "auto" }/*EDITMODE-END*/; function Toast({ msg }) { return
{msg}
; } function Topbar({ page, onHome, onList, specialty }) { return (
Diktat
{specialty}
); } function App() { // Simple route state: "home" | "rec" | "list" | "detail" const [page, setPage] = useStateApp("home"); const [currentId, setCurrentId] = useStateApp(null); const [recordings, setRecordings] = useStateApp(() => Store.load()); const [toast, setToast] = useStateApp(""); // Tweaks const tweaks = window.useTweaks ? window.useTweaks(TWEAK_DEFAULTS) : [TWEAK_DEFAULTS, () => {}]; const [tweakState, setTweakState] = tweaks; const refresh = useCallbackApp(() => setRecordings(Store.load()), []); const showToast = useCallbackApp((msg) => { setToast(msg); setTimeout(() => setToast(""), 2000); }, []); // Kicks the user back to the home screen so the inline recorder is visible. const handleStartRecording = () => setPage("home"); const handleDoneRecording = async ({ transcript, duration }) => { // Create recording skeleton const id = uid(); const rec = { id, createdAt: Date.now(), updatedAt: Date.now(), duration, transcript, letter: "", title: "Neue Aufnahme", format: null, status: "processing", needsFormat: false, }; Store.add(rec); refresh(); setCurrentId(id); setPage("detail"); // Detect format (unless user forced a specific one) let fmt = tweakState.defaultFormat && tweakState.defaultFormat !== "auto" ? tweakState.defaultFormat : null; if (!fmt) { const detected = await detectFormat(transcript); if (detected === "unsure") { Store.update(id, { needsFormat: true, status: "ready" }); refresh(); // Also generate title in background generateTitle(transcript).then(title => { Store.update(id, { title }); refresh(); }); return; } fmt = detected; } // Generate letter + title in parallel try { const [letter, title] = await Promise.all([ generateLetter(transcript, fmt, tweakState.specialty || "Kinderchirurgie"), generateTitle(transcript), ]); Store.update(id, { letter, title, format: fmt, status: "ready" }); refresh(); } catch (e) { Store.update(id, { letter: "Fehler bei der Briefgenerierung. Bitte im Ansicht-Modus Format wählen, um es erneut zu versuchen.", format: fmt, status: "ready", needsFormat: true, }); refresh(); } }; const handleOpen = (id) => { setCurrentId(id); setPage("detail"); }; const handleChange = (id, patch) => { Store.update(id, patch); refresh(); }; const handleDelete = (id) => { Store.remove(id); refresh(); setPage("list"); showToast("Aufnahme gelöscht"); }; const currentRecording = recordings.find(r => r.id === currentId); return (
setPage("home")} onList={() => setPage("list")} specialty={tweakState.specialty || "Kinderchirurgie"} /> {page === "home" && ( setPage("list")} recordingsCount={recordings.length} /> )} {page === "list" && ( )} {page === "detail" && currentRecording && ( setPage("list")} onChange={handleChange} onDelete={handleDelete} showToast={showToast} /> )} {page === "detail" && !currentRecording && (
Aufnahme nicht gefunden.
)} {/* Tweaks */} {window.TweaksPanel && ( setTweakState({ specialty: v })} options={[ { value: "Kinderchirurgie", label: "Kinderchirurgie" }, { value: "Allgemeinchirurgie", label: "Allgemeinchirurgie" }, { value: "Pädiatrie", label: "Pädiatrie" }, { value: "Viszeralchirurgie", label: "Viszeralchirurgie" }, { value: "Unfallchirurgie", label: "Unfallchirurgie" }, { value: "Urologie (Kinder)", label: "Kinderurologie" }, ]} /> setTweakState({ defaultFormat: v })} options={[ { value: "auto", label: "Automatisch erkennen" }, { value: "arztbrief", label: "Arztbrief" }, { value: "konsult", label: "Konsultationsbericht" }, { value: "soap", label: "SOAP-Notiz" }, { value: "opbericht", label: "Operationsbericht" }, ]} /> )}
); } const root = ReactDOM.createRoot(document.getElementById("root")); root.render();