Springer:in für heute gesucht
+18:00–21:30 · Café Bach · Noch 1 Position offen
+diff --git a/notes/2026-09-16_AHK-Ehrenamtsmanagement_Entscheidungsprotokoll-bisher.md b/notes/2026-09-16_AHK-Ehrenamtsmanagement_Entscheidungsprotokoll-bisher.md new file mode 100644 index 0000000..8950f6e --- /dev/null +++ b/notes/2026-09-16_AHK-Ehrenamtsmanagement_Entscheidungsprotokoll-bisher.md @@ -0,0 +1,897 @@ +# AHK-Ehrenamtsmanagement — Entscheidungsprotokoll und bisheriger Aufbau + +> **Stand:** 2026-09-16 +> **Projekt:** AHK-Ehrenamtsmanagement +> **Technischer Name:** `ahk-ehrenamtsmanagement` +> **Status:** Greenfield-Neustart; Phase 0 vollständig geplant, Phase 1 vollständig entschieden, Phase 2 begonnen +> **Verbindliche Grundlage:** `PROJECT_DESIGN.md` +> **Visuelle Referenz:** `PROJECT_DESIGN_VOR_ORT_MOCKUP.png` +> **Arbeitsprinzip:** Wenn `PROJECT_DESIGN.md` etwas nicht entscheidet, wird nicht geraten, sondern bewusst gemeinsam entschieden. + +--- + +# 1. Grundsatzentscheidungen + +## 1.1 Greenfield statt Legacy-Umbau + +Der bestehende Prototyp wird **nicht migriert**. + +- kein erhaltenswerter Produktivdatenbestand +- nur ein einzelner Testnutzer ohne relevante Inhalte +- Ziel ist ausdrücklich, den Aufbau der App von Grund auf zu lernen +- kein Legacy-Code wird nur deshalb übernommen, weil er bereits existiert +- keine Legacy-Datenmigration +- keine Kompatibilitätsschicht +- keine Datenrettung aus der alten SQLite-Datenbank + +Falls alte Implementierungen später als Referenz dienen, werden sie zuerst gegen `PROJECT_DESIGN.md` geprüft. + +## 1.2 Projektname + +Sichtbarer Produktname: + +```text +AHK-Ehrenamtsmanagement +``` + +Technischer Name: + +```text +ahk-ehrenamtsmanagement +``` + +Der technische Name soll konsistent für Repository, App-Verzeichnis, Stack-Verzeichnis und Container verwendet werden. + +## 1.3 Entwicklungsumgebung + +```text +Betriebssystem: EndeavourOS / Arch Linux +Shell: Bash +Python-Verwaltung: pyenv +Virtuelle Umgebungen: pyenv-virtualenv +Python-Serie: 3.14 +``` + +Fish ist nicht mehr Standardshell. + +## 1.4 Python-Environment + +Eigenes pyenv-virtualenv: + +```text +ahk-ehrenamtsmanagement +``` + +Projektbindung: + +```bash +pyenv local ahk-ehrenamtsmanagement +``` + +`.python-version` wird versioniert. Das eigentliche Virtualenv liegt außerhalb des Repositories in der pyenv-Verwaltung. + +--- + +# 2. Phase 0 — Greenfield-Projektbasis + +## Ziel + +Am Ende von Phase 0 existiert eine minimale, lauffähige und getestete Flask-Anwendung. + +Noch nicht enthalten: + +- Datenbankmodelle +- Auth +- Organisationen +- Teams +- Rollen +- Templates +- Branding +- Docker +- Fachmodule + +## 2.1 Git-Grundlage + +Initiale Struktur: + +```text +ahk-ehrenamtsmanagement/ +├── PROJECT_DESIGN.md +├── PROJECT_DESIGN_VOR_ORT_MOCKUP.png +└── .gitignore +``` + +Initialisierung: + +```bash +mkdir ahk-ehrenamtsmanagement +cd ahk-ehrenamtsmanagement +git init +``` + +Minimale `.gitignore`: + +```gitignore +# Python +__pycache__/ +*.py[cod] + +# Optional local virtual environments +.venv/ +venv/ + +# Local application data +instance/ + +# Environment / secrets +.env + +# OS / editor noise +.DS_Store +``` + +Baseline-Commit: + +```bash +git add . +git status +git commit -m "Initialize AHK-Ehrenamtsmanagement project" +``` + +Ein Remote ist in Phase 0 nicht Pflicht. + +## 2.2 Abhängigkeiten + +Runtime und Development werden getrennt. + +`requirements.txt`: + +```text +Flask +``` + +`requirements-dev.txt`: + +```text +-r requirements.txt + +pytest +``` + +Installation: + +```bash +python -m pip install -r requirements-dev.txt +``` + +Bevorzugte Form: + +```text +python -m pip ... +``` + +## 2.3 Erste Flask-Struktur + +Verbindlich: + +- Flask App Factory +- Blueprints +- eigener Core-Bereich für `/health` + +Struktur: + +```text +app/ +├── __init__.py +└── core/ + ├── __init__.py + └── health/ + ├── __init__.py + └── 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 +``` + +Initiale `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 +``` + +Start: + +```bash +flask --app 'app:create_app' run --debug +``` + +Healthcheck: + +```bash +curl -i http://127.0.0.1:5000/health +``` + +Erwartung: + +```json +{"status": "ok"} +``` + +mit HTTP 200. + +## 2.4 Erste Testbasis + +`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 +``` + +--- + +# 3. Phase 1 — Technisches App-Fundament + +## Reihenfolge + +```text +1.1 Architektur-Gerüst +1.2 Konfigurationssystem +1.3 Flask Extensions +1.4 SQLAlchemy + SQLite +1.5 Flask-Migrate / Alembic +1.6 Teststruktur / Fixtures +1.7 Foundation-Tests +1.8 Checkpoint +``` + +## 3.1 Architektur-Gerüst + +Die vollständige Ordnerstruktur wird als Gerüst angelegt. + +Wichtig: +- nur Ordner/Pakete vorbereiten +- nicht vorsorglich überall `models.py`, `routes.py`, `services.py`, `permissions.py` oder `forms.py` anlegen +- solche Dateien entstehen erst bei echter Implementierung + +```text +app/ +├── __init__.py +├── core/ +│ ├── __init__.py +│ ├── health/ +│ ├── auth/ +│ ├── accounts/ +│ ├── organizations/ +│ ├── teams/ +│ ├── memberships/ +│ ├── invitations/ +│ ├── permissions/ +│ ├── branding/ +│ ├── storage/ +│ ├── notifications/ +│ ├── dashboard/ +│ ├── profiles/ +│ └── audit/ +├── modules/ +│ ├── __init__.py +│ ├── scheduling/ +│ ├── documents/ +│ └── chat/ +├── templates/ +│ ├── core/ +│ └── components/ +└── static/ + ├── css/ + ├── js/ + └── avatars/ + +tests/ +instance/ +``` + +Git speichert keine leeren Ordner. Keine künstliche `.gitkeep`-Orgie. + +## 3.2 `config.py` + +Liegt im **Projektroot**. + +Zunächst: + +```python +class Config: + pass + +class DevelopmentConfig(Config): + DEBUG = True + +class TestingConfig(Config): + TESTING = True +``` + +Noch nicht vorsorglich anlegen: +- `ProductionConfig` +- `SECRET_KEY` +- SMTP +- Uploadlimits +- Auth-Zeiten +- Polling-Intervalle + +## 3.3 App-Factory-Interface + +Festgelegt: + +```python +def create_app( + config_class=DevelopmentConfig, + config_overrides=None, +): + ... +``` + +Reihenfolge: + +```text +Factory starten +→ Config-Klasse laden +→ optionale Overrides +→ Extensions initialisieren +→ Blueprints registrieren +→ später Module registrieren +``` + +Sinngemäß: + +```python +from flask import Flask +from config import DevelopmentConfig + +def create_app( + config_class=DevelopmentConfig, + config_overrides=None, +): + app = Flask(__name__) + + app.config.from_object(config_class) + + if config_overrides: + app.config.from_mapping(config_overrides) + + return app +``` + +## 3.4 Zentrale Flask-Extensions + +Datei: + +```text +app/extensions.py +``` + +Nur Extension-Instanzen, keine Models, keine Businesslogik, keine Konfiguration. + +Zunächst: + +```python +from flask_sqlalchemy import SQLAlchemy + +db = SQLAlchemy() +``` + +Später: + +```python +from flask_migrate import Migrate +from flask_sqlalchemy import SQLAlchemy + +db = SQLAlchemy() +migrate = Migrate() +``` + +In der Factory: + +```python +db.init_app(app) +migrate.init_app(app, db) +``` + +## 3.5 SQLite-Strategie + +Development: + +```text +instance/app.db +``` + +Sinngemäß: + +```python +SQLALCHEMY_DATABASE_URI = "sqlite:///app.db" +``` + +Tests: +- pro Test eigene temporäre echte SQLite-Datei +- keine In-Memory-Datenbank +- keine gemeinsam genutzte Testdatenbank +- nie `instance/app.db` + +```text +DevelopmentConfig → instance/app.db +TestingConfig → temporäre SQLite-Datei pro Test +Production später → /app/instance/app.db +``` + +## 3.6 Flask-Migrate / Alembic + +Runtime-Abhängigkeiten: + +```text +Flask +Flask-SQLAlchemy +Flask-Migrate +``` + +Initialisierung bereits in Phase 1: + +```bash +flask --app 'app:create_app' db init +``` + +Ergebnis: + +```text +migrations/ +├── alembic.ini +├── env.py +├── script.py.mako +└── versions/ +``` + +Festgelegt: +- `migrations/` wird versioniert +- keine künstliche leere erste Migration +- erste echte Migration erst mit erstem Domain-Model in Phase 2 +- `db.create_all()` ist keine Produktionsstrategie + +## 3.7 pytest-Fixtures + +```text +tests/ +├── conftest.py +└── test_health.py +``` + +In Phase 1 nur: +- `app` +- `client` +- temporäre SQLite-Testdatenbank + +Noch keine Domain-Fixtures. + +## 3.8 Foundation-Tests + +Mindestens: + +1. `/health` liefert 200 und `{"status": "ok"}` +2. `TestingConfig` ist aktiv +3. `config_overrides` überschreibt Config-Werte +4. SQLAlchemy erreicht die temporäre SQLite-DB (`SELECT 1`) +5. Tests verwenden niemals `instance/app.db` + +## 3.9 Phase-1-Checkpoint + +Phase 1 ist fertig, wenn: + +- Architektur-Gerüst vorhanden +- Root-`config.py` vorhanden +- Factory-Interface umgesetzt +- `app/extensions.py` vorhanden +- SQLAlchemy integriert +- Development-SQLite auf `instance/app.db` +- Tests je eigene temporäre SQLite-Datei +- Flask-Migrate/Alembic initialisiert +- keine künstliche Migration erzeugt +- `/health` funktioniert +- alle Foundation-Tests grün +- keine Secrets oder lokalen DB-Daten versioniert +- sauber committed + +Beispiel: + +```bash +git add . +git status +git commit -m "Establish application foundation" +``` + +--- + +# 4. Phase 2 — Plattform-Core / Datenmodell + +## Reihenfolge + +```text +2.1 Organization +2.2 Account +2.3 BrandProfile +2.4 Team +2.5 OrganizationMembership +2.6 TeamMembership +2.7 Permission-Grundlagen +2.8 TeamInvitation +2.9 OrganizationAdminInvitation +2.10 TeamModule +2.11 AuditLog +2.12 Core-Integritätstests +2.13 Checkpoint +``` + +`BrandProfile` wird bewusst **vor `Team`** gebaut, damit `Team.brand_profile_id` von Anfang an auf ein reales Modell zeigt. + +--- + +# 5. Globale Model-Konventionen + +## 5.1 Primary Keys + +Normale Domain-Models verwenden: + +```text +Integer Primary Keys +``` + +Opaque/kryptographische Werte werden dort verwendet, wo sie tatsächlich nötig sind, z. B. Auth-Tokens oder Storage Keys. + +## 5.2 Statusfelder + +Begrenzte Statusfelder: +- Python-Enums +- klare Stringwerte in der Datenbank + +Beispiele: + +```text +active +archived +disabled +``` + +## 5.3 Zeitstempel + +Global: +- intern UTC +- Python-Seite timezone-aware +- lokale Zeitzone erst bei Darstellung +- keine lokale Serverzeit als Domain-Wahrheit + +--- + +# 6. `Organization` + +Beschlossen: + +```text +Organization +├── id Integer PK +├── name required +├── slug required, unique, stabil +├── status active | archived +└── created_at UTC timestamp +``` + +## Slug + +Ergänzt, weil Organisations-URLs Slugs verwenden: + +```text +Aidshilfe Köln e.V. +→ aidshilfe-koeln +``` + +URL: + +```text +/org/aidshilfe-koeln/manage/ +``` + +Der Slug ist eine **stabile technische Kennung** und folgt Namensänderungen nicht automatisch. + +## Betriebsrealität + +Die Architektur behält `Organization`, aber der reale Betrieb ist auf genau eine Organisation ausgerichtet: + +```text +Aidshilfe Köln e.V. +``` + +Es ist nicht geplant, die App anderen Aidshilfen oder externen Organisationen bereitzustellen. + +--- + +# 7. `Account` + +Beschlossen: + +```text +Account +├── id Integer PK +├── email required, unique, normalized +├── display_name optional +├── status +│ ├── active +│ └── disabled +├── created_at UTC +└── last_login_at optional, UTC +``` + +## E-Mail-Normalisierung + +Beispiel: + +```text +" Patsy@Example.DE " +→ "patsy@example.de" +``` + +Regeln: +- Whitespace entfernen +- Groß-/Kleinschreibung für Account-Identität nicht unterscheiden +- gleiche Normalisierung vor Suche und Speicherung +- zentrale Normalisierungsfunktion + +## Status + +Neue verifizierte/eingeladene Accounts starten als: + +```text +active +``` + +Kein zusätzlicher `pending`-Accountstatus. + +## `display_name` + +Optional, weil Account vor abgeschlossenem Onboarding existieren kann. + +## `last_login_at` + +Initial `NULL`. + +Nur nach erfolgreicher Authentifizierung aktualisieren, nicht bei jedem Seitenaufruf oder bei normaler Nutzung einer gültigen Device Session. + +## `disabled` + +Wirkt sofort: + +```text +Account → disabled +→ alle aktiven DeviceSessions widerrufen +→ bestehende Sitzungen verlieren Zugriff +→ neue Logins nicht zugelassen +``` + +Bei späterer Reaktivierung: +- alte Sessions bleiben widerrufen +- neue Authentifizierung nötig +- neue Device Session wird erzeugt + +--- + +# 8. `BrandProfile` + +Aktueller Stand: + +```text +BrandProfile +├── id +├── organization_id +├── name +├── logo_storage_key optional +├── primary_color +├── primary_strong +├── accent_color +├── accent_strong +├── heading_font +└── body_font +``` + +## 8.1 Name + +Da die App real nur für eine Organisation betrieben wird: + +```text +name +``` + +wird global eindeutig gehalten. + +`organization_id` bleibt trotzdem Teil des Architekturmodells. + +## 8.2 Logo + +Logo ist optional. + +Gespeichert wird: + +```text +logo_storage_key +``` + +Kein Dateipfad, kein Originaldateiname. + +Der zentrale Storage-Service löst den opaque Key später auf. + +## 8.3 Farben + +Validierte und normalisierte HEX-Strings: + +```text +#RRGGBB +``` + +Beispiele: + +```text +#00858A +#005F61 +#52E0CF +#2596BE +``` + +Nicht als freie Brandwerte: +- `rgb(...)` +- `rgba(...)` +- `hsl(...)` +- `#RRGGBBAA` +- beliebige CSS-Ausdrücke + +Semantische Ableitungen und Transparenzen entstehen im zentralen Designsystem. + +## 8.4 Fonts + +Font-Auswahl über Code-Registry. + +Die Datenbank speichert nur stabile Keys: + +```text +heading_font = "big-shoulders-display" +body_font = "noto-sans" +``` + +Keine freien Fontnamen, URLs oder CSS-Fragmente. + +## 8.5 Font-Auslieferung + +Fonts werden lokal mit der Anwendung ausgeliefert. + +Keine Pflichtabhängigkeit von Google Fonts, CDN oder anderen externen Font-Diensten. + +Vor Nutzung einer Schrift wird geprüft, ob die Lizenz lokales Webhosting erlaubt. + +## 8.6 Offene Entscheidung + +Noch nicht entschieden: + +```text +created_at +updated_at +``` + +für `BrandProfile`. + +Variante A: +- beide ergänzen + +Variante B: +- keine Zeitstempel +- Änderungsnachvollziehbarkeit nur über `AuditLog` + +Dies ist der aktuelle nächste offene Punkt. + +--- + +# 9. Noch nicht im Detail entschieden + +- `Team` +- `OrganizationMembership` +- `TeamMembership` +- Permission-Helper +- Einladungsmodelle +- `TeamModule` +- `AuditLog` +- `AuthChallenge` +- `DeviceSession` +- Passwordless-Flow +- BrandProfile-Seed für Vor-Ort +- konkrete Font-Registry-Implementierung +- Core Storage +- UI-Komponenten +- Dashboard +- Scheduling +- Documents +- Chat +- Deployment +- SMTP +- Docker +- Caddy +- Backup/Restore + +Alle diese Punkte bleiben an `PROJECT_DESIGN.md` gebunden und werden schrittweise entschieden. + +--- + +# 10. Arbeitsmethode + +Für jede Phase: + +```text +1. PROJECT_DESIGN.md lesen +2. bereits Festgelegtes übernehmen +3. echte offene Entscheidungen identifizieren +4. gemeinsam entscheiden +5. erst danach Tasks ableiten +6. implementieren +7. testen +8. Checkpoint / Commit +``` + +Regel: + +> `TASKS.md` darf keine neue Architektur erfinden. + +--- + +# 11. Aktueller nächster Schritt + +Aktueller Stand: + +```text +Phase 2.3 — BrandProfile +``` + +Nächste Entscheidung: + +```text +Soll BrandProfile created_at und updated_at erhalten? +``` + +Danach wird `BrandProfile` abgeschlossen und Phase 2.4 `Team` begonnen. diff --git a/notes/index.html b/notes/index.html new file mode 100644 index 0000000..ba6c5a7 --- /dev/null +++ b/notes/index.html @@ -0,0 +1,503 @@ + + +
+ + +Ehrenamtsmanagement
+Mit deiner E-Mail-Adresse erhältst du einen einmaligen Code oder einen direkten Anmeldelink.
+Mockup: Es werden keine Daten versendet.
+Fast geschafft
+Wir haben einen sechsstelligen Code an patsy@example.org geschickt.
+Dein Profil
+Dieser Name ist für dein Team sichtbar. Deine Login-E-Mail bleibt für normale Teammitglieder verborgen.
+Deine Teams
+Du kannst später jederzeit zwischen deinen Teams wechseln.
+Montag, 17. August
+Hier ist, was heute für dich und dein Team wichtig ist.
+18:00–21:30 · Café Bach · Noch 1 Position offen
+18:30–21:30 · Café Bach
+ Theke +17:30–21:00 · Pipinstraße
+ Hauptposition +Vor-Ort-Arbeit
Menschen, Kontakte und aktuelle Team-Infos.
Wir besprechen die September-Planung und sammeln Feedback zur neuen Einsatzstruktur.
+ +Vor-Ort-Arbeit
Kontaktdaten sind nur sichtbar, wenn sie freiwillig geteilt wurden.
+Keine Kontaktdaten freigegeben
Login: mehmet@example.org · Einladung offen
Beitritt: noch nicht erfolgtEinsatzplanung
Verfügbarkeit offen bis 23. August · Veröffentlichung geplant für 25. August.
+Bitte gib für jeden Termin an, ob du verfügbar bist.
+17:30–21:00 · Pipinstraße
18:00–21:30 · Café Bach
17:30–21:00 · Pipinstraße
Teamwissen
Freigegebene Materialien für Vor-Ort-Arbeit.
Aktuelle Hinweise für Beratung und Vor-Ort-Arbeit.
PDF · 1,8 MB · aktualisiert 12.08.2026Foto mit markierter Positionierung der Materialien.
JPG · 840 KB · aktualisiert 02.08.2026Nur für dich und Team-Verwaltung sichtbar.
DOCX · 95 KB · von dir hochgeladenTeamkommunikation
Keine Dateien, keine Embeds – nur klare Teamkommunikation.
Mein Konto
So sehen dich deine Teamkolleg:innen.
Glocke
Nur Ereignisse, die deine Aufmerksamkeit brauchen – dauerhafte Zustände bleiben im Dashboard.
Heute, 18:00 · Springer:in noch unbesetzt.
Am 21.08. bist du jetzt für „Theke“ eingeteilt.
„Perfekt, danke! Dann schreibe ich das so …“
Neue Ankündigung von Alex.
Team-Verwaltung
Operative Aufgaben im Team – ohne Organisationsverwaltung.
Organisationsverwaltung
Struktur, Rechte, Brand Profiles und Module – keine teaminternen Fachinhalte.
Organisation
Nur relevante administrative Änderungen. Chat und Verfügbarkeiten werden hier nicht protokolliert.
Alex wurde für „Vor-Ort-Arbeit“ als Team-Verwaltung ernannt.
Patsy · 17.08.2026, 10:42„Dokumente“ wurde für Team „Café Bach“ aktiviert.
Org-Admin · 16.08.2026, 14:08„Alte Kontaktdaten.xlsx“ wurde dauerhaft entfernt.
Alex · 15.08.2026, 18:31