feat(docs): update architecture documentation references and enhance handover details
All checks were successful
Deploy Development / deploy (push) Successful in 42s
Test Suite / pytest-backend (push) Successful in 35s
Test Suite / lint-backend (push) Successful in 0s
Test Suite / build-frontend (push) Successful in 12s
Test Suite / playwright-tests (push) Successful in 57s

- Added references to the architecture target image, refactor roadmap, and binding Shinkan rules in CLAUDE.md and HANDOVER.md for better project clarity.
- Updated the Dashboard component to improve user authentication handling and optimize data loading, enhancing overall performance and user experience.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Lars 2026-05-14 06:42:13 +02:00
parent 1c268555f6
commit 7043addd15
8 changed files with 446 additions and 40 deletions

View File

@ -14,6 +14,7 @@
> | Medien-Archiv, Lifecycle, Inline (Plan §11) | `.claude/docs/technical/MEDIA_ASSETS_AND_ARCHIVE_SPEC.md` |
> | Handover / nächste Session | **`docs/HANDOVER.md`** |
> | Fachlicher Nutzerüberblick (Design/Product) | **`docs/FACHLICHE_NUTZERFUNKTIONEN.md`** |
> | Architektur-Zielbild, Refaktor-Roadmap, verbindliche Shinkan-Regeln | **`docs/architecture/README.md`** |
## Projekt-Übersicht

View File

@ -20,6 +20,7 @@ Das Schema ist gegenüber dem Code zurück: Migration **`022_skills_schema_compl
| Thema | Pfad |
|--------|------|
| **Architektur-Zielbild, Refaktor, verbindliche Regeln (nach MVP)** | **`docs/architecture/README.md`** |
| Projekt-Setup, Domain grob | `.claude/docs/working/SHINKAN_PROJECT_SETUP.md` |
| **Projekt-Status (aktuell)** | `.claude/docs/PROJECT_STATUS.md` |
| **Medien-Archiv, Lifecycle, Inline-Plan (§11)** | `.claude/docs/technical/MEDIA_ASSETS_AND_ARCHIVE_SPEC.md` |

View File

@ -0,0 +1,21 @@
# Architektur: Zielbild, Refaktor, Regeln (Shinkan Jinkendo)
Dieses Bündel ist die **Leitlinie für die große Refaktorierung** nach dem MVP. Es ergänzt die bestehenden Pflichtdokumente (`.claude/rules/ARCHITECTURE.md`, `CODING_RULES.md`, Zugriffsschicht, Media-Spec) und ist für **Wartbarkeit, Performance und sichere Erweiterung** verbindlich, soweit hier ausdrücklich festgelegt.
## Inhalt
| Datei | Zweck |
|--------|--------|
| [ZIELBILD_ARCHITEKTUR.md](./ZIELBILD_ARCHITEKTUR.md) | Zielarchitektur (Frontend, API, Daten), Qualitätsziele, Einbindung neuer Features |
| [SCHULDEN_UND_REMEDIATION.md](./SCHULDEN_UND_REMEDIATION.md) | Erfasste Architekturschuld, Reihenfolge und Massnahmen zur Behebung |
| [UMSETZUNGSPLAN_ROADMAP.md](./UMSETZUNGSPLAN_ROADMAP.md) | Phasen, Meilensteine, Abnahmekriterien, Aufwandsschwerpunkte |
| [VERBINDLICHE_REGELN_SHINKAN.md](./VERBINDLICHE_REGELN_SHINKAN.md) | **Verbindliche** Shinkan-spezifische Regeln (Ergänzung zu den globalen Rules) |
## Pflege
- Bei abgeschlossenen Phasen: Roadmap und Remediation-Dokument aktualisieren; bei Regeländerungen: nur mit **expliziter Projektfreigabe** (gleiches Verfahren wie bei `.claude/rules/ARCHITECTURE.md`).
- Querschnitt: **`docs/HANDOVER.md`** soll auf die aktive Roadmap-Phase verweisen.
## Bezug MVP
Die aktuelle Codebasis ist funktional MVP-tauglich; strukturell bestehen bekannte Schwerpunkte (grosse Seiten-Monolithen, API-Monolith im Client, redundante Lesepfade, schwere Listenqueries). Dieses Bündel definiert, wie nach **dem** MVP weitergebaut wird, ohne jedes neue Feature erneut mit **architektonischer Schuld** zu überfrachten.

View File

@ -0,0 +1,131 @@
# Architekturschuld Erfassung und Behebungsschritte
Dieses Dokument listet **bewusst** die aus MVP und Code-Review bekannten strukturellen Themen auf und ordnet **konkrete Massnahmen** zu. Reihenfolge ist an die Roadmap gekoppelt; hier die inhaltliche Detailierung.
---
## A. Frontend
### A1 „God Pages“ (Training, Übungsformular, Vereine)
**Schuld:** Sehr grosse Dateien (tausende Zeilen) mit viel State, vielen Effekten und eingebetteten Modals.
**Risiko:** Hohe Re-Render-Kosten, schwerer zu testen, hoher RAM auf schwachen Geräten, neue Features vergrössern die Datei weiter.
**Behebungsschritte:**
1. **Inventar:** pro Page kurze Gliederung (Abschnitte) und Ziel-Komponenten benennen.
2. **Extrahieren:** Zuerst isolierbare Blöcke (Listen, Modals, Sidebar, Form-Sektionen) in Unterkomponenten; Props/Oberfläche dokumentieren.
3. **Hooks:** wiederkehrende Logik (`useEffect`-Ketten, Filter-State) in `useXxx`-Hooks pro Domäne.
4. **Optional `features/training/` o. ä.:** wenn 3+ zusammengehörige Komponenten entstehen.
**Erfolgskriterium:** Page-Datei unter dem in `VERBINDLICHE_REGELN_SHINKAN.md` genannten Soft-Limit oder dokumentierte Ausnahme.
---
### A2 Monolithischer API-Client (`utils/api.js`)
**Schuld:** Eine Datei bündelt alle Endpoints; erschwert Tree-Shaking, Navigation und domänenweise Ownership.
**Behebungsschritte:**
1. Verzeichnisstruktur festlegen, z.B. `frontend/src/api/` mit `client.js` (Token, `request`), `exercises.js`, `planning.js`, …
2. Bestehende `api.js` schrittweise zur **Facade** (`export * from …`) degradieren oder re-exportieren.
3. Neue Features **nur** in domänenspezifischen Dateien implementieren.
**Erfolgskriterium:** Kein Wachstum des Monolithen über bestehende Endpoint-Anzahl hinaus; mittelfristig dominieren kleine Module.
---
### A3 Redundante und „chatty“ Client-Requests
**Schuld (Beispiele):** Dashboard lädt Profil erneut trotz Auth; mehrere nahezu gleiche `listTrainingUnits`-Aufrufe; doppelte `listExercises` für KPIs.
**Risiko:** Mehr Last auf API/DB, schlechtere UX auf langsamen Geräten.
**Behebungsschritte:**
1. **Profil:** eine kanonische Quelle (Auth-Profil reicht für Anzeige; fehlende Felder gezielt nachladen oder Auth-Check erweitern fachlich klären).
2. **Dashboard:** einen **Summary-Endpoint** spezifizieren und implementieren (siehe Backend B1) oder Client auf einen aggregierten Aufruf reduzieren.
3. **Org-Inbox / globale Fetches:** Ladestrategie definieren (on-demand vs. TTL vs. sichtbarkeitsabhängig) und `OrgInboxContext` entsprechend umbauen.
**Erfolgskriterium:** Dashboard-Initialisierung ohne redundanten `getCurrentProfile`; ohne drei parallele fast gleiche Trainingslisten (oder dokumentierte Ausnahme).
---
### A4 Schwere Abhängigkeiten
**Schuld:** PDF/Markdown/Canvas-Pfade ziehen grosse Chunks.
**Behebungsschritte:** Strikte `import()` an Nutzeraktion; keine statischen Top-Level-Imports schwerer Libs in gemeinsamen Einstiegspfaden.
**Erfolgskriterium:** Lighthouse / Bundle-Analyse zeigt schwere Libs nur auf betroffenen Routen.
---
## B. Backend
### B1 Aggregations- und Summary-APIs
**Schuld:** Bildschirme holen mehrere Listen und aggregieren im Client.
**Behebungsschritte:**
1. Endpoint(s) z.B. `GET /api/dashboard/summary` oder domänenspezifisch mit gleicher Sichtbarkeitslogik wie Einzel-Listen.
2. Tests oder manuelle Checkliste gegen **Tenant-Leaks** (nur eigene/sehbare Daten).
3. Versionierung in `version.py` bei neuem Router-Block oder signifikantem Modul-Update.
**Erfolgskriterium:** Fertigest Dashboard mit einer serverseitigen Zusammenfassung (oder festgelegte Client-Reduktion mit Messung).
---
### B2 Listenqueries (z.B. Übungsliste)
**Schuld:** Korrelierte Subqueries pro Zeile können bei Wachstum teuer werden.
**Behebungsschritte:**
1. `EXPLAIN (ANALYZE, BUFFERS)` auf Produktions-näher Konfiguration mit realistischem `limit`.
2. Indizes für Filter und Sortierung ergänzen.
3. Refactoring: JOINs/LATERAL statt N-facher Subquery, wo messbar besser.
**Erfolgskriterium:** Dokumentierte p95-Zielwerte erreicht oder Trend verbessert (siehe Roadmap).
---
### B3 Pagination
**Schuld:** Tiefe `OFFSET`-Werte skalieren schlecht.
**Behebungsschritte:** Keyset-Pagination für grosse Listen in späteren Phasen einführen; API-Vertrag dokumentieren.
---
## C. Querschnitt
### C1 Messbarkeit
**Schuld:** Optimierung ohne Baseline.
**Behebungsschritte:** Einmalig Baseline (API p95, Bundle-Grössen Haupt-Route, ein Lasttest-Szenario) festhalten; wiederholen nach grossen Phasen.
---
### C2 Dokumentation und Audit
**Schuld:** Wissen nur in Chats.
**Behebungsschritte:** `HANDOVER.md` und `ACCESS_LAYER_ENDPOINT_AUDIT.md` bei jedem grösseren API-Block aktualisieren; Roadmap-Phase abhaken.
---
## Mapping: Schuld → Regel
| Schuld | Primär-Regel (Shinkan) |
|--------|-------------------------|
| God Pages | S1, S2 |
| API-Monolith | S3 |
| Globale Fetches | S4 |
| Chatty API | S5 |
| Caching-Ideen | S6 |
| Grössere Features ohne Messung | S7, S8 |

View File

@ -0,0 +1,132 @@
# Umsetzungsplan und Roadmap Refaktorierung Shinkan Jinkendo
**Aktueller Stand (laufend):** Phase 1 begonnen Dashboard: kein zweites `getCurrentProfile`, eine `listTrainingUnits`-Abfrage für „Nächste Termine“ und Notiz-Pool statt zweier identischer Calls.
**Ziel:** Nach MVP eine **nachhaltige** Architektur für Wachstum, **Performance** (Server + schwache Clients) und **sichere Feature-Erweiterung**.
**Leitdokumente:** [ZIELBILD_ARCHITEKTUR.md](./ZIELBILD_ARCHITEKTUR.md), [SCHULDEN_UND_REMEDIATION.md](./SCHULDEN_UND_REMEDIATION.md), [VERBINDLICHE_REGELN_SHINKAN.md](./VERBINDLICHE_REGELN_SHINKAN.md).
---
## Leitplanken (vereinbart)
- **Kein Breaking** der Zugriffsschicht: neue und geänderte Endpoints folgen `get_tenant_context` / Audit wie bisher.
- **Inkrementell:** Jede Phase liefert **nutzbaren** Stand (kein Big-Bang-Stillstand).
- **Neue Features** während der Roadmap: **S8 Checkliste** und **S1/S3** strikt; wo möglich gleich im neuen API-Modul-Pfad.
---
## Phase 0 Baseline (kurz, Pflicht)
**Dauer:** 0,51 Sprint (je nach Teamgrösse).
| Task | Output |
|------|--------|
| API p95 der Top-5-Routen messen (z.B. `profiles/me`, `exercises` list, `training-units` list, `media-assets` list) | Notiz in `docs/architecture/` oder Verweis in `HANDOVER` |
| Ein Lasttestszenario (Login → Dashboard → Übungen → Planung) | Skript/Notiz + Ergebnis |
| Bundle: Grösse Einstieg + schwerste Route | Screenshot oder `vite build`-Logablage |
**Abnahme:** Zahlen dokumentiert; wiederholbar.
---
## Phase 1 Quick Wins Netzwerk (hoher ROI, geringes Risiko)
**Fokus:** Weniger redundante Requests, bessere Mobile-UX, kaum strukturelle Risiken.
| Task | Bezug Remediation |
|------|-------------------|
| Dashboard: Doppel-`getCurrentProfile` auflösen; kanonisches Profil klären | A3 |
| Dashboard: `listTrainingUnits`/`listExercises`-Reduktion oder erster Summary-Call | A3, B1 |
| Org-Inbox: Ladestrategie festlegen (Technik-Kurzkonzept 1 Seite); Umsetzung mindestens Teil 1 (z.B. lazy oder TTL) | A3 |
**Abnahme:** Kein funktionales Leck; Netzwerk-Tab zeigt messbar weniger parallele gleiche Muster beim ersten Dashboard-Load.
---
## Phase 2 Backend Lesepfade (Skalierung „viele Nutzer“)
**Fokus:** DB und API stabil unter parallelen Lesern.
| Task | Bezug |
|------|--------|
| `EXPLAIN` + Index-Tuning für `list_exercises` und nächste schwere Listen | B2 |
| Summary-API finalisieren/erweitern falls in P1 nur Teilbereich | B1 |
| Optional: erste Keyset-Pagination für eine Liste mit bekanntem Sort-Key | B3 |
**Abnahme:** p95 der optimierten Routen **verbessert** ggü. Phase 0 oder dokumentierte Obergrenze eingehalten.
---
## Phase 3 Frontend-Struktur (Wartbarkeit + Client-Performance)
**Fokus:** God-Pages abbauen, Virtualisierung wo nötig.
| Task | Bezug |
|------|--------|
| Eine Page komplett zerteilen als Referenz (z.B. `TrainingPlanningPage` **oder** `ExerciseFormPage`) Rest priorisiert nach Nutzung | A1 |
| Virtualisierung für die längste produktive Liste | A1, S2 |
| Schwere Imports auf `import()` umziehen (gezielt) | A4 |
**Abnahme:** Referenz-Page unter Soft-Limit; Regel S1 für neue Änderungen durchsetzbar.
---
## Phase 4 API-Client Modularisierung
**Fokus:** Wartbarkeit für viele neue Features.
| Task | Bezug |
|------|--------|
| `frontend/src/api/` anlegen, `request`/`client` zentral | A2 |
| Facade: bestehende Importe von `utils/api` nicht sofort alle brechen; Migration in Wellen | A2 |
| Neue Endpoints nur noch in Domänen-Dateien | S3 |
**Abnahme:** Anteil neuer Module > X% der neuen Zeilen (Team-Ziel); Monolith wächst nicht weiter.
---
## Phase 5 Vertiefung DB & Pagination
**Fokus:** Wachstum Datenbestand.
| Task | Bezug |
|------|--------|
| Keyset für weitere Listen | B3 |
| Weitere Query-Refactorings nach Monitoring | B2 |
**Abnahme:** Dokumentierte Paginierungs-API; keine Regression in der Zugriffsschicht.
---
## Meilensteine (empfohlen)
| Meilenstein | Inhalt |
|-------------|--------|
| **M1** | Phase 0 + 1 abgeschlossen, HANDOVER aktualisiert |
| **M2** | Phase 2 abgeschlossen, Lasttest wiederholt |
| **M3** | Phase 3 Referenz-Page + Virtualisierung live |
| **M4** | Phase 4 migrationsbereit für alle neuen Features |
| **M5** | Phase 5 für Top-Listen abgeschlossen |
---
## Parallel: neue Features
- Jedes Feature: [VERBINDLICHE_REGELN_SHINKAN.md](./VERBINDLICHE_REGELN_SHINKAN.md) **S8**.
- Berührung schwerer Pfade: kurzer Performance-Nachweis (S7).
---
## Risiken und Mitigation
| Risiko | Mitigation |
|--------|------------|
| Summary-Endpoint falsch gefiltert | Code-Review + Abgleich mit Einzel-Endpoint-Logik; Tests mit mehreren Rollen |
| Refaktor bricht PWA/Offline | Smoke-Test nach grossen Frontend-Phasen |
| Keyset bricht alte Clients | Versionierte Query-Parameter oder Übergangsfenster |
---
## Pflege
Nach jeder Phase: **README** dieses Bündels prüfen; **Roadmap** Checkboxen/Status; **HANDOVER** nächste Phase nennen.

View File

@ -0,0 +1,62 @@
# Verbindliche Architekturregeln Shinkan (Ergänzung)
**Status:** verbindlich für die Shinkan-Codebasis, **ergänzend** zu:
- `.claude/rules/ARCHITECTURE.md`
- `.claude/rules/CODING_RULES.md`
- `.claude/docs/technical/ACCESS_LAYER_AND_GOVERNANCE_PLAN.md`
- `.claude/docs/technical/MEDIA_ASSETS_AND_ARCHIVE_SPEC.md`
Bei Widerspruch gewinnt die **spezifischere** Regel zur **Zugriffsschicht und Governance** (Sicherheit vor Komfort). Bei Widerspruch zwischen diesem Dokument und allgemeinen Mitai-Template-Resten in `ARCHITECTURE.md` gilt für **Shinkan** dieses Dokument und die Shinkan-Pflichtlektüre in `CLAUDE.md`.
---
## S1 Frontend: Grösse und Zerlegung von Seiten
1. **Soft-Limit:** Neue oder stark erweiterte Seiten sollen **unter ~500 Zeilen** im Page-File bleiben. Darüber: Auslagern in Komponenten/Hooks/Feature-Module mit klaren Namen.
2. **Ausnahmen** nur mit Kurzbegründung im PR und Verweis auf Messung (Bundle/Performance) oder fachliche Unteilbarkeit.
3. **Wiederkehrende UI-Blöcke** nicht per Copy-Paste über Seiten hinweg duplizieren; extrahieren in `components/` oder `features/`.
## S2 Frontend: Listen und Speicher
1. Listen, die **typischerweise > 100 sichtbare oder gehaltene Einträge** im DOM ermöglichen, **müssen** virtualisiert werden (oder serverseitig strikt begrenzt + „mehr laden“ mit dokumentiertem UX nicht beides unbegründet ignorieren).
2. **Modals und zweite Raster** gleichzeitig zum Hauptbaum nur laden, wenn geöffnet (lazy mount), wo technisch machbar ohne UX-Bruch.
## S3 Frontend: API-Zugriff
1. **Alle** API-Aufrufe über die zentrale Schicht (`utils/api` bzw. nach Modularisierung dessen Module). **Kein** `fetch('/api/...')` ohne diese Schicht.
2. Während der Migration vom API-Monolithen: **neue** Endpoints ausschliesslich im **domänenspezifischen** Modul anlegen; nur bei Bedarf Re-Export über die Facade.
## S4 Frontend: Globale Daten und Context
1. Neue **global** geladene Daten (jede authentifizierte Session) **bedürfen** technischer Begründung (Badge-Kritikalität, Sicherheit). Alternative: **on-demand** beim ersten Bezug oder **TTL-Cache** mit dokumentierter Invalidierung (`shinkan:…`-Events bleiben möglich).
2. Context-`value`-Objekte **müssen** stabil gehalten werden (`useMemo` / `useCallback`), wenn nicht-triviale Unterbäume davon abhängen (bereits etabliert für Auth; gleiches Muster für neue Contexts).
## S5 Backend: Lesepfad-Design
1. **Keine** mehrfachen fast identischen Listenaufrufe durch den Client für **denselben** zusammensetzbaren Bildschirm, wenn ein **einzelner** Summary-Endpoint unter gleicher Sichtbarkeitslogik möglich ist. Ausnahme: nachweislich unterschiedliche Cache-Lebensdauer oder unterschiedliche Rechte dokumentieren.
2. Neue Listen-Endpoints: **Paginierung** (`limit`/`offset` oder Keyset nach Roadmap) und Obergrenzen; keine „unbegrenzt alles“-Defaults für grosse Tabellen.
3. Schwere SQL-Konstruktionen (viele korrelierte Subqueries pro Zeile) **nur** mit Kommentar **Warum** und Hinweis auf Indexlage oder geplantes Refactoring-Ticket.
## S6 Backend: Mandanten und Caching
1. **Kein** HTTP- oder Anwendungs-Cache für mandantenspezifische oder nutzerspezifische Daten **ohne** expliziten Schlüssel (mindestens: Tenant-Kontext + relevante Parameter) und **Invalidierungsstrategie**.
2. Öffentliche oder global geteilte Katalogdaten dürfen mit `ETag` / kurzem Cache optimiert werden **nach** Abgleich mit Governance.
## S7 Performance und Messung (Definition of Done für grössere Features)
1. Features, die neue Listen schwerer als bestehende Top-10-Queries machen oder **> ~50 KB** zusätzliches Client-JS pro Route erzeugen: **kurz** messen (Lighthouse mobil oder Netzwerk-Timing) und im PR festhalten.
2. Regressions in **p95** der betroffenen API nach Deploy: bei Bedarf Rollback- oder Nachsteuerungskriterium mit Team vereinbaren (Zahlen Zielbild/Roadmap).
## S8 Feature-Checkliste (DoD)
Vor Merge einer grösseren Erweiterung:
- [ ] Zugriffsschicht / Audit aktualisiert (falls zutreffend)
- [ ] Kein Verstoss gegen S1S7 ohne dokumentierte Ausnahme
- [ ] Keine neue direkte DB-Nutzung im Frontend
- [ ] Medien/Lifecycle (falls Medien betroffen) nach Media-Spec
---
**Änderungen** an diesen Regeln nur mit **expliziter Projektfreigabe** (analog zu `ARCHITECTURE.md`).

View File

@ -0,0 +1,78 @@
# Architektur-Zielbild Shinkan Jinkendo
**Geltungsbereich:** Trainer-/Vereinsplattform, Multi-Tenancy und Governance nach bestehender Zugriffsschicht.
**Ziele:** dauerhaft tragfähig, performant bei vielen gleichzeitigen Nutzern, akzeptabel auf **geringer Client-Leistung** (wenig RAM/CPU), **wartbar** und so strukturiert, dass **neue Features** ohne neue Grosseinkaufe an technischer Schuld einbindbar sind.
---
## 1. Leitprinzipien
1. **API-first, Mandanten-sicher** Fachlogik und Sichtbarkeit serverseitig; das Frontend orchestriert und zeigt. Unverändert gemäss bestehender Regeln (`ACCESS_LAYER`, Governance-Helfer).
2. **Schlanke Client-Oberfläche** JavaScript pro Route begrenzen; schwere Abhängigkeiten nur bei Bedarf laden; Listen dort virtualisieren, wo Grössenordnungen wachsen.
3. **Explizite Lesepfade** Aggregation und Zusammenfassungen dort, wo mehrere fast gleiche Requests heute nötig sind (Dashboard, Badges), **statt** Chatty-Client-Muster.
4. **Vorhersehbarkeit für die DB** Listenqueries ohne unnötige O(n)·Subquery-Kosten pro Zeile; Indizes und Paginierungsstrategie sind Teil des Designs.
5. **Feature-Einbindung per Checkliste** Jedes neue Feature durchläuft die gleiche Architektur- und Performance-Checkliste (siehe Regeldokument), bevor es als „fertig“ gilt.
---
## 2. Zielbild Frontend
### 2.1 Struktur
- **Seiten (`pages/`)** bleiben Routing-Einstiege und Komposition; **keine** Dauerlösung für Logikblöcke > ~400500 Zeilen in einer Datei Auslagerung in `components/`, `hooks/`, `features/<name>/`.
- **Feature-Ordner (Ziel):** wo sinnvoll `frontend/src/features/<domäne>/` mit klarer Grenze: UI + feature-spezifische Hooks; geteilte Helfer in `utils/` nur wenn domänenübergreifend.
- **State:** Server-State über API (keine Business-Duplikation); UI-State lokal oder in bestehenden Contexts nur, wenn mehrere Schichten der Shell betroffen sind.
### 2.2 Performance und schwache Endgeräte
- Route-basiertes Code-Splitting bleibt Standard; **zusätzlich** innere `dynamic import()` für schwere Pakete (PDF, grosse Editoren), sobald eine Route sie braucht.
- Lange Listen: **Virtualisierung** ab einer projektdefinierten Schwelle (siehe Regeln).
- Globale Daten (Posteingang, Badges): **bedarfsgesteuert oder mit klar dokumentiertem Cache/TTL**, nicht pauschal jede Session mit voller Last konkrete Strategie in Roadmap/Remediation.
### 2.3 API-Schicht im Client
- **Ziel:** Aufteilung des heutigen `utils/api.js`-Monolithen in **domänenspezifische Module** (z.B. `api/exercises`, `api/planning`, `api/media`), mit einer dünnen **Barrel- oder Facade-Export** für Kompatibilität während der Migration.
- **Konstante:** alle HTTP-Aufrufe mit Token/Mandanten-Headern zentral; kein Rohtransport aus Komponenten.
---
## 3. Zielbild Backend / API
- **Router-Disziplin** unverändert: ein fachliches Modul, ein Router (bestehende Architekturregeln).
- **Read-Model / Summary-Endpoints** für Dashboards und wiederkehrende Kacheln: **eine** abgestimmte Antwort pro Bildschirm, wo heute mehrere Listen parallel zusammengerechnet werden unter strikt gleicher Sichtbarkeitslogik wie die Einzel-Endpoints.
- **Listen:** sortierte Indizes passend zu `WHERE` + `ORDER BY`; für grosse Datenmengen langfristig **Keyset-Pagination** statt tiefer Offsets.
- **Schwere Queries:** Korrelierte Subqueries pro Zeile nur, wenn messbar unkritisch; sonst JOIN-/Aggregate-Refactoring mit Review.
---
## 4. Zielbild Datenhaltung
- PostgreSQL bleibt System der Wahrheit; Migrationen nummeriert, wie heute.
- Kein Mandanten-Cache ohne expliziten Key und Invalidierungskonzept (Regeldokument).
---
## 5. Einbindung neuer Features (vereinbartes Muster)
1. Fachliche Kurzspez (oder Ticket) mit **Sichtbarkeit** und **Nutzungskontext** (Mobile/Desktop, erwartete Listenlängen).
2. API-Design: Endpoints, Payload-Grösse, Paginierung; Zugriffsschicht-Check.
3. UI-Modul: Route lazy, Komponentengrösse, ggf. Virtualisierung.
4. Messung: minimal Lighthouse/Netzwerk oder Server-Timing für den neuen Pfad.
5. Audit-Eintrag bei neuen geschützten Endpoints (bestehendes Verfahren).
---
## 6. Nicht-Ziele dieses Zielbilds
- Ersetzen der Zugriffsschicht oder der Medien-Spec.
- Microservices oder zweite Schreib-Datenbank ohne ausdrücklichen Projektbeschluss.
- „Framework-Wechsel“ (React bleibt, solange nicht separat entschieden).
---
## 7. Abnahme „Zielbild erreicht“ (high level)
- Keine bekannten **God-Pages** oberhalb dokumentierter Schwellen ohne dokumentierte Ausnahme.
- API-Client modularisiert oder klar phasierter Migrationsstand mit festem Enddatum.
- Dashboard und vergleichbare Homescreens ohne redundante Mehrfach-Listen desselben Objekttyps (oder dokumentierte technische Begründung + Messung).
- Datenbank-Lesepfade der Top-5-Listen unter definierter Latenz-Schwelle auf Referenz-Hardware in Lasttests (Werte in Roadmap festzulegen).

View File

@ -21,19 +21,13 @@ function formatCappedCount(n, capped) {
}
function Dashboard() {
const [profile, setProfile] = useState(null)
const [loading, setLoading] = useState(true)
const [trainingHome, setTrainingHome] = useState(null)
const [trainingHomeErr, setTrainingHomeErr] = useState(null)
const [phase0Stats, setPhase0Stats] = useState(null)
const [phase0Err, setPhase0Err] = useState(null)
const { user } = useAuth()
const { user, loading: authLoading } = useAuth()
const tenantClubDepKey = useMemo(() => getTenantClubDependencyKey(user), [user])
useEffect(() => {
loadData()
}, [])
useEffect(() => {
if (!user?.id) {
setTrainingHome(null)
@ -45,20 +39,7 @@ function Dashboard() {
setTrainingHomeErr(null)
try {
const today = new Date().toISOString().slice(0, 10)
const [upcomingRaw, reviewPendingRaw, plannedPool] = await Promise.all([
api.listTrainingUnits({
assigned_to_me: true,
status: 'planned',
start_date: today,
sort: 'asc',
limit: 8,
}),
api.listTrainingUnits({
assigned_to_me: true,
debrief_pending: true,
sort: 'desc',
limit: 8,
}),
const [plannedPoolRaw, reviewPendingRaw] = await Promise.all([
api.listTrainingUnits({
assigned_to_me: true,
status: 'planned',
@ -66,15 +47,25 @@ function Dashboard() {
sort: 'asc',
limit: 40,
}),
api.listTrainingUnits({
assigned_to_me: true,
debrief_pending: true,
sort: 'desc',
limit: 8,
}),
])
const noteHits = (plannedPool || []).filter((u) => {
const tn = (u.trainer_notes || '').trim()
const n = (u.notes || '').trim()
return Boolean(tn || n)
}).slice(0, 5)
const plannedPool = Array.isArray(plannedPoolRaw) ? plannedPoolRaw : []
const upcoming = plannedPool.slice(0, 8)
const noteHits = plannedPool
.filter((u) => {
const tn = (u.trainer_notes || '').trim()
const n = (u.notes || '').trim()
return Boolean(tn || n)
})
.slice(0, 5)
if (!cancelled) {
setTrainingHome({
upcoming: Array.isArray(upcomingRaw) ? upcomingRaw : [],
upcoming,
reviewPending: Array.isArray(reviewPendingRaw) ? reviewPendingRaw : [],
plannedWithNotes: noteHits,
})
@ -146,18 +137,7 @@ function Dashboard() {
}
}, [user?.id, tenantClubDepKey])
const loadData = async () => {
try {
const profileData = await api.getCurrentProfile()
setProfile(profileData)
} catch (err) {
console.error('Failed to load data:', err)
} finally {
setLoading(false)
}
}
if (loading) {
if (authLoading) {
return (
<div className="app-page" style={{ padding: '2rem 0', textAlign: 'center' }}>
<div className="spinner"></div>
@ -182,7 +162,7 @@ function Dashboard() {
</p>
</div>
</div>
{profile && <EmailVerificationBanner profile={profile} />}
{user ? <EmailVerificationBanner profile={user} /> : null}
{user?.id ? (
<>