Web-App zur Schicht- und Personalplanung in Zahnarztpraxen. Ersetzt Papierpläne und Excel durch einen digitalen Wochenplan mit automatischer Planung, Abwesenheitsverwaltung und WhatsApp-Versand.
Live: https://statt-tipex.de
- Single-File-App: Die komplette Anwendung steckt in
server/index.html(vorkompiliertes React überReact.createElement, kein Build-Schritt). Zum Ändern wird direkt diese Datei bearbeitet und deployt. - Backend: Firebase Realtime Database (Daten) + Firebase Authentication (Login). Kein eigener Server-Code — nur statisches Hosting via Nginx.
- Mandantenfähig: Jede Praxis hat einen Code; die Daten liegen unter
tenants/{CODE}/….
- Wochenplan mit Stationen (Empfang, Büro, Behandlungszimmer, Steri, Prophylaxe, Springer) und automatischem Vorschlag (Arzt-Assistenz-Paarung, faire Verteilung).
- Team-Verwaltung mit Rollen (Arzt, ZFA, ZMP, ZMV, Azubi), Verfügbarkeiten pro Wochentag und Ausnahmen (z. B. Berufsschule: pro Wochentag andere Zeit oder frei).
- Abwesenheiten (Urlaub, Krank, Fortbildung, Überstunden-Abbau, Sonstiges); nur „Urlaub" zählt gegen die Urlaubstage.
- Sonderöffnungszeiten als Datumsbereich oder einzelner Sondertag; Knopf „Außerh. Öffnungszeiten räumen" entfernt Schichten außerhalb der Öffnungszeiten, ohne den ganzen Plan neu zu generieren.
- WhatsApp-Versand persönlicher Wochenpläne, Urlaubsanträge mit Freigabe, Echtzeit-Sync über alle Geräte.
Der Zugang pro Praxis erfolgt über Praxis-Code + Name + Passwort
(Firebase Authentication; intern wird eine synthetische E-Mail
name@praxiscode.stattipex.local verwendet).
Drei Rollen:
- Admin — Vollzugriff, kann Nutzer verwalten (Einstellungen → „Zugang & Nutzer").
- Nutzer — kann den Plan bearbeiten.
- Nur ansehen — reiner Lesezugriff, Bearbeiten ist gesperrt.
Wer eine Praxis absichert, wird ihr erster Admin. Ein Banner in der App führt unabgesicherte Praxen einmalig durch das Einrichten.
Statisches Hosting via Nginx, Web-Root /var/www/statt-tipex/.
Host, SSH-User und Zugangsdaten stehen bewusst nicht im Repo — unten die
Platzhalter <user> und <server> durch die echten Werte ersetzen.
Auf dem Mac hochladen:
scp ~/Downloads/index.html <user>@<server>:~/index.htmlAuf dem Server an ihren Platz verschieben (braucht sudo, da /var/www root gehört):
ssh <user>@<server>
sudo mv ~/index.html /var/www/statt-tipex/index.htmlDanach im Browser mit Cmd+Shift+R hart neu laden.
Die Rules regeln die eigentliche Zugriffskontrolle. Sie liegen nicht im Git, sondern werden in der Firebase Console (Realtime Database → Regeln) gepflegt. Aktueller Stand:
{
"rules": {
"tenants": {
"DEMO": { ".read": true, ".write": true },
"$tid": {
".read": "auth != null && root.child('tenantMembers').child($tid).child(auth.uid).exists()",
".write": "auth != null && root.child('tenantMembers').child($tid).child(auth.uid).exists() && root.child('tenantMembers').child($tid).child(auth.uid).child('role').val() != 'viewer'"
}
},
"tenantMembers": {
"$tid": {
".read": "auth != null && data.child(auth.uid).exists()",
"$uid": {
".write": "auth != null && ( root.child('tenantMembers').child($tid).child(auth.uid).child('role').val() == 'admin' || ( !data.parent().exists() && $uid == auth.uid ) )"
}
}
},
"tenantSecured": {
".read": true,
"$tid": {
".write": "auth != null && ( !data.exists() || root.child('tenantMembers').child($tid).child(auth.uid).child('role').val() == 'admin' )"
}
},
"feedback": { ".read": true, ".write": true }
}
}Hinweise:
DEMOundfeedbackbleiben bewusst offen (öffentliche Demo bzw. Beta-Feedback).tenantSecured/{CODE}ist ein öffentlich lesbares Boolean, damit der Client vor dem Login weiß, ob eine Praxis Anmeldung verlangt.- Beim Ausrollen neuer Rules gilt: erst alle aktiven Praxen absichern (Admin-Konto anlegen), dann die Rules scharf schalten — sonst sperren sich bestehende Praxen aus, bis sie ein Konto haben.
tenants/{CODE}/
praxis-staff Team (Array von Mitarbeiter-Objekten)
praxis-hours Sonderöffnungszeiten
praxis-standard-week Standardwoche
praxis-station-reqs Stationen-Bedarf
praxis-vacations Abwesenheiten
praxis-wk-YYYY-MM-DD Wochenplan je Woche
praxis-lastedit-wk-… Letzte Änderung je Woche { at, by, what }
praxis-audit-wk-… Änderungsverlauf je Woche (rollend, max. 50)
praxis-pin / praxis-unlocked
tenantMembers/{CODE}/{uid} { name, role, created }
tenantSecured/{CODE} true
feedback/{ts} Beta-Feedback
- Rollen-Wechsel eines bestehenden Nutzers geht nur über Entfernen + Neu-Anlegen.
- Admin kann Passwörter anderer nicht zurücksetzen (bräuchte eine Cloud Function).
- Für Nur-Lese-Nutzer sind Bearbeiten-Knöpfe noch sichtbar, aber wirkungslos (nichts wird gespeichert; serverseitig ohnehin blockiert).
- Letzte Änderung & Verlauf: Der Wochenplan merkt sich pro Woche, wer wann
zuletzt geändert hat (
praxis-lastedit-…), inkl. kurzer Zusammenfassung (z. B. „+2 h geplant"). Ein aufklappbarer Verlauf zeigt die letzten Einträge (praxis-audit-…, rollend max. 50). Beides in Echtzeit synchron. - Rückgängig (Undo): macht die letzte Planänderung der aktuellen Woche zurück (bis zu 30 Schritte, pro Woche eigener Verlauf).
- Kalender-Export (.ics): persönlicher Wochenplan bzw. Team-Plan als
.ics-Datei (im „WhatsApp"-Dialog). Import in Apple/Google/Outlook-Kalender. - Arzt-Assistenz-Paar-Präferenzen: pro Person „Bevorzugte Zusammenarbeit" (im Team-Editor); der automatische Vorschlag versucht, bevorzugte Paare als Arzt-Assistenz zusammen einzuteilen.
- Druck/PDF: Druck-Button jetzt auch mobil; Druck-Header zeigt Praxis-Code und „Stand: …".
- Login & Rollen: Anmeldung pro Praxis über Firebase Authentication (Praxis-Code + Name + Passwort), Nutzerverwaltung, „Praxis absichern"-Banner. Rollen Admin / Nutzer / Nur ansehen.
- Nur-Lese-Rolle: Viewer sieht alles, kann aber nichts ändern (Client-Sperre + Security Rule).
- Abwesenheiten: neuer Typ Überstunden-Abbau (eigene Farbe; zählt nicht gegen die Urlaubstage).
- Sonderöffnungszeiten: einzelner Sondertag (offen mit Zeit oder geschlossen) mit Vorrang vor Datumsbereichen; Knopf „Außerh. Öffnungszeiten räumen" zum Aufräumen ohne Neu-Generieren.
- Wochenarbeitszeit: Übersicht der verplanten Wochenstunden pro Person im Team-Tab (läuft runter, „+X“ bei Überschreitung); Warn-Banner im Plan, wenn manuell über die Wochenarbeitszeit geplant wird; der Auto-Vorschlag hält die Wochenarbeitszeit ein.
- Mitarbeiter-Ausnahmen: pro Wochentag abweichende Zeit oder frei (z. B. Berufsschule) — nur gesetzte Tage werden überschrieben.
- Kleinkram: Team-Ansicht alphabetisch nach Vorname; Rollenbezeichnung ZMP korrigiert (vorher ZNP); Enter/Return in Login- und Code-Feldern.
MIT © 2026 André Bajorat.
Frei nutz-, änder- und weiterverteilbar unter den Bedingungen der MIT-Lizenz;
Details in der LICENSE-Datei. Wer den Code weiterverwendet, muss lediglich
den Copyright- und Lizenzhinweis beibehalten.