meldestelle/docs/04_Agents/Roadmaps/Frontend_Roadmap.md
StefanMoCoAt f82dbd64a5 Integrate Ktor HTTP clients and repositories for Veranstalter and Turnier features:
- Add `ApiRoutes` for central backend routing configuration.
- Implement `DefaultVeranstalterRepository` and `DefaultTurnierRepository` with Ktor clients.
- Add domain models (`Turnier`, `Bewerb`, `Abteilung`, `Veranstalter`) and respective repository interfaces.
- Replace fake VeranstalterRepository with real implementation.
- Update DI with `veranstalterModule` and HTTP client injection.
- Simplify TokenProvider and update HttpClient setup (timeouts, retries, logging).
- Mark roadmap tasks B-2 as partially complete.
2026-04-03 01:09:35 +02:00

164 lines
9.5 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 🎨 [Frontend Expert] — Schritt-für-Schritt Roadmap
> **Stand:** 3. April 2026
> **Rolle:** KMP, Compose Desktop, State-Management, MVVM/UDF, Backend-Anbindung
---
## 🔴 Sprint A — Sofort (diese Woche)
- [x] **A-1** | ViewModel-Architektur definieren und Referenz-Implementierung umsetzen
- [x] MVVM mit UDF (Unidirectional Data Flow) als verbindliches Muster festlegen
- [x] `Intent`- und `State`-Klassen-Struktur definieren (Vorlage für alle anderen ViewModels)
- [x] `VeranstalterViewModel` als vollständige Referenz-Implementierung umsetzen
- [x] `State`-Klasse definieren
- [x] `Intent`-Klasse (Sealed Class) definieren
- [x] Business-Logik aus Composables herausziehen (keine `StoreV2`-Aufrufe mehr direkt in `onSaved`)
- [x] Lokalen `remember`-State durch ViewModel-State ersetzen
- [x] Ergebnis als Muster-Dokument in `docs/06_Frontend/` ablegen
Referenzen:
- docs/06_Frontend/MVVM_UDF_Pattern.md (Regeln, Vorlage, Referenz-Code)
- frontend/features/veranstalter-feature/src/commonMain/.../VeranstalterViewModel.kt
- frontend/features/veranstalter-feature/src/jvmMain/kotlin/at/mocode/frontend/features/veranstalter/data/remote/DefaultVeranstalterRepository.kt
- frontend/features/veranstalter-feature/src/jvmMain/.../VeranstalterAuswahlScreen.kt (nutzt ViewModel/Intents)
- [x] **A-2** | Abteilungs-Logik im Bewerb-Dialog berücksichtigen
- [x] Dialog enthält Abteilungs-Auswahl als Teil des „Bewerb anlegen“-Flows (im selben Modal)
- [x] CSN-C-NEU: Automatischer Vorschlag der Pflicht-Teilung mit 4 Abteilungen:
- [x] Ohne Lizenz · R1
- [x] Ohne Lizenz · R2+
- [x] Mit Lizenz · R1
- [x] Mit Lizenz · R2+
- [x] Beim Auto-Vorschlag Default-Setzung des Abteilungs-Typs auf `SEPARATE_SIEGEREHRUNG`
- [x] Manuelle Umschaltung des Abteilungs-Typs möglich: `SEPARATE_SIEGEREHRUNG` oder `ORGANISATORISCH`
- [x] UX: Bei erkanntem Typ „CSN-C-NEU“ wird ein AssistChip „Pflicht-Teilung vorgeschlagen“ angezeigt
Akzeptanzkriterien:
- [x] Der „Bewerb anlegen“-Dialog zeigt ein Eingabefeld „Bewerbs-Typ“ und eine Auswahl für den Abteilungs-Typ (zwei Chips)
- [x] Bei Eingabe „CSN-C-NEU“ wird automatisch die oben definierte 4er-Teilung in der Abteilungs-Liste angezeigt
- [x] Die Auto-Teilung kann angezeigt werden, ohne dass der Dialog neu geöffnet werden muss (Live-Reaktion auf Eingabe)
- [x] Der gesetzte Abteilungs-Typ ist im State sichtbar und wird vom Dialog korrekt reflektiert
- [x] Kein Vorschlag für andere Typen; Liste bleibt leer bis manuell hinzugefügt/implementiert (aktuell out-of-scope)
Referenzen (konkret):
- frontend/features/turnier-feature/src/commonMain/kotlin/at/mocode/turnier/feature/presentation/BewerbAnlegenViewModel.kt
- `BewerbAnlegenState`, `BewerbAnlegenIntent`, `applySuggestion()` (Auto-Vorschlag + Default-AbteilungsTyp)
- frontend/features/turnier-feature/src/jvmMain/kotlin/at/mocode/turnier/feature/presentation/TurnierBewerbeTab.kt
- `BewerbAnlegenDialog(...)`: Eingabe „Bewerbs-Typ“, AssistChip, Auswahl Abteilungs-Typ, Anzeige der vorgeschlagenen Abteilungen
---
## 🟠 Sprint B — Kurzfristig (nächste Woche)
- [x] **B-1** | ViewModels für alle V3-Screens umsetzen
- [x] `TurnierViewModel`
- [x] `BewerbViewModel` (inkl. Abteilungs-Logik via Dialog-VM)
- [x] `PferdProfilViewModel`
- [x] `ReiterProfilViewModel`
- [x] `VereinsViewModel`
- [x] `FunktionaerViewModel`
- [x] `AbteilungViewModel` (Startliste, Ergebnisse)
- [ ] **B-2** | Ktor-Clients und Repositories für Backend-Anbindung vorbereiten (V3-ready)
- [x] KMP-Ktor-Client zentral konfigurieren (BaseURL, Auth, Timeout, JSON, Logging)
- [x] BaseURL per `PlatformConfig.resolveApiBaseUrl()` (SSoT; JS: `globalThis.API_BASE_URL`/`window.location.origin`, JVM: `.env`/Systemprop) → frontend/core/network
- [x] Auth: Bearer Token über Interceptor; Token-Quelle: core/auth (`AuthApiClient`) bzw. Session-Store → Header `Authorization: Bearer <token>`
- [x] Timeouts: connect = 5s, request = 15s, socket = 30s (prod); dev je 2× höher; Retry-Policy max 2 Versuche bei 5xx/Network
- [x] JSON: `kotlinx.serialization` mit `ignoreUnknownKeys=true`, `explicitNulls=false`, `coerceInputValues=true`
- [x] Logging: `LogLevel.HEADERS` in dev, `LogLevel.NONE` in prod; PII nie loggen
- [x] Engines: JVM=CIO, JS=fetch (ktor-client-js), WASM=js (vorbereitet)
- [ ] Repository-Schnittstellen je Domäne definieren (Mock ↔ Real austauschbar)
- [ ] Pakete/Orte (commonMain):
- [x] `at.mocode.frontend.features.veranstalter.domain.VeranstalterRepository`
- [x] `at.mocode.turnier.feature.domain.TurnierRepository`
- [ ] `at.mocode.turnier.feature.domain.BewerbRepository`
- [ ] `at.mocode.turnier.feature.domain.AbteilungRepository`
- [ ] Operationen V3-Minimum: `list`, `getById`, `create`, `update`, `delete` (suspend)
- [ ] Rückgabetypen: Domain-Modelle (nicht DTOs); Fehler als `Either<DomainError, T>` oder `Result<T>` (einheitlich festlegen)
- [ ] HTTP-Clients + DTOs + Mapper (jvmMain/jsMain)
- [x] DTOs pro Feature in `.../data/remote/dto` mit `@Serializable` (Veranstalter)
- [x] Mapper: `Dto ↔ Domain` in `.../data/mapper` (reine Funktionen) (Veranstalter)
- [x] Client-Implementierungen in `.../data/remote/Default*Repository` mit Ktor (Veranstalter)
- [x] Fehlerbehandlung: Mapping HTTP 401→`AuthError.Expired`, 403→`AuthError.Forbidden`, 404→`NotFound`, 409→`Conflict`, 5xx→`ServerError`
- [ ] Koin-DI-Module
- [x] `core/network`: `HttpClient`-Factory als `single { provideHttpClient(env) }`
- [ ] Feature-Module binden `Repository`-Interfaces auf Default-Impl
- [ ] `AuthApiClient` (core/auth) integrieren, Token-Provider injizierbar (z. B. `() -> String?`)
- [ ] Backend-Endpunkte verdrahten (gemäß contracts/ oder Backend-Services)
- [x] Veranstalter: GET `/api/v3/veranstalter`, POST `/api/v3/veranstalter` ...
- [ ] Turniere: GET `/api/v3/turniere`, ...
- [ ] Bewerbe: GET `/api/v3/turniere/{id}/bewerbe`, ...
- [ ] Abteilungen: GET `/api/v3/bewerbe/{id}/abteilungen`, ...
- [ ] Versionierung: Präfix `/api/v3` zentral in `ApiRoutes`
- [ ] Migration: `StoreV2` schrittweise ablösen
- [ ] ViewModels von `StoreV2` auf Repositories umschalten (Feature für Feature)
- [ ] Parallelbetrieb per Toggle: `useRealBackend=true/false` (Konfig/DI)
- [ ] Entfernen von `StoreV2`, sobald Feature vollständig migriert und stabil
- [ ] Qualität & DX
- [ ] Akzeptanztests per Fake-Server (Mock Engine) gegen Repos (happy + error paths)
- [ ] Network-Error-UX: Einheitliche Fehlermeldungen/Retry in ViewModels (UDF)
- [ ] Dokumentation in `docs/06_Frontend/Networking.md` (Beispiele, Guidelines)
Referenzen (bestehend):
- frontend/core/network/src/commonMain/.../PlatformConfig.kt (expect) und js/jvm actuals
- frontend/core/auth/src/commonMain/.../AuthApiClient.kt (Keycloak/PKCE, Token-Erhalt)
- frontend/core/network/build.gradle.kts (Ktor- und Engine-Dependencies)
- frontend/core/network/src/commonMain/.../NetworkModule.kt (HttpClient-Setup, Retry/Timeout, Token-Inject)
- frontend/features/veranstalter-feature/src/jvmMain/kotlin/at/mocode/frontend/features/veranstalter/data/remote/DefaultVeranstalterRepository.kt
Akzeptanzkriterien (B-2 abgeschlossen):
- [x] `HttpClient`-Factory vorhanden, konfiguriert und via Koin injizierbar
- [x] Repository-Interfaces existieren in commonMain, mit Domain-Typen und suspend-APIs (Veranstalter, Turnier vorbereitet)
- [x] Mindestens `VeranstalterRepository` nutzt echten Backend-Client und liefert Daten
- [x] Fehler werden einheitlich modelliert und bis ins ViewModel propagiert
- [x] Ein Feature-ViewModel (z. B. Veranstalter) läuft ohne `StoreV2`
- [ ] **B-3** | Validierungs-Live-Feedback in Edit-Dialogen
- [ ] Spezifikation von 📜 Rulebook Expert (Sprint A-5) als Basis nutzen
- [ ] OEPS-Nummer: Inline-Validierung beim Tippen
- [ ] FEI-ID: Inline-Validierung beim Tippen
- [ ] Lizenzklasse × Bewerbs-Klasse: Warnung wenn nicht erlaubt
- [ ] Altersklasse Pferd: Warnung wenn nicht kompatibel
- [ ] **B-4** | Kassa-Screen: Veranstaltungs-Kassa implementieren
- [ ] Gesamt-Saldo-Ansicht (Salden aus allen Turnieren der Veranstaltung)
- [ ] Turnier-übergreifender Zahlvorgang (eine Zahlung, mehrere Rechnungen)
- [ ] Rechnungsvorschau je Turnier
---
## 🟡 Sprint C — Mittelfristig (in 2 Wochen)
- [ ] **C-1** | Mock-Store (`StoreV2`) vollständig ablösen
- [ ] Alle verbleibenden `StoreV2`-Referenzen durch echte Repositories ersetzen
- [ ] `StoreV2` nach vollständiger Ablösung entfernen oder als `@Deprecated` markieren
- [ ] **C-2** | LAN-Sync-UI vorbereiten (nach ADR von Architect)
- [ ] Verbindungsstatus-Anzeige (Online/Offline/LAN)
- [ ] Sync-Trigger manuell und automatisch
> ⏸️ **USB-Stick Fallback (Export/Import UI)** — Separate Besprechung zu einem späteren Zeitpunkt
---
## 📌 Abhängigkeiten
| Warte auf | Von wem |
|----------------------------------|--------------------|
| Domänen-Modell final (Abteilung) | 🏗️ Architect |
| CRUD-Endpunkte | 👷 Backend |
| Validierungs-Spezifikation | 📜 Rulebook Expert |
| Wireframes Edit-Formulare | 🖌️ UI/UX Designer |
| Meine Aufgabe | Ermöglicht wem |
|--------------------------|-----------------------------------------------|
| ViewModel-Referenz (A-1) | Alle anderen ViewModels folgen diesem Muster |
| Ktor-Repositories (B-2) | Ablösung von StoreV2, echte Daten im Frontend |