- Introduced `escapeHtml` method to sanitize HTML content, enhancing security against XSS attacks. - Refactored player name rendering in tournament results to utilize the new HTML escaping method, ensuring safe display of player names and table data.
13 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: Zentraler Nav-Graph (z. B. Navigation Compose) mit Back-Stack pro Tab oder einheitlichem Stack; Tiefe: Club → Modul → Unterseiten
- Feature-Paketierung: UI/API pro Bereich trennen (
diary,members,schedule, …), Composables entschlacken - Use-Cases: Geschäftslogik aus Composables in testbare Funktionen/Klassen im
shared - 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): Socket-Events wie Web (
socketService) oder dokumentiertes Polling nach Schreiboperationen - Medien: Entscheidung Bilder/PDF (Coil, Cache, Download, Intents)
- Ö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)
- Kalender-/Listenansicht, CRUD oder Sync wie Web
- API-Endpunkte aus
ScheduleView.vueinssharedübernehmen
Phase 7 – Turniere
TournamentsView.vue– VereinsturniereOfficialTournaments.vue/ offizielle TeilnahmenTournamentTab.vue– eingebettete Logik, soweit mobil relevant- API aus jeweiligen Views dokumentieren und abarbeiten
Phase 8 – Freigaben & Verwaltung
- Ausstehende Freigaben (
PendingApprovalsView.vue) - Team-Management (
TeamManagementView.vue) - Berechtigungen (
PermissionsView.vue) – rollenbasiert - Logs (
LogsView.vue) – eher Admin; nur wenn nötig mobil
Phase 9 – Vereins- & Stammdaten-Einstellungen
- ClubSettings (
ClubSettings.vue) – alle Unterbereiche - Predefined Activities Verwaltung (
PredefinedActivities.vue) – CRUD, Bilder/Zeichnungen falls API - Mitgliedstransfer-Einstellungen (
MemberTransferSettingsView.vue)
Phase 10 – Persönliche Konten & Integrationen
- Persönliche Einstellungen vollständig (
PersonalSettings.vuevs. aktuelles „Mehr“) - MyTischtennis-Konto (
MyTischtennisAccount.vue) - ClickTT-Konto (
ClickTtAccount.vue) - ClickTT-Ansicht (
ClickTtView.vue)
Phase 11 – Abrechnung & Bestellungen
- Orders (
OrdersView.vue) - Billing (
BillingView.vue) - Rechtliches/UX: ggf. WebView oder Deep-Link, wenn Zahlungsflüsse web-only
Phase 12 – 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.