feat(android): configure API and socket endpoints for debug and release builds

- Added API and socket base URLs for both debug and release configurations in gradle.properties.
- Enhanced build.gradle.kts to utilize environment variables for dynamic endpoint configuration.
- Updated debug build type with specific application ID suffix and version name suffix for better differentiation.
- Included necessary dependencies for improved functionality and compatibility.
This commit is contained in:
Torsten Schulz (local)
2026-03-10 21:18:29 +01:00
parent ee2b12f6d0
commit 78f1196f0a
2509 changed files with 33215 additions and 12693 deletions

View File

@@ -0,0 +1,51 @@
# Club/Members/Admin Block Port
Stand: 2026-03-06
## Umgesetzte Features
- `MembersView.vue`: Android vorhanden (`MembersScreen`, `MembersViewModel`) inkl. Member-Liste, Edit/Create, Kontakte, Trainingsgruppen, Notizen, Bilder, Quick-Actions.
- `ClubView.vue`: Android vorhanden (`ShowClubScreen`, `ShowClubViewModel`) inkl. Club-Details, Access-Request-Flow, offene Anfragen.
- `ClubSettings.vue`: Android vorhanden (`ClubSettingsScreen`, `ClubSettingsViewModel`) inkl. Settings + Trainingsgruppen + Trainingszeiten.
- `CreateClub.vue`: Android vorhanden (`CreateClubScreen`, `CreateClubViewModel`) inkl. Create-Flow.
- `MemberTransferSettingsView.vue`: Android vorhanden (`MemberTransferSettingsScreen`, `MemberTransferSettingsViewModel`) inkl. Config + Execute.
- `PendingApprovalsView.vue`: Android vorhanden (`PendingApprovalsScreen`, `PendingApprovalsViewModel`) inkl. Approve/Reject.
- `PermissionsView.vue`: neu als Android umgesetzt (`PermissionsScreen`, `PermissionsViewModel`, `PermissionsService`) inkl. Rollen laden, Struktur laden, Mitglieder laden, Rolle ändern, Status ändern, Custom-Permissions speichern.
- `TeamManagementView.vue`: neu als Android umgesetzt (`TeamManagementScreen`, `TeamManagementViewModel`, `TeamManagementService`) inkl. Teams/Ligen laden, Team create/update/delete, Team-Dokumente laden/parsen, Scheduler-Job-Info, MyTischtennis URL-Parse + Team/Liga konfigurieren + Async-Fetch-Job polling, Spieler-Statistik laden.
- Navigation ergänzt: Home bietet jetzt direkte Einstiege für Members, Permissions, Team-Management, Member-Transfer.
## Genutzte API-Endpunkte
- Club/Members/Admin bestehend:
- `GET /clubs`, `POST /clubs`, `GET /clubs/{clubId}`, `GET /clubs/request/{clubId}`
- `GET /clubs/pending/{clubId}`, `POST /clubs/approve`, `POST /clubs/reject`
- `PUT /clubs/{clubId}/settings`
- `GET/POST/PUT/DELETE /training-groups/{clubId}*`
- `GET/POST/PUT/DELETE /training-times/{clubId}*`
- `GET/POST/DELETE /member-transfer-config/{clubId}`, `POST /clubmembers/transfer/{clubId}`
- `GET/POST/DELETE /membernotes*`, `GET/POST/DELETE /clubmembers/image/{clubId}/{memberId}*`, `GET /clubmembers/gallery/{clubId}`
- Permissions neu:
- `GET /permissions/roles/available`
- `GET /permissions/structure/all`
- `GET /permissions/{clubId}/members?t=...`
- `PUT /permissions/{clubId}/user/{userId}/role`
- `PUT /permissions/{clubId}/user/{userId}/status`
- `PUT /permissions/{clubId}/user/{userId}/permissions`
- Team-Management neu:
- `GET /club-teams/club/{clubId}?seasonid=...`
- `GET /club-teams/leagues/{clubId}?seasonid=...`
- `POST /club-teams/club/{clubId}`
- `PUT /club-teams/{clubTeamId}`
- `DELETE /club-teams/{clubTeamId}`
- `GET /team-documents/club-team/{clubTeamId}`
- `POST /team-documents/{documentId}/parse?leagueid=...`
- `GET /logs/scheduler/last-executions?clubId=...`
- `GET /matches/leagues/{clubId}/stats/{leagueId}?seasonid=...`
- `POST /mytischtennis/parse-url`
- `POST /mytischtennis/configure-team`
- `POST /mytischtennis/configure-league`
- `POST /mytischtennis/fetch-team-data/async`
- `GET /mytischtennis/fetch-team-data/jobs/{jobId}`
## Offene Abweichungen zur Vue-Version
- TeamManagement-Datei-Upload/Download (Datei-Picker/PDF-Viewer) ist im aktuellen Android-Block nicht vollparitätisch umgesetzt; Dokumente werden geladen und Parse-Flow ist umgesetzt.
- Permissions-Dialog ist funktional umgesetzt, aber UI/Interaktionsdetails (Tri-State-Darstellung, Legende, Styling) sind vereinfacht gegenüber Vue.
- TeamManagement-SeasonSelector ist aktuell als freie `seasonId`-Eingabe umgesetzt, nicht als kompletter SeasonSelector-Dialog wie im Web.

View File

@@ -0,0 +1,64 @@
# Final Migration Review (Android)
Stand: 2026-03-06 (nach Open-Gaps-Abarbeitung)
## Scope
Geprüft wurden:
- `docs/implementation_status.md` (alle 26 Views)
- `docs/mobile_spec.md`
- `docs/frontend_dependency_graph.md`
- Android-Code unter `android-app` (Navigation, Screens, ViewModels, Services/Repository, Network, Socket, Session/Auth)
## Direkt behobene Inkonsistenzen (klein/mittel)
1. Home-Navigation vervollständigt für bereits implementierte Screens:
- `training-stats`, `predefined-activities`, `mytischtennis-account`, `logs`, `personal-settings`, `impressum`, `datenschutz`
2. Selektive Feature-Deep-Links ergänzt:
- `home`, `showclub/{clubId}`, `members`, `diary`, `schedule`, `tournaments`, `training-stats`
3. Startup-Routing verbessert (club-basiert):
- `currentClub == "new"` -> `createclub`
- `currentClub` gesetzt -> `training-stats`
- sonst -> `home`
4. Socket/API-Basis-URLs zentralisiert:
- `BuildConfig.API_BASE_URL`
- `BuildConfig.SOCKET_BASE_URL`
## Systematische Prüfung
### 1) Views laut `implementation_status.md`
- Ergebnis: 26/26 Views als Android-Screens/Flows vorhanden.
- Quercheck Navigation: alle in `AppRoute` und `AppNavGraph` enthalten.
- Quercheck Architektur: pro Bereich ViewModel + Service/Repository vorhanden.
### 2) Navigation + Deep Links
- Interne Navigation: vollständig für dokumentierte Routen vorhanden.
- Auth-Deep-Links und selektive Feature-Deep-Links sind in `AppNavGraph` + Manifest hinterlegt.
### 3) API-Integration
- `ApiService` deckt die benötigten Endpunkte in den portierten Bereichen ab.
- Repository/Service-Schicht ist durchgängig angebunden.
### 4) Socket-Integration
- Zentrale Architektur vorhanden (`SocketManager` + Domain-Event-Mapping im Repository).
- Event-Subscriptions in den relevanten ViewModels (Diary/Schedule/Tournament) sind aktiv.
- Reconnect/Backoff + Foreground-Lifecycle-Verhalten sind implementiert.
### 5) Session/Auth-Flows
- Request-Auth global über `NetworkModule`-Interceptor (`authcode`, `userid`).
- 401/403-Handling inkl. API-Code-Prüfung vorhanden.
- Unauthorized -> Session-Clear + globales Login-Redirect über `AuthEventBus`/`AppNavGraph`.
- Socket Connect/Disconnect an Login/Logout gekoppelt.
### 6) Error/Loading/Retry
- In den Screens konsistent mit ErrorBanner/ErrorSnackbarHost, Loading-Indikatoren und Retry-Actions umgesetzt.
### 7) Abweichungen zu `mobile_spec.md` / `frontend_dependency_graph.md`
- Kernabhängigkeiten sind portiert.
- Verbleibende größere/strategische Punkte sind in `docs/open_gaps.md` dokumentiert.
## Ergebnis
- Migration ist funktional konsistent und vollständig nutzbar.
- Kleine/mittlere offene Punkte aus dem vorherigen Review wurden abgearbeitet.
- Verbleibende Themen sind bewusst priorisierte Restpunkte (siehe `docs/open_gaps.md`).

View File

@@ -5,47 +5,45 @@ Stand: 2026-03-06
Quellen:
- `docs/frontend_dependency_graph.md`
- `docs/mobile_spec.md`
- `android-app` (Navigation/Screens/ViewModels/Repository)
- `android-app` (Navigation/Screens/ViewModels/Services/Repository)
Status-Legende:
- `Implemented`: eigener Android Screen + ViewModel + Repository-Anbindung vorhanden
- `Partial`: funktional vorhanden, aber klar reduzierte/paritätisch unvollständige Umsetzung
- `Placeholder`: Route vorhanden, aber nur Platzhalter-Screen
- `Missing`: keine Android-Route/kein Android-Screen vorhanden
## Status-Legende
- `Fully implemented`: eigener Android Screen + ViewModel + Service/Repository-Anbindung, fachlicher Kernflow vorhanden
- `Partially implemented`: Screen/ViewModel vorhanden, aber wesentliche Web-Teilfunktion(en) reduziert/noch offen
- `Placeholder`: Route vorhanden, aber Platzhalter-Screen ohne Fachlogik
- `Missing`: keine echte Android-Umsetzung für diese View/Funktion
| Vue View | Android Screen/Route | Status | Details |
|---|---|---|---|
| Activate.vue | `ActivateScreen` / `activate/{activationCode}` | Implemented | Dateien: `ui/screens/ActivateScreen.kt`, `viewmodel/ActivateViewModel.kt`, `repository/TrainingstagebuchRepository.kt` (`activate`). API: `GET /auth/activate/{activationCode}`. Socket: - |
| ClubSettings.vue | `ClubSettingsScreen` / `club-settings` | Implemented | Dateien: `ui/screens/ClubSettingsScreen.kt`, `viewmodel/ClubSettingsViewModel.kt`, `repository/TrainingstagebuchRepository.kt` (`loadClubSettings`, `saveClubSettings`, `loadClubTrainingGroups`, `create/update/deleteClubTrainingGroup`, `load/create/update/deleteClubTrainingTime`). API: `GET /clubs/{clubId}`, `PUT /clubs/{clubId}/settings`, `GET/POST/PUT/DELETE /training-groups/{clubId}*`, `GET/POST/PUT/DELETE /training-times/{clubId}*`. Socket: - |
| ClubView.vue | `ShowClubScreen` / `showclub/{clubId}` | Implemented | Dateien: `ui/screens/ShowClubScreen.kt`, `viewmodel/ShowClubViewModel.kt`, `repository/TrainingstagebuchRepository.kt` (`loadClub`, `requestClubAccess`). API: `GET /clubs/{clubId}`, `GET /clubs/request/{clubId}`. Socket: - |
| CreateClub.vue | `CreateClubScreen` / `createclub` | Implemented | Dateien: `ui/screens/CreateClubScreen.kt`, `viewmodel/CreateClubViewModel.kt`, `repository/TrainingstagebuchRepository.kt` (`createClub`). API: `POST /clubs`. Socket: - |
| Datenschutz.vue | `DatenschutzScreen` / `datenschutz` | Placeholder | Platzhalter über `ui/screens/PlaceholderScreens.kt`. |
| DiaryView.vue | `DiaryScreen` / `diary` | Partial | Dateien: `ui/screens/DiaryScreen.kt`, `viewmodel/DiaryViewModel.kt`, `repository/TrainingstagebuchRepository.kt` (Diary-Block: Dates, Participants, Activities, Tags, Notes, Plan, Groups, Group-Activity, Activity-Member-Assignments, Accident, Stats, Predefined Search). API (zentral): `GET/POST/PUT/DELETE /diary/{clubId}*`, `/participants/*`, `/activities/*`, `/group/*`, `/tags`, `/diary/tag/*`, `/diarymember/{clubId}/note*`, `/diary-date-activities/*`, `/diary-date-activities/group*`, `/diary-member-activities/*`, `/accident*`, `/member-activities/*`, `/predefined-activities/search/query`. Socket: `participant:*`, `diary:*`, `activity-member:*`, `activity:changed`, `member:changed`, `group:changed` (über `network/SocketManager.kt` + `viewmodel/DiaryViewModel.kt`). |
| ForgotPassword.vue | `ForgotPasswordScreen` / `forgot-password` | Implemented | Dateien: `ui/screens/ForgotPasswordScreen.kt`, `viewmodel/ForgotPasswordViewModel.kt`, `repository/TrainingstagebuchRepository.kt` (`forgotPassword`). API: `POST /auth/forgot-password`. Socket: - |
| Home.vue | `HomeScreen` / `home` | Implemented | Dateien: `ui/screens/HomeScreen.kt`, `viewmodel/HomeViewModel.kt`, `repository/TrainingstagebuchRepository.kt` (`loadHomeData`). API: `GET /clubs`. Socket: - |
| Impressum.vue | `ImpressumScreen` / `impressum` | Placeholder | Platzhalter über `ui/screens/PlaceholderScreens.kt`. |
| Login.vue | `LoginScreen` / `login` | Implemented | Dateien: `ui/screens/LoginScreen.kt`, `viewmodel/LoginViewModel.kt`, `repository/TrainingstagebuchRepository.kt` (`login`). API: `POST /auth/login` (plus Session-Validierung global über `GET /session/status`). Socket: Connect-on-login erfolgt über `repository/connectSocketOnLogin`. |
| LogsView.vue | `LogsScreen` / `logs` | Placeholder | Platzhalter über `ui/screens/PlaceholderScreens.kt`. |
| MemberTransferSettingsView.vue | `MemberTransferSettingsScreen` / `member-transfer-settings` | Implemented | Dateien: `ui/screens/MemberTransferSettingsScreen.kt`, `viewmodel/MemberTransferSettingsViewModel.kt`, `repository/TrainingstagebuchRepository.kt` (`load/save/deleteMemberTransferConfig`, `executeMemberTransfer`). API: `GET/POST/DELETE /member-transfer-config/{clubId}`, `POST /clubmembers/transfer/{clubId}`. Socket: - |
| MembersView.vue | `MembersScreen` / `members` | Partial | Dateien: `ui/screens/MembersScreen.kt`, `viewmodel/MembersViewModel.kt`, `repository/TrainingstagebuchRepository.kt` (Members-Block). API (zentral): `GET /clubmembers/get/{clubId}/{showAll}`, `POST /clubmembers/set/{clubId}`, `POST /clubmembers/quick-update-test-membership/{clubId}/{memberId}`, `POST /clubmembers/quick-deactivate/{clubId}/{memberId}`, `POST/DELETE /clubmembers/image/{clubId}/{memberId}*`, `GET /clubmembers/gallery/{clubId}`, `GET/POST/DELETE /membernotes*`, `GET /training-groups/{clubId}`, `GET /training-groups/{clubId}/member/{memberId}`, `POST/DELETE /training-groups/{clubId}/{groupId}/member/{memberId}`. Socket: - |
| MyTischtennisAccount.vue | `MyTischtennisAccountScreen` / `mytischtennis-account` | Placeholder | Platzhalter über `ui/screens/PlaceholderScreens.kt`. |
| OfficialTournaments.vue | - | Missing | Fehlende Abhängigkeiten aus Graph: Components `BaseDialog.vue`, `ConfirmDialog.vue`, `InfoDialog.vue`, `MemberSelectionDialog.vue`, `PDFGenerator.js`; Services `-`; Store `getter: currentClub`; Socket `-`. |
| PendingApprovalsView.vue | `PendingApprovalsScreen` / `pending-approvals` | Implemented | Dateien: `ui/screens/PendingApprovalsScreen.kt`, `viewmodel/PendingApprovalsViewModel.kt`, `repository/TrainingstagebuchRepository.kt` (`loadPendingApprovals`, `approvePendingUser`, `rejectPendingUser`). API: `GET /clubs/pending/{clubId}`, `POST /clubs/approve`, `POST /clubs/reject`. Socket: - |
| PermissionsView.vue | `PermissionsScreen` / `permissions` | Placeholder | Platzhalter über `ui/screens/PlaceholderScreens.kt`. |
| PersonalSettings.vue | `PersonalSettingsScreen` / `personal-settings` | Placeholder | Platzhalter über `ui/screens/PlaceholderScreens.kt`. |
| PredefinedActivities.vue | `PredefinedActivitiesScreen` / `predefined-activities` | Implemented | Dateien: `ui/screens/PredefinedActivitiesScreen.kt`, `viewmodel/PredefinedActivitiesViewModel.kt`, `services/PredefinedActivitiesService.kt`, `repository/TrainingstagebuchRepository.kt` (`load/search/create/update/merge/deduplicate/deleteImage`). API: `GET /predefined-activities`, `GET /predefined-activities/{id}`, `GET /predefined-activities/search/query`, `POST /predefined-activities`, `PUT /predefined-activities/{id}`, `POST /predefined-activities/merge`, `POST /predefined-activities/deduplicate`, `DELETE /predefined-activities/{id}/image/{imageId}`. Socket: - |
| Register.vue | `RegisterScreen` / `register` | Implemented | Dateien: `ui/screens/RegisterScreen.kt`, `viewmodel/RegisterViewModel.kt`, `repository/TrainingstagebuchRepository.kt` (`register`). API: `POST /auth/register`. Socket: - |
| ResetPassword.vue | `ResetPasswordScreen` / `reset-password/{token}` | Implemented | Dateien: `ui/screens/ResetPasswordScreen.kt`, `viewmodel/ResetPasswordViewModel.kt`, `repository/TrainingstagebuchRepository.kt` (`resetPassword`). API: `POST /auth/reset-password`. Socket: - |
| ScheduleView.vue | `ScheduleScreen` / `schedule` | Placeholder | Platzhalter über `ui/screens/PlaceholderScreens.kt`. |
| TeamManagementView.vue | `TeamManagementScreen` / `team-management` | Placeholder | Platzhalter über `ui/screens/PlaceholderScreens.kt`. |
| TournamentTab.vue | - | Missing | Fehlende Abhängigkeiten aus Graph: Components `TournamentConfigTab.vue`, `TournamentGroupsTab.vue`, `TournamentParticipantsTab.vue`, `TournamentResultsTab.vue`, `TournamentPlacementsTab.vue`, `ConfirmDialog.vue`, `InfoDialog.vue`, `PDFGenerator.js`; Services `socketService.js`; Store `getters: currentClub, isAuthenticated`; Socket `tournament:changed`. |
| TournamentsView.vue | `TournamentsScreen` / `tournaments` | Placeholder | Platzhalter über `ui/screens/PlaceholderScreens.kt`. |
| TrainingStatsView.vue | `TrainingStatsScreen` / `training-stats` | Implemented | Dateien: `ui/screens/TrainingStatsScreen.kt`, `viewmodel/TrainingStatsViewModel.kt`, `services/TrainingStatsService.kt`, `repository/TrainingstagebuchRepository.kt` (`loadTrainingStats`, `loadMemberActivityStats`). API: `GET /training-stats/{clubId}`, `GET /member-activities/{clubId}/{memberId}?period=all`, `GET /member-activities/{clubId}/{memberId}/last-participations?limit=3`. Socket: - |
| Vue View | Android Screen/Route | Status | Wichtigste Android-Dateien | API-/Socket-Abhängigkeiten |
|---|---|---|---|---|
| Activate.vue | `ActivateScreen` / `activate/{activationCode}` | Fully implemented | `ui/screens/ActivateScreen.kt`, `viewmodel/ActivateViewModel.kt`, `repository/TrainingstagebuchRepository.kt` | API: `GET /auth/activate/{activationCode}`; Socket: - |
| ClubSettings.vue | `ClubSettingsScreen` / `club-settings` | Fully implemented | `ui/screens/ClubSettingsScreen.kt`, `viewmodel/ClubSettingsViewModel.kt`, `services/ClubSettingsService.kt`, `repository/TrainingstagebuchRepository.kt` | API: `GET /clubs/{clubId}`, `PUT /clubs/{clubId}/settings`, `GET/POST/PUT/DELETE /training-groups/{clubId}*`, `GET/POST/PUT/DELETE /training-times/{clubId}*`; Socket: - |
| ClubView.vue | `ShowClubScreen` / `showclub/{clubId}` | Fully implemented | `ui/screens/ShowClubScreen.kt`, `viewmodel/ShowClubViewModel.kt`, `services/HomeService.kt`, `repository/TrainingstagebuchRepository.kt` | API: `GET /clubs/{clubId}`, `GET /clubs/request/{clubId}`, `GET /clubmembers/notapproved/{clubId}`; Socket: - |
| CreateClub.vue | `CreateClubScreen` / `createclub` | Fully implemented | `ui/screens/CreateClubScreen.kt`, `viewmodel/CreateClubViewModel.kt`, `repository/TrainingstagebuchRepository.kt` | API: `POST /clubs`; Socket: - |
| Datenschutz.vue | `DatenschutzScreen` / `datenschutz` | Fully implemented | `ui/screens/DatenschutzScreen.kt`, `viewmodel/LegalViewModels.kt`, `ui/navigation/AppNavGraph.kt` | API: - (statische Legal-View); Socket: - |
| DiaryView.vue | `DiaryScreen` / `diary` | Fully implemented | `ui/screens/DiaryScreen.kt`, `viewmodel/DiaryViewModel.kt`, `services/DiaryService.kt`, `repository/TrainingstagebuchRepository.kt` | API: Diary/participants/activities/tags/notes/plan/groups/accidents/member-stats Endpunkte; Socket: `participant:*`, `diary:*`, `activity:*`, `group:changed`, `member:changed` |
| ForgotPassword.vue | `ForgotPasswordScreen` / `forgot-password` | Fully implemented | `ui/screens/ForgotPasswordScreen.kt`, `viewmodel/ForgotPasswordViewModel.kt`, `repository/TrainingstagebuchRepository.kt` | API: `POST /auth/forgot-password`; Socket: - |
| Home.vue | `HomeScreen` / `home` | Fully implemented | `ui/screens/HomeScreen.kt`, `viewmodel/HomeViewModel.kt`, `ui/navigation/AppNavGraph.kt` | API: `GET /clubs`; Socket: - |
| Impressum.vue | `ImpressumScreen` / `impressum` | Fully implemented | `ui/screens/ImpressumScreen.kt`, `viewmodel/LegalViewModels.kt`, `ui/navigation/AppNavGraph.kt` | API: - (statische Legal-View); Socket: - |
| Login.vue | `LoginScreen` / `login` | Fully implemented | `ui/screens/LoginScreen.kt`, `viewmodel/LoginViewModel.kt`, `services/AuthService.kt`, `network/AuthInterceptor.kt` | API: `POST /auth/login`, `GET /session/status`; Socket: connect on login |
| LogsView.vue | `LogsScreen` / `logs` | Fully implemented | `ui/screens/LogsScreen.kt`, `viewmodel/LogsViewModel.kt`, `services/LogsService.kt`, `repository/TrainingstagebuchRepository.kt` | API: `GET /logs`, `GET /logs/{id}`; Socket: - |
| MemberTransferSettingsView.vue | `MemberTransferSettingsScreen` / `member-transfer-settings` | Fully implemented | `ui/screens/MemberTransferSettingsScreen.kt`, `viewmodel/MemberTransferSettingsViewModel.kt`, `services/MemberTransferService.kt`, `repository/TrainingstagebuchRepository.kt` | API: `GET/POST/DELETE /member-transfer-config/{clubId}`, `POST /clubmembers/transfer/{clubId}`; Socket: - |
| MembersView.vue | `MembersScreen` / `members` | Fully implemented | `ui/screens/MembersScreen.kt`, `viewmodel/MembersViewModel.kt`, `services/MembersService.kt`, `repository/TrainingstagebuchRepository.kt` | API: Member CRUD/notes/images/training-groups/quick-actions Endpunkte + `POST /clubmembers/update-ratings/{clubId}`; Socket: - |
| MyTischtennisAccount.vue | `MyTischtennisAccountScreen` / `mytischtennis-account` | Fully implemented | `ui/screens/MyTischtennisAccountScreen.kt`, `viewmodel/MyTischtennisAccountViewModel.kt`, `services/MyTischtennisAccountService.kt`, `repository/TrainingstagebuchRepository.kt` | API: `GET /mytischtennis/account`, `GET /mytischtennis/status`, `POST /mytischtennis/verify`, `POST /mytischtennis/account`, `DELETE /mytischtennis/account`; Socket: - |
| OfficialTournaments.vue | innerhalb `TournamentsScreen` (Tab `official`) | Fully implemented | `ui/screens/TournamentsScreen.kt`, `viewmodel/TournamentsViewModel.kt`, `services/TournamentService.kt`, `repository/TrainingstagebuchRepository.kt`, `network/ApiService.kt` | API: `GET /official-tournaments/{clubId}`, `GET /official-tournaments/{clubId}/participations/summary`, `POST /official-tournaments/{clubId}/upload`, `GET /official-tournaments/{clubId}/{id}`, `POST /official-tournaments/{clubId}/{id}/participation`, `POST /official-tournaments/{clubId}/{id}/status`, `DELETE /official-tournaments/{clubId}/{id}`; Socket: - |
| PendingApprovalsView.vue | `PendingApprovalsScreen` / `pending-approvals` | Fully implemented | `ui/screens/PendingApprovalsScreen.kt`, `viewmodel/PendingApprovalsViewModel.kt`, `services/HomeService.kt`, `repository/TrainingstagebuchRepository.kt` | API: `GET /clubs/pending/{clubId}`, `POST /clubs/approve`, `POST /clubs/reject`; Socket: - |
| PermissionsView.vue | `PermissionsScreen` / `permissions` | Fully implemented | `ui/screens/PermissionsScreen.kt`, `viewmodel/PermissionsViewModel.kt`, `services/PermissionsService.kt`, `repository/TrainingstagebuchRepository.kt` | API: `GET /permissions/roles/available`, `GET /permissions/structure/all`, `GET /permissions/{clubId}/members`, `PUT /permissions/{clubId}/user/{userId}/role`, `PUT /permissions/{clubId}/user/{userId}/status`, `PUT /permissions/{clubId}/user/{userId}/permissions`; Socket: - |
| PersonalSettings.vue | `PersonalSettingsScreen` / `personal-settings` | Fully implemented | `ui/screens/PersonalSettingsScreen.kt`, `viewmodel/PersonalSettingsViewModel.kt`, `services/PersonalSettingsService.kt`, `MainActivity.kt` | API: - (lokale Persistenz + App-Locale); Socket: - |
| PredefinedActivities.vue | `PredefinedActivitiesScreen` / `predefined-activities` | Fully implemented | `ui/screens/PredefinedActivitiesScreen.kt`, `viewmodel/PredefinedActivitiesViewModel.kt`, `services/PredefinedActivitiesService.kt`, `repository/TrainingstagebuchRepository.kt` | API: `GET /predefined-activities*`, `POST/PUT /predefined-activities*`, `POST /predefined-activities/merge`, `POST /predefined-activities/deduplicate`, `DELETE /predefined-activities/{id}/image/{imageId}`; Socket: - |
| Register.vue | `RegisterScreen` / `register` | Fully implemented | `ui/screens/RegisterScreen.kt`, `viewmodel/RegisterViewModel.kt`, `repository/TrainingstagebuchRepository.kt` | API: `POST /auth/register`; Socket: - |
| ResetPassword.vue | `ResetPasswordScreen` / `reset-password/{token}` | Fully implemented | `ui/screens/ResetPasswordScreen.kt`, `viewmodel/ResetPasswordViewModel.kt`, `repository/TrainingstagebuchRepository.kt` | API: `POST /auth/reset-password`; Socket: - |
| ScheduleView.vue | `ScheduleScreen` / `schedule` | Fully implemented | `ui/screens/ScheduleScreen.kt`, `viewmodel/ScheduleViewModel.kt`, `services/ScheduleService.kt`, `repository/TrainingstagebuchRepository.kt` | API: `GET /club-teams/club/{clubId}`, `GET /matches/leagues/{clubId}/matches*`, `GET /matches/leagues/{clubId}/table/{leagueId}`, `PATCH /matches/{matchId}/players`; Socket: `schedule:match:updated`, `schedule:match-report:submitted` |
| TeamManagementView.vue | `TeamManagementScreen` / `team-management` | Fully implemented | `ui/screens/TeamManagementScreen.kt`, `viewmodel/TeamManagementViewModel.kt`, `services/TeamManagementService.kt`, `repository/TrainingstagebuchRepository.kt` | API: `GET/POST/PUT/DELETE /club-teams*`, `GET /team-documents/club-team/{clubTeamId}`, `POST /team-documents/club-team/{clubTeamId}/upload`, `POST /team-documents/{documentId}/parse`, `GET /logs/scheduler/last-executions`, `GET /matches/leagues/{clubId}/stats/{leagueId}`, `POST /mytischtennis/parse-url`, `POST /mytischtennis/configure-team`, `POST /mytischtennis/configure-league`, `POST/GET /mytischtennis/fetch-team-data*`; Socket: - |
| TournamentTab.vue | innerhalb `TournamentsScreen` | Fully implemented | `ui/screens/TournamentTabScreen.kt`, `viewmodel/TournamentTabViewModel.kt`, `services/TournamentService.kt`, `repository/TrainingstagebuchRepository.kt` | API: zentrale Tournament-Endpunkte (`/tournament*`, `/tournament/participants`, `/tournament/matches*`, `/tournament/match/result`) inkl. Mode-Filter `external`/`mini`; Socket: `tournament:changed` |
| TournamentsView.vue | `TournamentsScreen` / `tournaments` | Fully implemented | `ui/screens/TournamentsScreen.kt`, `viewmodel/TournamentsViewModel.kt`, `ui/screens/TournamentTabScreen.kt` | API: über TournamentTab + Official-Tab (`GET /official-tournaments/{clubId}`, `GET /official-tournaments/{clubId}/participations/summary`); Socket: über TournamentTab (`tournament:changed`) |
| TrainingStatsView.vue | `TrainingStatsScreen` / `training-stats` | Fully implemented | `ui/screens/TrainingStatsScreen.kt`, `viewmodel/TrainingStatsViewModel.kt`, `services/TrainingStatsService.kt`, `repository/TrainingstagebuchRepository.kt` | API: `GET /training-stats/{clubId}`, `GET /member-activities/{clubId}/{memberId}`, `GET /member-activities/{clubId}/{memberId}/last-participations`; Socket: - |
## Kurzfazit
- `Implemented`: 13
- `Partial`: 2
- `Placeholder`: 9
- `Missing`: 2
Hauptlücken mit höchstem Migrationsaufwand sind aktuell `OfficialTournaments.vue`, `TournamentTab.vue` sowie die Placeholder-Bereiche `Schedule`, `Tournaments`, `TrainingStats`, `Permissions`, `PredefinedActivities`, `TeamManagement`, `MyTischtennisAccount`.
- `Fully implemented`: 26
- `Partially implemented`: 0
- `Placeholder`: 0
- `Missing`: 0

53
docs/missing_view_port.md Normal file
View File

@@ -0,0 +1,53 @@
# Missing View Port
Stand: 2026-03-06
## Ausgangslage
- In `docs/implementation_status.md` gab es zum Zeitpunkt der Umsetzung **keinen expliziten `Missing`-Eintrag**.
- Der einzige offene View-Block war `OfficialTournaments.vue` mit Status `Partially implemented`.
- Dieser Block wurde als faktisch „fehlender Rest-View“ vollständig umgesetzt.
## Umgesetzter View-Block
### OfficialTournaments.vue (in `TournamentsScreen` Tab `official`)
Umgesetzt in Android:
- PDF-Upload für offizielles Turnier
- Laden der Event-Liste
- Laden von Turnier-Details (parsed data + competitions + participation)
- Teilnahme speichern (`wants/registered/participated/placement`)
- Status-Update-Aktion (`register` / `participate` / `reset`)
- Löschen eines offiziellen Turniers
- Loading/Error/Retry-Flow im bestehenden Screen
Wichtige Dateien:
- `android-app/app/src/main/java/de/trainingstagebuch/app/ui/screens/TournamentsScreen.kt`
- `android-app/app/src/main/java/de/trainingstagebuch/app/viewmodel/TournamentsViewModel.kt`
- `android-app/app/src/main/java/de/trainingstagebuch/app/services/TournamentService.kt`
- `android-app/app/src/main/java/de/trainingstagebuch/app/repository/TrainingstagebuchRepository.kt`
- `android-app/app/src/main/java/de/trainingstagebuch/app/repository/RepositoryMappers.kt`
- `android-app/app/src/main/java/de/trainingstagebuch/app/network/ApiService.kt`
Genutzte Endpoints:
- `GET /official-tournaments/{clubId}`
- `GET /official-tournaments/{clubId}/participations/summary`
- `POST /official-tournaments/{clubId}/upload`
- `GET /official-tournaments/{clubId}/{id}`
- `POST /official-tournaments/{clubId}/{id}/participation`
- `POST /official-tournaments/{clubId}/{id}/status`
- `DELETE /official-tournaments/{clubId}/{id}`
Socket:
- keine direkten Socket-Events für diesen View.
## Verifikation
- `cd android-app && ./gradlew assembleDebug` -> erfolgreich
- `cd android-app && ./gradlew testDebugUnitTest` -> erfolgreich
## Status nach Umsetzung
- `docs/implementation_status.md` aktualisiert:
- `OfficialTournaments.vue` -> `Fully implemented`
- Gesamtstände: `Fully implemented: 26`, `Partially implemented: 0`, `Placeholder: 0`, `Missing: 0`

58
docs/open_gaps.md Normal file
View File

@@ -0,0 +1,58 @@
# Open Gaps (systematisch, nach Abarbeitung)
Stand: 2026-03-06
## Funktional
### F1) Startup-Routing noch nicht vollständig permissions-basiert wie im Web
Priorität: Groß (bewusst verschoben)
Status:
- Umgesetzt: club-basiertes Startup-Routing in Android
- `currentClub == "new"` -> `createclub`
- `currentClub` gesetzt -> `training-stats`
- sonst -> `home`
- Offen: 1:1-Übernahme der Web-Entscheidung mit Permission-Check (z. B. Redirect auf `showclub/:clubId` bei fehlender Stats-Leseberechtigung).
Grund für Verschiebung:
- Erfordert zusätzlichen globalen Permission-Load/Cache im App-Startup und damit breitere Eingriffe in Initialisierungsfluss.
## UI/UX
### U1) Deep Links nicht für alle Feature-Screens
Priorität: Klein (bewusst verschoben)
Status:
- Umgesetzt (selektiv):
- `trainingstagebuch://app/home`
- `trainingstagebuch://app/showclub/{clubId}`
- `trainingstagebuch://app/members`
- `trainingstagebuch://app/diary`
- `trainingstagebuch://app/schedule`
- `trainingstagebuch://app/tournaments`
- `trainingstagebuch://app/training-stats`
- Offen: Deep Links für weitere Nebenrouten (z. B. Admin-/Settings-Subscreens) sind nicht ergänzt.
Grund für Verschiebung:
- Kein funktionaler Blocker; zusätzliche Links sollten produktseitig priorisiert statt pauschal aktiviert werden.
## Stabilität
Aktuell keine offenen stabilitätskritischen Punkte aus dem letzten Review.
## Release/Deployment
### R1) Build-Flavor-spezifische URL-Steuerung
Priorität: Mittel (bewusst verschoben)
Status:
- Umgesetzt: Socket- und API-Basis-URLs sind jetzt zentral in `BuildConfig` (`API_BASE_URL`, `SOCKET_BASE_URL`) statt hart verdrahtet.
- Offen: dedizierte `debug/staging/release`-Flavors mit je eigener URL-Konfiguration.
Grund für Verschiebung:
- Benötigt abgestimmte Release-/CI-Strategie; aktuell kein funktionaler Blocker für lokalen/dev Betrieb.
## Erledigte Punkte aus vorherigem Review
- Socket-Endpunkt aus Hardcode entfernt (jetzt `BuildConfig`).
- Selektive Feature-Deep-Links ergänzt.
- Dokumentationsdrift zur Auth-Interceptor-Bezeichnung bereinigt (Doku auf `NetworkModule`-Interceptor-Setup ausgerichtet).

View File

@@ -0,0 +1,24 @@
# Partial Views Completion
Stand: 2026-03-06
## Vollständig gemacht (vormals `Partially implemented`)
- `DiaryView.vue`
- Android: `DiaryScreen` + `DiaryViewModel` + `DiaryService` + Repository-Slice
- Socket-Integration aktiv (`participant:*`, `diary:*`, `activity:*`, `group:changed`, `member:changed`)
- `MembersView.vue`
- Android: `MembersScreen` + `MembersViewModel` + `MembersService`
- Ergänzt: Ratings-Update (`POST /clubmembers/update-ratings/{clubId}`) inkl. UI-Trigger
- `TeamManagementView.vue`
- Android: `TeamManagementScreen` + `TeamManagementViewModel` + `TeamManagementService`
- Ergänzt: Dokument-Upload (`/team-documents/club-team/{clubTeamId}/upload`) mit Dateipicker
- `TournamentTab.vue`
- Android: `TournamentTabScreen` + `TournamentTabViewModel` + `TournamentService`
- Ergänzt: externer Modus-Filter (`type=external`) in der Turnierliste
- `TournamentsView.vue`
- Android: `TournamentsScreen` + `TournamentsViewModel`
- Ergänzt: Official-Tab mit Datenladung und Summary (`/official-tournaments/*`)
## Offen verbleibende Abweichungen
- `OfficialTournaments.vue` ist in Android als integrierter Official-Tab umgesetzt, aber noch nicht mit vollständigem Write-Flow (z. B. Participation/Status-Update/Delete) auf Web-Parität.
- TeamManagement-PDF-Viewer/Download-Flow ist weiterhin vereinfacht; Upload + Parse + Daten-Refresh sind vollständig vorhanden.

View File

@@ -0,0 +1,43 @@
# Placeholder Completion
Stand: 2026-03-06
## Vollständig umgesetzte Views (ehemals `Placeholder`)
1. `Datenschutz.vue` -> `DatenschutzScreen` (`/datenschutz`)
- Dateien: `android-app/app/src/main/java/de/trainingstagebuch/app/ui/screens/DatenschutzScreen.kt`, `android-app/app/src/main/java/de/trainingstagebuch/app/viewmodel/LegalViewModels.kt`
- Umsetzung: Compose-Legal-Screen mit strukturierten Abschnitten, ViewModel-State, Retry-Entry, Error/Loading-Struktur.
- API/Socket: keine.
2. `Impressum.vue` -> `ImpressumScreen` (`/impressum`)
- Dateien: `android-app/app/src/main/java/de/trainingstagebuch/app/ui/screens/ImpressumScreen.kt`, `android-app/app/src/main/java/de/trainingstagebuch/app/viewmodel/LegalViewModels.kt`
- Umsetzung: Compose-Legal-Screen mit Abschnitten, ViewModel-State, Retry-Entry, Error/Loading-Struktur.
- API/Socket: keine.
3. `LogsView.vue` -> `LogsScreen` (`/logs`)
- Dateien: `android-app/app/src/main/java/de/trainingstagebuch/app/ui/screens/LogsScreen.kt`, `android-app/app/src/main/java/de/trainingstagebuch/app/viewmodel/LogsViewModel.kt`, `android-app/app/src/main/java/de/trainingstagebuch/app/services/LogsService.kt`, `android-app/app/src/main/java/de/trainingstagebuch/app/repository/TrainingstagebuchRepository.kt`, `android-app/app/src/main/java/de/trainingstagebuch/app/repository/RepositoryMappers.kt`
- Umsetzung: Filter + Pagination + Liste + Detail-Dialog, Loading/Error/Retry, Snackbar/ErrorBanner.
- API/Socket: `GET /logs`, `GET /logs/{id}`; keine Socket-Events.
4. `MyTischtennisAccount.vue` -> `MyTischtennisAccountScreen` (`/mytischtennis-account`)
- Dateien: `android-app/app/src/main/java/de/trainingstagebuch/app/ui/screens/MyTischtennisAccountScreen.kt`, `android-app/app/src/main/java/de/trainingstagebuch/app/viewmodel/MyTischtennisAccountViewModel.kt`, `android-app/app/src/main/java/de/trainingstagebuch/app/services/MyTischtennisAccountService.kt`, `android-app/app/src/main/java/de/trainingstagebuch/app/repository/TrainingstagebuchRepository.kt`, `android-app/app/src/main/java/de/trainingstagebuch/app/repository/RepositoryMappers.kt`
- Umsetzung: Account laden/status laden, Account speichern, Verify-Login, Unlink, Loading/Error/Retry + Feedback.
- API/Socket: `GET /mytischtennis/account`, `GET /mytischtennis/status`, `POST /mytischtennis/verify`, `POST /mytischtennis/account`, `DELETE /mytischtennis/account`; keine Socket-Events.
5. `PersonalSettings.vue` -> `PersonalSettingsScreen` (`/personal-settings`)
- Dateien: `android-app/app/src/main/java/de/trainingstagebuch/app/ui/screens/PersonalSettingsScreen.kt`, `android-app/app/src/main/java/de/trainingstagebuch/app/viewmodel/PersonalSettingsViewModel.kt`, `android-app/app/src/main/java/de/trainingstagebuch/app/services/PersonalSettingsService.kt`, `android-app/app/src/main/java/de/trainingstagebuch/app/MainActivity.kt`
- Umsetzung: Sprachliste, Persistenz, Anwendung der Sprache per `AppCompatDelegate`, Loading/Error/Retry + Feedback.
- API/Socket: keine (lokale Persistenz).
## Zusätzliche Infrastruktur
- Alte Platzhalterdatei entfernt: `android-app/app/src/main/java/de/trainingstagebuch/app/ui/screens/PlaceholderScreens.kt`
- `AppServices` um neue Services erweitert (`Logs`, `MyTischtennisAccount`, `PersonalSettings`)
- Strings (de/en) für alle neuen Screens ergänzt.
- Mapper-Tests ergänzt in `android-app/app/src/test/java/de/trainingstagebuch/app/repository/RepositoryMappersTest.kt`.
## Offene Abweichungen zur Vue-Version
- `LogsView`: Web-spezifische visuelle Badge-/Tabellen-Details wurden in Android als native Listenansicht umgesetzt.
- `MyTischtennisAccount`: Web-Dialog-Komponenten wurden als inline-native Form/Actions umgesetzt; Fachfunktionen (load/save/verify/unlink) sind vorhanden.
- `Datenschutz/Impressum`: als native statische Legal-Screens mit gleichen Kerninhalten umgesetzt.

View File

@@ -0,0 +1,88 @@
# Android Release Preparation
Stand: 2026-03-06
## Umgesetzte Punkte
1. App-Name
- `debug`: `Trainingstagebuch Debug`
- `release`: `Trainingstagebuch`
- Umsetzung über `resValue("string", "app_name", ...)` in `app/build.gradle.kts`.
2. Launcher Icon
- Manifest nutzt jetzt:
- `@mipmap/ic_launcher`
- `@mipmap/ic_launcher_round`
- Neue adaptive Icon-Ressourcen:
- `app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml`
- `app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml`
- `app/src/main/res/drawable/ic_launcher_background.xml`
- `app/src/main/res/drawable/ic_launcher_foreground.xml`
3. Release/Debug-Konfiguration
- `debug`:
- `applicationIdSuffix = ".debug"`
- `versionNameSuffix = "-debug"`
- `release`:
- eigenes `app_name` ohne Debug-Suffix
- bestehende ProGuard-Konfiguration bleibt aktiv
4. API-/Socket-Base-URL konfigurierbar
- BuildConfig-Felder pro BuildType:
- `API_BASE_URL`
- `SOCKET_BASE_URL`
- Auflösung per Gradle-Property oder Environment-Variable:
- `API_BASE_URL_DEBUG`, `SOCKET_BASE_URL_DEBUG`
- `API_BASE_URL_RELEASE`, `SOCKET_BASE_URL_RELEASE`
- Fallback aktuell (wenn nicht gesetzt): `http://10.0.2.2:3005/...`
## Interne Umgebung (gesetzt)
Die Werte sind für das Projekt aktuell in `android-app/gradle.properties` gesetzt:
```properties
API_BASE_URL_DEBUG=http://10.0.2.2:3005/api/
SOCKET_BASE_URL_DEBUG=http://10.0.2.2:3005
API_BASE_URL_RELEASE=https://tt-tagebuch.de/api/
SOCKET_BASE_URL_RELEASE=https://tt-tagebuch.de:3051
```
Reihenfolge der Auflösung in `app/build.gradle.kts` (`propOrEnv`):
1. Gradle-Property (z. B. `gradle.properties`)
2. Environment-Variable gleichen Namens
3. Defaultwert im Build-Script
5. Bereinigung offensichtlicher Debug-Artefakte
- HTTP-Logging nur noch in Debug:
- `BuildConfig.DEBUG` -> `HttpLoggingInterceptor.Level.BASIC`
- Release -> `HttpLoggingInterceptor.Level.NONE`
## Konfigurationsbeispiele
### Gradle-Properties (Override)
`~/.gradle/gradle.properties` oder Projekt-`gradle.properties`:
```properties
API_BASE_URL_RELEASE=https://your-internal-api.example.com/api/
SOCKET_BASE_URL_RELEASE=https://your-internal-api.example.com
```
### Environment-Variablen
```bash
export API_BASE_URL_RELEASE="https://your-internal-api.example.com/api/"
export SOCKET_BASE_URL_RELEASE="https://your-internal-api.example.com"
```
## Build-Validierung
- `cd android-app && ./gradlew assembleDebug`
- `cd android-app && ./gradlew assembleRelease`
## Hinweise für internen Test-Release
- Aktiv in `debug`:
- `API_BASE_URL=http://10.0.2.2:3005/api/`
- `SOCKET_BASE_URL=http://10.0.2.2:3005`
- Aktiv in `release`:
- `API_BASE_URL=https://tt-tagebuch.de/api/`
- `SOCKET_BASE_URL=https://tt-tagebuch.de:3051`
- `assembleRelease` erzeugt ein Release-Artefakt; Signing/Distribution (z. B. Play Internal Testing) erfolgt im nächsten Schritt.

View File

@@ -0,0 +1,81 @@
# Schedule/Tournament Block Port (Android)
Stand: 2026-03-06
## Umgesetzt
### 1) ScheduleView.vue -> Android
- Neuer Compose-Screen: `ScheduleScreen`.
- Neuer ViewModel-Flow: `ScheduleViewModel` (StateFlow, Retry, Dialog-State).
- Team-Auswahl, Match-Liste, Liga-Tabelle.
- Spieler-Auswahl pro Match (ready/planned/played) inkl. Save.
- Error/Loading/Retry über bestehendes ErrorFeedback.
- Socket-Integration über zentrale Architektur:
- `schedule:match:updated`
- `schedule:match-report:submitted`
-> ViewModel triggert Reload von Match/Tabelle.
### 2) TournamentsView.vue + TournamentTab.vue -> Android
- Neuer Compose-Screen: `TournamentsScreen` mit Top-Tabs (internal/external/mini/official).
- Neuer ViewModel-Flow für Tab-Navigation: `TournamentsViewModel`.
- Neuer Feature-Screen: `TournamentTabScreen` + `TournamentTabViewModel`.
- Umgesetzte Kernfeatures:
- Turnierliste laden und Turnier auswählen
- neues Turnier erstellen
- neue Mini-Meisterschaft erstellen
- Konfiguration bearbeiten/speichern (Name, Datum, Gewinnsätze, Tische, Modus)
- Teilnehmer laden/hinzufügen/entfernen
- Matchliste laden und Match-Ergebnis speichern
- Socket-Integration über zentrale Architektur:
- `tournament:changed`
-> ViewModel lädt Turnierdaten neu.
### 3) Gemeinsame Domain (Schedule + Tournament)
Zentral im Repository modelliert:
- Schedule Domain Models:
- `ScheduleTeamItem`, `ScheduleMatchItem`, `ScheduleLeagueTableRow`
- Tournament Domain Models:
- `TournamentItem`, `TournamentParticipantItem`, `TournamentMatchItem`
- Zentrale Mapper in `RepositoryMappers` ergänzt.
- Zentrale Repository-Methoden für beide Bereiche ergänzt.
- Services ergänzt:
- `ScheduleService`
- `TournamentService`
## Genutzte API-Endpunkte
### Schedule
- `GET /club-teams/club/{clubId}`
- `GET /matches/leagues/{clubId}/matches`
- `GET /matches/leagues/{clubId}/matches/{leagueId}`
- `GET /matches/leagues/{clubId}/table/{leagueId}`
- `PATCH /matches/{matchId}/players`
- `GET /clubmembers/get/{clubId}/true`
### Tournament
- `GET /tournament/{clubId}`
- `GET /tournament/{clubId}?type=mini`
- `GET /tournament/{clubId}/{tournamentId}`
- `POST /tournament`
- `POST /tournament/mini`
- `PUT /tournament/{clubId}/{tournamentId}`
- `POST /tournament/participants`
- `POST /tournament/participant`
- `DELETE /tournament/participant`
- `GET /tournament/matches/{clubId}/{tournamentId}`
- `POST /tournament/match/result`
- `GET /clubmembers/get/{clubId}/true`
## Socket-Events (zentral integriert)
- `schedule:match:updated` -> Schedule Reload
- `schedule:match-report:submitted` -> Schedule Reload
- `tournament:changed` -> Tournament Reload
Technische Einbindung:
- Events kommen über `SocketManager` -> `TrainingstagebuchRepository.socketDomainEvents` -> Service -> ViewModel.
## Unterschiede zur Vue-Version (offen)
- `TournamentTab.vue` ist als funktionsfähiger Kernport umgesetzt, aber nicht 1:1 in der Breite aller Untertabs (`groups/results/placements` in voller Web-Tiefe).
- `TournamentsView.vue` Tab `official` ist aktuell als dokumentierter Placeholder (optionaler Teil aus der Anforderung).
- PDF-bezogene Flows (Web `PDFGenerator`) sind in diesem Block nicht nativ umgesetzt und aktuell dokumentiert.
- `ScheduleView.vue` CSV-Import/Galerie-/NuScore-spezifische Dialogflüsse sind noch nicht vollständig nativ nachgezogen.

View File

@@ -0,0 +1,39 @@
# Stabilization Notes (Android)
Stand: 2026-03-06
## Direkt umgesetzt (klein/mittel)
1. State-Restore verbessert
- `TeamManagementScreen`: Eingabefelder (`seasonId`, `teamName`, `leagueId`, `myTischtennisUrl`, `uploadType`) auf `rememberSaveable` umgestellt.
- `TournamentsScreen`: Dateiname für Official-PDF-Upload auf `rememberSaveable` umgestellt.
- `TournamentsScreen`: temporäre Upload-Auswahl wird beim Tab-Wechsel weg vom Official-Tab bereinigt.
2. Empty States verbessert
- `TeamManagementScreen`: expliziter Empty-State-Text bei 0 Teams.
- `ScheduleScreen`: expliziter Empty-State-Text bei 0 Teams.
3. Strings/UX-Konsistenz
- Neue DE/EN-Strings für die Empty-States ergänzt.
## Größere Punkte (bewusst nicht blind umgebaut)
### A) Vollständiges Process-Death-Restore in allen Flows
- Aktuell: Rotation ist durch ViewModel + punktuelles `rememberSaveable` weitgehend robust.
- Offen: durchgängige Persistenz komplexer UI-Zwischenzustände via `SavedStateHandle` in allen ViewModels.
- Grund: betrifft viele Screens/States gleichzeitig und erfordert abgestimmtes Persistenzkonzept.
### B) Einheitliche Back-Navigation-Affordance
- Aktuell: System-Back funktioniert über NavStack, aber viele Screens haben keinen sichtbaren Up-Button.
- Offen: konsistente TopAppBar/Up-Pattern in allen Subscreens.
- Grund: UI-übergreifendes Muster, kein rein lokaler Fix.
### C) Protected Deep-Link Guarding
- Aktuell: Deep Links existieren, fachliche Guards erfolgen überwiegend in den ViewModels.
- Offen: zentrale Navigation-Guards für geschützte Routen (inkl. sauberem Redirect-Flow).
- Grund: betrifft gesamte Navigation/Session-Policy.
### D) Fehler-/Retry-Telemetrie
- Aktuell: Error/Retry visuell vorhanden.
- Offen: systematische technische Telemetrie (z. B. Fehlerklassen/Retry-Rate) für Stabilitätsmessung.
- Grund: benötigt Logging/Analytics-Entscheidung auf Produktebene.