- Added CalendarEvent model to the backend, establishing relationships with the Club model for better event management. - Updated server.js to include calendarEventRoutes, enabling API access for calendar events. - Enhanced CalendarView.vue to support custom event creation and management, improving user interaction with the calendar. - Refactored various components to streamline event handling and improve overall user experience in the calendar interface. - Updated TODO and DEVELOPMENT documentation to reflect new calendar features and architectural decisions.
19 KiB
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
- Navigation: Single-Activity-Modell mit
MainTab+ zustandsbasierter Tiefe (Tagebuch-Tag, Mitglieder-Details, Einstellungen-Unterseiten); Rail/Bottom-Nav; kein zentraler Navigation-Compose-NavHostfür die Shell (bewusste Entscheidung, später migrierbar) – sieheDEVELOPMENT.md§ Architektur Phase 0, UmsetzungAppRoot.kt - Feature-Paketierung: Richtlinie und Ist: APIs/DTOs/Manager im
shared, UI incomposeApp/.../ui/mit featurebezogenen Dateien; schrittweise Entschlackung großer Dateien –DEVELOPMENT.md§ Architektur Phase 0 - Use-Cases: Geschäftslogik überwiegend in
shared/state/*Managerund gezielt in plattformnahen Helfern (CalendarAggregatoro. ä.); Composables orchestrieren –DEVELOPMENT.md§ Architektur Phase 0 - Fehlerbild (Basis): JSON-Fehlerbody (
error/message) aus API-Antworten → [ApiException]-Text (ApiErrorMessage.kt, authed + public Client); bekannte Tokens (alreadyexists, …) → Deutsch; Retries bewusst noch offen - Echtzeit (optional): v1 ohne Web-Socket; Aktualisierung über
LaunchedEffect/ manuelles Neuladen dokumentiert; optional Socket/Polling später –DEVELOPMENT.md§ Architektur Phase 0 - Medien: Coil für Bilder, FileProvider + Share-Intents für PDF; dokumentiert in
DEVELOPMENT.md§ Architektur Phase 0 - Öffentlicher HTTP-Client ohne Auth-Header (
PublicHttpClient,PublicAuthApiimshared)
Phase 1 – Auth, Onboarding, öffentliche Seiten
Web-Routen: /login, /register, /activate/:code, /forgot-password, /reset-password/:token, /impressum, /datenschutz, / (Home öffentlich)
- Registrierung (
Register.vue) – Android:RegisterScreen,POST /api/auth/register - Account aktivieren (
Activate.vue) – Android:ActivateAccountScreen,GET /api/auth/activate/:code - Passwort vergessen / E-Mail (
ForgotPassword.vue) –ForgotPasswordScreen,POST /api/auth/forgot-password - Passwort setzen mit Token (
ResetPassword.vue) –ResetPasswordScreen,POST /api/auth/reset-password - Home / Landing eingeloggt (
Home.vue) – Tab Start: Willkommen, Kacheln zu Tagebuch/Mitglieder/Statistik/Mehr, Vereinsinfos viaGET /api/clubs/:id(HomeScreen,ClubsApi.getClub) - Impressum – Einstellungen → öffnet
BACKEND_BASE_URL/impressumim Browser - Datenschutz – Einstellungen → öffnet
BACKEND_BASE_URL/datenschutzim Browser - SEO-Marketingseiten (
TableTennisClubSoftware,ClubMemberManagementPage, …): nur falls im Store gefordert; sonst[ ] optional / nicht mobil
Phase 2 – Verein anlegen & Vereins-Ansicht
- Verein erstellen (
CreateClub.vue) + API –POST /api/clubs,ClubSelectScreen+ClubManager.createClub - Vereins-Profil / Show Club (
ClubView.vue) – Basis: erweitertesClub-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
- Tagebuch-Daten vollständig laden beim Datumswechsel – Plan, Trainingsgruppen und Teilnehmer parallel in einem
LaunchedEffect(DiaryDetailScreen) - Aktivitäten-Liste
/activities/:dateId—DiaryApi.listFreeformActivities/addFreeformActivity, Abschnitt „Weitere Tages-Aktivitäten“ im Tagebuch-Detail
3.2 Trainingsplan (CRUD)
- Plan laden:
GET /diary-date-activities/:clubId/:diaryDateId—DiaryApi/ Tagebuch-Detail - Einträge anlegen:
POST /diary-date-activities/:clubId, ggf. GruppePOST /diary-date-activities/group— Android-Formulare +CreateDiaryPlanActivityRequest/AddDiaryPlanGroupActivityRequest - Einträge bearbeiten:
PUT /diary-date-activities/:clubId/:id(ZuordnunggroupIdim Body wie Backend) — Dialog „Bearbeiten“; Unter-Einträge (GroupActivity): APIupdateNestedGroupActivity, mobil z. B. nur Löschen - Reihenfolge:
PUT .../order— ↑/↓ je Trainingsgruppen-Scope - Löschen:
DELETE ..., Gruppen-AktivitätDELETE .../group/... - Zeitblöcke (
isTimeblock) inkl. UX wie Web — Checkbox beim Anlegen, Kennzeichnung in der Karte - 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
-
Liste:
GET /participants/:dateId -
Hinzufügen/Entfernen:
POST /participants/add,POST /participants/remove -
Status entschuldigt/abgesagt:
PUT /participants/:dateId/:memberId/status -
Gruppenzuordnung Training:
PUT /participants/:dateId/:memberId/group—ParticipantsApi.updateParticipantGroup, Dropdown bei anwesenden Teilnehmern wenn Trainingsgruppen existieren -
UX: Teilnehmerliste im Tagebuch-Detail aufklappbar, standard eingeklappt (
DiaryDetailScreen) -
GET /diary-member-activities/:clubId/:activityId—DiaryMemberActivitiesApi/ „Wer macht mit?“ je Planzeile bzw. Gruppen-Aktivität -
Zuweisen:
POST ...mitparticipantIds— inkl.ensureParticipantRowIdwenn noch keine Teilnehmer-Zeile -
Entfernen:
DELETE .../:participantId
3.5 Mitgliedsbezogene Notizen/Tags im Tagebuch
GET/POST .../diarymember/:clubId/note,DELETE .../note/:id(mitdiaryDateId/memberIdals Query) —DiaryMemberApi/ Dialog „Notizen & Tags“ je TeilnehmerGET/POST .../diarymember/:clubId/tag,POST .../tag/remove— bestehende Tags, neu anlegen (POST /tags+ Verknüpfung)
3.6 Predefined Activities (Auswahl & Suche im Tagebuch)
GET /predefined-activities,GET ...?scope=standard,GET /search/query— Trainingsplan anlegen (global + Gruppe) und Eintrag bearbeiten (Suche / vordefinierte ID)GET /predefined-activities/:id—PredefinedActivitiesApi.getById/DiaryManager.getPredefinedActivity(für spätere Detail-UI)
3.7 Unfälle / Vorfälle
POST /accident,GET /accident/:clubId/:diaryDateId—AccidentApi/DiaryManager/ Abschnitt im Tagebuch-Detail
3.8 Galerie & Bilder
- Mitgliederbilder:
memberProfileImagePath+AuthenticatedAsyncImage(Coil mitdiaryAuthHeaders) in der Teilnehmerliste beidiary.read+members.read - Übungs-/Predefined-Bilder:
DiaryImagePaths(mainActivityImagePath/nestedActivityImagePath) +ApiConfig.toAbsoluteUrl, Vollbild-Dialog im Tagebuch-Detail - Gruppenfotos:
MemberGroupPhotosApi(GET/POST/DELETE /api/member-group-photos/:clubId), Galerie-Abschnitt +PickVisualMediainDiaryDetailScreen
3.9 PDF / Export
- Trainingsplan- und Tages-PDF (
DiaryPdfExporter/writeTrainingPlanPdf,writeTrainingDaySummaryPdf), Teilen überFileProvider+sharePdfFile(DiaryPdfShare)
3.10 Sonstiges Diary-UX
- 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.
- Berechtigungen:
ClubPermissionHelpers(canReadDiary,canWriteDiary,canReadMembers,canWriteMembers) – Lese-Hinweis, Schreib-Aktionen und Gruppenfoto-Löschen inAppRoot.kt/DiaryDetailScreenentsprechend gekapselt
Phase 4 – Mitglieder (MembersView) — erledigt
- Mitglied-CRUD:
POST /api/clubmembers/set/:clubId—MembersApi.setMember,MemberSetBody/Member.toSetBody,MembersManager.saveMember; neu + bearbeiten inMemberEditRoute(AppRoot.kt) - 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 - Profilbild:
POST /api/clubmembers/image/...(Multipart) —MembersApi.uploadMemberPortrait, Galerie-Pick + UCrop inMemberDetailRoute/MemberPortraitCrop.kt - Aktivität / letzte Teilnahmen:
GET /api/member-activities/:clubId/:memberId(Perioden-Query),.../last-participations—MemberActivitiesApi, Abschnitte im Profil - Trainingsgruppen:
GET/POST/DELETE /api/training-groups/...—TrainingGroupsApi, Zuweisen/Entfernen im Profil - Trainingszeiten (Verein):
GET /api/training-times/:clubId—TrainingTimesApi, Anzeige je Gruppe im Profil - Bild zuschneiden: Yalantis UCrop nach
PickVisualMedia— JitPack,UCropActivityim Manifest
Phase 5 – Trainings-Statistik (Parität TrainingStatsView) — erledigt
- DTO / API:
TrainingStats.kt–weekdayStats,monthlyTrend,memberDistribution,overview.bestTrainingDay, Mitglied mitbirthDate,participationRate12Months,trainingGroups,trainingDetails,lastTrainingTsusw. - Ableitungen wie Web:
TrainingStatsDerived.kt– Filter (Wochentag, Trainingstag, Gruppe),filteredOverview, Monats-/Wochentags-Trends, Mitgliederstruktur, Gruppen-Performance, Altersklassen, Sortierung, CSV - UI:
TrainingStatsScreen.kt– Kennzahlen-Kacheln, Panels, kollabierbare Listen Trainingstage/Mitglieder, Detail-Dialog, CSV-Share (shareFileWithMimeinDiaryPdfShare.kt) - i18n-Local:
LanguageLocals.kt(LocalLanguageCode) ausAppRoot.ktausgelagert - Hinweis: Web hat keinen CSV-Export für diese Statistik; mobil zusätzlich CSV exportieren (gefilterte/sortierte Mitgliederliste)
Phase 6 – Terminplan (ScheduleView) — erledigt
- DTOs:
Schedule.kt–ClubTeamDto,ScheduleMatchDto,LeagueTableRowDto,UpdateMatchPlayersBody,ScheduleMatchScope,ScheduleViewMode - APIs:
ClubTeamsApi(GET /api/club-teams/club/:clubId),MatchesApi(/api/matches/leagues/...matches + Tabelle,PATCH /api/matches/:matchId/players) - Logik:
ScheduleLogic.kt– Sortierung, Merge, Filter „Erwachsene“, Mannschafts-Scope wie Web - State:
ScheduleManager.kt– Mannschaften laden, Mannschafts-/Gesamt-/Erwachsenen-Ansicht, Tabelle, Spieler-Patch + Refresh - Berechtigungen:
canReadSchedule/canWriteScheduleinClubPermissionHelpers.kt - UI:
ScheduleScreen.kt– Tab Terminplan (MainTab.Schedule), Home-Kachel bei Lese-Recht, Liste + Detail, Aufstellung (R/P/S) bei Schreib-Recht - 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
- Vereins-Turniere (
TournamentsView.vue/TournamentTab.vue):GET /api/tournament/:clubId(optional?type=mini), clientseitig intern/offen wie Web;GET /api/tournament/:clubId/:idfür Detail – UITournamentsScreen.kt,TournamentsApi,ClubInternalTournamentsManager - Offizielle Teilnahmen (
OfficialTournaments.vue):GET /api/official-tournaments/:clubId,GET .../participations/summary–OfficialTournamentsApi,OfficialTournamentsReadManager, gleicher Screen - Navigation: Tab Turniere (
MainTab.Tournaments) beicanReadTournaments(), Start-Kachel; Deep-Link/tournamentsfü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
- Ausstehende Freigaben –
ClubApprovalsApi,PendingApprovalsManager, Screen unter „Mehr“ → Club-Verwaltung (ClubAdminScreens.kt) - Team-Management – Deep-Link
openBackendPath("/team-management")beicanReadTeams()(volle Parität zur Web-TeamManagementViewbewusst nicht mobil) - Berechtigungen – erweiterte
PermissionsApi,PermissionsAdminManager, UI Rolle/Status/Anpassen mitRolePermissionMatrix(ClubAdminScreens.kt) - Logs –
ApiLogsApi,ApiLogsManager, Liste + Pagination + Detail (ClubAdminScreens.kt);AppDependencies/ Logout / 401 räumen Manager auf
Phase 9 – Vereins- & Stammdaten-Einstellungen
- ClubSettings (
ClubSettings.vue) – Kernfelder inClubStammdatenScreens.kt+ Link Web; tiefergehend nur Web - Predefined Activities (
PredefinedActivities.vue) – Liste, Neu, Bearbeiten (PredefinedActivitiesApi); Medien/Web-only falls nötig - Mitgliedstransfer (
MemberTransferSettingsView.vue) – Basis in App, erweitert über Web-Link
Phase 10 – Persönliche Konten & Integrationen
- Persönliche Einstellungen (
PersonalSettings.vue) – Mehr → Hub: Sprache, MyTT, ClickTT;PersonalHubScreens.kt+AppRoot.ktSettingsScreen - MyTischtennis-Konto (
MyTischtennisAccount.vue) –MyTischtennisApi, Dialog/Verify/Unlink inPersonalHubScreens.kt - ClickTT-Konto (
ClickTtAccount.vue) –ClickTtAccountApi, analog inPersonalHubScreens.kt - ClickTT-Ansicht (
ClickTtView.vue) – Proxy-URL +WebViewinClickTtBrowserScreen
Phase 11 – Abrechnung & Bestellungen
- Orders (
OrdersView.vue/OrdersPanel.vueglobal) –MemberOrdersApi,MemberOrderDtos.kt,BillingOrdersScreens.kt(GlobalOrdersScreen) - 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 - 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).
- Navigation & Shell: Haupt-Tab
MainTab.Calendar+ Start-Hub-Kachel (AppRoot.kt), Monat vor/zurück, „Heute“, Monatsüberschrift (CalendarScreen.kt) - Monatsgitter: 7×6, außerhalb Monat / heute, Wochentagskopf (
CalendarScreen.kt,CalendarAggregator.kt) - Legende / Filter: Eventtypen mit Schalter und Zähler (
CalendarScreen.kt) - Daten laden (parallel, teilfehlertolerant):
supervisorScope+asyncinCalendarScreen.kt; Quellen:DiaryManager.listDates, Trainingszeiten,TrainingCancellationApi,TournamentsApi,MatchesApi,OfficialTournamentsReadManager,CalendarHolidayApi(CalendarDtos.kt, APIs inshared) - Logik:
CalendarAggregator.kt(wiederkehrende Zeiten, Merge, Ausfälle) – an Web angelehnt - Trainingsausfall: Formular + Liste im Monat + API (
TrainingCancellationApi,CalendarScreen.kt) - Agenda: sortierte Monatsliste (
CalendarAggregator.eventsInMonth,CalendarScreen.kt) - Event-Aktion:
CalendarEventAction→ Tagebuch-Tag/Tab, Terminplan, Turniere, Web/tournament-participations(calendar/CalendarAggregator.kt/CalendarScreen.kt, VerdrahtungAppRoot.kt) - i18n:
MobileStrings-Keys mit Fallback inCalendarScreen.kt/AppRoot.kt(navigation.calendar,home.tileCalendar,mobile.calendar*) - 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
- iOS: Kalender-UI + Tab (derzeit nur Android
composeApp/MainTab.Calendar) - i18n: Kalender-Keys in
MobileStrings.ktfü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 = trueProGuard/R8 für Ktor,kotlinx.serialization, ggf. Koin (composeApp/proguard-rules.pro) - Turniere (Produktiv-Crash):
LazyColumnmit eindeutigen Keys (itemsIndexedfür Vereins-Turniere, offizielle Liste, Teilnahmen-Zeilen), vermeidet Duplicate-Key-Abstürze; zudem tolerante Deserialisierung fürallowsExternal/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
- Beim Abhaken eines Punktes kurz vermerken (Commit-Message oder Unterpunkt), welche Datei/API neu ist.
- Wenn Web eine Funktion einstellt oder API ändert: TODO hier und
DEVELOPMENT.mdanpassen. - 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.