# TASKS.md — Build-Checkliste CheckPoint Ehrenamt App > Eine Aufgabe pro Session. Nach Abhängigkeit geordnet — von oben nach unten abarbeiten. > Pro Session: **Tool**, **Ziel**, **Session-Prompt** (zum Einfügen), **Deliverables**. > Nach jeder Session: alle Häkchen setzen, committen, erst dann die nächste starten. > > Tool-Legende: 🛠 Claude Code · 💻 Continue + Qwen · 🏛 Continue + Mistral > Modell wechseln: `llama-switch` → [1] Architect / [2] Coder --- ## Session 1 · Scaffold & Projektgerüst 🛠 **Ziel:** Lauffähiges Flask-Grundgerüst mit App-Factory, SQLite, zwei leeren Shell-Templates, Static-Einbindung und Team-Scoping-Stub. **Prompt:** ``` Lies CLAUDE.md. Erstelle das Projektgerüst gemäß der vorgeschlagenen Struktur: Flask-App-Factory, SQLAlchemy mit SQLite (instance/), requirements.txt, zwei minimale Shell-Templates base_user.html (Klasse cp-shell-user) und base_admin.html (Klasse cp-shell-admin), jeweils mit eingebundener tokens.css und ihrem jeweiligen Komponenten-CSS, sowie ein leeres Team-Scoping (Helper, der das aktive Team aus der Session liest). Zeig mir zuerst den Plan und die Dateiliste, bevor du schreibst. ``` **Deliverables:** - [ ] App startet lokal (`flask run`) und zeigt eine leere Startseite - [ ] `base_user.html` und `base_admin.html` existieren, je mit korrekter Shell-Klasse - [ ] `tokens.css` in beiden eingebunden; `components.css` nur in `base_user.html`, `components-admin.css` nur in `base_admin.html` - [ ] SQLite initialisiert, `instance/` in `.gitignore` - [ ] Team-Scoping-Helper vorhanden (noch ohne echte Logik) - [ ] `requirements.txt` minimal gehalten --- ## Session 2 · Datenmodell & Mandanten 💻 **Ziel:** Alle Modelle aus CLAUDE.md, jedes mit `team_id`. Seed für das erste Team (CheckPoint) inkl. Brand-Tokens und den zwei Chat-Kanälen. **Prompt:** ``` @file app/models.py @file CLAUDE.md Erst den Ansatz erklären, dann implementieren — ein Modell nach dem anderen. Baue die Modelle: team, user, planungszeitraum, einsatz, verfuegbarkeit, zuteilung, dokument, kanal, nachricht. Jedes mit team_id. Danach ein Seed-Skript, das das CheckPoint-Team mit Brand-Tokens und den Kanälen "ankuendigungen" und "team" anlegt. Halte dich an die Feldvorgaben in CLAUDE.md. ``` **Deliverables:** - [ ] Alle Modelle vorhanden, jedes mit `team_id` - [ ] Beziehungen sauber (Einsatz↔Planungszeitraum, Zuteilung↔User/Einsatz) - [ ] Seed legt CheckPoint-Team + 2 Kanäle an - [ ] DB lässt sich anlegen, Seed läuft ohne Fehler --- ## Session 3 · Auth & Admin-Anlage 💻 **Ziel:** Login (Nutzername + Passwort, gehasht), Session, Logout, Team-Scoping aktiv, Admin kann Nutzer anlegen. Keine Selbstregistrierung. **Prompt:** ``` @file app/auth.py @file app/models.py @file CLAUDE.md Ansatz zuerst. Baue: Login mit Nutzername+Passwort (werkzeug-Hashing), Session, Logout. Aktiviere das Team-Scoping (aktives Team aus der Session). Admin-geschützte Route zum Anlegen neuer Nutzer (Rolle wählbar). Geschützte Routen leiten ohne Login zur Anmeldung um. Ein Stück nach dem anderen. ``` **Deliverables:** - [ ] Login/Logout funktioniert, Passwörter gehasht - [ ] Geschützte Routen ohne Login → Redirect - [ ] Admin kann Nutzer mit Rolle anlegen - [ ] Alle Queries laufen team-gescoped - [ ] 🏛 Mini-Review (Mistral): Auth gegen CLAUDE.md prüfen — als Liste, nicht umschreiben --- ## Session 4a · User-Shell: Base-Layout, Theming & Dark-Mode-Fix 💻 **Ziel:** `base_user.html` final (Topbar, Bottom-Nav Start/Termine/Team/Profil, FAB-Slot), User-Brand-Tokens pro Team in den `` injizieren, Mockup-CSS auf `--cp-user-*`-Tokens umstellen. **Prompt:** ``` @file app/templates/base_user.html @file app/static/components.css @file app/static/tokens.css @file CLAUDE.md Ansatz zuerst. 1) base_user.html mit Topbar (Logo, Glocke, Avatar), Body-Klasse cp-shell-user und Bottom-Nav (Start, Termine, Team, Profil). 2) User-Brand-Tokens (--cp-user-brand-*) des aktiven Teams serverseitig in den schreiben. 3) Im components.css alle fest verdrahteten Weißwerte (Topbar, Bottom-Nav, .cp-page-Verlauf) auf --cp-user-*-Tokens umstellen, damit der Dunkelmodus greift. Erst die Token-Umstellung erklären, dann umsetzen. ``` **Deliverables:** - [ ] `base_user.html` mit korrekter Bottom-Nav (4 Punkte, „Termine"), Klasse `cp-shell-user` - [ ] User-Brand-Tokens kommen pro Team aus der DB in den `` - [ ] Keine festen Weiß-/Schwarzwerte mehr im `components.css` - [ ] Hell- und Dunkelmodus optisch sauber (System-Umschaltung testen) --- ## Session 4b · Admin-Shell: Sidebar, Off-Canvas & Theming 💻 **Ziel:** `base_admin.html` mit Desktop-Sidebar (Start, Planung, Dienste, Freigaben, Team, Chat-Verwaltung, Profil, Abmelden), Admin-Brand-Tokens pro Team in den ``, mobiles Off-Canvas-Menü per Hamburger-Icon und kleinem Vanilla-JS. **Prompt:** ``` @file app/templates/base_admin.html @file app/static/components-admin.css @file app/static/tokens.css @file app/static/sidebar.js @file CLAUDE.md Ansatz zuerst. 1) base_admin.html mit Body-Klasse cp-shell-admin: Sidebar links (Logo oben, Navigationspunkte Start/Planung/Dienste/Freigaben/Team/Chat-Verwaltung/ Profil, Abmelden unten abgetrennt) und Hauptbereich rechts. 2) Admin-Brand-Tokens (--cp-admin-brand-*) serverseitig in den schreiben. 3) components-admin.css ausschließlich mit --cp-admin-*-Tokens aufbauen (Cards, Tabellen, Buttons gemäß CLAUDE.md-Regeln). 4) Unter dem Breakpoint: Sidebar kollabiert zu einer schmalen Topbar mit Hamburger-Icon; sidebar.js togglet eine Klasse, die die Sidebar als Off-Canvas-Overlay einblendet, schließt bei Klick auf Overlay-Fläche oder einen Menüpunkt. Vanilla, keine Bibliothek. Erst den Ansatz fürs Off-Canvas-Verhalten erklären, dann umsetzen. ``` **Deliverables:** - [ ] `base_admin.html` mit Sidebar (alle 7 Punkte + Abmelden), Klasse `cp-shell-admin` - [ ] Admin-Brand-Tokens kommen pro Team aus der DB in den `` - [ ] `components-admin.css` nutzt ausschließlich `--cp-admin-*`-Tokens, keine Vermischung mit User-Tokens - [ ] Sidebar kollabiert unter dem Breakpoint zu Hamburger + Off-Canvas-Overlay - [ ] Hell- und Dunkelmodus der Arbeitsfläche sauber (Sidebar bleibt bewusst dunkel in beiden Modi) - [ ] Touch-Ziele auch im Admin-Bereich ≥ 44px --- ## Session 5 · Planungszeitraum & Einsätze (Admin) 💻 **Ziel:** Admin legt Planungszeiträume an und trägt Einsätze ein (Datum, Zeit, Art). Läuft in der Admin-Shell. **Prompt:** ``` @file app/routes/planung.py @file app/templates/base_admin.html @file app/models.py @file CLAUDE.md Ansatz zuerst. Admin-Ansichten (erweitern base_admin.html): Planungszeitraum anlegen (Status in_planung) und darin Einsätze hinzufügen/bearbeiten/löschen (Datum, Start/Ende, Art/Ort, je 2 Haupt- + 1 Springerplatz). Reine POST-Formulare, kein JS. Liste der Einsätze eines Zeitraums als Cards/Tabelle im Admin-Stil. ``` **Deliverables:** - [ ] Admin kann Zeitraum anlegen - [ ] Admin kann Einsätze anlegen/bearbeiten/löschen - [ ] Einsatz kennt seine Platzstruktur (2 + Springer) - [ ] Alles team-gescoped, ohne JavaScript, in der Admin-Shell --- ## Session 6 · Verfügbarkeit melden (Ehrenamtliche) 💻 **Ziel:** Ehrenamtliche melden pro Einsatz „kann / kann nicht", solange `in_planung`. **Prompt:** ``` @file app/routes/planung.py @file app/models.py @file CLAUDE.md Ansatz zuerst. Ansicht für Ehrenamtliche: Liste der Einsätze im laufenden Zeitraum mit Umschaltung kann/kann_nicht pro Einsatz (POST-Formular). Änderung nur bei Status in_planung. Übersichtlich für mobile. ``` **Deliverables:** - [ ] Ehrenamtliche sehen die Einsätze des aktuellen Zeitraums - [ ] kann/kann_nicht wird gespeichert und ist änderbar (nur in_planung) - [ ] Nach Veröffentlichung gesperrt - [ ] Mobile sauber bedienbar (44px-Ziele) --- ## Session 7 · Dienstplan bauen (Admin) 💻 **Ziel:** Admin verteilt manuell 2 Haupt + 1 Springer pro Einsatz, mit Fairness-Anzeige. Läuft in der Admin-Shell (Tabellen-Stil). **Prompt:** ``` @file app/routes/dienste.py @file app/templates/base_admin.html @file app/models.py @file CLAUDE.md Ansatz zuerst. Admin-Ansicht pro Einsatz (in der Admin-Shell, Tabelle im Stil aus CLAUDE.md: Navy-Tabellenkopf, Statusspalten mit Text+Farbe): verfügbare Personen auswählen und auf Haupt/Springer setzen. Pro Person anzeigen: Anzahl bisheriger Einsätze und letzter Einsatz (Fairness-Hilfe). Einsatz als „unterbesetzt" markieren, wenn zu wenige. Keine automatische Verteilung — nur Anzeige + manuelle Auswahl. ``` **Deliverables:** - [ ] Admin kann Haupt- und Springerplätze besetzen - [ ] Fairness-Anzeige (Anzahl + letzter Einsatz) pro Person sichtbar - [ ] Unterbesetzte Einsätze klar markiert - [ ] Tabelle nutzt ausschließlich `--cp-admin-*`-Tokens - [ ] 🏛 Review (Mistral): Zuteilungslogik gegen CLAUDE.md prüfen --- ## Session 8 · Veröffentlichen & persönliche Terminübersicht 💻 **Ziel:** Admin veröffentlicht den Zeitraum; jede:r sieht „Meine Termine" als Liste. **Prompt:** ``` @file app/routes/dienste.py @file app/templates/ @file CLAUDE.md Ansatz zuerst. 1) Admin-Aktion „Veröffentlichen" (Status veroeffentlicht, sperrt Verfügbarkeit). 2) Ansicht „Meine Termine": Liste der eigenen zugeteilten Einsätze, chronologisch, mit Rolle (Haupt/Springer) und Status. Keine Kalenderansicht. ``` **Deliverables:** - [ ] Veröffentlichen funktioniert und sperrt die Verfügbarkeit - [ ] „Meine Termine" zeigt eigene Einsätze als Liste - [ ] Haupt/Springer und Status erkennbar (nicht nur über Farbe) --- ## Session 9 · Absage & Übernahme 💻 **Ziel:** Absage einer Hauptperson → Springer rückt automatisch nach → Springerplatz wird offen → „Übernehmen" für alle. **Prompt:** ``` @file app/routes/dienste.py @file app/models.py @file CLAUDE.md Ansatz zuerst. Logik laut CLAUDE.md: Sagt eine Hauptperson ab, rückt der Springer automatisch nach (status uebernommen) und der Springerplatz wird offen. Offene Plätze erscheinen mit „Übernehmen"-Button; wer zuerst klickt, bekommt ihn (sauber gegen Doppel-Klicks absichern). Kein Springer → Platz direkt offen. ``` **Deliverables:** - [ ] Absage einer Hauptperson → Springer rückt automatisch nach - [ ] Frei gewordener Springerplatz wird offen zum Übernehmen - [ ] „Übernehmen" sicher gegen gleichzeitige Klicks - [ ] Fall „kein Springer" korrekt behandelt - [ ] 🏛 Review (Mistral): Übernahme-Logik prüfen (Randfälle!) --- ## Session 10 · Dokumente & Freigabe 💻 **Ziel:** Upload (alle), Freigabe-Warteschlange (Admin), Liste Aktuell/Archiv. **Hinweis — gemischte Shell:** Die Upload-/Listenansicht für Ehrenamtliche läuft in der User-Shell (`base_user.html`), die Freigaben-Ansicht ausschließlich in der Admin-Shell (`base_admin.html`). Beide Templates entsprechend ansprechen, nicht vermischen. **Prompt:** ``` @file app/routes/dokumente.py @file app/templates/base_user.html @file app/templates/base_admin.html @file app/models.py @file CLAUDE.md Ansatz zuerst. 1) Upload-Ansicht in der User-Shell (PDF/Word/Bild) mit Pflicht-Titel → Status wartet_auf_freigabe (nur Admin + Uploader sichtbar), dort auch die Liste Aktuell/Archiv für Ehrenamtliche. 2) Freigaben-Ansicht in der Admin-Shell: freigeben/ablehnen (optional Grund), Admin-Uploads direkt freigegeben, Verschieben Aktuell↔Archiv. Dateigrößen-Limit und Dateityp-Prüfung serverseitig. ``` **Deliverables:** - [ ] Upload landet in wartet_auf_freigabe (nicht öffentlich sichtbar), in der User-Shell - [ ] Admin kann freigeben/ablehnen in der Admin-Shell, Admin-Uploads direkt sichtbar - [ ] Liste Aktuell + Archiv (User-Shell), Verschieben durch Admin (Admin-Shell) - [ ] Server prüft Dateityp und -größe --- ## Session 11 · Chat (2 Kanäle, Polling) 💻 **Ziel:** Ankündigungen (nur Admin postet) + Team (alle). Nur Text. Neue Nachrichten per `fetch`-Polling (einzige v1-JS-Ausnahme). **Prompt:** ``` @file app/routes/chat.py @file app/models.py @file CLAUDE.md Ansatz zuerst. Zwei Kanäle: ankuendigungen (nur Admin darf posten, alle lesen) und team (alle posten). Nur Text. Nachrichten chronologisch. Dazu ein kleiner JSON-Endpunkt „neue Nachrichten seit X" und ~30 Zeilen Vanilla-JS, das im Hintergrund pollt und neue Nachrichten anhängt. Kein Framework, kein WebSocket. ``` **Deliverables:** - [ ] Beide Kanäle funktionieren, Rechte korrekt (Ankündigungen nur Admin) - [ ] Nachrichten werden gespeichert und angezeigt (nur Text) - [ ] Polling lädt neue Nachrichten ohne Seiten-Reload - [ ] JS ist minimal und vanilla --- ## Session 12 · Profil 💻 **Ziel:** Profilbild-Upload + Platzhalter für optionale freiwillige Angaben. **Prompt:** ``` @file app/routes/profil.py @file app/models.py @file CLAUDE.md Ansatz zuerst. Profil-Ansicht: Profilbild hochladen/ändern, Anzeigename, optionale freiwillige Felder (vorerst ein, zwei Freitextfelder als Platzhalter — Struktur so, dass Felder später leicht ergänzt werden). Nichts davon Pflicht. ``` **Deliverables:** - [ ] Profilbild hochladbar - [ ] Optionale Felder vorhanden, nichts Pflicht - [ ] Struktur erlaubt späteres Ergänzen von Feldern --- ## Session 13 · In-App-Benachrichtigungen 💻 **Ziel:** Dezente in-App-Hinweise (neuer Dienst, offener Dienst, neue Nachricht) in beiden Shells. **Prompt:** ``` @file app/models.py @file app/templates/base_user.html @file app/templates/base_admin.html @file CLAUDE.md Ansatz zuerst. Einfaches Benachrichtigungsmodell + Anzeige: Glocke in der Topbar der User-Shell mit Zähler, Liste beim Antippen; in der Admin-Shell entsprechend in der Sidebar/Kopfbereich. Auslöser: neue Zuteilung, neuer offener Dienst, neue Nachricht. Als gelesen markierbar. Kein Push, keine E-Mail. ``` **Deliverables:** - [ ] Benachrichtigungen werden bei den drei Auslösern erzeugt - [ ] Glocke/Hinweis zeigt ungelesene an, in beiden Shells passend platziert - [ ] Als gelesen markierbar --- ## Session 14 · Gesamt-Review 🏛 **Ziel:** Durchsicht des fertigen Stands gegen CLAUDE.md — Korrektheit, Konsistenz, Barrierearmut, Dunkelmodus. **Prompt:** ``` @file CLAUDE.md @file app/models.py @file app/routes/ Review den aktuellen Stand gegen CLAUDE.md. Korrektheitsprobleme zuerst, dann Konsistenz und Wartbarkeit, dann Barrierearmut und Dunkelmodus-Lücken. Nichts umschreiben — alles als nummerierte Liste mit Fundstelle. ``` **Deliverables:** - [ ] Review-Liste erstellt - [ ] Gefundene Korrektheitsprobleme in 💻-Folgeschritten behoben - [ ] A11y- und Dark-Mode-Lücken geschlossen --- ## Session 15 · Deployment auf den VPS 🛠 **Ziel:** App läuft auf dem EU-VPS hinter einem produktiven Server. **Prompt:** ``` Lies CLAUDE.md. Hilf mir beim Deployment auf einen EU-VPS: WSGI-Server (z. B. gunicorn) hinter nginx, Umgebungsvariablen/Secrets, Persistenz für SQLite und Uploads, einfaches Backup der DB, HTTPS. Gib mir die Schritte als Fish-Befehle, wo Terminal nötig ist. Plan zuerst. ``` **Deliverables:** - [ ] App läuft unter gunicorn hinter nginx - [ ] HTTPS aktiv - [ ] Secrets über Umgebungsvariablen, nicht im Code - [ ] DB + Uploads persistent, einfaches DB-Backup eingerichtet --- ## Future Log (nicht in v1) 1:1-Chat · Push-/E-Mail-Benachrichtigungen · E-Mail+Code-Login (Magic Link) · manueller Hell/Dunkel-Umschalter · automatische Fairness-Verteilung · weitere Rollen · Laptop-Spiegelung / Desktop-Fallback über LAN.