diff --git a/VAULT/hinunher/PROJECT_DESIGN.md b/VAULT/hinunher/PROJECT_DESIGN.md new file mode 100644 index 0000000..addb0d2 --- /dev/null +++ b/VAULT/hinunher/PROJECT_DESIGN.md @@ -0,0 +1,2490 @@ +# PROJECT_DESIGN.md — Ehrenamt-Plattform + +> **Status:** Verbindliche Architektur- und Produktgrundlage für v1 +> **Stand:** 13.08.2026 +> **Arbeitsname:** Ehrenamt-Plattform +> **Ausgangspunkt:** bestehende CheckPoint-Ehrenamt-App, künftig als modulare Plattform für mehrere Ehrenamtsteams der Aidshilfe Köln e.V. + +--- + +## 0. Zweck dieses Dokuments + +Dieses Dokument ist die **zentrale Quelle der Wahrheit** für Produktumfang, Architektur, Rollen, Berechtigungen, Datenmodell, Module, UI-Grundsätze, Sicherheit, Deployment und Weiterentwicklung der Ehrenamt-Plattform. + +Es ersetzt die bisher verteilten bzw. teilweise widersprüchlichen Architekturentscheidungen aus älteren Konzept-, `CLAUDE.md`- und `TASKS.md`-Ständen. + +### Dokument-Hierarchie + +1. **`PROJECT_DESIGN.md`** — verbindliche Produkt- und Architekturentscheidung. +2. **Code + Tests** — Implementierung dieser Entscheidungen. +3. **`TASKS.md`** — daraus abgeleiteter Arbeits-/Migrationsplan; darf keine eigene Architektur erfinden. +4. **`CLAUDE.md` / andere Agentenhinweise** — wenn weiterhin verwendet, nur als kompakte Arbeitsanweisung mit Verweis auf `PROJECT_DESIGN.md`, nicht als zweite Architekturquelle. +5. Alte Konzeptdateien werden als historische Referenz archiviert und nicht parallel gepflegt. + +Bei einem Widerspruch zwischen Implementierung und diesem Dokument wird **nicht geraten**. Die Abweichung wird bewusst entschieden und anschließend entweder der Code oder dieses Dokument angepasst. + +--- + +# 1. Produktziel + +Die Anwendung ist eine **interne, mobile-first Web-App für Ehrenamtsteams**. Sie soll alltägliche Teamorganisation einfacher, klarer und datensparsamer machen als eine Mischung aus WhatsApp-/Signal-Gruppen, Tabellen, Umfragen und verstreuten Dateien. + +Die erste Organisation ist: + +- **Aidshilfe Köln e.V.** + +Die Plattform soll von Anfang an mehrere Teams dieser Organisation unterstützen. Perspektivisch sind ungefähr mehrere unterschiedliche Ehrenamtsbereiche denkbar, die nicht alle dieselben Funktionen benötigen. + +### Leitprinzipien + +1. **Lesbarkeit und Orientierung vor Funktionsfülle.** +2. **Mobile Nutzung ist der Normalfall**, nicht die abgespeckte Desktop-Version. +3. **Technisch wenig affine Menschen müssen die App ohne Schulungsbedarf bedienen können.** +4. **Ein Team sieht nur sein Team.** Organisationsrechte sind kein Generalschlüssel für Team-Inhalte. +5. **Module sind optional pro Team.** Nicht jedes Team bekommt Funktionen, die es nicht braucht. +6. **Automatisierung nur bei eindeutigem Ergebnis.** Die App unterstützt Entscheidungen; sie entscheidet nicht unnötig für Menschen. +7. **Dashboard zeigt Zustände, Benachrichtigungen melden Ereignisse.** +8. **Erweiterbarkeit entsteht durch klare Grenzen**, nicht durch ein maximal generisches System. +9. **Deployment, Backup und Wiederherstellung werden von Anfang an mitgebaut.** + +--- + +# 2. Scope von v1 + +## 2.1 Core + +Der Core enthält die Plattformfunktionen, ohne die die Anwendung als Mehrteam-System nicht funktionieren kann: + +- Passwordless Auth per E-Mail-Code / Direktlink +- Accounts +- Device Sessions +- Organisation +- Teams +- Team-Mitgliedschaften +- Team-Einladungen +- Organisationsadministration +- Rollen und Berechtigungen +- Brand Profiles / Corporate Design +- zentraler Datei-/Storage-Service +- In-App-Benachrichtigungen +- Team-Dashboard und Verwaltungsdashboard +- Team-Mitgliederverzeichnis / freiwillige Teamkontakte +- Profile und Avatare +- Audit Log +- Health-/Deployment-Grundlagen + +## 2.2 Fachmodule + +v1 enthält genau drei fachliche Module: + +1. **Scheduling / Einsatzplanung** +2. **Documents / Dokumente** +3. **Chat / Teamkommunikation** + +Nicht jedes Team muss alle drei Module verwenden. + +--- + +# 3. Bewusst nicht Teil von v1 + +Folgende Funktionen werden nicht in v1 gezogen, nur weil sie technisch möglich wären: + +- öffentliche Seiten +- Klient:innen- oder Beratungsdaten +- Bezahlfunktionen +- Kalender-Synchronisierung +- automatische Dienstverteilung +- 1:1-Direktnachrichten +- Bilder oder Dateien im Chat +- GIFs / Sticker / Meme-Previews +- Emoji-Reactions +- `@mentions` +- operative Push-Benachrichtigungen +- operative E-Mail-Benachrichtigungen +- Dokumentordner +- Dokumentversionierung +- vollwertige Dokumentbearbeitung +- Fotogalerie +- Inventar-/Materialverwaltung +- Qualifikations-/Schulungsverwaltung +- feingranulare Spezialrollen +- Passkeys / 2FA als regulärer Loginweg +- PostgreSQL in v1 +- dynamisches Installieren/Entfernen von Python-Modulen zur Laufzeit + +Diese Punkte dürfen später ergänzt werden, wenn ein realer Bedarf besteht und der Modulvertrag eingehalten wird. + +--- + +# 4. Technischer Rahmen + +| Bereich | Entscheidung | +|---|---| +| Backend | Python + Flask | +| App-Struktur | Flask App Factory + Blueprints | +| Rendering | Jinja2, server-rendered Multi-Page-App | +| ORM | SQLAlchemy / Flask-SQLAlchemy | +| Datenbank | SQLite für v1 | +| Schemaänderungen | Flask-Migrate / Alembic ab Beginn der neuen Architektur | +| CSS | eigenes Designsystem, kein CSS-Framework | +| JavaScript | Vanilla JS, nur wo es echten Nutzen bringt | +| Chat-Aktualisierung | kleines Fetch-Polling, keine WebSockets in v1 | +| Produktionsserver | Gunicorn | +| Container | Docker / Compose, non-root | +| Reverse Proxy | Caddy | +| Hosting | eigener VPS in der EU | +| UI-Sprache | Deutsch | +| Zielplattform | Browser-App, mobil zum Homescreen hinzufügbar | + +### Architekturregel + +Server-rendered HTML bleibt die Basis. JavaScript verbessert Komfort, darf aber nicht unnötig die gesamte App in eine SPA verwandeln. + +--- + +# 5. Organisations- und Teammodell + +Die zentrale Hierarchie lautet: + +```text +Organization +└── Team + └── TeamMembership + └── Account +``` + +## 5.1 Organization + +Die erste Organisation ist Aidshilfe Köln e.V. + +`Organization` existiert trotzdem als eigenes Modell, damit die Anwendung strukturell nicht auf ein einzelnes Team oder eine einzelne Abteilung fest verdrahtet wird. + +Konzeptionelle Felder: + +```text +Organization +├── id +├── name +├── status +└── created_at +``` + +## 5.2 Team + +Ein Team ist ein klar abgegrenzter Arbeitsbereich innerhalb der Organisation. + +```text +Team +├── id +├── organization_id +├── name +├── slug +├── brand_profile_id +├── status +└── created_at +``` + +Status mindestens: + +- `active` +- `archived` + +Teams werden **archiviert statt gelöscht**, solange keine zwingende Datenschutz-/Löschanforderung etwas anderes verlangt. + +Ein aktives Team muss mindestens eine Team-Verwaltung besitzen. + +--- + +# 6. Accounts, Mitgliedschaften und Rollen + +## 6.1 Account ist eine Person, kein Teammitglied + +Ein Account gehört **nicht** direkt einem Team. + +```text +Account +├── id +├── email +├── display_name +├── status +├── created_at +└── last_login_at +``` + +Die E-Mail-Adresse ist: + +- eindeutig und normalisiert, +- Login-Identität, +- nicht automatisch öffentliche Kontaktadresse, +- nicht Teil des normalen Teamverzeichnisses. + +Account-Status mindestens: + +- `active` +- `disabled` + +Accounts werden nicht leichtfertig hart gelöscht, damit historische Zuteilungen und andere Referenzen erhalten bleiben können. + +## 6.2 TeamMembership + +Die Beziehung einer Person zu einem Team wird separat modelliert: + +```text +TeamMembership +├── id +├── team_id +├── account_id +├── role +├── status +├── joined_at +└── ended_at +``` + +Rollen in v1: + +- `member` +- `manager` + +Status mindestens: + +- `active` +- `inactive` + +Pro Account und Team darf nur eine aktive Mitgliedschaft existieren. + +### Wichtige Semantik + +`manager` ist **keine getrennte Benutzerart**. Eine Team-Verwaltung ist immer auch normales Mitglied des Teams und kann die Mitgliederoberfläche genauso benutzen wie andere Teammitglieder. + +--- + +# 7. Organisationsadministration + +Organisationale Rechte werden separat von Team-Mitgliedschaften modelliert: + +```text +OrganizationMembership +├── id +├── organization_id +├── account_id +├── role +└── status +``` + +Für v1 reicht: + +- `organization_admin` + +## 7.1 Organisationsadministration darf + +- Teams anlegen und archivieren, +- Brand Profiles anlegen/zuordnen, +- Module für Teams aktivieren/deaktivieren, +- Team-Verwalter:innen ernennen und entfernen, +- organisationsweite technische/strukturelle Einstellungen verwalten, +- weitere Organisationsadmins über einen gesicherten Prozess ernennen, +- sicherheitsrelevante Account-Sperren durchführen. + +## 7.2 Organisationsadministration darf ausdrücklich nicht automatisch + +- Teamchats lesen, +- Dokumente eines Teams sehen, +- Einsatzplanung oder Verfügbarkeiten eines Teams sehen, +- normale Teamkontakte sehen, +- die vollständigen Mitgliederlisten aller Teams durchsuchen, +- allein wegen der Org-Rolle Team-Fachmodule öffnen. + +Die Organisationsverwaltung sieht **Teamstruktur und Team-Verwaltungen**, nicht automatisch den internen Teambetrieb. + +Wenn eine organisationsadministrierende Person interne Teamdaten sehen soll, braucht sie zusätzlich eine normale `TeamMembership` in diesem Team. + +### Sicherheitsinvariante + +> `organization_admin` darf niemals eine fehlende `TeamMembership` für Team-Inhalte umgehen. + +Diese Regel erhält eigene automatisierte Tests. + +--- + +# 8. Ernennung von Team-Verwaltungen + +Nur die **Organisationsverwaltung** darf Team-Verwalter:innen ernennen oder entfernen. + +Team-Verwalter:innen dürfen: + +- normale Mitglieder einladen, +- normale Teammitgliedschaften verwalten, +- Inhalte und Modulkonfiguration ihres Teams verwalten, + +aber **keine weiteren Team-Verwaltungsrechte vergeben**. + +Zusätzliche Regeln: + +1. Eine Team-Verwaltung kann sich nicht selbst zur normalen Mitgliedschaft herunterstufen. +2. Ein aktives Team darf nicht ohne Team-Verwaltung zurückbleiben. +3. Beim Entfernen der letzten Team-Verwaltung muss zuerst eine neue bestimmt werden. +4. Eine neue Team-Verwaltung kann von der Organisationsverwaltung aus einer bestehenden Teammitgliedschaft befördert oder direkt als Team-Verwaltung eingeladen werden, ohne dass der Org-Admin dazu die komplette Teammitgliederliste sehen muss. + +--- + +# 9. Technische Serveradministration ist keine App-Rolle + +Zugriff auf VPS, Docker, Caddy, Backups oder Git verleiht **keine fachliche Rolle innerhalb der App**. + +Technische Administration und Anwendungsberechtigungen bleiben konzeptionell getrennt. + +--- + +# 10. Passwordless Auth + +## 10.1 Grundprinzip + +Es gibt in v1 **keine Benutzerpasswörter**. + +Login erfolgt per E-Mail: + +1. Person gibt ihre E-Mail-Adresse ein oder folgt einer Einladung. +2. App erzeugt einen zeitlich begrenzten Auth-Challenge. +3. E-Mail enthält: + - einen kurzen Einmal-Code, + - zusätzlich einen Direktlink. +4. Nach erfolgreicher Verifikation wird eine langlebige, widerrufbare Device Session angelegt. +5. Beim normalen erneuten Öffnen der Homescreen-/Browser-App ist die Person weiterhin angemeldet. + +Es gibt damit keinen Passwort-Reset-Flow. + +## 10.2 AuthChallenge + +```text +AuthChallenge +├── id +├── email +├── code_hash +├── link_token_hash +├── created_at +├── expires_at +├── attempts +└── consumed_at +``` + +Code und Linktoken werden nicht im Klartext gespeichert. + +Ablaufzeit, maximale Fehlversuche und Rate Limits sind Konfiguration, nicht Geschäftslogik. + +## 10.3 DeviceSession + +```text +DeviceSession +├── id +├── account_id +├── token_hash +├── created_at +├── last_seen_at +├── expires_at +├── revoked_at +└── device_label +``` + +Session-Cookies müssen mindestens: + +- `Secure`, +- `HttpOnly`, +- sinnvolle `SameSite`-Einstellung + +verwenden. + +Gerätesitzungen sind serverseitig widerrufbar. Eine spätere Profilseite „Angemeldete Geräte“ ist mit dem Modell bereits möglich. + +## 10.4 Frische Re-Authentifizierung + +Besonders sensible Organisationsaktionen, z. B. Änderung von Org-Admin- oder Team-Manager-Rechten, verlangen eine **frische E-Mail-Re-Authentifizierung**, auch wenn eine langlebige Device Session besteht. + +Passkeys/2FA können später ergänzt werden, sind nicht Teil von v1. + +--- + +# 11. Einladungen + +## 11.1 TeamInvitation + +```text +TeamInvitation +├── id +├── team_id +├── email +├── role +├── invited_by +├── status +├── created_at +└── expires_at +``` + +Status mindestens: + +- `pending` +- `accepted` +- `revoked` +- `expired` + +Normale Team-Verwaltungen dürfen nur `role = member` einladen. + +### Ablauf + +```text +Team-Verwaltung trägt E-Mail ein +→ Einladung wird erstellt +→ Empfänger:in verifiziert die E-Mail +→ vorhandenen Account finden oder neuen Account anlegen +→ TeamMembership erstellen +→ bei neuem Account: Onboarding für Anzeigename +``` + +## 11.2 OrganizationAdminInvitation + +Organisationsadministration erhält einen getrennten, sicherheitssensiblen Einladungsflow: + +```text +OrganizationAdminInvitation +├── id +├── organization_id +├── email +├── invited_by +├── status +├── created_at +└── expires_at +``` + +Der erste Organisationsadmin wird über einen CLI-Bootstrap angelegt/eingeladen. Es gibt keine hardcodierte Admin-E-Mail. + +Nach vorhandenem Org-Admin soll der Bootstrap standardmäßig nicht einfach weitere globale Administratoren erzeugen. + +Operational wird empfohlen, mindestens zwei Org-Admins zu haben, damit die Organisation nicht von einer einzelnen Person abhängig ist. + +--- + +# 12. Profil und Teamkontakte + +## 12.1 Anzeigename + +Die Login-E-Mail ist nicht der öffentliche Name. + +Beim ersten erfolgreichen Login wird, falls noch kein Anzeigename existiert, gefragt: + +> Wie sollen wir dich nennen? + +`display_name`: + +- wird in Chat, Einsatzplanung und Teamverzeichnis verwendet, +- darf von der Person im Profil geändert werden, +- muss nicht organisationsweit eindeutig sein. + +Team-spezifische Anzeigenamen werden in v1 nicht gebaut. + +## 12.2 Profilbild und Avatare + +Ein Account kann optional ein eigenes Profilbild verwenden. + +Alternativ stehen drei rein visuelle Standardvarianten zur Auswahl: + +- Avatar A +- Avatar B +- Avatar C + +Die Avatare werden **nicht geschlechtlich beschriftet**. + +Wenn nichts gewählt wurde, kann Avatar A als neutraler technischer Default verwendet werden. + +Das Profilbild gilt accountweit und wird z. B. angezeigt in: + +- Teamverzeichnis, +- Chat, +- Einsatzansichten. + +Uploadbilder werden serverseitig auf geeignete Größen/Thumbnails verarbeitet. Erlaubte Profilbildformate orientieren sich an den Bildformaten des Dokumenten-Uploads, einschließlich moderner Smartphone-Formate. + +## 12.3 Freiwilliges TeamMemberProfile + +Kontaktdaten sind **teambezogen und freiwillig**: + +```text +TeamMemberProfile +├── team_membership_id +├── phone_number optional +├── contact_by_call optional +├── contact_by_sms optional +├── contact_by_signal optional +├── contact_note optional +└── share_contact_details +``` + +Beispiel: + +```text +Andrea +[Profilbild] +Team-Verwaltung + +📞 0176 ... +Erreichbar per Anruf / Signal +„Bei kurzfristigen Dienstausfällen gerne anrufen.“ +``` + +### Datenschutzregeln + +- `share_contact_details` ist standardmäßig aus. +- Freigegebene Kontaktdaten sehen nur aktive Mitglieder desselben Teams. +- Eine Person kann in einem Team teilen und in einem anderen nicht. +- Nicht freigegebene freiwillige Kontaktdaten werden auch Team-Verwaltungen nicht „heimlich“ angezeigt. +- Beim Ende der Teammitgliedschaft endet der Zugriff für andere Teammitglieder. +- Es gibt in v1 keinen organisationsweiten Kontaktdatenexport. + +### Login-E-Mail + +Die Login-E-Mail wird **nicht** normalen Teammitgliedern angezeigt. + +Team-Verwaltungen dürfen sie für die Mitgliederadministration ihres Teams sehen. + +Organisationsadmins sehen nicht automatisch die Login-E-Mails aller normalen Teammitglieder; ihr struktureller Zugriff bleibt auf ihre Aufgaben begrenzt. + +Eine spätere freiwillige `contact_email` wäre ein separates Feld und niemals automatisch `Account.email`. + +--- + +# 13. Teammitgliederverzeichnis + +Normale Teammitglieder sehen im eigenen Team: + +- Anzeigename, +- Profilbild / Avatar, +- Kennzeichnung „Team-Verwaltung“, falls zutreffend, +- freiwillig freigegebene Teamkontaktdaten. + +Sie sehen nicht: + +- Login-E-Mail, +- Device Sessions, +- Auth-Daten, +- interne Account-/Einladungsmetadaten. + +Team-Verwaltungen sehen zusätzlich für die Mitgliederadministration: + +- Login-E-Mail, +- Mitgliedsstatus, +- Einladungsstatus, +- Beitrittsdatum. + +--- + +# 14. Brand Profiles / Corporate Design + +Branding ist Core-Infrastruktur, kein Fachmodul. + +```text +BrandProfile +├── id +├── organization_id +├── name +├── logo +├── primary_color +├── primary_strong +├── accent_color +├── accent_strong +├── heading_font +└── body_font +``` + +Die genaue Tokenmenge darf sich bei der Implementierung verfeinern. Entscheidend ist: + +- Brand Profiles speichern **Design-Eingaben**, kein beliebiges CSS. +- Ein Brand Profile gehört der Organisation. +- Mehrere Teams dürfen dasselbe Brand Profile verwenden. +- In der Startkonfiguration sind mehrere unterschiedliche Profile vorgesehen, z. B. Aidshilfe-Standard, Café Bach, CheckPoint/Vor-Ort. +- Layout und Komponenten bleiben gleich; Farben, Schriften und Logo verändern die Identität. +- Light-/Dark-Darstellung wird über semantische CSS-Tokens abgeleitet. + +Der bestehende Ansatz mit getrennten Brand- und semantischen Tokens wird beibehalten, aber auf das neue BrandProfile-Modell ausgerichtet. + +## 14.1 Initiales Brand Profile „Vor-Ort-Arbeit“ + +Für die **Vor-Ort-Arbeit** wird die visuelle Basis aus dem vorhandenen CheckPoint-CSS übernommen. Es werden nur die grundlegenden Markenfarben und Schriften als Ausgangsmaterial verwendet; die Ehrenamt-Plattform übernimmt nicht das gesamte Layout der Quell-App. + +### Typografie + +| Einsatz | Schrift | Nutzung | +|---|---|---| +| Display / große Überschriften | **Big Shoulders Display** | Seitentitel, große Kennzahlen, markante Abschnittsüberschriften | +| Body / UI | **Noto Sans** | Fließtext, Navigation, Formulare, Buttons, Tabellen, Hilfetexte | + +Grundregel: **Big Shoulders Display sparsam und gezielt**, Noto Sans trägt den Großteil der Oberfläche. Das hält die App charakteristisch, ohne sie plakativ oder unruhig wirken zu lassen. + +### Basisfarben + +| Token / Rolle | Basiswert | Zweck | +|---|---:|---| +| Ink / Navy | `#07182E` | Haupttext, sehr dunkle Flächen, Navigation | +| Deep | `#061126` | tiefster Hintergrund / Sidebar-Basis | +| Teal strong | `#005F61` | dunkle Markenfläche, Sidebar / Header | +| Teal | `#00858A` | primäre Markenfarbe und aktive Navigation | +| Aqua | `#52E0CF` | freundlicher Akzent, Fokus, leichte Highlights | +| Blue | `#2596BE` | sekundärer Informationsakzent | +| Coral | `#F04A67` | wichtig / Aktion; Rot nur gezielt einsetzen | +| Yellow | `#F6E74A` | sparsame Hervorhebung / Hinweis | +| Mist | `#F4FAFC` | ruhiger Seitenhintergrund | +| Paper | `#FFFFFF` | Karten und Arbeitsflächen | + +Für helle Arbeitsflächen dürfen kontrastoptimierte semantische Ableitungen verwendet werden, z. B. dunkleres Teal oder Blue für Textlinks und Buttons. Die oben genannten Werte bleiben die **Brand-Basis**, nicht zwangsläufig der direkte CSS-Wert jedes Elements. + +### Logo-Nutzung + +Das Vor-Ort-Logo darf und soll **sichtbar wiederkehren**, insbesondere: + +- im mobilen Header bzw. App-Kopf, +- oben in der Desktop-Sidebar, +- in Team-/Brand-Kontexten, +- auf Login-/Einladungsansichten, sofern das Team bereits feststeht. + +Das Logo ist ein Identitätsanker, ersetzt aber keine Überschrift und soll nicht als dekoratives Hintergrundmotiv vervielfacht werden. + +## 14.2 Visuelle Referenz — Quick Mockup v1 + +Die abgestimmte visuelle Richtung ist bewusst **clean, ruhig und informationszentriert**. Bilder und motivierende Illustrationen sind nicht Teil des normalen Dashboard-Rasters. Die Oberfläche soll freundlich wirken, aber primär beim Arbeiten helfen. + +Verbindliche Gestaltungsrichtung: + +- helle Arbeitsfläche mit viel Weißraum, +- dunkle teal/navy Navigation als klarer Rahmen, +- weiße Cards mit leichter Kontur bzw. sehr zurückhaltendem Schatten, +- wenige, klare Statusfarben, +- Logo gut sichtbar, +- Icons funktional statt dekorativ, +- keine großflächigen Motivbilder im Dashboard, +- keine regelmäßigen Motivationssprüche oder Gamification-Elemente, +- große Display-Schrift nur für Hierarchie und Orientierung, nicht für jeden Kartentitel. + +Die drei Dashboard-Profile teilen dieselbe visuelle Sprache, unterscheiden sich aber in Informationsdichte und Aufgabenfokus: + +1. **Mitglieder-Dashboard** — persönlich, mobil-first, Fokus auf nächste Einsätze, offene Rückmeldungen, Verfügbarkeit und ungelesenen Teamchat. +2. **Teamverwaltungs-Dashboard** — sachlicher und dichter, Fokus auf offene Aufgaben, Rückmeldungen, Freigaben, Einladungen und unbesetzte Positionen. +3. **Organisationsverwaltungs-Dashboard** — am nüchternsten; Fokus ausschließlich auf Struktur, Teams, Module, Brand Profiles, Einladungen und organisationsweite Verwaltungsaufgaben. Keine teaminternen Fachinhalte. + +Quick-Mockup-Referenz im Projektordner: + +```text +vor_ort_arbeit_dashboard_mockups.png +``` + +Diese Grafik ist eine **visuelle Referenz, kein Pixel-Perfect-Vertrag**. Bei der Implementierung haben Barrierearmut, Responsive-Verhalten, reale Inhalte und das zentrale Komponentensystem Vorrang. Die Grundwirkung — Farben, Typografie, Informationsruhe, Logo-Präsenz und klare Dashboard-Hierarchie — soll jedoch erhalten bleiben. + +--- + +# 15. Modularchitektur + +## 15.1 Abhängigkeitsregel + +```text +Fachmodul → Core erlaubt +Core → Fachmodul verboten +``` + +Der Core darf nicht wissen, wie ein Einsatz, Dokument oder Chat fachlich funktioniert. + +## 15.2 Verzeichnisstruktur + +Zielstruktur, sinngemäß: + +```text +app/ +├── core/ +│ ├── auth/ +│ ├── accounts/ +│ ├── organizations/ +│ ├── teams/ +│ ├── memberships/ +│ ├── invitations/ +│ ├── permissions/ +│ ├── branding/ +│ ├── storage/ +│ ├── notifications/ +│ ├── dashboard/ +│ ├── profiles/ +│ └── audit/ +│ +└── modules/ + ├── scheduling/ + ├── documents/ + └── chat/ +``` + +Ein größeres Modul darf intern besitzen: + +```text +module/ +├── __init__.py +├── models.py +├── routes.py +├── services.py +├── permissions.py +├── forms.py +├── templates/ +└── tests/ +``` + +Nicht jedes Modul braucht jede Datei; die Verantwortlichkeiten bleiben aber getrennt. + +## 15.3 Routes sind dünn + +Eine Route erledigt hauptsächlich: + +```text +Request +→ Team-Kontext auflösen +→ Berechtigung prüfen +→ Service aufrufen +→ Response / Redirect / Template +``` + +Fachlogik gehört in Services, nicht in große Routendateien. + +## 15.4 Permission Helpers statt Rollenabfragen überall + +Nicht überall: + +```python +if membership.role == "manager": +``` + +sondern fachliche Funktionen wie: + +```text +can_manage_schedule() +can_review_document() +can_manage_channel() +``` + +Dadurch können Rollen später feiner werden, ohne die gesamte Codebasis umzuschreiben. + +--- + +# 16. Modulregistrierung + +Die Existenz eines Moduls wird **im Code** definiert, nicht in einer Datenbanktabelle. + +Beispiel: + +```text +ModuleDefinition +├── key +├── name +├── description +├── version +├── navigation +├── member_dashboard_provider +├── manager_dashboard_provider +└── dependencies +``` + +Stabile Modulkeys für v1: + +- `scheduling` +- `documents` +- `chat` + +Alle installierten Module/Blueprints werden beim App-Start registriert. + +### TeamModule + +Die Datenbank entscheidet nur, ob ein vorhandenes Modul für ein Team aktiv ist: + +```text +TeamModule +├── team_id +├── module_key +└── enabled +``` + +Die Kombination `team_id + module_key` ist eindeutig. + +### Verantwortlichkeiten + +**Organisationsverwaltung:** aktiviert/deaktiviert Module für Teams. +**Team-Verwaltung:** konfiguriert und nutzt bereits aktivierte Module. + +Ein deaktiviertes Modul ist nicht nur aus der Navigation verschwunden. Direkte Routes müssen ebenfalls Zugriff verweigern. + +Prüfreihenfolge: + +```text +aktive TeamMembership? +→ Modul für Team aktiviert? +→ fachliche Berechtigung vorhanden? +→ Zugriff +``` + +## 16.1 Modulabhängigkeiten + +Abhängigkeiten müssen explizit deklariert sein. v1 soll möglichst ohne Fachmodul-zu-Fachmodul-Abhängigkeiten auskommen: + +```text +scheduling → core +documents → core +chat → core +``` + +Module dürfen nicht ungeordnet gegenseitig interne Modelle abfragen. Falls Modul A Funktionalität von Modul B benötigt, geschieht das über eine definierte Service-Schnittstelle oder die gemeinsame Funktion gehört in den Core. + +## 16.2 Moduleinstellungen + +Komplexere Moduleinstellungen gehören dem jeweiligen Modul, z. B. `SchedulingSettings`. + +Kein vorschnelles generisches JSON-Einstellungsmonster. + +--- + +# 17. Team-Datenbesitz und Scoping + +Alle teaminternen Fachobjekte tragen explizit `team_id`, auch wenn die Teamzugehörigkeit theoretisch über eine Beziehung ableitbar wäre. + +Beispiele: + +```text +Shift.team_id +Document.team_id +Channel.team_id +Message.team_id +``` + +Grundprüfung: + +```text +requested_object.team_id == current_team.id +``` + +und aktive TeamMembership. + +Diese Redundanz ist bewusst: Teamgrenzen sollen in Code, Queries und Tests klar sichtbar bleiben. + +--- + +# 18. Dashboard- und Navigationsarchitektur + +## 18.1 Eindeutiger Team-Kontext + +Die App arbeitet bei Teamfunktionen immer in einem klaren Teamkontext, vorzugsweise auch sichtbar in der URL: + +```text +/team// +/team//scheduling/ +/team//documents/ +/team//chat/ +``` + +Die Session darf das zuletzt verwendete Team merken, aber **keine Berechtigung daraus ableiten**. + +### Loginverhalten + +- nur ein aktives Team → direkt dort starten, +- mehrere Teams + bekannte letzte Auswahl → dort starten, +- erster Login mit mehreren Teams → einmalige Teamauswahl. + +## 18.2 Team-Startseite + +Die normale Startseite zeigt persönliche/relevante Informationen des aktuellen Teams. + +Beispiel: + +```text +Guten Morgen, Patsy + +Dringend +Heute wird noch ein Springer gesucht. + +Deine nächsten Einsätze +... + +Verfügbarkeit +4 Termine noch offen + +Teamchat +3 neue Nachrichten +``` + +Team-Verwalter:innen landen nicht automatisch in einem Backend, sondern benutzen dieselbe Mitgliederstartseite. + +## 18.3 Team-Verwaltungsbereich + +Zusätzlich existiert: + +```text +/team//manage/ +``` + +Nur Team-Verwaltungen dürfen ihn öffnen. + +Dort erscheinen Aufgaben/Zustände wie: + +- unterbesetzte Einsätze, +- fehlende Verfügbarkeiten, +- Dokumentfreigaben, +- offene Einladungen. + +## 18.4 Organisationsverwaltung + +Separater Bereich: + +```text +/org//manage/ +``` + +Dieser zeigt ausschließlich strukturelle Organisationsinformationen, z. B.: + +- Teams, +- Team-Verwaltungen, +- Brand Profiles, +- Modulaktivierungen, +- Org-Admin-Verwaltung. + +Keine Teamchat-, Dokument- oder Scheduling-Inhalte. + +--- + +# 19. Dashboard Extension Contract + +Der Core kennt keine Fachmodule, muss deren relevante Hinweise aber darstellen können. + +Deshalb registrieren Module strukturierte Dashboard-Beiträge. + +```text +DashboardItem +├── title +├── text +├── category +├── count optional +├── action_label optional +├── action_endpoint optional +└── module_key +``` + +Erlaubte Kategorien: + +- `URGENT` +- `ACTION` +- `UPCOMING` +- `INFO` + +Sortierung zentral: + +1. Dringend +2. Handlungsbedarf +3. Bevorstehend +4. Information + +Das Modul liefert Daten, **nicht eigenes Dashboard-HTML oder eigene Farben**. Der Core rendert standardisierte Komponenten und wendet das Brand Profile an. + +Es gibt in v1 keine Tabellen wie `DashboardWidget` oder personalisierbare Dashboard-Layouts. Karten werden aus dem aktuellen Systemzustand berechnet. + +--- + +# 20. Core Notifications + +Notifications sind Infrastruktur, kein aktivierbares Fachmodul. + +Fachmodule rufen einen zentralen Notification-Service auf. + +Konzeptionelles Modell: + +```text +Notification +├── id +├── account_id +├── team_id optional +├── type +├── priority +├── title +├── text +├── action_endpoint optional +├── created_at +└── read_at optional +``` + +Prioritäten: + +- `normal` +- `important` +- `urgent` + +### Grundregel + +> Dashboard zeigt Zustände. Benachrichtigungen melden Ereignisse. + +Beispiel: + +- Benachrichtigung: „Andrea hat ihren Einsatz abgesagt.“ +- Dashboard: „1 Position ist aktuell unbesetzt.“ + +Wenn der Platz besetzt ist, verschwindet der Dashboardzustand; die historische Benachrichtigung darf bestehen bleiben. + +v1 nutzt operative Benachrichtigungen **nur in der App**. E-Mail wird in v1 für Auth und Einladungen verwendet, nicht für normalen Teamverkehr. + +--- + +# 21. Modul 1: Scheduling / Einsatzplanung + +## 21.1 Ziel + +Scheduling bleibt das umfangreichste Fachmodul. Es soll unterschiedliche Teammodelle unterstützen, ohne die Bedienung komplizierter zu machen. + +Die bisherige feste Regel „2 Haupt + 1 Springer“ wird **nicht** Plattformregel. Sie wird eine Team-/Vorlagenkonfiguration. + +## 21.2 Workflow + +```text +Vorbereitung +→ Verfügbarkeit offen +→ Plan wird erstellt +→ Veröffentlicht +``` + +Statuswerte: + +- `draft` +- `availability_open` +- `scheduling` +- `published` + +Ein vergangener Zeitraum kann später archiviert werden, ohne dafür einen komplizierten zusätzlichen Arbeitsstatus zu benötigen. + +### Semantik + +**Vorbereitung:** Verwaltung legt Zeitraum, Einsätze und Positionen an. +**Verfügbarkeit offen:** Mitglieder geben Ja/Nein pro Einsatz ab und dürfen ändern. +**Plan wird erstellt:** Verfügbarkeit ist eingefroren; Verwaltung teilt ein. +**Veröffentlicht:** Plan ist für Mitglieder verbindlich sichtbar; Änderungen laufen über Absage/Nachbesetzung. + +## 21.3 PlanningPeriod + +```text +PlanningPeriod +├── id +├── team_id +├── name +├── start_date +├── end_date +├── status +├── availability_deadline optional +├── created_at +└── archived_at optional +``` + +Keine erzwungene Zweimonatsdauer. + +## 21.4 PositionDefinition + +```text +PositionDefinition +├── id +├── team_id +├── name +├── description optional +├── sort_order +├── active +└── replacement_for_id optional +``` + +Beispiele: + +```text +Vor-Ort: +Haupt +Springer → Ersatz für Haupt + +Café Bach: +Theke +Springer → Ersatz für Theke +``` + +Das Ersatzmodell bleibt absichtlich einfach. Keine komplexen Vertretungsketten in v1. + +## 21.5 ShiftTemplate + +```text +ShiftTemplate +├── id +├── team_id +├── name +├── default_title +├── default_start_time +├── default_end_time +├── default_location +├── active +└── Template-Positionen +``` + +```text +ShiftTemplatePosition +├── shift_template_id +├── position_definition_id +├── required_count +└── sort_order +``` + +Vorlagen sind Komfort, keine Pflicht. Ein konkreter Einsatz darf nach Erzeugung verändert werden. + +## 21.6 Shift + +```text +Shift +├── id +├── team_id +├── planning_period_id +├── title +├── date +├── start_time +├── end_time +├── location +├── description optional +└── created_at +``` + +## 21.7 ShiftPosition + +```text +ShiftPosition +├── id +├── team_id +├── shift_id +├── position_definition_id +├── required_count +└── sort_order +``` + +Beispiel: + +```text +Haupt required_count = 2 +Springer required_count = 1 +``` + +Ein offener Platz wird aus `required_count - aktive Assignments` berechnet. Es braucht keinen künstlichen „offenen Assignment-Datensatz ohne Person“. + +## 21.8 Availability + +Mitglieder melden nur **Ja oder Nein zum gesamten Einsatz**, keine Positionspräferenzen. + +```text +Availability +├── id +├── team_id +├── shift_id +├── team_membership_id +├── status +└── updated_at +``` + +Status: + +- `available` +- `unavailable` + +Kein Datensatz = noch nicht beantwortet. + +Beim Wechsel des Planungszeitraums zu `scheduling` werden Verfügbarkeiten eingefroren. + +## 21.9 Assignment + +```text +Assignment +├── id +├── team_id +├── shift_id +├── shift_position_id +├── team_membership_id +├── status +├── assigned_at +└── assigned_by optional/system +``` + +Status mindestens: + +- `assigned` +- `cancelled` +- `promoted` + +Die Erstverteilung bleibt **manuell durch Team-Verwaltung**. + +## 21.10 Fairness-Hilfe + +Bei der Auswahl einer verfügbaren Person zeigt die App mindestens: + +- Anzahl bisheriger Einsätze, +- Datum des letzten Einsatzes. + +Keine automatische Fairness-Verteilung und keine Gewichtungsformeln in v1. + +## 21.11 Unterbesetzung + +Unterbesetzung wird aus Daten berechnet: + +```text +aktive Assignments < benötigte Positionsanzahl +``` + +Kein manuell zu pflegender Wahrheitswert, wenn er zuverlässig ableitbar ist. + +--- + +# 22. Scheduling: Ausfall und Nachbesetzung + +## 22.1 Grundregel + +Nach Veröffentlichung wird eine Zuteilung nicht durch nachträgliches Ändern der ursprünglichen Availability korrigiert. + +Wenn jemand ursprünglich `available` war und später absagt: + +```text +Availability = available +Assignment = cancelled +``` + +Damit bleibt die historische Information korrekt. + +## 22.2 Automatisches Nachrücken + +Wenn eine Zielposition ausfällt und eine passende Ersatzposition definiert und besetzt ist: + +```text +Haupt fällt aus +→ Springer rückt auf Haupt +→ Springerplatz wird offen +``` + +Automatisches Nachrücken findet nur statt, wenn das Ergebnis eindeutig ist. Bei mehreren möglichen Ersatzpersonen oder anderen Mehrdeutigkeiten entscheidet die Team-Verwaltung. + +## 22.3 Springer fällt aus + +Es wird lediglich die Springerposition offen. Niemand wird künstlich umverteilt. + +## 22.4 Kein Springer vorhanden + +Die ausgefallene Zielposition selbst wird offen. + +## 22.5 Mehrere Ausfälle + +Automatisierung endet nach den eindeutig möglichen Nachrückschritten. Die App versucht nicht, selbstständig eine komplette neue Dienstplanung zu errechnen. + +## 22.6 Selbstübernahme offener Positionen + +Pro Team kann konfiguriert werden, ob offene Positionen nach Veröffentlichung selbst übernommen werden dürfen. + +Eine Person darf übernehmen, wenn: + +- aktive TeamMembership, +- Scheduling aktiviert, +- Einsatz veröffentlicht, +- Position wirklich offen, +- Person nicht bereits in diesem Einsatz eingeteilt, +- Platz nicht im selben Moment bereits vergeben wurde. + +Der letzte Punkt muss **transaktional / durch DB-Constraint bzw. atomare Logik** abgesichert werden, nicht nur durch den Buttonzustand im Browser. + +Eine ursprünglich als `unavailable` gemeldete Person darf später trotzdem einen offenen Dienst übernehmen, falls sich ihre Situation geändert hat. + +Auch ein übernommener Ersatzdienst kann später wieder abgesagt werden. + +## 22.7 Dringlichkeit + +Eine offene Position wird in v1 **24 Stunden vor Einsatzbeginn** als `urgent` behandelt. + +Das verändert nicht die Fachlogik, nur Priorität und Darstellung. + +Beispiel: + +> Dringend: Heute 19:00 – Springer gesucht. + +Der 24-Stunden-Wert ist in v1 ein fester Produktstandard und kann später teamkonfigurierbar werden. + +--- + +# 23. Scheduling: Benachrichtigungen + +### Mitglieder + +| Ereignis | Empfänger | Priorität | +|---|---|---| +| Verfügbarkeit geöffnet | aktive Teammitglieder | normal | +| Rückmeldung unvollständig, ca. 48 h vor Frist | nur betroffene Mitglieder | normal | +| Plan veröffentlicht / eigene neue Einsätze | betroffene Mitglieder, sinnvoll gebündelt | wichtig | +| eigene Einteilung geändert | betroffene Person | wichtig | +| automatisches Nachrücken | nachrückende Person | wichtig | +| offener Dienst | berechtigte Teammitglieder | normal | +| offener Dienst <24 h | berechtigte Teammitglieder | dringend | +| Einsatzzeit/-ort geändert | eingeteilte Personen | wichtig | +| Einsatz abgesagt | eingeteilte Personen | wichtig | + +### Team-Verwaltung + +| Ereignis | Priorität | +|---|---| +| Mitglied sagt Zuteilung ab | wichtig | +| Person übernimmt offenen Platz | normal | +| kurzfristige Unterbesetzung | dringend | + +Dauerhafte Zustände wie „6 Rückmeldungen fehlen“ gehören auf das Dashboard, nicht als dauernder Notification-Spam in die Glocke. + +Benachrichtigungen sollen bei Veröffentlichung sinnvoll dedupliziert/gebündelt werden, z. B. „Der Dienstplan ist da – du hast 3 neue Einsätze“ statt unmittelbar mehrerer redundanter Meldungen. + +--- + +# 24. Scheduling: Verlauf + +Das Scheduling-Modul soll fachlich nachvollziehbar machen, wie eine aktuelle Besetzung entstanden ist, ohne den globalen Security-Audit mit jedem Fachereignis zu überladen. + +Ein kleiner Scheduling-Verlauf darf Ereignisse wie diese dokumentieren: + +```text +Andrea hat abgesagt. +Chris ist automatisch von Springer auf Haupt nachgerückt. +Springerposition wurde geöffnet. +Marco hat die offene Springerposition übernommen. +``` + +Konkrete Implementierung kann über ein schlankes `SchedulingEvent`-Modell erfolgen. + +--- + +# 25. Modul 2: Documents / Dokumente + +## 25.1 Ziel + +Das Dokumentenmodul ist **keine Nextcloud** und kein komplexes DMS. + +Die sichtbare Struktur bleibt: + +```text +Aktuell +Archiv +``` + +## 25.2 Document + +```text +Document +├── id +├── team_id +├── uploader_membership_id +├── title +├── description optional +├── original_filename +├── storage_key +├── mime_type +├── file_size +├── approval_status +├── library_status +├── reviewed_by_membership_id optional +├── rejection_reason optional +├── created_at +├── reviewed_at optional +└── archived_at optional +``` + +## 25.3 Freigabestatus + +```text +pending +approved +rejected +``` + +## 25.4 Bibliotheksstatus + +```text +current +archived +``` + +Freigabe und Archivierung sind getrennte Dimensionen: + +- Freigabe = darf das Team die Datei sehen? +- Archiv = ist die Datei noch aktuell? + +## 25.5 Uploadrechte + +Normale Mitglieder dürfen hochladen. Diese Uploads starten mit `pending`. + +Sichtbarkeit im Pending-Status: + +- Uploader:in, +- Team-Verwaltung. + +Team-Verwaltungsuploads sind direkt freigegeben. + +Normale Mitglieder dürfen eigene noch wartende Uploads zurückziehen. + +## 25.6 Dateiformate + +Normale Teammitglieder dürfen in v1 mindestens hochladen: + +- PDF +- Word (`.doc`, `.docx`) +- JPG / JPEG +- PNG +- HEIC / HEIF + +HEIC/HEIF wird aufgenommen, damit Smartphone-Fotos — insbesondere aus verbreiteten iPhone-Konfigurationen — praktisch nutzbar bleiben. + +### HTML-Sonderfall + +HTML darf **ausschließlich durch Team-Verwaltungen** hochgeladen/bereitgestellt werden. + +Ziel: kleine statische, gut lesbare Anleitungen, z. B. für Café-Abläufe. + +HTML wird nicht als vertrauenswürdige Mini-Web-App behandelt. Darstellung erfolgt isoliert und restriktiv: + +- kein JavaScript, +- keine Formulare, +- keine frei laufenden aktiven Inhalte, +- keine unkontrollierten externen Embeds, +- sandboxed iframe / restriktive CSP bzw. äquivalente Schutzmechanismen. + +HTML ist Top-down-Inhalt; normale Mitglieder können es nicht einreichen. + +## 25.7 Dateiprüfung + +Serverseitig prüfen: + +- erlaubte Erweiterung, +- plausibler MIME-/Dateityp, +- Dateigrößenlimit, +- Dateiname wird nie als Speicherpfad verwendet. + +Maximale Uploadgröße ist konfigurierbar, nicht im Fachcode fest verdrahtet. + +## 25.8 Storage + +Dateien werden nicht als SQLite-BLOB gespeichert. + +Beispielstruktur im persistenten App-Volume: + +```text +/app/instance/ +├── app.db +└── uploads/ + ├── documents/ + └── profiles/ +``` + +Physische Dateinamen/Storage Keys sind opaque (z. B. UUIDs). `original_filename` ist nur Metadatum. + +Caddy darf private Uploads nicht einfach öffentlich als statische Dateien ausliefern. + +Download/Preview läuft durch kontrollierten Zugriff: + +```text +Login +→ TeamMembership +→ team_id prüfen +→ Dokument-/Statusberechtigung +→ Datei ausliefern +``` + +## 25.9 Bildvorschau + +Bilder erhalten serverseitig erzeugte Thumbnails/Vorschauen in einem Browser-kompatiblen Format. Das Original bleibt erhalten. + +So können auch HEIC-/HEIF-Ursprungsbilder zuverlässig in der App als Vorschau dargestellt werden. + +## 25.10 Freigabe + +Team-Verwaltung kann: + +- freigeben, +- ablehnen, +- optional Ablehnungsgrund nennen, +- archivieren, +- wiederherstellen, +- dauerhaft löschen. + +Dauerhaftes Löschen entfernt Datei und Domain-Datensatz. Im Security-/Admin-Audit kann eine minimale Löschaktion verbleiben, ohne unnötig sensible Dateinamen fortzuschreiben. + +## 25.11 Foto-Hinweis + +Beim Bild-Upload wird freundlich darauf hingewiesen, nur Fotos hochzuladen, für deren teaminterne Nutzung keine ungeklärten Rechte/Einwilligungen Dritter entgegenstehen. + +Keine Checkbox-Orgie; der Freigabeprozess ist die zweite Kontrollstufe. + +## 25.12 Benachrichtigungen + +- Mitglied lädt hoch → Team-Verwaltung erhält Freigabe-Ereignis. +- Dokument freigegeben → Uploader:in erhält normale/wichtige Info. +- Dokument abgelehnt → Uploader:in erhält Info inkl. Grund, falls vorhanden. +- Nicht jedes neue freigegebene Dokument erzeugt eine Nachricht an das gesamte Team. + +Wartende Freigaben erscheinen auf dem Verwaltungsdashboard. + +--- + +# 26. Zukünftige Fotogalerie + +Eine echte Galerie wird **nicht** in das Dokumentenmodul hineingebaut. + +Sie kann später als eigenes optionales Modul entstehen, z. B.: + +```text +gallery/ +├── Fotoübersicht +├── Alben / Veranstaltungen +├── große Bildansicht +└── Downloadoptionen +``` + +Sie darf denselben Core-Storage-Service nutzen. Ob Galerie und Dokumente dieselben Domain-Datensätze verwenden, wird erst entschieden, wenn das Galerie-Modul wirklich gebaut wird. + +--- + +# 27. Modul 3: Chat / Teamkommunikation + +## 27.1 Ziel + +Der Chat ist **Arbeitskommunikation, kein WhatsApp-Ersatz für Unterhaltung**. + +Er soll Teamabsprachen erleichtern, ohne zum Meme-, GIF- oder Dateiaustauschkanal zu werden. + +## 27.2 Channel + +```text +Channel +├── id +├── team_id +├── name +├── description +├── type +├── sort_order +├── status +└── created_at +``` + +Typen: + +- `announcement` +- `discussion` + +Beim Aktivieren des Chat-Moduls entstehen standardmäßig: + +- **Ankündigungen** +- **Team** + +Team-Verwaltungen dürfen weitere **teamweite Discussion-Channels** anlegen und archivieren. + +Keine privaten Untergruppen in v1. + +Channel-Status: + +- `active` +- `archived` + +## 27.3 Ankündigungen + +- Team-Verwaltung schreibt. +- Alle aktiven Teammitglieder lesen. +- Mitglieder antworten dort nicht. +- Neue Ankündigung erzeugt eine In-App-Benachrichtigung. + +## 27.4 Teamchat / Discussion + +Alle aktiven Teammitglieder dürfen: + +- lesen, +- schreiben, +- auf einzelne Nachrichten antworten, +- eigene Nachrichten bearbeiten, +- eigene Nachrichten löschen. + +Team-Verwaltungen dürfen zusätzlich moderieren/entfernen. + +## 27.5 Message + +```text +Message +├── id +├── team_id +├── channel_id +├── author_membership_id +├── text +├── reply_to_message_id optional +├── created_at +├── edited_at optional +└── deleted_at optional +``` + +Gelöschte Nachrichten zeigen in der UI einen neutralen Platzhalter. Bei Moderation darf sichtbar sein, dass die Team-Verwaltung entfernt hat. + +Es gibt kein öffentliches Edit-Versionsarchiv in v1. + +## 27.6 Keine Medien/Dateien + +Im Chat sind ausdrücklich nicht erlaubt: + +- Bilder, +- Dokumente, +- Videos, +- Audio, +- GIFs, +- Sticker, +- andere Dateianhänge. + +Fotos gehören in das Dokumenten-/spätere Galerie-System, nicht in den Chat. + +## 27.7 Links + +Externe Links werden bewusst eingeschränkt: + +- nur `https://`, +- maximal ein externer Link pro normaler Chatnachricht, +- keine reinen Link-only-Nachrichten; kurzer Kontexttext ist erforderlich, +- keine Link-Vorschau, +- kein Thumbnail, +- kein YouTube-/Social-Embed, +- bekannte URL-Shortener werden blockiert, +- externe Links werden als extern kenntlich gemacht. + +Interne Links innerhalb der eigenen App dürfen normal verwendet werden. + +Später kann bei echtem Bedarf eine Domain-Allowlist ergänzt werden. + +## 27.8 Replies + +Ein einfaches `reply_to_message_id` ist Teil von v1. + +Threads oder verschachtelte Diskussionen werden nicht gebaut. + +Eine direkte Antwort auf die eigene Nachricht darf eine normale In-App-Benachrichtigung auslösen. + +## 27.9 Ungelesen / Zustellung / Gelesen + +```text +ChannelReadState +├── team_membership_id +├── channel_id +├── last_delivered_message_id +└── last_read_message_id +``` + +Anzeige für eigene Nachrichten: + +- `✓` grau = erfolgreich auf dem Server gespeichert, +- `✓✓` grau = von allen relevanten aktuellen Empfänger:innen technisch abgerufen, +- `✓✓` in der Brand-Akzentfarbe = von allen relevanten Empfänger:innen gelesen. + +Mitglieder, die erst später einem Team beitreten, zählen nicht rückwirkend für alte Nachrichten. + +Optionaler Detailhinweis darf z. B. „Gelesen von 8/10“ zeigen, aber keine detaillierte Überwachungsliste mit individuellen Lesezeitpunkten. + +## 27.10 Polling + +Neue Nachrichten werden mit kleinem Vanilla-JS-`fetch`-Polling nachgeladen. + +- kein WebSocket-System in v1, +- Polling-Intervall konfigurierbar, +- Chat bleibt grundsätzlich auch durch normales Neuladen funktional. + +## 27.11 Chat-Benachrichtigungen + +Normale neue Chatnachrichten erzeugen **keine Glockenbenachrichtigung pro Nachricht**. + +Stattdessen: + +- Channel zeigt Ungelesen-Zähler, +- Ankündigung → Notification, +- direkte Antwort auf eigene Nachricht → Notification. + +--- + +# 28. Core Storage Service + +Da Dokumente, Profilbilder und spätere Medienmodule Dateien benötigen, gibt es einen gemeinsamen Storage-Service im Core. + +Aufgaben: + +- sichere opaque Storage Keys erzeugen, +- Pfade kontrollieren, +- Dateien schreiben/lesen/löschen, +- Thumbnails/Varianten verwalten, +- Dateigrößenlimits und erlaubte Speicherbereiche unterstützen, +- keine öffentliche Pfadableitung aus User-Dateinamen zulassen. + +Domain-Modelle behalten ihre eigenen Metadaten und Berechtigungen; der Storage-Service entscheidet nicht selbst, wer ein Dokument sehen darf. + +--- + +# 29. Audit Log + +Der globale `AuditLog` ist für sicherheits- und administrationsrelevante Änderungen gedacht, **nicht für jeden Klick**. + +```text +AuditLog +├── id +├── organization_id +├── team_id optional +├── actor_account_id +├── action +├── target_type +├── target_id +└── created_at +``` + +Beispiele: + +- Team erstellt/archiviert, +- Brand Profile geändert, +- Modul aktiviert/deaktiviert, +- Team-Verwaltung ernannt/entfernt, +- Org-Admin-Rechte geändert, +- Account gesperrt, +- Mitgliedschaft administrativ beendet, +- sensibles Dokument dauerhaft gelöscht. + +Chatnachrichten, normale Verfügbarkeitsklicks oder jeder Seitenaufruf gehören nicht in diesen Audit Log. + +--- + +# 30. Berechtigungsmatrix v1 + +| Funktion | Mitglied | Team-Verwaltung | Organisationsadmin ohne TeamMembership | +|---|:---:|:---:|:---:| +| eigenes Team öffnen | ✓ | ✓ | ✗ | +| Teamverzeichnis sehen | ✓ | ✓ | ✗ | +| freiwillig geteilte Teamkontakte sehen | ✓ | ✓ | ✗ | +| Login-E-Mails normaler Mitglieder sehen | ✗ | ✓ | ✗ | +| Mitglieder einladen | ✗ | ✓ | ✗ | +| normale Mitgliedschaft verwalten | ✗ | ✓ | ✗ | +| Team-Verwaltung ernennen | ✗ | ✗ | ✓ | +| Team-Verwaltung entfernen | ✗ | ✗ | ✓ | +| Team erstellen/archivieren | ✗ | ✗ | ✓ | +| Brand Profile zuordnen | ✗ | ✗ | ✓ | +| Module aktivieren/deaktivieren | ✗ | ✗ | ✓ | +| aktivierte Module konfigurieren | ✗ | ✓ | ✗ | +| Teamchat lesen | ✓ | ✓ | ✗ | +| Dokumente sehen | ✓* | ✓ | ✗ | +| Scheduling sehen | ✓* | ✓ | ✗ | +| Teaminterne Inhalte quer über alle Teams sehen | ✗ | ✗ | ✗ | + +`*` nur wenn das entsprechende Modul für das Team aktiviert ist und die konkrete Objektberechtigung erfüllt ist. + +--- + +# 31. UI- und UX-Grundsätze + +## 31.1 Mobile first + +Primärer Testbereich: + +- ca. 360–460 px App-Breite. + +Desktop ist vollständig nutzbar, aber nicht der Ausgangspunkt für die Informationsarchitektur. + +## 31.2 Barrierearmut + +Pflicht: + +- Touch-Ziele mindestens ca. 44 px, +- sichtbarer Tastaturfokus, +- sinnvolle Labels, +- Status nie nur über Farbe, +- ausreichender Kontrast, +- Fehlermeldungen mit konkreter Handlungsanweisung, +- semantisches HTML, +- keine zentrale Funktion ausschließlich per JavaScript. + +## 31.3 Sprache + +- Deutsch, +- klare aktive Verben, +- ein Begriff bleibt im Flow gleich, +- keine unnötige Verwaltungs-/IT-Sprache in der Mitgliederoberfläche. + +## 31.4 Komponenten + +Zentrale, wiederverwendbare Komponenten, z. B.: + +- Buttons, +- Cards, +- Badges, +- Form Fields, +- Confirmation Pattern, +- Empty State, +- Tabellen, +- Listen, +- Navigation, +- Dashboard Cards. + +Fachmodule dürfen fachspezifische Komponenten ergänzen, aber keine eigenen Grund-Buttons oder eigene Designwelt erfinden. + +## 31.5 Visuelle Dichte und Bildsprache + +Die Ehrenamt-Plattform ist **kein Kampagnenauftritt**, sondern ein Arbeitswerkzeug. Deshalb gilt für alle Brand Profiles, insbesondere für Vor-Ort: + +- Illustrationen und Fotos nur dort einsetzen, wo sie inhaltlich helfen. +- Dashboards bleiben grundsätzlich bildarm. +- Motivierende Texte sind optional und selten; sie dürfen keine Arbeitsinformation verdrängen. +- Status, nächste Schritte und Handlungsbedarf stehen visuell vor Dekoration. +- Wiederkehrende Markenidentität entsteht primär durch Logo, Typografie, Farben, Navigation und Komponentenrhythmus. + +Für Vor-Ort gilt der Quick Mockup aus Abschnitt 14.2 als Referenz für diese Balance. + +## 31.6 Navigation + +Die genaue mobile Belegung wird in der UI-Implementierungsphase anhand der aktivierten Module festgelegt, aber: + +- **Start**, **Team** und **Profil** sind Core-Ziele, +- Module registrieren Navigationseinträge, +- Navigation muss auch bei mehreren aktiven Modulen übersichtlich bleiben, +- Verwaltungsbereiche bleiben klar von der Mitgliederoberfläche getrennt. + +Diese Detailfrage darf bei der UI-Implementierung entschieden werden; sie ändert die Architektur nicht. + +--- + +# 32. Sicherheit + +## 32.1 Invite-only + +Es gibt keine offene Selbstregistrierung. Accounts entstehen über Einladung bzw. verifizierte E-Mail im Zusammenhang mit einer bestehenden Einladung/Organisation. + +## 32.2 Autorisierung + +Jeder teaminterne Zugriff prüft serverseitig: + +1. gültige Account-/Device-Session, +2. aktive TeamMembership, +3. Teamzugehörigkeit des Objekts, +4. Modulaktivierung, +5. konkrete Permission. + +Keine UI-Ausblendung ersetzt eine serverseitige Prüfung. + +## 32.3 Auth-Schutz + +- Auth-Codes/-Tokens nur gehasht speichern, +- Ablaufzeiten, +- Rate Limits, +- Fehlversuchslimit, +- konsumierte Challenges nicht wiederverwenden, +- generische Antworten vermeiden unnötige E-Mail-Enumeration, +- sichere Cookies. + +## 32.4 Web-Sicherheit + +Mindestens berücksichtigen: + +- CSRF-Schutz für zustandsändernde Requests, +- XSS-Vermeidung durch Jinja Autoescaping und gezielte Sanitization, +- Uploadpfade nie aus ungeprüften Dateinamen bauen, +- restriktive HTML-Einbettung, +- keine direkte öffentliche Auslieferung privater Uploads, +- sichere Redirects, +- Proxy-Headers nur bei aktivierter vertrauenswürdiger Proxy-Konfiguration. + +## 32.5 Datenschutz + +- keine Klient:innendaten, +- Datensparsamkeit, +- Teamgrenzen strikt, +- freiwillige Kontaktdaten opt-in, +- Login-E-Mail nicht als Teamkontakt missbrauchen, +- Foto-/Einwilligungsregel, +- Datenschutzinformationen und Verarbeitungsverzeichnis müssen vor echtem Betrieb organisatorisch mit der Aidshilfe abgestimmt werden. + +--- + +# 33. Datenbankstrategie + +## 33.1 SQLite bleibt v1-Entscheidung + +Für die geplante Größenordnung bleibt SQLite bewusst bestehen. + +Trotzdem: + +- ausschließlich über SQLAlchemy arbeiten, +- Flask-Migrate/Alembic ab Beginn der neuen Architektur, +- keine unnötigen SQLite-Spezialkonstruktionen, +- Datenmodell so halten, dass ein späterer PostgreSQL-Wechsel möglich bleibt. + +Ein PostgreSQL-Umstieg wird nicht vorweggebaut. + +## 33.2 Persistenter Pfad + +Produktion: + +```text +/app/instance/app.db +``` + +und Uploads ebenfalls unter dem persistenten `/app/instance`-Mount. + +Der konkrete Host-Volume-/Named-Volume-Name wird im Deployment-Stack dokumentiert. + +--- + +# 34. Migrationen + +Es gibt einen gemeinsamen Alembic-/Flask-Migrate-Verlauf für die gesamte App: + +```text +migrations/ +``` + +Module besitzen keine separaten, konkurrierenden Migrationssysteme. + +Migrationen werden mit der jeweiligen Featureänderung erzeugt, getestet und versioniert. + +Deployment führt ausstehende Migrationen bewusst aus; Schemaänderungen werden nicht durch `db.create_all()` in Produktion „magisch“ erledigt. + +--- + +# 35. Tests + +Jedes Core-Teil und jedes Fachmodul bringt Tests für seine Regeln mit. + +## 35.1 Pflicht-Sicherheitstests für jedes Teammodul + +Mindestens: + +1. keine TeamMembership → Zugriff verboten, +2. falsches Team → Zugriff verboten, +3. Modul deaktiviert → Zugriff verboten, +4. normales Mitglied → nur Mitgliederfunktionen, +5. Team-Verwaltung → Verwaltungsfunktionen, +6. Organisationsadmin ohne TeamMembership → Team-Inhalte verboten, +7. Objekt aus anderem Team → niemals sichtbar/manipulierbar. + +## 35.2 Auth-Tests + +- Code nur einmal verwendbar, +- abgelaufener Code abgelehnt, +- Fehlversuchslimit, +- widerrufene Device Session ungültig, +- frische Re-Auth für sensible Aktionen. + +## 35.3 Scheduling-Tests + +- Statusübergänge, +- Verfügbarkeit nach Freeze nicht änderbar, +- manuelle Zuteilung, +- Unterbesetzung, +- eindeutiges Springer-Nachrücken, +- kein automatisches Raten bei Mehrdeutigkeit, +- offene Position übernehmen, +- gleichzeitige Übernahme nur einmal erfolgreich, +- ursprüngliches `unavailable` blockiert spätere Selbstübernahme nicht, +- Dringlichkeit <24 h. + +## 35.4 Documents-Tests + +- Pending-Sichtbarkeit, +- Freigabe/Ablehnung, +- normale Mitglieder können kein HTML hochladen, +- HTML nur isoliert dargestellt, +- unerlaubte Dateitypen abgelehnt, +- Storage Path Traversal verhindert, +- Teamgrenzen bei Download/Preview, +- Rückzug eigener Pending-Uploads. + +## 35.5 Chat-Tests + +- Announcement-Schreibrecht, +- Discussion-Schreibrecht, +- Teamgrenzen, +- Reply, +- Edit/Delete nur eigene Nachrichten bzw. Moderation, +- keine Dateianhänge, +- Linkregeln, +- Read/Delivered-State, +- neue Teammitglieder zählen nicht rückwirkend für alte Read Receipts. + +## 35.6 UI-/A11y-Tests + +Mindestens kritische Flows in mobilen Breiten prüfen: + +- Login, +- Teamwechsel, +- Verfügbarkeit, +- Dienstübernahme, +- Dokumentupload, +- Chat, +- Teamverwaltung. + +--- + +# 36. Deployment-Vertrag + +Deployment ist Teil der Architektur. + +## 36.1 VPS-Struktur + +Bestehendes Betriebsprinzip: + +```text +~/apps// → Git-Arbeitskopie der Anwendung +~/stacks// → Compose-/Stack-Konfiguration +Caddy → Reverse Proxy / TLS +``` + +Der endgültige App-/Repo-/Containername ist noch festzulegen; danach wird **derselbe Name konsistent** für Repo, App-Verzeichnis, Stack-Verzeichnis und Container verwendet. + +## 36.2 Versioniert + +Mindestens: + +- `Dockerfile` +- `compose.yaml` bzw. versionierte Stack-Vorlage +- `.env.example` +- `requirements.txt` +- Python-Versionsfestlegung +- Migrationen +- `INSTALL.md` / Recovery-Anleitung +- `PROJECT_DESIGN.md` + +## 36.3 Nicht versioniert + +- echte `.env` +- `SECRET_KEY` +- SMTP-Passwort / Credentials +- Device-/Auth-Secrets +- Produktionsdatenbank +- Uploads +- Backups + +## 36.4 Docker + +- fester Python-Major/Minor-Stand, +- Gunicorn vorhanden, +- Prozess läuft als non-root User, +- persistentes `/app/instance`, +- keine Secrets im Image, +- Healthcheck bzw. `/health`. + +## 36.5 Proxy + +Caddy terminiert TLS und proxyt zur Flask/Gunicorn-App. + +`ProxyFix` wird nur aktiviert, wenn eine explizite Konfiguration wie `TRUST_PROXY_HEADERS=true` dies verlangt und der Proxy vertrauenswürdig ist. + +## 36.6 Health Endpoint + +`/health`: + +- sehr leichtgewichtig, +- ohne personenbezogene oder sensible Daten, +- geeignet für Container-/Deploymentprüfung. + +--- + +# 37. E-Mail als Produktionsabhängigkeit + +Passwordless Login macht E-Mail zu einer kritischen Infrastrukturkomponente. + +SMTP-/Mailkonfiguration ausschließlich über Environment, z. B. sinngemäß: + +```text +MAIL_HOST +MAIL_PORT +MAIL_USERNAME +MAIL_PASSWORD +MAIL_FROM +``` + +Konkrete Variablennamen können bei Implementierung finalisiert werden, müssen dann in `.env.example` dokumentiert sein. + +Keine Provider-Credentials im Code. + +Auth-/Einladungsmails sollen funktionieren, bevor v1 als betriebsbereit gilt. + +--- + +# 38. Backup- und Restore-Vertrag + +Ein vollständiges Backup besteht mindestens aus einem zusammengehörenden Stand von: + +1. SQLite-Datenbank, +2. Upload-/Storage-Verzeichnis. + +Nur `app.db` zu sichern ist **kein vollständiges Backup**. + +Backups müssen außerhalb des laufenden Container-Dateisystems verfügbar sein. + +Ein Restore-Prozess gilt erst als belastbar, wenn er mindestens einmal praktisch getestet wurde. + +Restore-Check: + +- Account/Org/Teams vorhanden, +- TeamMemberships vorhanden, +- Dokumentdateien funktionieren, +- Profilbilder funktionieren, +- Migrationen auf Zielstand laufen, +- Login-Mail funktioniert, +- Teamgrenzen weiterhin korrekt. + +--- + +# 39. Reinstall-/Recovery-Vertrag + +Eine Neuinstallation auf einem frischen Server soll reproduzierbar sein: + +```text +1. Host-Voraussetzungen installieren +2. Caddy / Docker vorbereiten +3. App-Repo nach ~/apps/ klonen +4. Stack nach ~/stacks/ bereitstellen +5. .env aus .env.example erstellen und Secrets eintragen +6. persistentes Datenvolume anlegen bzw. Backup wiederherstellen +7. Image bauen / Compose starten +8. Migrationen ausführen +9. Caddy-Route aktivieren +10. /health prüfen +11. E-Mail-Login testen +12. Team-/Modulzugriff als Smoke Test prüfen +``` + +Die App soll nicht davon abhängen, dass eine einzelne Person „noch weiß, wie das damals eingerichtet war“. + +--- + +# 40. Bootstrap des ersten Org-Admins + +Es gibt einen CLI-Befehl bzw. äquivalenten kontrollierten Bootstrap: + +```text +flask ... bootstrap-org-admin +``` + +Der genaue Commandname wird bei Implementierung festgelegt. + +Er: + +- nimmt eine E-Mail entgegen, +- erzeugt keinen hardcodierten Masteraccount, +- startet einen verifizierten Org-Admin-Einladungsflow, +- verweigert standardmäßig unkontrollierte weitere Bootstrap-Admins, wenn bereits Organisationsadministration existiert. + +Nach Restore kommen Org-Admin-Rechte aus der Datenbank zurück; Bootstrap ist kein normaler täglicher Verwaltungsweg. + +--- + +# 41. Vorgeschlagene Projektstruktur + +```text +/ +├── PROJECT_DESIGN.md +├── CLAUDE.md # optional: kurze Agentenregeln + Verweis hierher +├── TASKS.md # neuer Build-/Migrationsplan +├── README.md +├── INSTALL.md +├── .env.example +├── .python-version # oder gleichwertige Versionierung +├── requirements.txt +├── Dockerfile +├── compose.yaml # oder Stack-Vorlage +├── migrations/ +│ +├── app/ +│ ├── __init__.py +│ │ +│ ├── core/ +│ │ ├── auth/ +│ │ ├── accounts/ +│ │ ├── organizations/ +│ │ ├── teams/ +│ │ ├── memberships/ +│ │ ├── invitations/ +│ │ ├── permissions/ +│ │ ├── branding/ +│ │ ├── storage/ +│ │ ├── notifications/ +│ │ ├── dashboard/ +│ │ ├── profiles/ +│ │ └── audit/ +│ │ +│ ├── modules/ +│ │ ├── scheduling/ +│ │ ├── documents/ +│ │ └── chat/ +│ │ +│ ├── templates/ +│ │ ├── core/ +│ │ └── components/ +│ │ +│ └── static/ +│ ├── css/ +│ ├── js/ +│ └── avatars/ +│ +├── tests/ +└── instance/ # lokal; nicht versioniert +``` + +Die genaue Unterteilung kann pragmatisch angepasst werden. Nicht verhandelbar sind die fachlichen Grenzen Core ↔ Module und Services ↔ Routes. + +--- + +# 42. Migration vom bestehenden CheckPoint-Prototyp + +Der vorhandene Stand hat bereits große Teile des alten Einzelteam-Konzepts implementiert. Deshalb soll der Umbau nicht als unkontrolliertes „weiter draufprogrammieren“ erfolgen. + +## Phase 0 — Bestand sichern + +- aktuellen funktionierenden Stand committen, +- Legacy-Tag/Branch anlegen, +- aktuelle SQLite-/Uploaddaten sichern, falls reale Daten existieren, +- relevante bestehende Tests/Flows dokumentieren. + +## Phase 1 — Dokumente entwirren + +- dieses `PROJECT_DESIGN.md` als Source of Truth einführen, +- alte Konzeptdateien als historisch markieren, +- `CLAUDE.md` auf Arbeitsregeln + Verweis reduzieren, +- `TASKS.md` nicht weiter nach altem Architekturmodell abarbeiten. + +## Phase 2 — Plattform-Core + +In sinnvoll kleinen Migrationen: + +1. `Organization` +2. `Account` +3. `Team` +4. `OrganizationMembership` +5. `TeamMembership` +6. Einladungsmodelle +7. `BrandProfile` +8. `TeamModule` +9. `AuditLog` +10. Core Storage / Profile + +Bestehende Daten nur migrieren, wenn sie erhaltenswert/real sind. Bei reinem Entwicklungsdatenbestand darf ein sauberer Neu-Seed einfacher und sicherer sein als künstliche Datenrettung. + +## Phase 3 — Passwordless Auth + +- alten Benutzername-/Passwort-Flow entfernen, +- Mailkonfiguration, +- AuthChallenge, +- DeviceSession, +- Invite-only Onboarding, +- Bootstrap Org Admin, +- Re-Auth für sensible Aktionen. + +## Phase 4 — Team-/Org-Oberflächen + +- Teamkontext in URLs, +- Teamwechsler, +- Mitgliederstartseite, +- Teamverwaltung, +- Organisationsverwaltung, +- Mitgliederverzeichnis, +- freiwillige Teamkontakte, +- Avatar/Profile. + +## Phase 5 — Module Registry + Dashboard + +- `ModuleDefinition` +- `TeamModule` +- Navigation Contribution +- Member-/Manager-Dashboard Provider +- standardisierte DashboardItem-Komponente. + +## Phase 6 — Scheduling refactor + +- flexible Planungszeiträume, +- PositionDefinitions, +- Templates, +- ShiftPositions, +- neuer Statusflow, +- Availability Freeze, +- Assignments, +- Springerlogik, +- dringende Nachbesetzung, +- Scheduling Notifications/History. + +## Phase 7 — Documents refactor + +- Core Storage, +- Teamscoping, +- Approval/Archive trennen, +- HEIC/HEIF, +- Thumbnails, +- HTML nur für Team-Verwaltung und isoliert, +- Backup-/Restore-Test mit echten Testdateien. + +## Phase 8 — Chat refactor + +- generische Channels, +- Announcement + Discussion, +- Replies, +- Moderation, +- Linkrestriktionen, +- Polling, +- delivered/read states, +- Notification-Ausnahmen. + +## Phase 9 — Produktionshärtung + +- CSRF / Cookies / Rate Limits, +- Permission-Invariant-Tests, +- `/health`, +- Gunicorn, +- non-root Docker, +- ProxyFix-Konfiguration, +- Logging, +- Backup-/Restore-Dokumentation, +- SMTP Smoke Test. + +## Phase 10 — Deployment + +Erst wenn die neue Architektur, Auth und Teamgrenzen getestet sind, erfolgt der produktive VPS-Deploy nach dem Reinstall-Vertrag. + +--- + +# 43. Erweiterung um ein neues Modul + +Ein neues Fachmodul folgt immer demselben Rezept: + +```text +1. echten Bedarf beschreiben +2. stabilen module_key festlegen +3. ModuleDefinition registrieren +4. Datenmodell mit team_id anlegen +5. Migration erstellen +6. Services implementieren +7. Permissions definieren +8. Routes dünn halten +9. Templates aus zentralen UI-Komponenten bauen +10. Navigation/Dashboard-Beiträge registrieren +11. Team-Scoping- und Permission-Tests schreiben +12. erst dann für ausgewählte Teams aktivieren +``` + +Ein neues Modul darf nicht verlangen, den Core fachlich umzubauen, nur um sich selbst sichtbar zu machen. + +--- + +# 44. Future Log + +Bewusst vorbereitete, aber nicht vorweg implementierte Erweiterungen: + +## Galerie + +Eigenständiges Modul, ggf. gemeinsamer Core Storage. + +## Inventory + +Eigenständiges Modul für Material-/Bestandsverwaltung, falls ein Team echten Bedarf meldet. + +## Qualifications + +Spätere Erweiterung, z. B. erforderliche Schulungen für Positionen. Das Scheduling-Positionsmodell lässt dafür Platz, ohne Qualifikationen heute einzubauen. + +## Kalenderintegration + +Später denkbar, aber nicht Teil des Scheduling-Kerns von v1. + +## Push / operative E-Mail + +Kann später den Notification-Service als Ausgabekanal ergänzen. + +## Passkeys / 2FA + +Vor allem für sensible Organisationsadministration später denkbar. + +## feinere Rollen / Permissions + +Der Code benutzt schon Permission Helpers, damit später neue Rollen möglich sind, ohne Rollenstrings überall zu ändern. + +## PostgreSQL + +Falls Last, Parallelität oder Betrieb es später sinnvoll machen. Kein aktuelles Ziel. + +--- + +# 45. Noch bewusst offene Konfigurationsdetails + +Diese Punkte sind **keine offenen Architekturfragen**, sondern dürfen bei Implementierung/Deployment konkretisiert werden. Sie sollen nicht stillschweigend erfunden, sondern als kleine Produkt-/Betriebsentscheidung dokumentiert werden: + +1. endgültiger Produkt-/Repo-/Containername, +2. exakte Auth-Code-Gültigkeit und Device-Session-Dauer, +3. konkrete Upload-Größenlimits, +4. konkrete Liste blockierter URL-Shortener, +5. endgültige mobile Navigation bei vielen aktivierten Modulen, +6. konkreter SMTP-Provider und Env-Variablennamen, +7. genaue Backup-Aufbewahrungsdauer, +8. ob normale Mitglieder in v1 ihre Teammitgliedschaft selbst verlassen dürfen oder ob eine Team-Verwaltung sie beendet, +9. genaue Regeln für erneutes Senden/Verlängern abgelaufener Einladungen. + +Für diese Details gilt: klein entscheiden, dokumentieren, testen — keine neue Plattformarchitektur daraus machen. + +--- + +# 46. Definition of Done für v1 + +v1 ist nicht fertig, nur weil die Seiten funktionieren. + +Sie gilt als bereit, wenn: + +- Passwordless Login zuverlässig funktioniert, +- Einladung + Onboarding funktionieren, +- mehrere Accounts und mehrere Teams getestet sind, +- dieselbe Person in mehreren Teams funktionieren kann, +- Teamwechsel klar ist, +- Org-Admin ohne TeamMembership keine Team-Inhalte lesen kann, +- Team-Verwaltungen keine weiteren Manager ernennen können, +- Brand Profiles teamweise funktionieren, +- Module pro Team aktivierbar/deaktivierbar sind, +- Scheduling komplett im neuen Positionsmodell funktioniert, +- Documents inkl. Freigabe/Storage/Preview funktionieren, +- Chat die festgelegten Arbeitskommunikationsregeln einhält, +- Dashboard-Providers modulunabhängig funktionieren, +- mobile Hauptflows auf ca. 360–460 px gut bedienbar sind, +- kritische A11y-Grundsätze eingehalten sind, +- DB-Migrationen reproduzierbar laufen, +- `/health` funktioniert, +- Docker/Gunicorn/Caddy-Setup dokumentiert ist, +- Backup von DB + Uploads funktioniert, +- ein Restore praktisch getestet wurde, +- SMTP-Loginmail im Produktionssetup getestet wurde, +- Reinstall-Anleitung von einer frischen Umgebung aus nachvollziehbar ist. + +--- + +# 47. Kurzfassung der nicht verhandelbaren Architekturregeln + +1. **Account ist teamunabhängig.** +2. **Teamzugehörigkeit läuft ausschließlich über TeamMembership.** +3. **Team-Verwaltung ist zusätzliches Recht eines normalen Teammitglieds.** +4. **Nur Organisationsadministration ernennt/entfernt Team-Verwaltungen.** +5. **Org-Admin ist kein Generalschlüssel für Teamdaten.** +6. **Teaminterne Fachobjekte tragen `team_id`.** +7. **Module dürfen den Core nutzen; der Core kennt keine Fachlogik einzelner Module.** +8. **Code definiert vorhandene Module, `TeamModule` definiert Aktivierung pro Team.** +9. **Org-Verwaltung aktiviert Module; Team-Verwaltung konfiguriert sie.** +10. **Routes bleiben dünn; Businesslogik lebt in Services.** +11. **Permission Helpers statt verstreuter Rollenchecks.** +12. **Dashboard zeigt Zustände; Notifications melden Ereignisse.** +13. **Keine Passwörter in v1 — E-Mail-Code + langlebige widerrufbare Device Session.** +14. **Private Uploads werden nie unkontrolliert statisch ausgeliefert.** +15. **SQLite + Migrationen von Anfang an; PostgreSQL ist kein v1-Projekt.** +16. **Backup bedeutet Datenbank + Uploads.** +17. **Deployment und Restore sind Teil des Produkts, nicht Nacharbeit.** +18. **Neue Funktionen werden als klar begrenzte Module ergänzt, nicht in bestehende Module hineingestopft.** + +--- + +# 48. Nächster Schritt + +Aus diesem Dokument wird als nächstes ein **neues `TASKS.md` für den Umbau/Migrationspfad** abgeleitet. + +Dabei gilt: + +- kleine Sessions, +- jede Session hat ein klar prüfbares Ergebnis, +- zuerst Core und Sicherheitsgrenzen, +- erst danach Fachmodule, +- bestehende funktionierende Teile werden nur übernommen, wenn sie zum neuen Modell passen, +- keine alte Architektur erhalten, nur weil bereits Code dafür existiert. + +`PROJECT_DESIGN.md` bleibt während dieses Umbaus die verbindliche Grundlage. diff --git a/VAULT/hinunher/PROJECT_DESIGN_VOR_ORT_MOCKUP.png b/VAULT/hinunher/PROJECT_DESIGN_VOR_ORT_MOCKUP.png new file mode 100644 index 0000000..6014969 Binary files /dev/null and b/VAULT/hinunher/PROJECT_DESIGN_VOR_ORT_MOCKUP.png differ diff --git a/VAULT/hinunher/TASKS_Phase0.md b/VAULT/hinunher/TASKS_Phase0.md new file mode 100644 index 0000000..00b4472 --- /dev/null +++ b/VAULT/hinunher/TASKS_Phase0.md @@ -0,0 +1,538 @@ +# TASKS.md — AHK-Ehrenamtsmanagement + +> **Status:** Phase 0 freigegeben zur Umsetzung +> **Stand:** 13.08.2026 +> **Source of Truth:** `PROJECT_DESIGN.md` +> **Arbeitsweise:** kleine Sessions mit klar prüfbarem Ergebnis +> **Wichtig:** Dieses Dokument darf keine neue Architektur erfinden. Offene Entscheidungen werden markiert und gemeinsam entschieden. + +--- + +# Phase 0 — Greenfield-Projektbasis + +## Ziel der Phase + +Das Projekt wird **komplett neu von Grund auf aufgebaut**. + +Der bestehende CheckPoint-/Legacy-Prototyp ist **kein Migrationsziel**. Es gibt keine erhaltenswerten Produktivdaten und keine bestehende Codebasis, auf deren Kompatibilität Rücksicht genommen werden muss. + +Bestehender Legacy-Code wird nicht übernommen, nur weil er bereits existiert. Falls später einzelne Ideen oder Implementierungen als Referenz dienen, müssen sie zuerst gegen `PROJECT_DESIGN.md` geprüft werden. + +Am Ende von Phase 0 existiert eine minimale, lauffähige und getestete Flask-Anwendung mit: + +- sauberem Git-Repository, +- festgelegter Python-3.14-Projektumgebung über `pyenv`, +- eigenem `pyenv-virtualenv`, +- Flask App Factory, +- eigenem Core-Blueprint für `/health`, +- getrennten Runtime- und Development-Abhängigkeiten, +- erstem automatisierten Test. + +Noch **nicht** Teil dieser Phase: + +- Datenbank, +- SQLAlchemy, +- Flask-Migrate / Alembic, +- Accounts oder Rollen, +- Authentifizierung, +- Teams oder Organisation, +- Templates / Dashboard, +- CSS / Branding, +- Docker / Gunicorn / Caddy, +- Fachmodule. + +--- + +# 0.1 — Projektidentität festlegen + +## Entscheidungen + +- [x] Produktname: **AHK-Ehrenamtsmanagement** +- [x] Technischer Name: **`ahk-ehrenamtsmanagement`** +- [x] Greenfield-Neustart statt Legacy-Migration +- [x] Lokale Shell: **Bash** +- [x] Entwicklungsbetriebssystem: **EndeavourOS / Arch Linux** +- [x] Python-Serie: **3.14** +- [x] Python-Versionierung: **pyenv** +- [x] Virtuelle Umgebung: **pyenv-virtualenv** + +## Konvention + +Der technische Name `ahk-ehrenamtsmanagement` soll später konsistent für Repository, App-Verzeichnis, Stack-Verzeichnis und Container verwendet werden. + +## Prüfergebnis + +Phase 0.1 ist abgeschlossen, wenn keine weiteren Namensentscheidungen für den Projektstart notwendig sind. + +**Status:** ✅ entschieden + +--- + +# 0.2 — Neues Git-Projekt initialisieren + +## Ziel + +Ein sauberes lokales Git-Repository ohne Legacy-Code und ohne vorweggenommene Anwendungsstruktur. + +## Aufgaben + +- [ ] Neuen Projektordner `ahk-ehrenamtsmanagement` anlegen. +- [ ] In den Projektordner wechseln. +- [ ] Neues Git-Repository initialisieren. +- [ ] `PROJECT_DESIGN.md` in den Projektroot übernehmen. +- [ ] Vor-Ort-Mockup als visuelle Referenz in den Projektroot übernehmen. +- [ ] Minimale `.gitignore` anlegen. +- [ ] Prüfen, dass keine lokalen Secrets oder Entwicklungsdaten versioniert werden. +- [ ] Ersten Baseline-Commit erstellen. + +## Beispiel + +```bash +mkdir ahk-ehrenamtsmanagement +cd ahk-ehrenamtsmanagement +git init +``` + +Die beiden Ausgangsdateien anschließend in den Projektroot kopieren. + +### `.gitignore` + +```gitignore +# Python +__pycache__/ +*.py[cod] + +# Optional local virtual environments +.venv/ +venv/ + +# Local application data +instance/ + +# Environment / secrets +.env + +# OS / editor noise +.DS_Store +``` + +Danach: + +```bash +git add . +git status +git commit -m "Initialize AHK-Ehrenamtsmanagement project" +``` + +## Noch nicht tun + +- keinen Remote erzwingen, +- kein GitHub/Gitea-Setup vorwegnehmen, +- kein Flask installieren, +- keine komplette spätere Verzeichnisstruktur leer vorbauen. + +## Prüfergebnis + +```bash +git status +``` + +soll einen sauberen Arbeitsbaum zeigen. + +**Done wenn:** Projektroot + Source-of-Truth + Mockup + `.gitignore` sauber versioniert sind. + +--- + +# 0.3 — Python 3.14 als Projektbasis festlegen + +## Ziel + +Die Anwendung wird bewusst auf Python 3.14 entwickelt. + +## Aufgaben + +- [x] Python-Major/Minor festgelegt: **3.14** +- [ ] Konkreten installierten `3.14.x`-Patchstand für die Projektumgebung verwenden. +- [ ] Sicherstellen, dass dieser Interpreter über `pyenv` verfügbar ist. + +> Die konkrete Patchversion wird nicht in `TASKS.md` erfunden. Verwendet wird der bewusst ausgewählte/installierte Python-3.14-Patchstand. + +## Prüfergebnis + +```bash +pyenv versions +``` + +zeigt einen geeigneten Python-3.14-Interpreter. + +**Done wenn:** Python 3.14 lokal über pyenv bereitsteht. + +--- + +# 0.4 — Projekt-Virtualenv mit pyenv-virtualenv anbinden + +## Ziel + +Das Projekt besitzt eine eigene isolierte Python-Umgebung und aktiviert sie projektbezogen über `.python-version`. + +## Voraussetzung + +`pyenv` und `pyenv-virtualenv` funktionieren auf dem Entwicklungsrechner bereits und wurden mehrfach getestet. Die globale Bash-Konfiguration wird deshalb in dieser Phase **nicht erneut umgebaut**. + +## Aufgaben + +- [ ] Virtualenv für das Projekt auf Basis des gewählten Python-3.14-Interpreters erzeugen. +- [ ] Environment-Name: **`ahk-ehrenamtsmanagement`** +- [ ] Environment im Projektroot mit `pyenv local` zuweisen. +- [ ] Prüfen, dass `.python-version` entstanden ist. +- [ ] `.python-version` versionieren. +- [ ] Prüfen, dass `python` tatsächlich aus dem Projekt-Environment kommt. + +## Beispiel + +`` durch die tatsächlich verwendete pyenv-Version ersetzen: + +```bash +pyenv virtualenv ahk-ehrenamtsmanagement +pyenv local ahk-ehrenamtsmanagement +``` + +Prüfen: + +```bash +python --version +which python +pyenv version +``` + +## Erwartung + +- `python --version` zeigt Python 3.14.x. +- `pyenv version` zeigt `ahk-ehrenamtsmanagement`. +- `.python-version` liegt im Projektroot. + +## Prüfergebnis + +**Done wenn:** Das Projekt verwendet automatisch sein eigenes pyenv-virtualenv. + +--- + +# 0.5 — Minimale Python-Abhängigkeiten einführen + +## Ziel + +Runtime- und Development-Abhängigkeiten werden von Beginn an getrennt gepflegt. + +## Entscheidungen + +- [x] Runtime-Abhängigkeiten: `requirements.txt` +- [x] Development-/Test-Abhängigkeiten: `requirements-dev.txt` +- [x] Entwicklungsdatei bindet Runtime-Abhängigkeiten mit `-r requirements.txt` ein. + +## Aufgaben + +- [ ] `requirements.txt` anlegen. +- [ ] Nur Flask als erste Runtime-Abhängigkeit eintragen. +- [ ] `requirements-dev.txt` anlegen. +- [ ] `requirements.txt` darin referenzieren. +- [ ] `pytest` als erste Development-Abhängigkeit eintragen. +- [ ] Development-Abhängigkeiten im aktiven pyenv-Environment installieren. +- [ ] Keine späteren Pakete vorsorglich hinzufügen. + +### `requirements.txt` + +```text +Flask +``` + +### `requirements-dev.txt` + +```text +-r requirements.txt + +pytest +``` + +Installation: + +```bash +python -m pip install -r requirements-dev.txt +``` + +## Konvention + +Für Paketbefehle bevorzugen wir: + +```bash +python -m pip ... +``` + +statt eines unqualifizierten `pip ...`, damit eindeutig der Paketmanager des aktiven Python-Interpreters verwendet wird. + +## Prüfergebnis + +```bash +python -c "import flask; print(flask.__version__)" +python -m pytest --version +``` + +Beide Befehle müssen funktionieren. + +**Done wenn:** Flask und pytest im Projekt-Environment verfügbar sind und die Abhängigkeiten getrennt dokumentiert sind. + +--- + +# 0.6 — Minimale Flask-App mit App Factory bauen + +## Ziel + +Die erste Anwendung startet über eine Flask App Factory und besitzt einen eigenen Core-Blueprint für den Healthcheck. + +## Architekturentscheidung + +`/health` wird **nicht** direkt in `app/__init__.py` definiert. + +Stattdessen erhält die Betriebsinfrastruktur einen kleinen eigenen Core-Bereich: + +```text +app/ +├── __init__.py +└── core/ + ├── __init__.py + └── health/ + ├── __init__.py + └── routes.py +``` + +Die Factory baut die App zusammen; der Blueprint besitzt die Route. + +## Aufgaben + +- [ ] Verzeichnis `app/core/health/` anlegen. +- [ ] notwendige `__init__.py`-Dateien anlegen. +- [ ] Health-Blueprint definieren. +- [ ] `/health`-Route definieren. +- [ ] `create_app()` in `app/__init__.py` implementieren. +- [ ] Health-Blueprint in der Factory registrieren. +- [ ] Flask Development Server starten. +- [ ] `/health` manuell prüfen. + +### Verzeichnisse anlegen + +```bash +mkdir -p app/core/health + +touch app/__init__.py +touch app/core/__init__.py +touch app/core/health/__init__.py +touch app/core/health/routes.py +``` + +### `app/core/health/__init__.py` + +```python +from flask import Blueprint + + +bp = Blueprint("health", __name__) + + +from app.core.health import routes +``` + +### `app/core/health/routes.py` + +```python +from app.core.health import bp + + +@bp.get("/health") +def health(): + return {"status": "ok"}, 200 +``` + +### `app/__init__.py` + +```python +from flask import Flask + + +def create_app(): + app = Flask(__name__) + + from app.core.health import bp as health_bp + app.register_blueprint(health_bp) + + return app +``` + +## Starten + +```bash +flask --app 'app:create_app' run --debug +``` + +In einem zweiten Terminal: + +```bash +curl -i http://127.0.0.1:5000/health +``` + +## Erwartung + +HTTP-Status: + +```text +200 OK +``` + +JSON: + +```json +{"status": "ok"} +``` + +Der Endpoint enthält keine personenbezogenen, sensiblen oder unnötigen Systeminformationen. + +## Noch nicht tun + +- keine Datenbank, +- keine Konfigurationsarchitektur vorwegnehmen, +- keinen `SECRET_KEY` erfinden, +- keine Templates, +- keinen Startseiten-Blueprint, +- keine Fachmodule. + +**Done wenn:** `create_app()` startet und `/health` zuverlässig `200` liefert. + +--- + +# 0.7 — Testbasis mit pytest aufsetzen + +## Ziel + +Die erste Funktion der App wird sofort automatisiert getestet. + +## Aufgaben + +- [ ] Verzeichnis `tests/` anlegen. +- [ ] `tests/test_health.py` anlegen. +- [ ] Flask-App über `create_app()` im Test erzeugen. +- [ ] Flask Test Client verwenden. +- [ ] HTTP-Status von `/health` prüfen. +- [ ] JSON-Antwort prüfen. +- [ ] Gesamte Testsuite mit `python -m pytest` ausführen. + +### `tests/test_health.py` + +```python +from app import create_app + + +def test_health_endpoint(): + app = create_app() + client = app.test_client() + + response = client.get("/health") + + assert response.status_code == 200 + assert response.get_json() == {"status": "ok"} +``` + +Ausführen: + +```bash +python -m pytest +``` + +## Erwartung + +Mindestens: + +```text +1 passed +``` + +**Done wenn:** Der Healthcheck automatisiert getestet wird und die komplette aktuelle Testsuite grün ist. + +--- + +# 0.8 — Phase-0-Checkpoint + +## Ziel + +Einen klaren, reproduzierbaren Ausgangspunkt schaffen, bevor Datenbank, Core-Domänenmodelle oder Authentifizierung beginnen. + +## Aufgaben + +- [ ] Projektstruktur kontrollieren. +- [ ] `git status` prüfen. +- [ ] `.python-version` ist versioniert. +- [ ] `.env`, `instance/` und lokale Daten sind nicht versioniert. +- [ ] `python --version` zeigt Python 3.14.x. +- [ ] `pyenv version` zeigt das Projekt-Environment. +- [ ] `python -m pytest` ist grün. +- [ ] `/health` funktioniert manuell. +- [ ] Keine fachliche Funktionalität wurde vorweg implementiert. +- [ ] Phase-0-Stand committen. + +Beispiel: + +```bash +git add . +git status +git commit -m "Build minimal Flask application foundation" +``` + +## Erwartete Struktur am Ende von Phase 0 + +```text +ahk-ehrenamtsmanagement/ +├── app/ +│ ├── __init__.py +│ └── core/ +│ ├── __init__.py +│ └── health/ +│ ├── __init__.py +│ └── routes.py +├── tests/ +│ └── test_health.py +├── PROJECT_DESIGN.md +├── PROJECT_DESIGN_VOR_ORT_MOCKUP.png +├── .gitignore +├── .python-version +├── requirements.txt +└── requirements-dev.txt +``` + +--- + +# Definition of Done — Phase 0 + +Phase 0 ist abgeschlossen, wenn alle folgenden Aussagen wahr sind: + +- [ ] Das Projekt ist ein neuer Greenfield-Codebestand. +- [ ] Der technische Projektname ist `ahk-ehrenamtsmanagement`. +- [ ] Das Projekt liegt in einem eigenen Git-Repository. +- [ ] `PROJECT_DESIGN.md` ist im Repository die verbindliche Source of Truth. +- [ ] Das Vor-Ort-Mockup liegt als visuelle Referenz vor. +- [ ] Python 3.14 wird über pyenv verwaltet. +- [ ] Das Projekt nutzt ein eigenes `pyenv-virtualenv`. +- [ ] `.python-version` bindet die lokale Projektumgebung. +- [ ] Runtime- und Development-Abhängigkeiten sind getrennt. +- [ ] Flask wird über `create_app()` erzeugt. +- [ ] `/health` liegt in einem eigenen Core-Blueprint. +- [ ] `/health` liefert `200` und nur einen minimalen Status. +- [ ] pytest ist eingerichtet. +- [ ] Der Healthcheck besitzt einen automatisierten Test. +- [ ] Alle Tests sind grün. +- [ ] Der Stand ist sauber committed. +- [ ] Es wurden noch keine nicht entschiedenen Architekturdetails vorweggenommen. + +--- + +# Danach + +Nach Phase 0 wird **nicht automatisch weiterimplementiert**. + +Die nächsten Phasen werden zuerst gemeinsam anhand von `PROJECT_DESIGN.md` durchgesprochen. Offene Produkt- oder Betriebsentscheidungen werden ausdrücklich geklärt, bevor daraus Aufgaben entstehen.