tagesdokumentation-ueberstunden-rufdienst

Projekt: Tagesdokumentation Überstunden & Rufdienst (Klinikum Freudenstadt)

tagesdokumentation_erfassung.html hat im Footer eine Versionsnummer („vX.Y · Alexander Zabelyshenskiy”), identisch in build_html.py gepflegt. Bei jeder inhaltlichen Änderung an HTML/JS (nicht bei reinen Doku-Änderungen) die Versionsnummer in BEIDEN Dateien hochzählen (Bugfix → Patch, z.B. 1.5→1.6; neues Feature → Minor, z.B. 1.6→1.7).

Kontext

Dr. Alexander Zabelyshenskiy (Oberarzt, Allgemein-, Viszeral- und Gefäßchirurgie, Klinikum Landkreis Freudenstadt) muss täglich einen offiziellen Klinikum-Bogen (“Tagesdokumentation: Überstunden und Inanspruchnahme im Rufdienst”) ausfüllen. Das Original-PDF-Layout/der Text darf nicht verändert werden (offizielles Dokument, Weiterleitung an Personalabteilung + Chefarzt-Unterschrift).

Ziel

  1. Ausfüllbare PDF (Tagesdokumentation_Ueberstunden_Rufdienst_ausfuellbar.pdf): Original-PDF + echte, interaktive AcroForm-Textfelder (keine reinen Text-Overlays).
    • Kopfteil fest ausgefüllt & schreibgeschützt: Name/Vorname, Personalnummer, Funktion/Position, Klinik/Abteilung (ohne “Klinikum Freudenstadt” im Wert, nur “Allgemein-, Viszeral- u. Gefäßchirurgie”)
    • Datum, Dienstart sowie alle Zeiteinträge/Rufdienst-Zeilen/Zusammenfassung: leer, direkt klickbar ausfüllbar
    • Dienstart/Grund der Überstunden/Art der Inanspruchnahme: exakter Wortlaut aus der Original-Formular-Legende (nicht verändern!)
  2. HTML-Erfassungstool (tagesdokumentation_erfassung.html): Eigenständige HTML-Datei mit eingebettetem Base64-PDF-Template + pdf-lib (CDN). Erfasst Tageswerte komfortabel im Browser, berechnet automatisch (Überstunden, Dauer je Rufdienst-Zeile, Summe, Anzahl Einsätze) und füllt beim Klick auf “PDF ausfüllen & herunterladen” die echten PDF-Formularfelder aus (per pdf-lib form.getTextField(id).setText(...)), dann Download.
    • Zusätzliches Feld “Zeitvorlage” (NICHT Teil des offiziellen Dienstart-Felds!) füllt bei Auswahl automatisch Dienstbeginn/planmäßiges Dienstende:
      • amb2: Mo, 07:10–16:05
      • amb4: 07:25–16:05
      • ZD1: Mo–Do, 11:00–18:00
      • ZD2: 11:00–18:00
      • RBD: nur Sa, 09:00–10:30 (danach zählt als RBA → Hinweis in Erläuterung)
      • RBD2: nur So/Feiertag, Arbeitszeit wie Sa
    • FRN (frei nach Dienst, nach Nachtoperation): Checkbox setzt Bemerkungstext “FRN – frei nach Dienst” in Erläuterung. Normale Arbeitszeit wird trotzdem eingetragen (kein Minusstunden-Abzug) – das ist Absicht, nicht korrigieren!
    • RBA = Aktivzeit (von/bis), RBT = Telefonat (von/bis) – das sind KEINE Dienstart-Optionen, sondern beschreiben Einsatzarten im Rufdienst (Abschnitt 2). Das Dropdown “Art der Inanspruchnahme” nutzt aber den offiziellen Formular-Wortlaut (telefonisch/Präsenz im Haus/Operation/Sonstiges).
    • Datum-Feld: Default = gestriges Datum, mit ‹ › Pfeilen zum Vor-/Zurückblättern

Wichtige technische Learnings (nicht wiederholen!)

Status: verifiziert (Stand: aktueller Commit)

Alle Feldkoordinaten wurden gegen die exakten Vektor-Zellgrenzen der Original-PDF geprüft (Kopfteil, Abschnitt 1, 2, 3). Kein bekannter Layout-Bug mehr offen. Bei künftigen Änderungen an Koordinaten immer page.get_drawings() verwenden (siehe oben), nicht schätzen.

Dateien in diesem Repo

Offene Punkte / mögliche nächste Schritte

Update (Redesign Dienstart + Cookies)

Zwei parallele Dateien – IMMER BEIDE pflegen! (nur für gemeinsame Logik)

PWA (app/index.html) – Stand nach Redesign-Session (13.09.2026)

Ausgangslage war ein Wunsch nach “schönerem Layout”. Verlauf zur Nachvollziehbarkeit für künftige Sessions:

  1. Erster Versuch: Emoji-Icons + dunkles Petrol-Theme nach einem groben ChatGPT-Mockup-Bild → Nutzer-Feedback: “hast alles nachgezeichnet, kannst du aber sehr schlecht” (Emoji als Icon-Ersatz wirkt billig).
  2. Nutzer hat daraufhin ChatGPT einen sehr detaillierten UI/UX-Redesign-Prompt entlocken lassen (Design-Tokens, Spacing-System, explizit KEIN Emoji/PNG, KEIN Telefon-Icon für Rufdienst, echte SVG-Icons, Ruhezeit-Status als Badge, kompakte Einsatz-Liste). Danach komplett neu umgesetzt:
    • Eigenes kleines inline-SVG-Icon-Set (Lucide/Feather-Stil): ICONS- Objekt + icon(name)-Helper in <script>. Statische Platzhalter im HTML als <span data-icon="clock"></span> werden beim Laden per document.querySelectorAll('[data-icon]') einmalig durch echtes <svg> ersetzt (spart, das SVG-Markup in jedem Card-Header zu duplizieren). Neues Icon hinzufügen = Eintrag in ICONS + data-icon- Attribut setzen.
    • Design-Tokens als CSS Custom Properties in :root (Farben, Radien, Spacing) – dadurch ließ sich der komplette spätere Theme-Wechsel (dunkel → hell) fast nur über die :root-Werte erledigen.
    • Rufdienst-Einsätze: kompakte Zeilen (Zeit/Dauer/Art-Badge/Chevron), Antippen klappt die volle Bearbeitungsmaske auf (expanded-Set in JS, Reset bei Tageswechsel über lastRenderedDay-Tracking). Alle Felder (Beginn/Ende/Art/Grund/Fallnummer) bleiben erhalten, nur die Darstellung ist neu.
    • Ruhezeit-Status: rein visuelles Badge (renderRuhezeitStatus()), das nur den Wert des bestehenden ja/nein-Dropdowns farbig darstellt – KEINE neue Berechnungslogik (der offene Punkt “Ruhezeit-Automatik” von weiter oben ist weiterhin NICHT umgesetzt).
  3. Live-Test auf iPhone deckte zwei echte Bugs auf:
    • header{position:sticky} hat auf dem Gerät nicht zuverlässig oben fixiert → Statusleiste überlappte Karteninhalt beim Scrollen. Fix: position:fixed (wie die Bottom-Nav), main-Padding-Top wird per JS (syncHeaderPad()) anhand der echten Header-Höhe gesetzt, nicht fest verdrahtet.
    • input[type=time/date] hatte eigenen Rahmen+Hintergrund UND das native iOS-Zeit-Widget zeigt selbst eine abgerundete Kapsel → “Doppelkapsel”-Optik. Fix: border-color:transparent; background:transparent für diese Input-Typen, nur das native Widget bleibt sichtbar.
    • Dienstplan-Listen (Meine Dienste/Team/Erfasste Tage) waren eine lange flache Liste → jetzt über renderGroupedList() nach Monat gruppiert (natives <details>/<summary>, kein extra JS-Toggle nötig, CSS rotiert das Chevron-Icon über .monthgroup[open] summary .ic).
  4. Nutzer-Feedback zum dunklen Theme: “zu dunkel und zu trivial” → auf Nachfrage explizit helles “Warm & Ruhig”-Theme (Creme/Weiß, warme Brauntöne) gewählt, nicht das neutral-graue “Minimalistisch”. Umgesetzt rein über die :root-Tokens plus:
    • apple-mobile-web-app-status-bar-style von black-translucent auf default (sonst wäre die Uhrzeit-Anzeige auf hellem Grund unsichtbar).
    • Tag-Badges (telefonisch/Präsenz/Operation/Sonstiges) von hell-auf-dunkel auf dunkel-auf-hell gedreht (sonst unlesbar auf Weiß).
    • Gegen “trivial”: dezente Farbverläufe + farbige Schatten auf den drei Haupt-Buttons (Gehen/PDF/Einsatz starten), aktiver Bottom-Nav-Tab als Farbpille statt nur Textfarbwechsel.
  5. Service-Worker-Falle (nicht wiederholen!): Nach den beiden letzten Theme-Commits vergessen, CACHE-Version in app/sw.js hochzuzählen → installierte PWA hat trotz gepushtem Code weiter die alte gecachte index.html ausgeliefert (“online, aber nicht sichtbar”-Verwirrung). Strukturell gefixt: index.html wird jetzt network-first geladen (Cache nur Offline-Fallback), alle anderen Assets (PDF, Icons, CDN-Libs) bleiben cache-first. Dadurch muss die CACHE-Konstante bei reinen index.html-Änderungen NICHT mehr manuell hochgezählt werden – nur noch, wenn sich sw.js selbst oder die SHELL-Liste ändert. Zweiter Teil derselben Falle: Auch mit network-first blieb ein Bugfix (PDF-Button hinter Bottom-Nav) auf dem Gerät unsichtbar, obwohl der Code per GitHub-API nachweislich korrekt auf main war – Ursache war der Service-Worker-Update-Mechanismus selbst: register('sw.js') ohne explizites reg.update() unterliegt dem Browser-Throttle, und selbst eine erfolgreich installierte neue SW-Version hat die Seite nicht automatisch neu geladen. Fix: reg.update() nach der Registrierung explizit aufrufen, plus location.reload() bei controllerchange bzw. wenn die neu installierte SW den Status activated erreicht (siehe Ende von app/index.html, Abschnitt “Start”). Zuverlässigster manueller Workaround bei Verdacht auf Cache-Trägheit: Home-Screen-Icon löschen und über Safari neu “Zum Home-Bildschirm hinzufügen” (komplett frische Installation ohne alten Service Worker).

PWA-Versionsanzeige + Bottom-Padding (13.09.2026)

PWA: Backup-Import, Monatsgruppen, Kalender-Export (13.09.2026, v1.8.0)

PWA v1.9.0 – Wortlaut-Angleich an das Desktop-Tool + Tag-Ansicht (13.09.2026)

PWA v1.10.0 – Kalender monatsweise + Telefonat-Button (13.09.2026)

PWA v1.12.0 – Diensttausch direkt in „wer hat wann Dienst” (13.09.2026)

PWA v1.12.2 – „Diesen Tag zurücksetzen” wirkt jetzt auch im Verlauf

Offene Punkte PWA (Stand 13.09.2026)

GitHub-Push-Learning (Fehlerquelle!)

Offene, noch nicht umgesetzte Nutzer-Anforderungen (Stand 11.09.2026)

  1. Cookie-Speicherung einschränken: Ab dem Feld “Dienstart” und allen darunterliegenden Feldern (Dienstzeit-Block, 8 Rufdienst-Zeilen, Zusammenfassung/Ausgleich) soll NICHT mehr automatisch in Cookies gespeichert werden – hat sich in der Praxis nicht bewährt (Grund: soll pro Tag neu/leer starten). Nur die Kopfdaten (Name, Personalnummer, Funktion, Klinik/Abteilung) sollen weiterhin persistiert werden. Betrifft saveFormToCookie()/loadFormFromCookie() in BEIDEN Dateien (Selektor .block müsste auf einen engeren Container eingeschränkt werden, der nur die Kopfdaten umfasst).
  2. Minusstunden-Logik – erledigt (11.09.2026): recalcDuty()/calc() haben bei negativer Differenz pauschal 24h addiert (Mitternacht-Wrap-Annahme für den regulären Dienst), dadurch kam bei frühem Dienstende ein falscher großer positiver Wert raus statt Minusstunden. Fix in BEIDEN Dateien (Desktop tagesdokumentation_erfassung.html/build_html.py + PWA app/index.html): kein +24-Wrap mehr bei tats_dienstende < plan_dienstende. Hinweis: Falls der reguläre Dienst je über Mitternacht gehen sollte, würde das jetzt fälschlich negativ berechnet – bisher kein bekannter Anwendungsfall.
  3. Dienstart-Label “Regeldienst (AMB4)”: Am 11.09.2026 umbenannt in “Regeldienst (Di–Fr, AMB4)” (Desktop-Tool) bzw. “Regeldienst (Di–Fr)” (PWA) – bereits erledigt und gepusht (Desktop-Commit 5af1987b, PWA-Commit 03d5d531).