first commit

This commit is contained in:
Bastian Wagner
2026-07-31 21:02:47 +02:00
commit 6bea4f766a
512 changed files with 64459 additions and 0 deletions

View File

@@ -0,0 +1,113 @@
# Public Team Sharing Implementation Plan
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
**Goal:** Add revocable public team links with controlled management, safe public DTOs, sharing, and QR presentation.
**Architecture:** The backend stores a disabled-by-default public-access flag and a random token on each team. Authenticated management endpoints enforce team membership and a minimum team role, while a separate public controller returns deliberately shaped responses and verifies player ownership. The modern Angular frontend consumes these endpoints through focused API services and presents management under “Mehr”.
**Tech Stack:** NestJS 9, TypeORM 0.3, Jest, Angular 21 standalone components/signals, Angular Material, Vitest, `qrcode`.
## Global Constraints
- Modify only `myteamwallet_backend` and `myteamwallet_frontend_modern`; leave the legacy frontend untouched.
- New teams are private by default; disabled or invalid links return HTTP 404.
- Team roles 35 and global admins may mutate sharing; authenticated team members may read and share an enabled link.
- Public responses expose only the agreed team, active-player, penalty, and transaction fields.
- Follow red-green-refactor for every behavior change.
---
### Task 1: Backend persistence and access policy
**Files:**
- Modify: `myteamwallet_backend/src/teams/entities/team.entity.ts`
- Create: `myteamwallet_backend/src/database/migrations/1785513600000-AddTeamPublicAccess.ts`
- Create: `myteamwallet_backend/src/teams/team-access.service.ts`
- Test: `myteamwallet_backend/src/teams/team-access.service.spec.ts`
**Interfaces:**
- Produce `Team.publicAccessEnabled`, `Team.publicAccessToken`, `TeamAccessService.assertMember(userId, teamId)`, and `TeamAccessService.assertManager(userId, teamId)`.
- [ ] Write tests proving admins bypass membership, members can read, roles 35 can manage, and lower/non-members receive `ForbiddenException`.
- [ ] Run the focused Jest test and confirm failure because the service and fields do not exist.
- [ ] Implement the entity fields, reversible migration, and repository-backed access service using the users highest role in the team.
- [ ] Run the focused test and backend build.
- [ ] Commit the backend persistence/policy change.
### Task 2: Backend management and public APIs
**Files:**
- Create: `myteamwallet_backend/src/teams/dto/public-access.dto.ts`
- Create: `myteamwallet_backend/src/teams/public-teams.controller.ts`
- Modify: `myteamwallet_backend/src/teams/teams.controller.ts`, `teams.service.ts`, `teams.module.ts`
- Modify: `myteamwallet_backend/src/penalty/penalty.controller.ts`
- Test: `myteamwallet_backend/src/teams/teams.service.spec.ts`, `teams.controller.spec.ts`
**Interfaces:**
- Produce `GET/PATCH /teams/:id/public-access`, `POST /teams/:id/public-access/rotate`, `GET /public/teams/:token`, and `GET /public/teams/:token/players/:playerId/transactions`.
- Public team response contains `name`, `balance`, `outstanding`, active players (`id`, names, balance), and penalties (`id`, description, amount`).
- [ ] Write failing service/controller tests for activation, stable reactivation, rotation, disabled/unknown tokens, cross-team player rejection, 20-item ordering, and response whitelisting.
- [ ] Run focused backend tests and verify expected failures.
- [ ] Implement DTOs, token generation with `randomBytes(32)`, management endpoints, safe public queries, and module wiring.
- [ ] Remove the old anonymous alias handlers and require authentication for direct team-penalty loading.
- [ ] Run focused tests, the full backend suite, and backend build.
- [ ] Commit the completed backend API.
### Task 3: Frontend API contracts and routes
**Files:**
- Create: `myteamwallet_frontend_modern/src/app/models/public-access.model.ts`
- Create: `myteamwallet_frontend_modern/src/app/core/team/public-access-api.ts`
- Modify: `myteamwallet_frontend_modern/src/app/core/team/public-team-api.ts`, `app.routes.ts`
- Test: matching API and route specs.
**Interfaces:**
- Produce typed status, public-team, and public-player-history responses plus API methods for status, enable/disable, rotate, public overview, and public history.
- [ ] Write failing HttpTestingController and router tests for all new URLs and payloads.
- [ ] Run focused Vitest specs and confirm expected failures.
- [ ] Implement the typed API clients and token-based route contract.
- [ ] Run focused specs and commit the frontend API layer.
### Task 4: Sharing management UI
**Files:**
- Create: `myteamwallet_frontend_modern/src/app/features/team/more/public-access/*`
- Modify: `myteamwallet_frontend_modern/src/app/features/team/more/more.html`, `app.routes.ts`, `package.json`
- Test: `public-access.spec.ts`, affected route/more specs.
**Interfaces:**
- Consume `PublicAccessApi`; render state, role-gated mutations, copy/share, QR, preview, and rotate confirmation.
- [ ] Install `qrcode` and its types, then write failing component tests for visibility, permissions, activation, disable, rotation confirmation, copy, native share, and fallback behavior.
- [ ] Run focused tests and verify failures.
- [ ] Implement the standalone Material component, responsive styles, accessible QR alternative, and “Mehr” navigation entry.
- [ ] Run focused tests and commit the management UI.
### Task 5: Public pages and end-to-end verification
**Files:**
- Modify: `myteamwallet_frontend_modern/src/app/features/public-team/public-team.*`, `public-player.*`
- Test: both public component specs.
**Interfaces:**
- Consume combined public overview and player-history DTOs; do not call the authenticated penalty API.
- [ ] Write failing tests proving combined response rendering, player identity rendering, invalid-token state, and absence of the separate penalty request.
- [ ] Run focused tests and verify failures.
- [ ] Update both public pages and models to use the token APIs.
- [ ] Run all frontend tests and production build; run all backend tests and build.
- [ ] Review both diffs for public-data leakage and confirm the legacy frontend remains untouched.
- [ ] Commit final integration fixes.

View File

@@ -0,0 +1,132 @@
# TeamWallet Frontend Modern — Design
Datum: 2026-07-31
Status: Genehmigt (Brainstorming abgeschlossen)
## 1. Kontext & Ziel
`myteamwallet_frontend_modern` ist ein frisch mit `ng new` erzeugtes Angular-21-Projekt (Standalone, esbuild, Vitest) ohne fachlichen Inhalt. Es soll die bestehende Angular-18-App (`myteamwallet_frontend`) vollständig ersetzen, die gegen das NestJS-Backend (`myteamwallet_backend`) arbeitet — eine Vereinskassen-App für Sportteams (TeamWallet).
Die neue App wird komplett neu gebaut. Backend-Logik/Datenmodell bleiben unverändert Referenz; visuelle Gestaltung und Informationsarchitektur werden bewusst neu gedacht, die etablierten Kern-User-Flows bleiben erhalten, damit bestehende Vereinsmitglieder sich nicht neu einarbeiten müssen.
## 2. Scope
**In Scope (Feature-Parität zum alten Frontend):**
- Auth: Login, Registrierung über Einladungslink (Token verknüpft neuen Account mit bestehendem Spieler), Passwort vergessen/zurücksetzen
- Team-Auswahl bei mehreren Teams/Spielern eines Nutzers
- Team-Übersicht mit Saldo und Aktivitäts-Feed
- Mitgliederverwaltung: Spielerliste mit Salden, neuen Spieler anlegen, Spielerdetail/Transaktionshistorie
- Buchungen: Spieler-Transaktionen (Mehrfachauswahl + Betrag-Split, Bestätigung ab 300 €), Team-Wallet-Transaktionen, Storno bestehender Buchungen
- Strafenkatalog pro Team (anzeigen, für berechtigte Rollen anlegen)
- Einladungslinks generieren und kopieren
- Öffentliche, nicht angemeldete Team-Ansicht über Alias-Link (Read-only Übersicht + Spieler-Transaktionshistorie + Strafenkatalog)
- Rollenbasierte Sichtbarkeit von Aktionen (globale `Role` admin/user, teamspezifische `TeamRole` player/scnd_treasurer/captain/treasurer/coach)
- PWA (installierbar, Service Worker)
**Explizit nicht in Scope:**
- Admin-UI für Team-/Userverwaltung (gab es im alten Frontend auch nicht, Backend-Endpunkte existieren, aber keine Oberfläche geplant)
- Mehrsprachigkeit/i18n-Layer (Deutsch fest im Code, kein ngx-translate/Backend-Translate-Anbindung)
- E2E-Test-Framework (nur Unit-/Component-Tests mit Vitest)
## 3. Ähnlichkeit zum alten Frontend
- **Optik:** komplett neu, eigenständiges Theming (siehe Abschnitt 8), keine Übernahme des alten Material-Blue-Looks.
- **Struktur/Informationsarchitektur:** bewusst neu gedacht (siehe Abschnitt 6) — statt einer großen Team-Detail-Seite mit vielen Dialogen eine App-Shell mit Bottom-Navigation und Team-Switcher.
- **User-Führung/Flows:** bleiben inhaltlich erhalten — dieselben Abläufe für Login, Einladung/Registrierung, Buchungen, Storno, öffentlichen Team-Link. Nur die Interaktionsdetails (Navigation, Layout) werden verbessert.
## 4. Architektur & Ordnerstruktur
Standalone-Components durchgängig, kein NgModule. Guards und Interceptors funktional (`CanActivateFn`, `HttpInterceptorFn`). App-weiter State über injizierbare Signal-Services (kein NgRx). Datenladen signal-/effect-basiert mit Skeleton-States (kein Router-Resolver-basiertes Blocking-Loading), damit die App auf mobilen Verbindungen unterwegs responsiv bleibt.
```
src/app/
core/
auth/ auth.store.ts (Signal-Store: currentUser, isLoggedIn), auth-api.service.ts,
auth.guard.ts (CanActivateFn), role.guard.ts (TeamRole-Check)
http/ auth.interceptor.ts (Bearer-Token), error.interceptor.ts (401 → Logout+Snackbar)
layout/ app-shell.component.ts (Header mit Team-Switcher, Bottom-Nav), not-found.component.ts
models/ user.model.ts, team.model.ts, player.model.ts, transaction.model.ts,
penalty.model.ts, team-role.enum.ts, transaction-type.enum.ts
features/
auth/ login/, register/, forgot-password/, reset-password/
team-select/ team-select.component.ts (Team-Auswahl bei >1 Team), teams.store.ts (eigene Teams-Liste)
team/
overview/ team-overview.component.ts (Saldo, Activity-Feed)
members/ members-list.component.ts, player-card/, player-detail/, create-player-dialog/
cashbox/ cashbox.component.ts, new-transaction-dialog/, team-transaction-dialog/, recent-bookings-list/
more/ more.component.ts, penalties/, invite/
team.store.ts, teams-api.service.ts, transactions-api.service.ts
public-team/ public-team.component.ts, public-team-shell/, public-team-api.service.ts
penalty/ penalty-api.service.ts (gemeinsam von team/more/penalties & public-team genutzt)
shared/ui/ confirm-dialog/, empty-state/, skeleton/, currency.pipe.ts
app.routes.ts, app.config.ts
```
## 5. Datenmodell (TS-Interfaces, gespiegelt vom Backend)
```ts
interface User { id: string; email: string; firstName: string; lastName: string; role: Role; status: Status; photo?: { id: string; path: string }; }
interface Team { id: string; name: string; alias: string; balance: number; }
interface Player { id: string; firstName: string; lastName: string; teamRole: TeamRole; balance: number; active: boolean; user?: User; }
enum TeamRole { Player = 1, ScndTreasurer = 2, Captain = 3, Treasurer = 4, Coach = 5 }
// Berechtigungshelfer (reine Funktionen in models/):
// canBook(role: TeamRole) => role >= TeamRole.ScndTreasurer
// canInvite(role: TeamRole) => role > TeamRole.ScndTreasurer
interface Transaction { id: string; note: string; date: string; amount: number; type: TransactionType; createdAt: string; }
interface TeamWalletTransaction { id: string; note: string; date: string; amount: number; type: TeamWalletTransactionType; createdAt: string; }
interface Penalty { id: string; teamId: string; description: string; amount: number; }
```
## 6. Routing-Map
| Route | Zugriff | Zeigt |
|---|---|---|
| `/auth/login` | öffentlich | Login |
| `/auth/register?token=` | öffentlich | Registrierung, liest Invite-Token via `auth/verify-invite`, verknüpft via `linkPlayerId` |
| `/auth/forgot-password` | öffentlich | Passwort vergessen |
| `/auth/reset-password/:hash` | öffentlich | Neues Passwort setzen |
| `/team-select` | AuthGuard | Team-Auswahl — nur erreichbar/angezeigt, wenn Nutzer >1 Team hat; bei genau einem Team automatischer Redirect zu dessen Übersicht |
| `/team/:id/overview` | AuthGuard | Saldo + chronologischer Activity-Feed (Spieler- & Team-Buchungen gemischt) |
| `/team/:id/members` | AuthGuard | Mitgliederliste mit Salden, Spieler anlegen |
| `/team/:id/members/:playerId` | AuthGuard | Spielerdetail/Transaktionshistorie |
| `/team/:id/cashbox` | AuthGuard | Buchungen erstellen (Spieler-Mehrfachauswahl+Split, Team-Buchung), Storno, letzte Buchungen |
| `/team/:id/more` | AuthGuard | Einstiegspunkt: Strafenkatalog, Einladen, Profil/Logout |
| `/team/:id/more/penalties` | AuthGuard | Strafenkatalog verwalten |
| `/team/:id/more/invite` | AuthGuard | Einladungslink generieren & kopieren (`POST auth/invite`) |
| `/t/:alias` | öffentlich | Public Read-Only-Übersicht (`GET teams/:alias`) |
| `/t/:alias/:playerId` | öffentlich | Public Spieler-Transaktionshistorie (`GET teams/:alias/:user`) |
| `/**` | — | NotFound |
`/` leitet abhängig von `AuthStore.isLoggedIn()` zu `/team-select` (bzw. direkt zum einzigen Team) oder `/auth/login` weiter.
Bottom-Nav (Übersicht/Mitglieder/Kasse/Mehr) und Team-Switcher im Header sind nur innerhalb `/team/:id/*` sichtbar. Der Team-Switcher wechselt die aktive `:id` in der Route und aktualisiert `TeamStore`. Die öffentliche Ansicht (`/t/:alias*`) nutzt eine eigene, schlanke Shell ohne Bottom-Nav.
## 7. Features im Detail
- **Auth**: Login/Register/Forgot/Reset wie im Backend-Flow vorgesehen. Register liest `?token=` aus der URL, ruft `verify-invite` auf, zeigt Team-/Spielername zur Bestätigung, registriert mit `linkPlayerId`.
- **Team-Select**: Kartenliste der Team-/Spieler-Zuordnungen des Nutzers (`GET /users/:id/teams`), Klick → Team-Übersicht. Bei genau einem Eintrag übersprungen.
- **Übersicht**: Prominenter Team-Saldo, darunter Activity-Feed der letzten Buchungen (Spieler- und Team-Wallet-Transaktionen chronologisch gemischt).
- **Mitglieder**: Spielerliste (Karten, sortier-/filterbar) mit Saldo und TeamRole-Badge, "+"-Button zum Anlegen (sichtbar je nach Berechtigung), Klick → Spielerdetail mit Transaktionshistorie.
- **Kasse**: Spieler-Buchung (Mehrfachauswahl + Split-Betrag, Bestätigungsdialog ab 300 €) und Team-Buchung, darunter Liste letzter Buchungen mit Storno-Aktion (`POST transactions/:id/reverse`).
- **Mehr**: Strafenkatalog (Liste, Anlegen für berechtigte Rollen via `POST /penalty`), Einladungslink erzeugen, Profil bearbeiten (`PATCH auth/me`), Logout.
- **Public-Team**: Sortierbare/filterbare Tabelle aller Spieler mit Saldo, Klick → Transaktionshistorie, eigener Bereich für den Strafenkatalog (read-only).
Sichtbarkeit/Aktivierung von Buchungs- und Einladungs-Aktionen wird durchgehend über `canBook`/`canInvite` (aus `TeamRole` des aktuellen Spielers im Team) gesteuert.
## 8. Tech-Stack & Design-Stil
- Angular 21, durchgängig Standalone Components
- Angular Material 21 als UI-Basis, eigenes Theming: frische/sportliche Akzentfarbe, große gut lesbare Saldo-Zahlen, klare Kontraste — bewusst kein Banking-Look
- PWA via `ng add @angular/pwa` (Manifest + Service Worker)
- State: native Angular Signals in injizierbaren Store-Services, kein NgRx
- HTTP: `provideHttpClient` mit funktionalen Interceptors
- Kein i18n-Layer, Texte direkt in Templates (Deutsch)
- Tests: Vitest (bereits vorhanden durch `ng new`), keine E2E-Suite im Scope
## 9. Fehlerbehandlung
Funktionaler `error.interceptor.ts`: bei 401 → `AuthStore` leeren, Redirect zu `/auth/login`, Snackbar „Sitzung abgelaufen". Bei sonstigen 4xx mit Backend-Fehlermeldung → Snackbar mit dieser Message. Bei 5xx → generische Fehlermeldung. Formulare (Reactive Forms + Validators) zeigen Feldfehler inline, kein globaler Error-State nötig.
## 10. Testing
Vitest-Unit-/Component-Tests für: Berechtigungslogik (`canBook`/`canInvite`), Split-Betrag-Berechnung im Buchungsdialog, Signal-Stores (Auth/Team/Teams) mit gemocktem `HttpClient` über `provideHttpClientTesting`. Kein E2E-Framework im Scope; kann bei Bedarf später (z.B. Playwright) ergänzt werden.