# 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 `` 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 `` 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.