CP_EHRENAMT/TASKS-backup.md
2026-06-25 18:58:44 +02:00

385 lines
13 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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, Basis-Layout,
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,
ein base.html mit eingebundener tokens.css und components.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:**
- [x] App startet lokal (`flask run`) und zeigt eine leere Startseite
- [x] `tokens.css` + `components.css` in `base.html` eingebunden
- [x] SQLite initialisiert, `instance/` in `.gitignore`
- [x] Team-Scoping-Helper vorhanden (noch ohne echte Logik)
- [x] `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:**
- [x] Alle Modelle vorhanden, jedes mit `team_id`
- [x] Beziehungen sauber (Einsatz↔Planungszeitraum, Zuteilung↔User/Einsatz)
- [x] Seed legt CheckPoint-Team + 2 Kanäle an
- [x] 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:**
- [x] Login/Logout funktioniert, Passwörter gehasht
- [x] Geschützte Routen ohne Login → Redirect
- [x] Admin kann Nutzer mit Rolle anlegen
- [x] Alle Queries laufen team-gescoped
- [ ] 🏛 Mini-Review (Mistral): Auth gegen CLAUDE.md prüfen — als Liste, nicht umschreiben
---
## Session 4 · Base-Layout, Theming & Dark-Mode-Fix 💻
**Ziel:** base.html final (Topbar, Bottom-Nav Start/Termine/Team/Profil, FAB-Slot),
Brand-Tokens pro Team in den `<head>` injizieren, Mockup-CSS auf Tokens umstellen.
**Prompt:**
```
@file app/templates/base.html
@file app/static/components.css
@file app/static/tokens.css
@file CLAUDE.md
Ansatz zuerst. 1) base.html mit Topbar (Logo, Glocke, Avatar) und Bottom-Nav
(Start, Termine, Team, Profil). 2) Brand-Tokens des aktiven Teams serverseitig
in den <head> schreiben. 3) Im components.css alle fest verdrahteten Weißwerte
(Topbar, Bottom-Nav, .cp-page-Verlauf) auf Tokens umstellen, damit der Dunkelmodus
greift. Erst die Token-Umstellung erklären, dann umsetzen.
```
**Deliverables:**
- [x] base.html mit korrekter Bottom-Nav (4 Punkte, „Termine")
- [x] Brand-Tokens kommen pro Team aus der DB in den `<head>`
- [x] Keine festen Weiß-/Schwarzwerte mehr im components.css (weiße Button-Schrift jetzt über Token `--cp-on-brand`, themeunabhängig)
- [x] Hell- und Dunkelmodus optisch sauber (System-Umschaltung testen)
---
## Session 5 · Planungszeitraum & Einsätze (Admin) 💻
**Ziel:** Admin legt Planungszeiträume an und trägt Einsätze ein (Datum, Zeit, Art).
**Prompt:**
```
@file app/routes/planung.py
@file app/models.py
@file CLAUDE.md
Ansatz zuerst. Admin-Ansichten: 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.
```
**Deliverables:**
- [x] Admin kann Zeitraum anlegen
- [x] Admin kann Einsätze anlegen/bearbeiten/löschen
- [x] Einsatz kennt seine Platzstruktur (2 + Springer)
- [x] Alles team-gescoped, ohne JavaScript
---
## 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:**
- [x] Ehrenamtliche sehen die Einsätze des aktuellen Zeitraums
- [x] kann/kann_nicht wird gespeichert und ist änderbar (nur in_planung)
- [x] Nach Veröffentlichung gesperrt
- [x] Mobile sauber bedienbar (44px-Ziele)
---
## Session 7 · Dienstplan bauen (Admin) 💻
**Ziel:** Admin verteilt manuell 2 Haupt + 1 Springer pro Einsatz, mit Fairness-Anzeige.
**Prompt:**
```
@file app/routes/dienste.py
@file app/models.py
@file CLAUDE.md
Ansatz zuerst. Admin-Ansicht pro Einsatz: 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:**
- [x] Admin kann Haupt- und Springerplätze besetzen
- [x] Fairness-Anzeige (Anzahl + letzter Einsatz) pro Person sichtbar
- [x] Unterbesetzte Einsätze klar markiert
- [ ] 🏛 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:**
- [x] Veröffentlichen funktioniert und sperrt die Verfügbarkeit
- [x] „Meine Termine" zeigt eigene Einsätze als Liste
- [x] 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:**
- [x] Absage einer Hauptperson → Springer rückt automatisch nach
- [x] Frei gewordener Springerplatz wird offen zum Übernehmen
- [x] „Übernehmen" sicher gegen gleichzeitige Klicks (atomares UPDATE)
- [x] Fall „kein Springer" korrekt behandelt
- [x] Review der Übernahme-Logik (Randfälle) — mit Claude Code statt Mistral
---
## Session 10 · Dokumente & Freigabe 💻
**Ziel:** Upload (alle), Freigabe-Warteschlange (Admin), Liste Aktuell/Archiv.
**Prompt:**
```
@file app/routes/dokumente.py
@file app/models.py
@file CLAUDE.md
Ansatz zuerst. Upload (PDF/Word/Bild) mit Pflicht-Titel → Status wartet_auf_freigabe
(nur Admin + Uploader sichtbar). Admin-Ansicht „Freigaben": freigeben/ablehnen
(optional Grund). Admin-Uploads direkt freigegeben. Liste geteilt in Aktuell/Archiv,
Verschieben durch Admin. Dateigrößen-Limit und Dateityp-Prüfung serverseitig.
```
**Deliverables:**
- [x] Upload landet in wartet_auf_freigabe (nicht öffentlich sichtbar)
- [x] Admin kann freigeben/ablehnen, Admin-Uploads direkt sichtbar
- [x] Liste Aktuell + Archiv, Verschieben durch Admin
- [x] 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:**
- [x] Beide Kanäle funktionieren, Rechte korrekt (Ankündigungen nur Admin)
- [x] Nachrichten werden gespeichert und angezeigt (nur Text)
- [x] Polling lädt neue Nachrichten ohne Seiten-Reload
- [x] 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).
**Prompt:**
```
@file app/models.py
@file app/templates/base.html
@file CLAUDE.md
Ansatz zuerst. Einfaches Benachrichtigungsmodell + Anzeige (Glocke in der Topbar mit
Zähler, Liste beim Antippen). 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 zeigt ungelesene an, Liste antippbar
- [ ] 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) ·
automatische Fairness-Verteilung · weitere Rollen ·
Laptop-Spiegelung / Desktop-Fallback über LAN ·
komfortablere Zeit-Auswahl (Rad-/Scroll-Picker für Startzeit & Dauer statt Dropdown).
## Härtung (vor Produktiv-Deploy / Session 15)
- [x] **CSRF-Schutz für alle POST-Formulare.** Erledigt (nach Session 3): eigenes,
Session-gebundenes Token in `app/csrf.py`, ohne Flask-WTF. `csrf_token()` als
Jinja-Global, Prüfung aller unsicheren Methoden via `before_request`.
**Pflicht für alle künftigen POST-Formulare:** verstecktes Feld
`<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">` einbauen.
- [ ] **Brand-Tokens im `<head>` werden mit `|safe` ausgegeben** (`base.html`,
wegen der Anführungszeichen im Font). Quelle ist aktuell nur Admin/Seed, daher
für v1 vertretbar. **Sobald eine UI zum Bearbeiten der Brand-Felder entsteht:**
Werte serverseitig validieren/escapen (CSS-Injection / `</style>`-Ausbruch
verhindern) z. B. Hex-Farben per Regex prüfen, Font auf Whitelist begrenzen.