213 lines
10 KiB
Markdown
213 lines
10 KiB
Markdown
# CLAUDE.md — CheckPoint Ehrenamt App
|
||
|
||
> Projektanker. Claude Code liest diese Datei automatisch zu Beginn jeder Session.
|
||
> Sie ist die verbindliche Quelle für Stack, Regeln und Logik. Bei Widerspruch
|
||
> zwischen Wunsch und dieser Datei: nachfragen, nicht raten.
|
||
|
||
---
|
||
|
||
## Zweck
|
||
|
||
Interne, mobile-first Web-App für das Vor-Ort-Team Prävention der AIDS-Hilfe.
|
||
Sie organisiert Einsätze (Terminabstimmung + Dienstverteilung) und ersetzt
|
||
schrittweise die WhatsApp/Signal-Zettelwirtschaft. **Das Herzstück ist die
|
||
Dienstplanung — alles andere ordnet sich dem unter.**
|
||
|
||
---
|
||
|
||
## Stack & Rahmenbedingungen (fest)
|
||
|
||
| Bereich | Entscheidung |
|
||
|---|---|
|
||
| Sprache/Backend | **Python + Flask** |
|
||
| Templating | **Jinja2**, server-gerendertes HTML (klassische Multi-Page-App) |
|
||
| ORM/DB | **SQLAlchemy**, **SQLite** für v1 (ein VPS, 17 Nutzer). Keine Annahmen treffen, die einen späteren Umstieg auf PostgreSQL verbauen. |
|
||
| Passwörter | `werkzeug.security` (Hashing), nie Klartext |
|
||
| CSS | **selbst gebaut, kein CSS-Framework**. Basis: `tokens.css` + `components.css` |
|
||
| JavaScript | **so wenig wie möglich, nur Vanilla, kein Framework**. Einzige Ausnahme v1: Chat-Polling (s. u.) |
|
||
| Hosting | **eigener VPS in der EU** |
|
||
| Plattform | mobile-first Web-App, **kein App Store**, „zum Homescreen" möglich |
|
||
| UI-Sprache | **Deutsch** |
|
||
|
||
**Erlaubte Zusatz-Bibliotheken (klein halten):** Flask, SQLAlchemy/Flask-SQLAlchemy,
|
||
Werkzeug, optional Flask-Login. Keine schweren Frameworks, kein CSS-/JS-Framework.
|
||
|
||
---
|
||
|
||
## Architektur-Prinzipien
|
||
|
||
1. **Server-rendered first.** Seiten kommen fertig vom Server. Formulare sind echte
|
||
`POST`-Formulare. Kein clientseitiges Routing, kein SPA-Verhalten.
|
||
2. **JavaScript nur, wo es sich beweist.** Einzige JS-Ausnahme in v1: der Chat holt
|
||
neue Nachrichten per kleinem `fetch`-Polling. Sonst nirgends JS verlangen.
|
||
3. **Mandantenfähig ab Tag 1.** Siehe eigener Abschnitt. Jeder Datensatz gehört zu
|
||
genau einem Team.
|
||
4. **Mobile-first.** Getestet in App-Breite 360–460px.
|
||
5. **Barrierearmut ist Pflicht, kein Extra.** 44px Touch-Ziele, sichtbarer Fokus,
|
||
Status nie nur über Farbe, sinnvolle `aria-label`.
|
||
|
||
---
|
||
|
||
## Rollen
|
||
|
||
| Rolle | Rechte |
|
||
|---|---|
|
||
| **Admin** | Logins vergeben, Planungszeiträume und Einsätze anlegen, Dienstplan bauen und veröffentlichen, Dokumente/Fotos freigeben, im Kanal „Ankündigungen" posten, Team verwalten. |
|
||
| **Ehrenamtliche:r** | Verfügbarkeit melden, eigenen Dienstplan sehen, freie Dienste übernehmen, Dokumente/Fotos hochladen (Freigabe nötig), im Kanal „Team" schreiben, Profil pflegen. |
|
||
|
||
- **Keine Selbstregistrierung.** Logins legt ausschließlich der Admin an.
|
||
- **v1:** Nutzername + Passwort. (E-Mail + Code = Future Log.)
|
||
|
||
---
|
||
|
||
## Mandantenfähigkeit (Pflichtregeln)
|
||
|
||
- Zentrale Einheit **`team`** (= Mandant). Das CheckPoint-Team ist das erste von
|
||
perspektivisch mehreren.
|
||
- **Jeder** Datensatz (user, planungszeitraum, einsatz, verfuegbarkeit, zuteilung,
|
||
dokument, kanal, nachricht) trägt eine **`team_id`**.
|
||
- **Jede** Datenbankabfrage filtert nach dem Team des eingeloggten Nutzers. Nie Daten
|
||
über Teamgrenzen hinweg ausliefern. Das aktive Team wird aus der Session abgeleitet.
|
||
- Das Corporate Design wird **pro Team** über die Brand-Tokens in `tokens.css`
|
||
gesetzt (serverseitig in den `<head>` injiziert). Code darf keine Markenfarbe
|
||
fest verdrahten — immer Tokens verwenden.
|
||
|
||
---
|
||
|
||
## Datenmodell (Regeln, konzeptionell)
|
||
|
||
Jede Tabelle hat `team_id`. Namen/Felder sind Richtwerte, keine starre Vorgabe.
|
||
|
||
- **team** — Mandant. Felder u. a.: Name, Brand-Tokens (Primär, Primär-stark, Akzent,
|
||
Akzent-stark, Schrift), Logo-Pfad.
|
||
- **user** — `rolle` (admin | ehrenamt), Nutzername, Passwort-Hash, Anzeigename,
|
||
optionales Profilbild, optionale freiwillige Angaben.
|
||
- **planungszeitraum** — Block über einen frei wählbaren Zeitraum (kein Mindestzeitraum).
|
||
`status`: in_planung | veroeffentlicht.
|
||
- **einsatz** — gehört **optional** zu einem Planungszeitraum (`planungszeitraum_id`
|
||
nullbar). Ohne Zeitraum = **Einzeltermin**, der direkt zu besetzen ist. Datum,
|
||
Uhrzeit (Start/Ende), Art/Ort (z. B. Tour Altstadt, Party, Sonderveranstaltung).
|
||
Braucht **2 Hauptplätze + 1 Springerplatz**.
|
||
- **verfuegbarkeit** — (user × einsatz) → kann | kann_nicht.
|
||
- **zuteilung** — (user × einsatz) mit `platztyp` (haupt | springer) und
|
||
`status` (zugeteilt | abgesagt | offen | uebernommen).
|
||
- **dokument** — Datei (PDF/Word/Bild). Felder: Titel (Pflicht), Uploader, `status`
|
||
(wartet_auf_freigabe | freigegeben | abgelehnt), `ablage` (aktuell | archiv).
|
||
- **kanal** — pro Team zwei feste: `ankuendigungen`, `team`.
|
||
- **nachricht** — Kanal, Autor, Text, Zeitstempel. **Nur Text.**
|
||
|
||
---
|
||
|
||
## Kern-Business-Logik (verbindlich)
|
||
|
||
### Planungszyklus
|
||
1. Admin legt einen Planungszeitraum (frei wählbare Länge) an und trägt die kuratierten
|
||
Einsätze ein (Freitags-Touren, Samstags-Einsätze/Partys, Sonderveranstaltungen).
|
||
Alternativ: einzelne Einzeltermine ohne Zeitraum, direkt zum Besetzen.
|
||
2. Status `in_planung` → Ehrenamtliche melden pro Einsatz **kann / kann nicht**.
|
||
Änderung der Verfügbarkeit nur solange `in_planung`.
|
||
3. Admin baut den Plan **manuell**: pro Einsatz **2 Haupt + 1 Springer**. Die App zeigt
|
||
pro Person **Anzahl bisheriger Einsätze** und **letzten Einsatz** als Hilfe
|
||
(keine automatische Verteilung in v1).
|
||
4. Bei zu wenigen Verfügbaren: Einsatz als **unterbesetzt** markieren.
|
||
5. Admin **veröffentlicht** → alle sehen ihren persönlichen Dienstplan.
|
||
|
||
### Absage & Übernahme
|
||
- Sagt eine **Hauptperson** ab → **Springer rückt automatisch nach** (Status `uebernommen`).
|
||
- Der frei gewordene **Springerplatz** wird `offen` → zum **„Übernehmen"** für alle.
|
||
- Gibt es keinen Springer / sagt er auch ab → offener Platz für alle.
|
||
- „Übernehmen" dient **nur dem Nachrücken**, nie der Erstverteilung.
|
||
|
||
### Dokumente
|
||
- Upload durch alle, aber Status startet **`wartet_auf_freigabe`** (nur Admin + Uploader sichtbar).
|
||
- Admin gibt frei oder lehnt ab (optional Grund). Erst nach Freigabe für alle sichtbar.
|
||
- Admin-Uploads gehen direkt durch.
|
||
- Liste geteilt in **Aktuell** und **Archiv** (Verschieben macht der Admin).
|
||
- **Foto-Regel:** nur ohne erkennbare Dritte bzw. mit deren Einverständnis.
|
||
|
||
### Chat
|
||
- Zwei Kanäle: **Ankündigungen** (nur Admin postet, alle lesen) und **Team** (alle posten).
|
||
- Nur Text. Neue Nachrichten via Hintergrund-`fetch`-Polling (einzige v1-JS-Ausnahme).
|
||
|
||
### Benachrichtigungen
|
||
- **v1: nur in-App** (neuer Dienst, offener Dienst, neue Nachricht). Push/E-Mail = Future Log.
|
||
|
||
---
|
||
|
||
## Designsystem
|
||
|
||
- Quelle der Wahrheit: **`tokens.css`** (mandantenfähig, hell + dunkel via `light-dark()`).
|
||
- **Hell/Dunkel** folgt standardmäßig der Systemeinstellung. Zusätzlich gibt es einen
|
||
manuellen Umschalter (Topbar-Icon, schaltet System → Hell → Dunkel). Er läuft
|
||
**serverseitig ohne JS**: ein POST setzt ein `theme`-Cookie, der Server schreibt
|
||
`data-theme="light|dark"` an `<html>` (bei „System" kein Attribut). „System" ist
|
||
Default und jederzeit wieder wählbar.
|
||
- **Nie feste Farben** im Komponenten-CSS — immer Tokens (`var(--cp-...)`).
|
||
- Erledigt (Session 4 + Nachzug): `components.css` enthält **kein** Farb-Literal mehr.
|
||
Die früher feste weiße Button-Schrift auf Türkis/Rot läuft jetzt über das Token
|
||
`--cp-on-brand` (BRAND-Ebene, bewusst themeunabhängig, kein `light-dark()`, da die
|
||
Flächen `--cp-teal`/`--cp-red` in beiden Themes derselbe Vollton sind). Pro Team
|
||
überschreibbar. Regel daher ausnahmslos: nie Farb-Literale im Komponenten-CSS.
|
||
- Status-Mapping: Türkis = offen/bestätigt/verfügbar · Rot = dringend/Vertretung ·
|
||
Neutral = Info/intern/erledigt.
|
||
- Schrift: DM Sans.
|
||
|
||
---
|
||
|
||
## UI-Regeln
|
||
|
||
- Mobile-first bei 360–460px Basisbreite; auf Tablet/Desktop wächst die zentrierte
|
||
Spalte (inkl. fixierter Topbar/Bottom-Nav) ab 768px auf ~720px. Viel Weißraum,
|
||
klare Karten (Radius 12px, Innenabstand 16px), keine verschachtelten Karten.
|
||
- **Bottom-Navigation, 4 Punkte: Start · Termine · Team · Profil.** (Nicht „Kalender".)
|
||
- Jede Ansicht hat **eine klare Hauptaktion** (Button oder FAB).
|
||
- Buttons: Primär = Türkis/weiß · Dringend = Rot/weiß · Ghost = Fläche mit feinem
|
||
Rahmen. Kurze Beschriftungen: „Speichern", „Eintragen", „Übernehmen", „Details".
|
||
- Formulare: Labels immer sichtbar über dem Feld, Felder ≥ 44px, kurze freundliche
|
||
Hilfetexte, Fehler klar und nicht nur über Farbe.
|
||
|
||
## Texte / Copy
|
||
|
||
- **Deutsch**, Satzanfang groß (sentence case), aktive Verben, keine Floskeln.
|
||
- Eine Aktion heißt im ganzen Flow gleich (Button „Übernehmen" → Bestätigung „Übernommen").
|
||
- Fehlermeldungen sagen, was passiert ist und wie es weitergeht — ohne Entschuldigung.
|
||
|
||
---
|
||
|
||
## Bewusst raus aus v1 (Out of Scope)
|
||
|
||
Keine öffentlichen Seiten · keine Klient:innendaten · keine Bezahlfunktion ·
|
||
kein Kalender-Sync (Terminübersicht = Liste) · keine Dateien/Bilder im Chat ·
|
||
keine automatische Dienstverteilung · kein 1:1-Chat · kein Push/E-Mail.
|
||
|
||
---
|
||
|
||
## Vorgeschlagene Projektstruktur
|
||
|
||
```
|
||
checkpoint-ehrenamt/
|
||
├── CLAUDE.md
|
||
├── TASKS.md
|
||
├── app/
|
||
│ ├── __init__.py # App-Factory, DB-Init, Team-Scoping
|
||
│ ├── models.py # SQLAlchemy-Modelle (alle mit team_id)
|
||
│ ├── auth.py # Login, Logout, Admin-Anlage
|
||
│ ├── routes/ # Blueprints: planung, dienste, dokumente, chat, profil
|
||
│ ├── templates/ # Jinja2 (base.html + Teilansichten)
|
||
│ └── static/
|
||
│ ├── tokens.css
|
||
│ └── components.css
|
||
├── instance/ # SQLite-DB, Uploads (nicht im Git)
|
||
└── requirements.txt
|
||
```
|
||
|
||
---
|
||
|
||
## Arbeitsweise mit den Modellen (Continue)
|
||
|
||
- **Mistral (Architect):** Planung, Design-Entscheidungen, Reviews. Nie zum Schreiben
|
||
von Produktivcode.
|
||
- **Qwen (Coder):** Implementierung, Bugfixes, Inline-Edits (`Ctrl+I`). Immer ein
|
||
Stück nach dem anderen (Datenmodell → Service → Route → Template).
|
||
- **Claude Code:** Scaffold, Mehrdateien-Arbeit, übergreifendes Verdrahten.
|
||
- **Eine Aufgabe pro Session.** Immer `@file`-Kontext. Plan vor Code.
|