Implement cross-club friendly match concept with invitations and shared matches
All checks were successful
Deploy tt-tagebuch / deploy (push) Successful in 49s

- Added controllers for handling friendly match invitations and shared matches.
- Created migration scripts for `friendly_match_invitation` and `friendly_match_shared` tables.
- Developed models for `FriendlyMatchInvitation` and `FriendlyMatchShared`.
- Established routes for managing invitations and shared matches.
- Implemented services for business logic related to invitations and shared matches.
- Documented the concept plan for the new feature including API endpoints and data models.
This commit is contained in:
Torsten Schulz (local)
2026-05-30 17:50:35 +02:00
parent 359527eb5b
commit 0ff67dae80
21 changed files with 1795 additions and 17 deletions

View File

@@ -0,0 +1,153 @@
# Konzeptplan: Vereinsuebergreifende Freundschaftsspiele
## Ziel
- Ein gemeinsames Freundschaftsspiel-Objekt fuer zwei Vereine (statt zwei isolierter Eintraege).
- Auffindbarkeit bestehender Spiele ueber Name, Datum und Startzeit.
- Einladungsprozess (anbieten, annehmen, ablehnen).
- Live-Synchronisierung zwischen beiden Vereinen per Socket.
## Produktumfang (MVP)
- [x] Vereinsuebergreifendes Shared-Match-Datenmodell einfuehren.
- [x] Match-Finder fuer Name, Datum, Startzeit implementieren.
- [x] Einladung erstellen (Zielverein aus Vereinsliste + Freitext).
- [x] Einladung im System speichern.
- [x] E-Mail-Benachrichtigung an hinterlegte Personen des Zielvereins versenden.
- [x] Einladung annehmen: Shared-Match fuer beide Vereine erstellen.
- [x] Einladung ablehnen: Einladung loeschen.
- [x] Socket-Sync fuer beide Vereine bei Aenderungen am Shared-Match.
## Datenmodell
### Neue Tabellen
- [x] `friendly_match_shared`
- [x] `id`
- [x] `home_club_id`
- [x] `guest_club_id`
- [x] `date`
- [x] `start_time`
- [x] `match_name` (oder Home/Gast-Namenstruktur)
- [x] `location_*` Felder
- [x] `match_system`, `singles_count`, `doubles_count`, `winning_sets`
- [x] `home_participants`, `guest_participants`
- [x] `result_details`, `home_match_points`, `guest_match_points`
- [x] `is_completed`
- [x] `created_by_user_id`
- [x] `created_from_invitation_id`
- [x] `created_at`, `updated_at`
- [x] `friendly_match_invitation`
- [x] `id`
- [x] `from_club_id`
- [x] `to_club_id`
- [x] `proposed_date`
- [x] `proposed_start_time`
- [x] `proposed_match_name`
- [x] `message` (Freitext)
- [x] `status` (`pending`, `accepted`)
- [x] `created_by_user_id`
- [x] `created_at`, `updated_at`
### Indizes und Constraints
- [x] Index auf `friendly_match_shared (home_club_id, date, start_time)`.
- [x] Index auf `friendly_match_shared (guest_club_id, date, start_time)`.
- [x] Index auf `friendly_match_invitation (to_club_id, status, proposed_date)`.
- [x] Verhindern, dass ein Verein sich selbst einlaedt (`from_club_id != to_club_id`).
## Backend API
### Finder
- [x] `GET /api/friendly-matches/find`
- [x] Query-Parameter: `clubId`, `name`, `date`, `startTime`.
- [x] Exakt-Matches und Kandidaten mit `confidence` zurueckgeben.
### Einladungen
- [x] `POST /api/friendly-match-invitations/:clubId`
- [x] Payload: `toClubId`, `date`, `startTime`, `matchName`, `message`.
- [x] `GET /api/friendly-match-invitations/:clubId/incoming`
- [x] `GET /api/friendly-match-invitations/:clubId/outgoing`
- [x] `POST /api/friendly-match-invitations/:clubId/:invitationId/accept`
- [x] `POST /api/friendly-match-invitations/:clubId/:invitationId/decline`
### Shared Matches
- [x] `GET /api/friendly-matches/shared/:clubId`
- [x] `PUT /api/friendly-matches/shared/:clubId/:matchId`
- [x] `PATCH /api/friendly-matches/shared/:clubId/:matchId/players`
- [x] Optional: `DELETE /api/friendly-matches/shared/:clubId/:matchId`
## Rechte und Zugriff
- [x] Zugriff pruefen: User muss in beteiligtem Verein freigeschaltet sein.
- [x] Schreibrechte auf Shared-Match nur mit `schedule.write`.
- [x] Einladung annehmen/ablehnen nur fuer `to_club_id`.
## E-Mail-Versand
- [x] Empfaengerermittlung: `user_club` (approved=true) + `user.email` fuer Zielverein.
- [x] Neue Mailfunktion in `emailService` fuer Einladung.
- [x] Inhalt: einladender Verein, Matchdaten, Freitext, Aktion (annehmen/ablehnen in App).
- [x] Fehlerbehandlung: Einladung bleibt erhalten, falls Mailversand fehlschlaegt (mit Log).
## Matching-Logik (Name/Datum/Startzeit)
- [x] Normalisierung von Namen (trim, lowercase, Sonderzeichenbereinigung).
- [x] Datum exakt matchen.
- [x] Startzeit exakt matchen (MVP).
- [x] Teamnamen in beide Richtungen pruefen (home/guest vertauscht).
- [x] `confidence`-Stufen definieren (`exact`, `high`, `medium`).
## Socket-Sync
- [x] Neue Socket-Events definieren:
- [x] `friendly:invitation:created`
- [x] `friendly:invitation:accepted`
- [x] `friendly:invitation:declined`
- [x] `friendly:shared:match:updated`
- [x] `friendly:shared:match:deleted`
- [x] Bei Shared-Match-Aenderung an beide Club-Raeume senden (`club-<home>`, `club-<guest>`).
- [x] Frontend und Mobile auf neue Events subscriben.
## Frontend (Web)
- [x] Bereich "Einladungen" (Eingehend/Ausgehend) in Freundschaftsspielen.
- [ ] Dialog "Verein einladen": Zielverein aus bestehender Vereinsliste + Freitext.
- [x] Buttons fuer annehmen/ablehnen in eingehenden Einladungen.
- [ ] Finder-UI fuer Name/Datum/Startzeit.
- [x] Shared-Match-Kennzeichnung in Liste und Detaildialog.
- [x] Socket-Event-Handling fuer Echtzeitupdates beider Vereine.
## Mobile (Android + Shared)
- [x] API-Endpoints in `MatchesApi` erweitern.
- [x] State-Manager fuer Einladungen und Shared-Matches erweitern.
- [ ] UI fuer Einladungen (eingehend/ausgehend) einbauen.
- [ ] Finder-UI fuer Name/Datum/Startzeit einbauen.
- [ ] Shared-Match-Bearbeitung fuer beide Vereine synchronisieren.
- [x] Socket-Events fuer Friendly-Shared-Flow verarbeiten.
## Migration und Abwaertskompatibilitaet
- [ ] Bestehende `friendly_match`-Eintraege unveraendert weiter lesbar halten.
- [ ] Schrittweise Migration/Koexistenzstrategie dokumentieren.
- [ ] Optionales Mapping alter Einzel-Eintraege auf Shared-Matches definieren.
## Tests
### Backend
- [ ] Unit-Tests fuer Match-Finder.
- [ ] Unit-Tests fuer Einladung (create/accept/decline).
- [ ] Integrations-Tests fuer Shared-Match-CRUD.
- [ ] Berechtigungstests (falscher Verein, fehlende Rechte).
### Frontend/Mobile
- [ ] UI-Tests fuer Einladung erstellen/anzeigen/entscheiden.
- [ ] UI-Tests fuer Finder.
- [ ] Realtime-Tests mit zwei gleichzeitig eingeloggten Vereinen.
### End-to-End
- [ ] Verein A laedt Verein B ein.
- [ ] E-Mail wird versendet.
- [ ] Verein B nimmt an, Shared-Match entsteht.
- [ ] Aenderung in Verein A erscheint sofort in Verein B und umgekehrt.
- [ ] Ablehnung loescht Einladung vollstaendig.
## Rollout in Phasen
- [ ] Phase 1: Datenmodell + Read-APIs.
- [ ] Phase 2: Einladung + E-Mail.
- [ ] Phase 3: Accept/Decline + Shared-Match-Erzeugung.
- [ ] Phase 4: Shared-Match-Edit + Socket-Sync.
- [ ] Phase 5: Finder + UX-Polish + E2E-Hardening.
## Offene Entscheidungen
- [ ] Mehrfache parallele Einladungen fuer gleiche Daten erlauben oder verhindern?
- [ ] Match-Name als einzelnes Feld oder Home/Gast getrennt fuehren?
- [ ] Konfliktstrategie bei gleichzeitiger Bearbeitung (Last-Write-Wins vs. Versionierung)?
- [ ] Ablehnung immer hard delete oder auditierbar speichern?