# 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` (User-Shell) + `components-admin.css` (Admin-Shell) |
| 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.** Zwei JS-Ausnahmen in v1: der Chat holt
   neue Nachrichten per kleinem `fetch`-Polling, und die Admin-Sidebar wird auf
   schmalen Bildschirmen per Hamburger-Icon als Off-Canvas-Overlay ein-/ausgeklappt
   (Klasse toggeln, kein Framework). 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.

---

## Zwei Oberflächen, zwei Shells (Pflichtregel)

Es gibt **zwei eigenständige Layout-Shells**, eine pro Rolle. Sie sind keine
Variante derselben Vorlage, sondern zwei getrennte Templates mit jeweils eigenem
Tokensatz:

| | **User-Shell** | **Admin-Shell** |
|---|---|---|
| Für wen | Ehrenamtliche | Admin |
| Vorlage | `base_user.html` | `base_admin.html` |
| Navigation | Bottom-Nav, 4 Punkte (Start, Termine, Team, Profil) | Sidebar links, fest: Start, Planung, Dienste, Freigaben, Team, Chat-Verwaltung, Profil, Abmelden |
| Grundform | Mobile-first (App-Breite 360–460px) | Desktop-first (Sidebar 260px), responsiv bis Mobile |
| Mobiles Verhalten | nativ mobil, keine Anpassung nötig | Sidebar wird unter dem Breakpoint zu einer **Off-Canvas-Navigation**: Hamburger-Icon oben links öffnet sie als Overlay |
| Tokens | `--cp-user-*` (Türkis/Rot, DM Sans) | `--cp-admin-*` (Navy/Teal/Gelb/Pink, Roboto Condensed + Alfa Slab One für Seitentitel) |
| CSS-Wurzelklasse | `.cp-shell-user` | `.cp-shell-admin` |

**Wichtig:** Die Admin-Startseite hat **keine eigene Kachel-Navigation** wie die
User-Startseite — die Sidebar selbst trägt die Hauptaktionen. Die Admin-Inhaltsfläche
zeigt direkt die Arbeit (Tabellen, Listen, Formulare), nicht zusätzliche Navigations-
Buttons.

**Mobile Sidebar-Mechanik (einzige zusätzliche v1-JS-Ausnahme neben dem Chat):**
Unter dem Breakpoint kollabiert die Sidebar zu einem Hamburger-Icon in einer
schmalen Topbar. Klick öffnet die Sidebar als Off-Canvas-Overlay (kleines
Vanilla-JS: Klasse toggeln, Overlay schließt bei Klick daneben oder auf einen
Menüpunkt). Kein Framework, keine Bibliothek.

**Mandantenfähigkeit gilt für beide Shells unabhängig:** ein neues Team tauscht
sowohl den `--cp-user-brand-*`- als auch den `--cp-admin-brand-*`-Block. Die beiden
Themes werden nie gemischt — Komponenten in der User-Shell nutzen ausschließlich
`--cp-user-*`, Komponenten in der Admin-Shell ausschließlich `--cp-admin-*`.

---

## 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 von mind. 2 Monaten. `status`: in_planung | veroeffentlicht.
- **einsatz** — gehört zu einem Planungszeitraum. 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 Planungszeitraum (≥ 2 Monate) an und trägt die kuratierten Einsätze ein
   (Freitags-Touren, Samstags-Einsätze/Partys, Sonderveranstaltungen).
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`** — enthält **zwei vollständig getrennte
  Tokensätze**, `--cp-user-*` (User-Shell) und `--cp-admin-*` (Admin-Shell), je
  mandantenfähig und mit hell + dunkel via `light-dark()`.
- **User-Theme:** Türkis/Rot, Schrift DM Sans. Status-Mapping: Türkis =
  offen/bestätigt/verfügbar · Rot = dringend/Vertretung · Neutral = Info/intern/erledigt.
- **Admin-Theme:** Navy-Sidebar (in beiden Farbmodi gleich dunkel — bewusst, sie ist
  das feste Markenelement), helle Arbeitsfläche, Teal für Aktionen/aktive Navigation,
  Gelb nur für Sidebar-Icons, Pink nur als Markenakzent (nie als Fehlerfarbe — dafür
  ist Rot da). Schrift: Roboto Condensed für UI/Tabellen/Formulare, Alfa Slab One nur
  für kurze Seitentitel, Roboto Mono für Zahlen/IDs.
- **Hell/Dunkel** folgt automatisch der Systemeinstellung. Manueller Override über
  `<html data-theme="dark|light">` ist vorbereitet (Toggle = Future Log).
- **Nie feste Farben** im Komponenten-CSS — immer Tokens. **Nie Theme-Tokens
  mischen** — eine Komponente innerhalb der User-Shell nutzt nur `--cp-user-*`,
  innerhalb der Admin-Shell nur `--cp-admin-*`.
- Bekanntes To-do: vorhandenes Mockup-CSS hat fest verdrahtetes Weiß (Topbar,
  Bottom-Nav, `.cp-page`-Verlauf). Beim Refactor auf Tokens umstellen, sonst bricht
  der Dunkelmodus.

---

## UI-Regeln

### User-Shell
- App-Breite 360–460px, 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".

### Admin-Shell
- Desktop-Grundraster: Sidebar 260px + fließender Hauptbereich, Seitenabstand 32px.
- **Sidebar-Punkte (fest, in dieser Reihenfolge):** Start · Planung · Dienste ·
  Freigaben · Team · Chat-Verwaltung · Profil — Abmelden unten abgetrennt.
- Aktiver Menüpunkt: Teal-Fläche, weißer Text, gelbes Icon. Inaktiv: gedimmtes Weiß,
  gelbes Icon.
- Cards/Panels: Radius 8–12px (max. 14px), Innenabstand 20–30px, sehr dezenter Schatten,
  Panel-Header darf `--cp-admin-surface-soft` nutzen.
- Tabellenkopf in Navy mit weißem, fettem Text. Statusspalten farbig **mit Text**, nie
  nur über Farbe.
- **Unter dem Breakpoint:** Sidebar kollabiert zu Hamburger-Icon, öffnet als
  Off-Canvas-Overlay. Touch-Ziele bleiben ≥ 44px auch im Admin-Bereich.

### Beide Shells
- 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/
│   │   ├── base_user.html    # User-Shell: Bottom-Nav
│   │   ├── base_admin.html   # Admin-Shell: Sidebar + mobiles Off-Canvas
│   │   └── ...               # Teilansichten je Route
│   └── static/
│       ├── tokens.css        # beide Theme-Tokensätze (--cp-user-*, --cp-admin-*)
│       ├── components.css    # Komponenten der User-Shell
│       ├── components-admin.css  # Komponenten der Admin-Shell
│       └── sidebar.js        # Hamburger/Off-Canvas-Logik (Vanilla, ~20 Zeilen)
├── 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.
