Files
teamwallet/myteamwallet_frontend_modern/docs/superpowers/specs/2026-07-31-teamwallet-frontend-modern-design.md
Bastian Wagner 6bea4f766a first commit
2026-07-31 21:02:47 +02:00

9.9 KiB

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)

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.