docs: add design spec for team creation via UI
Brainstormed with the user: any logged-in user should be able to self-service create a team and becomes its captain, via a dialog on team-select. Team deletion/archiving is scoped out as a separate follow-up feature. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
113
docs/superpowers/specs/2026-08-03-team-erstellen-design.md
Normal file
113
docs/superpowers/specs/2026-08-03-team-erstellen-design.md
Normal file
@@ -0,0 +1,113 @@
|
|||||||
|
# Team erstellen über die UI
|
||||||
|
|
||||||
|
Status: approved
|
||||||
|
Datum: 2026-08-03
|
||||||
|
|
||||||
|
## Kontext
|
||||||
|
|
||||||
|
Teams können in TeamWallet heute nur über einen bestehenden Backend-Endpoint
|
||||||
|
(`POST /api/v1/teams`, `teams.controller.ts`) angelegt werden, der ausschließlich globalen Admins
|
||||||
|
vorbehalten ist (`@Roles([RoleEnum.admin])`). Im modernen Angular-Frontend
|
||||||
|
(`myteamwallet_frontend_modern`) existiert dafür keine UI: `core/team/teams-api.ts` hat keine
|
||||||
|
`createTeam`-Methode, und `features/team-select/team-select.html` zeigt einem User ohne Teams nur
|
||||||
|
den Hinweistext „Du bist noch keinem Team zugeordnet." ohne jede Handlungsoption.
|
||||||
|
|
||||||
|
Ziel: jeder eingeloggte User soll selbstständig ein neues Team gründen können und wird dabei
|
||||||
|
automatisch dessen Kapitän. Das schließt eine der offensichtlichsten Lücken im Produkt
|
||||||
|
(Selbstregistrierung eines Teams) und folgt demselben Muster, das an anderer Stelle im Code bereits
|
||||||
|
als fehlend dokumentiert ist (`docs/plans/admin-user-management.md` merkt an, dass Admins auch noch
|
||||||
|
keine User anlegen können — ein verwandtes, aber bewusst getrenntes Folge-Thema).
|
||||||
|
|
||||||
|
## Entscheidungen aus dem Brainstorming
|
||||||
|
|
||||||
|
- **Berechtigung**: jeder eingeloggte User (`RoleEnum.user`) darf ein Team erstellen, nicht nur
|
||||||
|
Admins. Der Guard auf `POST /teams` wird entsprechend gelockert.
|
||||||
|
- **Automatische Mitgliedschaft**: der Ersteller wird automatisch **Kapitän** (`captain`,
|
||||||
|
Rollen-ID 3) des neuen Teams — die niedrigste Rollen-ID, die `TeamAccessService.assertManager`
|
||||||
|
bereits als „Manager" behandelt (`id >= 3`). Es gibt kein explizites „Owner"-Konzept, `captain`
|
||||||
|
ist die naheliegende Top-Rolle für den Ersteller.
|
||||||
|
- **Formularumfang**: nur der Teamname wird abgefragt (`CreateTeamDTO` bleibt `{ name: string }`).
|
||||||
|
Alias wird weiterhin automatisch aus dem Timestamp generiert, alles Weitere ist später über die
|
||||||
|
Team-Einstellungen anpassbar.
|
||||||
|
- **UI-Einstiegspunkt**: Button „Team erstellen" ist in `team-select` dauerhaft sichtbar — auch wenn
|
||||||
|
der User bereits Teams hat (nicht nur im Leerzustand), um auch das Anlegen weiterer Teams (z. B.
|
||||||
|
zweite Mannschaft) zu ermöglichen.
|
||||||
|
- **UI-Pattern**: Dialog/Modal statt eigener Route, passend zum bestehenden `MatDialog`-Muster im
|
||||||
|
Repo (z. B. `features/team/more/penalties/penalties.ts`) und angemessen für ein einzelnes
|
||||||
|
Eingabefeld.
|
||||||
|
- **Out of Scope**: Team löschen/archivieren ist ein eigenständiges Folge-Feature (andere
|
||||||
|
Berechtigungen/Risiken, z. B. Umgang mit bestehenden Spielern/Transaktionen) und wird separat
|
||||||
|
geplant.
|
||||||
|
|
||||||
|
## Architektur / Komponenten
|
||||||
|
|
||||||
|
### 1. Backend: `teams.controller.ts`
|
||||||
|
|
||||||
|
Guard auf `POST /teams` von `@Roles([RoleEnum.admin])` auf `[RoleEnum.user, RoleEnum.admin]`
|
||||||
|
erweitern — Muster wie an anderen Stellen desselben Controllers (z. B. Zeilen 113-114, 122-123).
|
||||||
|
Der aufrufende User wird wie überall im Controller über `@Req() req` → `req.user.id` an den Service
|
||||||
|
durchgereicht.
|
||||||
|
|
||||||
|
### 2. Backend: `teams.service.ts#createNewTeam`
|
||||||
|
|
||||||
|
Aktuell (Zeile 166-178) wird nur `Team` + Default-`TeamSetting`s (`generateBasicTeamSettings`)
|
||||||
|
angelegt; es entsteht kein `Player`-Datensatz, der User bleibt kein Mitglied des neuen Teams.
|
||||||
|
|
||||||
|
Erweiterung: nach dem Speichern von Team und Settings zusätzlich einen `Player` erzeugen —
|
||||||
|
verknüpft mit dem aufrufenden `User` (`userId`) und `teamRole = captain` (per
|
||||||
|
`rolesRepository.findOneBy({ id: 3 })`). Vorgehen spiegelt die bestehende Join-Erzeugung in
|
||||||
|
`createNewPlayer()` (Zeilen 122-164), dort wird bereits `Player` inkl. `TeamRole`-Verknüpfung für
|
||||||
|
andere Spieler angelegt.
|
||||||
|
|
||||||
|
Audit-Logging (`this.logger.info({ event: 'team_create', ... })`) bleibt erhalten.
|
||||||
|
|
||||||
|
### 3. Backend: `dto/create-team.dto.ts`
|
||||||
|
|
||||||
|
Unverändert (`{ name: string }`).
|
||||||
|
|
||||||
|
### 4. Frontend: `core/team/teams-api.ts`
|
||||||
|
|
||||||
|
Neue Methode `createTeam(name: string)` → `POST /teams`, analog zu den bestehenden Methoden wie
|
||||||
|
`createPlayer`.
|
||||||
|
|
||||||
|
### 5. Frontend: neue Dialog-Komponente
|
||||||
|
|
||||||
|
Kleine Standalone-Komponente mit Reactive Form (`FormBuilder`/`ReactiveFormsModule`,
|
||||||
|
`MatFormFieldModule`), ein Pflichtfeld „Teamname" — nach dem Muster von
|
||||||
|
`features/team/members/members.ts` (Form-Aufbau) kombiniert mit dem `MatDialog`-Öffnungsmuster aus
|
||||||
|
`features/team/more/penalties/penalties.ts`.
|
||||||
|
|
||||||
|
### 6. Frontend: `features/team-select/team-select.ts` / `.html`
|
||||||
|
|
||||||
|
Button „Team erstellen" dauerhaft im Template ergänzen (nicht nur im Leerzustand-Block). Klick öffnet
|
||||||
|
den Dialog über `MatDialog`; bei erfolgreichem Abschluss `MyTeamsStore` neu laden und per Router
|
||||||
|
direkt in das neu erstellte Team navigieren.
|
||||||
|
|
||||||
|
## Fehlerbehandlung
|
||||||
|
|
||||||
|
- Serverseitige Validierungsfehler (z. B. leerer/zu langer Name) werden als Formularfehler im Dialog
|
||||||
|
angezeigt, der Dialog bleibt offen.
|
||||||
|
- Netzwerk-/Serverfehler laufen über den bestehenden Snackbar/Toast-Mechanismus des Repos, wie bei
|
||||||
|
anderen Create-Flows (z. B. `createPlayer`).
|
||||||
|
|
||||||
|
## Testing
|
||||||
|
|
||||||
|
- Backend (`teams.service.spec.ts`): `createNewTeam()` legt zusätzlich zu Team und Settings einen
|
||||||
|
`Player` mit `teamRole = captain` und Verknüpfung zum aufrufenden User an.
|
||||||
|
- Backend (`teams.controller.spec.ts` bzw. e2e): `POST /teams` ist für `RoleEnum.user` erlaubt (nicht
|
||||||
|
mehr nur für `RoleEnum.admin`).
|
||||||
|
- Frontend: Komponententest für den neuen Dialog (analog `penalties.spec.ts`) — Formularvalidierung,
|
||||||
|
Aufruf von `teamsApi.createTeam`.
|
||||||
|
- Frontend: Test, dass `team-select` nach erfolgreicher Erstellung `MyTeamsStore` neu lädt und in das
|
||||||
|
neue Team navigiert.
|
||||||
|
- Manuelle Verifikation: als normaler User (nicht Admin) über `team-select` ein Team anlegen —
|
||||||
|
Aufruf sollte gelingen, User landet automatisch als Kapitän im neuen Team; Swagger-Aufruf von
|
||||||
|
`POST /teams` als normaler User bestätigt den gelockerten Guard.
|
||||||
|
|
||||||
|
## Out of Scope
|
||||||
|
|
||||||
|
- Team löschen/archivieren (eigenes Folge-Feature).
|
||||||
|
- Auswahl der initialen Rolle des Erstellers (immer `captain`, keine Wahlmöglichkeit).
|
||||||
|
- Eigene Alias-Vergabe durch den User (weiterhin automatisch generiert).
|
||||||
|
- Selbstständiges Anlegen von Usern durch Admins (verwandte, aber separate Lücke, siehe
|
||||||
|
`docs/plans/admin-user-management.md`).
|
||||||
Reference in New Issue
Block a user