# Mobile App – TODO Web-Parität (Android, KMP-Shared) Dieses Dokument ist die **Arbeitsliste**, um die **funktionale Abdeckung der Web-App** (`frontend/src/router.js`, Views unter `frontend/src/views/`) in der **nativen Android-App** (Jetpack Compose, Shared Code unter `mobile-app/shared`) nachzubauen. **Wichtig:** Ein „komplettes“ Umsetzen dieser Liste ist ein **Mehrmonats-/Team-Projekt**. Es wird **iterativ** abgearbeitet; unten sind nur die Punkte angehakt, die im Code **tatsächlich** vorhanden sind (Stand siehe Git). Alles andere bleibt offen. **Legende:** `[x]` umgesetzt · `[ ]` offen · Die „Baseline“ am Ende beschreibt den älteren Grundstock. **Vorgehen:** Pro Phase vertikale Schnitte (API im `shared` Modul → Manager/Use-Case → UI). Web-Referenz immer die gleichnamige `*.vue`-Datei und `apiClient`-Aufrufe darin. --- ## Bereits umgesetzt (Baseline, Stand 2026-05) - Auth (`authcode`), Token-Persistenz, 401 → Login - Club-Auswahl, Permissions, Access-Request - Tabs inkl. **Start**-Hub (`MainTab.Home`), Unter-Screens (Tagebuch-Tag, Mitglied-Detail), Tab-/Rail ausblenden in Details - **Tagebuch:** Datenliste, Tag-Detail, Zeiten, Notizen, Tags (Tagesbezug), **Freitext-Aktivitäten** (`/activities`), Trainingsplan **lesen & CRUD** (Phase 3.2), Teilnehmer an/ab (einklappbare Liste, standard eingeklappt), **Zuordnung zu Plan-Aktivitäten** (3.4), **Unfälle/Vorfälle** (3.7), Löschen Tag - **Mitglieder:** Liste, Suche, statisches Detail - **Trainings-Statistik:** Basis-KPIs + Top-Liste - **Einstellungen:** Sprache, Session-Check, Logout, Backend-Anzeige - i18n-Generator + `MobileStrings` - Theming näher an Web (`TtTagebuchTheme`) --- ## Phase 0 – Architektur & Grundlagen für Vollausbau - [x] **Navigation:** Single-Activity-Modell mit `MainTab` + zustandsbasierter Tiefe (Tagebuch-Tag, Mitglieder-Details, Einstellungen-Unterseiten); Rail/Bottom-Nav; **kein** zentraler Navigation-Compose-`NavHost` für die Shell (bewusste Entscheidung, später migrierbar) – siehe `DEVELOPMENT.md` § Architektur Phase 0, Umsetzung `AppRoot.kt` - [x] **Feature-Paketierung:** Richtlinie und Ist: APIs/DTOs/Manager im `shared`, UI in `composeApp/.../ui/` mit featurebezogenen Dateien; schrittweise Entschlackung großer Dateien – `DEVELOPMENT.md` § Architektur Phase 0 - [x] **Use-Cases:** Geschäftslogik überwiegend in `shared/state/*Manager` und gezielt in plattformnahen Helfern (`CalendarAggregator` o. ä.); Composables orchestrieren – `DEVELOPMENT.md` § Architektur Phase 0 - [x] **Fehlerbild (Basis):** JSON-Fehlerbody (`error` / `message`) aus API-Antworten → [ApiException]-Text (`ApiErrorMessage.kt`, authed + public Client); bekannte Tokens (`alreadyexists`, …) → Deutsch; Retries bewusst noch offen - [x] **Echtzeit (optional):** v1 ohne Web-Socket; Aktualisierung über `LaunchedEffect` / manuelles Neuladen dokumentiert; optional Socket/Polling später – `DEVELOPMENT.md` § Architektur Phase 0 - [x] **Medien:** Coil für Bilder, FileProvider + Share-Intents für PDF; dokumentiert in `DEVELOPMENT.md` § Architektur Phase 0 - [x] **Öffentlicher HTTP-Client** ohne Auth-Header (`PublicHttpClient`, `PublicAuthApi` im `shared`) --- ## Phase 1 – Auth, Onboarding, öffentliche Seiten Web-Routen: `/login`, `/register`, `/activate/:code`, `/forgot-password`, `/reset-password/:token`, `/impressum`, `/datenschutz`, `/` (Home öffentlich) - [x] **Registrierung** (`Register.vue`) – Android: `RegisterScreen`, `POST /api/auth/register` - [x] **Account aktivieren** (`Activate.vue`) – Android: `ActivateAccountScreen`, `GET /api/auth/activate/:code` - [x] **Passwort vergessen / E-Mail** (`ForgotPassword.vue`) – `ForgotPasswordScreen`, `POST /api/auth/forgot-password` - [x] **Passwort setzen mit Token** (`ResetPassword.vue`) – `ResetPasswordScreen`, `POST /api/auth/reset-password` - [x] **Home / Landing eingeloggt** (`Home.vue`) – Tab **Start**: Willkommen, Kacheln zu Tagebuch/Mitglieder/Statistik/Mehr, Vereinsinfos via `GET /api/clubs/:id` (`HomeScreen`, `ClubsApi.getClub`) - [x] **Impressum** – Einstellungen → öffnet `BACKEND_BASE_URL/impressum` im Browser - [x] **Datenschutz** – Einstellungen → öffnet `BACKEND_BASE_URL/datenschutz` im Browser - [ ] SEO-Marketingseiten (`TableTennisClubSoftware`, `ClubMemberManagementPage`, …): **nur falls** im Store gefordert; sonst `[ ] optional / nicht mobil` --- ## Phase 2 – Verein anlegen & Vereins-Ansicht - [x] **Verein erstellen** (`CreateClub.vue`) + API – `POST /api/clubs`, `ClubSelectScreen` + `ClubManager.createClub` - [x] **Vereins-Profil / Show Club** (`ClubView.vue`) – Basis: erweitertes `Club`-Modell + Karte auf **Start** (Begrüßung, Verbands-Nr., MyTischtennis-Kürzel); volle Parität (Links, Zahlen) offen --- ## Phase 3 – Tagebuch (DiaryView) – Hauptblock Web: `DiaryView.vue` (sehr groß). API-Cluster (Auszug – in Web nach `apiClient` verifizieren): ### 3.1 Tages-Metadaten & Listen - [x] Tagebuch-Daten **vollständig** laden beim Datumswechsel – Plan, Trainingsgruppen und Teilnehmer **parallel** in einem `LaunchedEffect` (`DiaryDetailScreen`) - [x] **Aktivitäten-Liste** `/activities/:dateId` — `DiaryApi.listFreeformActivities` / `addFreeformActivity`, Abschnitt „Weitere Tages-Aktivitäten“ im Tagebuch-Detail ### 3.2 Trainingsplan (CRUD) - [x] Plan laden: `GET /diary-date-activities/:clubId/:diaryDateId` — `DiaryApi` / Tagebuch-Detail - [x] Einträge anlegen: `POST /diary-date-activities/:clubId`, ggf. **Gruppe** `POST /diary-date-activities/group` — Android-Formulare + `CreateDiaryPlanActivityRequest` / `AddDiaryPlanGroupActivityRequest` - [x] Einträge bearbeiten: `PUT /diary-date-activities/:clubId/:id` (Zuordnung `groupId` im Body wie Backend) — Dialog „Bearbeiten“; Unter-Einträge (`GroupActivity`): API `updateNestedGroupActivity`, mobil z. B. nur **Löschen** - [x] Reihenfolge: `PUT .../order` — ↑/↓ je Trainingsgruppen-Scope - [x] Löschen: `DELETE ...`, Gruppen-Aktivität `DELETE .../group/...` - [x] **Zeitblöcke** (`isTimeblock`) inkl. UX wie Web — Checkbox beim Anlegen, Kennzeichnung in der Karte - [x] **Gruppen** für Plan: `GET/POST/DELETE /api/group` — `GroupApi` + Liste mit Löschen; **PUT** Umbenennen (`changeGroup`) nur API, keine eigene UI ### 3.3 Teilnehmer & Status - [x] Liste: `GET /participants/:dateId` - [x] Hinzufügen/Entfernen: `POST /participants/add`, `POST /participants/remove` - [x] Status **entschuldigt/abgesagt**: `PUT /participants/:dateId/:memberId/status` - [x] **Gruppenzuordnung Training**: `PUT /participants/:dateId/:memberId/group` — `ParticipantsApi.updateParticipantGroup`, Dropdown bei anwesenden Teilnehmern wenn Trainingsgruppen existieren - [x] **UX:** Teilnehmerliste im Tagebuch-Detail **aufklappbar**, standard **eingeklappt** (`DiaryDetailScreen`) - [x] `GET /diary-member-activities/:clubId/:activityId` — `DiaryMemberActivitiesApi` / „Wer macht mit?“ je Planzeile bzw. Gruppen-Aktivität - [x] Zuweisen: `POST ...` mit `participantIds` — inkl. `ensureParticipantRowId` wenn noch keine Teilnehmer-Zeile - [x] Entfernen: `DELETE .../:participantId` ### 3.5 Mitgliedsbezogene Notizen/Tags im Tagebuch - [x] `GET/POST .../diarymember/:clubId/note`, `DELETE .../note/:id` (mit `diaryDateId`/`memberId` als Query) — `DiaryMemberApi` / Dialog „Notizen & Tags“ je Teilnehmer - [x] `GET/POST .../diarymember/:clubId/tag`, `POST .../tag/remove` — bestehende Tags, neu anlegen (`POST /tags` + Verknüpfung) ### 3.6 Predefined Activities (Auswahl & Suche im Tagebuch) - [x] `GET /predefined-activities`, `GET ...?scope=standard`, `GET /search/query` — Trainingsplan anlegen (global + Gruppe) und Eintrag bearbeiten (Suche / vordefinierte ID) - [x] `GET /predefined-activities/:id` — `PredefinedActivitiesApi.getById` / `DiaryManager.getPredefinedActivity` (für spätere Detail-UI) ### 3.7 Unfälle / Vorfälle - [x] `POST /accident`, `GET /accident/:clubId/:diaryDateId` — `AccidentApi` / `DiaryManager` / Abschnitt im Tagebuch-Detail ### 3.8 Galerie & Bilder - [x] Mitgliederbilder: `memberProfileImagePath` + `AuthenticatedAsyncImage` (Coil mit `diaryAuthHeaders`) in der Teilnehmerliste bei `diary.read` + `members.read` - [x] Übungs-/Predefined-Bilder: `DiaryImagePaths` (`mainActivityImagePath` / `nestedActivityImagePath`) + `ApiConfig.toAbsoluteUrl`, Vollbild-Dialog im Tagebuch-Detail - [x] **Gruppenfotos:** `MemberGroupPhotosApi` (`GET/POST/DELETE /api/member-group-photos/:clubId`), Galerie-Abschnitt + `PickVisualMedia` in `DiaryDetailScreen` ### 3.9 PDF / Export - [x] Trainingsplan- und Tages-PDF (`DiaryPdfExporter` / `writeTrainingPlanPdf`, `writeTrainingDaySummaryPdf`), Teilen über `FileProvider` + `sharePdfFile` (`DiaryPdfShare`) ### 3.10 Sonstiges Diary-UX - [x] **Web-DiaryView → mobil (Kurzüberblick):** Tages-Metadaten, Plan inkl. Zeitblöcke/Gruppen, Teilnehmer inkl. Status/Gruppe, „Wer macht mit?“, Freitext-Aktivitäten, Tags/Notizen, Unfälle, Mitglieds-Notizen/Tags-Dialog, Galerie, PDF-Exporte – ohne die vielen Web-Tabs als 1:1-Spiegel; fehlende Parität steht in den Phasen 4+ / offenen Punkten. - [x] **Berechtigungen:** `ClubPermissionHelpers` (`canReadDiary`, `canWriteDiary`, `canReadMembers`, `canWriteMembers`) – Lese-Hinweis, Schreib-Aktionen und Gruppenfoto-Löschen in `AppRoot.kt` / `DiaryDetailScreen` entsprechend gekapselt --- ## Phase 4 – Mitglieder (MembersView) — erledigt - [x] **Mitglied-CRUD:** `POST /api/clubmembers/set/:clubId` — `MembersApi.setMember`, `MemberSetBody` / `Member.toSetBody`, `MembersManager.saveMember`; neu + bearbeiten in `MemberEditRoute` (`AppRoot.kt`) - [x] **Felder / Formulare:** Stammdaten, Adresse, **mehrere Telefon- und E-Mail-Zeilen** (Primär, Elternkontakt, Name) wie Web-`contacts`, Status (aktiv, Testmitglied, Formular, Freigaben, Bilder Internet), Geschlecht; TTR/QTTR nur Anzeige beim Bearbeiten - [x] **Profilbild:** `POST /api/clubmembers/image/...` (Multipart) — `MembersApi.uploadMemberPortrait`, Galerie-Pick + UCrop in `MemberDetailRoute` / `MemberPortraitCrop.kt` - [x] **Aktivität / letzte Teilnahmen:** `GET /api/member-activities/:clubId/:memberId` (Perioden-Query), `.../last-participations` — `MemberActivitiesApi`, Abschnitte im Profil - [x] **Trainingsgruppen:** `GET/POST/DELETE /api/training-groups/...` — `TrainingGroupsApi`, Zuweisen/Entfernen im Profil - [x] **Trainingszeiten (Verein):** `GET /api/training-times/:clubId` — `TrainingTimesApi`, Anzeige je Gruppe im Profil - [x] **Bild zuschneiden:** Yalantis **UCrop** nach `PickVisualMedia` — JitPack, `UCropActivity` im Manifest --- ## Phase 5 – Trainings-Statistik (Parität TrainingStatsView) — erledigt - [x] **DTO / API:** `TrainingStats.kt` – `weekdayStats`, `monthlyTrend`, `memberDistribution`, `overview.bestTrainingDay`, Mitglied mit `birthDate`, `participationRate12Months`, `trainingGroups`, `trainingDetails`, `lastTrainingTs` usw. - [x] **Ableitungen wie Web:** `TrainingStatsDerived.kt` – Filter (Wochentag, Trainingstag, Gruppe), `filteredOverview`, Monats-/Wochentags-Trends, Mitgliederstruktur, Gruppen-Performance, Altersklassen, Sortierung, CSV - [x] **UI:** `TrainingStatsScreen.kt` – Kennzahlen-Kacheln, Panels, kollabierbare Listen Trainingstage/Mitglieder, Detail-Dialog, CSV-Share (`shareFileWithMime` in `DiaryPdfShare.kt`) - [x] **i18n-Local:** `LanguageLocals.kt` (`LocalLanguageCode`) aus `AppRoot.kt` ausgelagert - [x] **Hinweis:** Web hat keinen CSV-Export für diese Statistik; mobil zusätzlich **CSV exportieren** (gefilterte/sortierte Mitgliederliste) ## Phase 6 – Terminplan (ScheduleView) — erledigt - [x] **DTOs:** `Schedule.kt` – `ClubTeamDto`, `ScheduleMatchDto`, `LeagueTableRowDto`, `UpdateMatchPlayersBody`, `ScheduleMatchScope`, `ScheduleViewMode` - [x] **APIs:** `ClubTeamsApi` (`GET /api/club-teams/club/:clubId`), `MatchesApi` (`/api/matches/leagues/...` matches + Tabelle, `PATCH /api/matches/:matchId/players`) - [x] **Logik:** `ScheduleLogic.kt` – Sortierung, Merge, Filter „Erwachsene“, Mannschafts-Scope wie Web - [x] **State:** `ScheduleManager.kt` – Mannschaften laden, Mannschafts-/Gesamt-/Erwachsenen-Ansicht, Tabelle, Spieler-Patch + Refresh - [x] **Berechtigungen:** `canReadSchedule` / `canWriteSchedule` in `ClubPermissionHelpers.kt` - [x] **UI:** `ScheduleScreen.kt` – Tab **Terminplan** (`MainTab.Schedule`), Home-Kachel bei Lese-Recht, Liste + Detail, Aufstellung (R/P/S) bei Schreib-Recht - [x] **Noch nicht mobil:** CSV-Import (`POST /api/matches/import`), MyTT-Tabellen-Fetch (`POST .../table/.../fetch`), Galerie/Lineup wie Web – bei Bedarf spätere Phase --- ## Phase 7 – Turniere - [x] **Vereins-Turniere** (`TournamentsView.vue` / `TournamentTab.vue`): `GET /api/tournament/:clubId` (optional `?type=mini`), clientseitig intern/offen wie Web; `GET /api/tournament/:clubId/:id` für Detail – UI `TournamentsScreen.kt`, `TournamentsApi`, `ClubInternalTournamentsManager` - [x] **Offizielle Teilnahmen** (`OfficialTournaments.vue`): `GET /api/official-tournaments/:clubId`, `GET .../participations/summary` – `OfficialTournamentsApi`, `OfficialTournamentsReadManager`, gleicher Screen - [x] **Navigation:** Tab **Turniere** (`MainTab.Tournaments`) bei `canReadTournaments()`, Start-Kachel; Deep-Link `/tournaments` für volle Web-Verwaltung - [ ] **Nicht mobil:** PDF-Import, Meldungen/ClickTT-Workflows, Spielstände pflegen, Gruppen/K.O. wie Web – bewusst Web-only --- ## Phase 8 – Freigaben & Verwaltung - [x] **Ausstehende Freigaben** – `ClubApprovalsApi`, `PendingApprovalsManager`, Screen unter „Mehr“ → Club-Verwaltung (`ClubAdminScreens.kt`) - [x] **Team-Management (Mannschaften + Editor)** – `TeamManagementScreen` + `TeamEditorScreen`: Saison, Suche, CRUD Mannschaften; Tabs Stammdaten, Spielerstatistik (Liga), **Aufstellung/Meldung**, Dokumente, Scheduler-Jobs, MyTT (`ClubTeamsApi`, `MatchesApi`, `TeamDocumentsApi`, `ApiLogsApi`, `MyTischtennisApi`, Rechte `canReadTeams`/`canWriteTeams`) - [ ] **Mannschafts-Planung (Android)** – siehe Unterabschnitt **Phase 8a** (Web hat das Planungsboard bereits; mobil bewusst **offen**) - [x] **Berechtigungen** – erweiterte `PermissionsApi`, `PermissionsAdminManager`, UI Rolle/Status/Anpassen mit `RolePermissionMatrix` (`ClubAdminScreens.kt`) - [x] **Logs** – `ApiLogsApi`, `ApiLogsManager`, Liste + Pagination + Detail (`ClubAdminScreens.kt`); `AppDependencies` / Logout / 401 räumen Manager auf ### Phase 8a – Mannschafts-Planung auf Android (Backlog / Plan) **Ziel:** Gleiche **fachliche** Funktion wie Web **Team-Verwaltung → Planung**: Pool „möchte spielen“, mehrere Mannschaften der Saison parallel, Spieler nur **eligibility-konform** zuordnen, lokale Stammdaten (Name, geplante Liga, AK/GK) pro Team, **Meldung (Lineup)** je Team und gewählter **Halbserie** lesen/schreiben, optional **Debounced Autosave** wie Web. **Web-Referenz (1:1 zum Durchlesen der Logik):** | Bereich | Datei / Abschnitt | |--------|-------------------| | Umschalter „Mannschaften“ / „Planung“, Board einbinden | `frontend/src/views/TeamManagementView.vue` (`activeMainSection`, `TeamPlanningBoard`) | | UI Pool, Teams, Suche, Interesse markieren | `frontend/src/components/team/TeamPlanningBoard.vue` | | Lanes / Drag-Drop | `frontend/src/components/team/TeamPlanningLane.vue` | **Backend-APIs (aus Web `apiClient`; im `shared` ergänzen, falls noch fehlend):** - [ ] `GET /api/clubmembers/play-interest/:clubId` mit Query `seasonId`, `lineupHalf` – geladene **Interessen** für Pool (`loadPlanningInterestedMemberIds`) - [ ] `POST /api/clubmembers/play-interest/:clubId` – Body `memberId`, `seasonId`, `lineupHalf`, `interested` – Mitglied als „interessiert“ markieren (`onPlanningMarkMemberInterested`) - [x] `GET /api/clubmembers/get/:clubId/:showAll` – bereits `MembersApi.listMembers` - [x] `GET /api/club-teams/...` Mannschaften Saison, `GET …/lineup?half=`, `PUT …/lineup` – `ClubTeamsApi` (Planung lädt Lineups **aller** Teams der Saison parallel wie Web `loadPlanningAssignments`) - [x] `PUT /api/club-teams/:id` – Stammdaten Planungsteam (`updateClubTeam`) - [x] `POST /api/club-teams/club/:clubId` – neue Planungs-Mannschaft (`createClubTeam` o. ä.) - [x] `DELETE /api/club-teams/:id` – Planungs-Mannschaft löschen **Shared / Domäne:** - [ ] DTOs für Play-Interest (Response-Zeilen: `memberId`, `interested`, …) + `MembersApi` Erweiterung - [ ] Optional: **PlanningState**-Use-Case (normalisierte Zuordnungen `teamId`/`memberId`/`position`, Halbserie, Recompute bei AK/GK-Wechsel) – Web: `normalizePlanningAssignments`, `removeAllIneligiblePlanningAssignments`, `isEligibleForPlanningTeam` (analog `TeamEditorLineupLogic.isEligibleForTeam` + Team-spezifische AK/GK aus `planningLocalTeams`) **Android UI (Vorschlag für Umsetzungsschritte):** 1. [ ] **Einstieg:** In `TeamManagementScreen` (oder `AppRoot`) zweiter Modus **„Planung“** neben **„Mannschaften“** (wie Web-Workspace-Buttons), gleiche Saison-Auswahl wie Liste 2. [ ] **Screen** `TeamPlanningScreen.kt` (oder modular `TeamPlanningPool.kt` / `TeamPlanningTeamCard.kt`): Pool + Liste der Teams; kein 1:1-Spiegel der Web-Grid-Optik nötig, aber **alle Aktionen** abdeckbar 3. [ ] **Drag-and-Drop:** Entweder Compose **Drag-and-Drop** (Plattform-API) oder **Fallback** ohne DnD: „Mitglied auswählen → Ziel-Mannschaft“ / „Aus Mannschaft entfernen“ / Reihenfolge **↑↓** in der Lane (wie Aufstellungs-Tab) 4. [ ] **Halbserie** (VR/RR) gemeinsam mit Web-Parameter `lineupHalf` an Play-Interest und Lineup-Requests 5. [ ] **„In Workspace öffnen“** / Team-Editor: Web `convertPlanningTeamToRegular` prüfen – ggf. nur Navigation zum bestehenden `TeamEditorScreen` statt Konvertierungs-API 6. [ ] **Autosave:** Web nutzt Debounce (`schedulePlanningTeamAutosave`) – mobil entweder gleichziehen oder explizit **„Speichern“** pro Team + Konflikt-Hinweis (Produktentscheidung) 7. [ ] **i18n:** Keys aus `teamManagement.planning*` / `markAsInterested` usw. in `MobileStrings` oder Fallback-Kette wie andere Admin-Screens 8. [ ] **Tests:** Serialisierung Play-Interest; optional Snapshot der Normalisierung `planningAssignments` **Hinweis im Code:** Solange 8a offen ist, kann im Tab Aufstellung ein kurzer Verweis auf die **zukünftige** in-App-Planung stehen (nach Umsetzung Text anpassen oder entfernen). --- ## Phase 9 – Vereins- & Stammdaten-Einstellungen - [x] **ClubSettings** (`ClubSettings.vue`) – Kernfelder in `ClubStammdatenScreens.kt` + Link Web; tiefergehend nur Web - [x] **Predefined Activities** (`PredefinedActivities.vue`) – Liste, Neu, Bearbeiten (`PredefinedActivitiesApi`); Medien/Web-only falls nötig - [x] **Mitgliedstransfer** (`MemberTransferSettingsView.vue`) – Basis in App, erweitert über Web-Link --- ## Phase 10 – Persönliche Konten & Integrationen - [x] **Persönliche Einstellungen** (`PersonalSettings.vue`) – Mehr → Hub: Sprache, MyTT, ClickTT; `PersonalHubScreens.kt` + `AppRoot.kt` `SettingsScreen` - [x] **MyTischtennis-Konto** (`MyTischtennisAccount.vue`) – `MyTischtennisApi`, Dialog/Verify/Unlink in `PersonalHubScreens.kt` - [x] **ClickTT-Konto** (`ClickTtAccount.vue`) – `ClickTtAccountApi`, analog in `PersonalHubScreens.kt` - [x] **ClickTT-Ansicht** (`ClickTtView.vue`) – Proxy-URL + `WebView` in `ClickTtBrowserScreen` --- ## Phase 11 – Abrechnung & Bestellungen - [x] **Orders** (`OrdersView.vue` / `OrdersPanel.vue` global) – `MemberOrdersApi`, `MemberOrderDtos.kt`, `BillingOrdersScreens.kt` (`GlobalOrdersScreen`) - [x] **Billing** (`BillingView.vue`) Kernfluss ohne Web-PDF-Mapping-Editor – `BillingApi`, `BillingDtos.kt`, `BillingClubScreen` (Vorlagen-Upload, Vorschau, Lauf anlegen, PDF erzeugen/teilen, Läufe); Mapping weiter über Browser - [x] Rechtliches/UX: Button **Abrechnung im Browser** (`/billing`) für volle Web-Funktion; PDF-Teilen wie Tagebuch-PDF --- ## Phase 12 – Kalender (Vereinskalender) Web-Route: `/calendar` · Referenz: `CalendarView.vue` (Aggregation mehrerer Datenquellen in einer Monatsansicht). - [x] **Navigation & Shell:** Haupt-Tab `MainTab.Calendar` + Start-Hub-Kachel (`AppRoot.kt`), Monat vor/zurück, „Heute“, Monatsüberschrift (`CalendarScreen.kt`) - [x] **Monatsgitter:** 7×6, außerhalb Monat / heute, Wochentagskopf (`CalendarScreen.kt`, `CalendarAggregator.kt`) - [x] **Legende / Filter:** Eventtypen mit Schalter und Zähler (`CalendarScreen.kt`) - [x] **Daten laden (parallel, teilfehlertolerant):** `supervisorScope` + `async` in `CalendarScreen.kt`; Quellen: `DiaryManager.listDates`, Trainingszeiten, `TrainingCancellationApi`, `TournamentsApi`, `MatchesApi`, `OfficialTournamentsReadManager`, `CalendarHolidayApi` (`CalendarDtos.kt`, APIs in `shared`) - [x] **Logik:** `CalendarAggregator.kt` (wiederkehrende Zeiten, Merge, Ausfälle) – an Web angelehnt - [x] **Trainingsausfall:** Formular + Liste im Monat + API (`TrainingCancellationApi`, `CalendarScreen.kt`) - [x] **Agenda:** sortierte Monatsliste (`CalendarAggregator.eventsInMonth`, `CalendarScreen.kt`) - [x] **Event-Aktion:** `CalendarEventAction` → Tagebuch-Tag/Tab, Terminplan, Turniere, Web `/tournament-participations` (`calendar/CalendarAggregator.kt` / `CalendarScreen.kt`, Verdrahtung `AppRoot.kt`) - [x] **i18n:** `MobileStrings`-Keys mit Fallback in `CalendarScreen.kt` / `AppRoot.kt` (`navigation.calendar`, `home.tileCalendar`, `mobile.calendar*`) - [x] **Web-Parität (Detail):** Meldung wenn **alle** Kalender-Quellen fehlschlagen; Legenden-Zähler aus **allen** Events (unabhängig von den Schaltern); Agenda-Datum **kurz/lokalisiert**; Ausfall-Liste nur wenn **Startdatum im Monat** liegt (wie `CalendarView.vue`) – `CalendarScreen.kt` ### Phase 12 – Backlog / offen - [ ] **i18n:** Kalender-Keys in `MobileStrings.kt` für alle unterstützten Sprachen ergänzen (nicht nur Fallback im Code) - [ ] **Kalender vs. Web:** Offizielle Teilnahmen mobil per Browser vs. Web in-app – bewusst lassen oder später angleichen - [ ] **Release:** Bei `isMinifyEnabled = true` ProGuard/R8 für Ktor, `kotlinx.serialization`, ggf. Koin (`composeApp/proguard-rules.pro`) - [x] **Turniere (Produktiv-Crash):** `LazyColumn` mit eindeutigen Keys (`itemsIndexed` für Vereins-Turniere, offizielle Liste, Teilnahmen-Zeilen), vermeidet Duplicate-Key-Abstürze; zudem tolerante Deserialisierung für `allowsExternal` / `isDoublesTournament` (0/1, Strings) – `TournamentsScreen.kt`, `FlexibleBooleanSerializers.kt`, `TournamentDtos.kt` --- ## Phase 13 – Qualität, Tests, Release - [ ] **Regression-Checkliste** pro Phase (manuell) - [ ] Automatisierte Tests: `shared` (Serialisierung, Mapper), wo möglich UI-Tests kritische Flows - [ ] **Barrierefreiheit:** Talkback, Kontraste, Touch-Ziele - [ ] **Performance:** große Listen (Paging), Bildcache - [ ] Store-Texte, Datenverarbeitung (Play Policy) für Medien und Kontakte --- ## Hinweise zur Pflege dieser Liste 1. Beim Abhaken eines Punktes **kurz** vermerken (Commit-Message oder Unterpunkt), welche Datei/API neu ist. 2. Wenn Web eine Funktion **einstellt** oder API ändert: TODO hier und `DEVELOPMENT.md` anpassen. 3. **DiaryView** zuerst in **Unterkapitel** zerlegen (3.1–3.10), dann Issues/PRs pro Unterkapitel – sonst bleibt der Block unendlich. --- ## Referenz: Web-Routen (Router) Siehe `frontend/src/router.js` – jede `path`-Zeile sollte langfristig einem mobilen Eintrag (oder einer bewussten Ausnahme) zugeordnet sein. **Kalender:** `/calendar` → Phase 12.