feat(backend): Wahl-Phasen, Verantwortliche-Invites, Auth fixes

- New VerantwortlicheInvite model: LT-issued invites so a person can
  register as Gemeinde Verantwortliche(r) for a specific Gemeinde,
  skipping the self-registration approval step.
- Wahl/Workshop/Teilnehmer gain phase support (phasenAnzahl,
  beschreibung), mirroring the WP plugin's multi-phase elections.
  Teilnehmer unique constraint now scoped per phase.
- Auth: team login + guest auth adjustments, spec coverage.
- sync.service.ts: register VerantwortlicheInvite as a synced model.
- wahl.service.ts: submitTeilnehmer updated for the new phase-scoped
  unique key.
- client: login/home screen rework, new theme.dart, FCM web tweaks.
- .gitignore: ignore .DS_Store.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-09-11 18:00:18 +02:00
co-authored by Claude Sonnet 5
parent 72367637aa
commit 8af927c0f2
21 changed files with 844 additions and 175 deletions
+8
View File
@@ -64,6 +64,14 @@ Teamer login only).
`AppState` calls `window.kcGetPushToken()` and registers the token
(`POST /push/register`). Inert until `apiKey` / `appId` / `vapidKey` are
filled into both files (see the `REPLACE_ME` placeholders).
- **Nutzungsanalysen (web)** — `web/index.html` also initialises Google
Analytics for Firebase (`firebase.analytics()`) on every page load,
independent of login/push. Automatically logs `page_view` /
`session_start` / `first_visit`; visible in the Firebase Console under
**Analytics** (data can take a few hours to first appear, and won't show
on `localhost` — Analytics filters out non-public hostnames by default).
Screen-level events inside the Flutter SPA aren't tracked without further
instrumentation, but overall reach/users/sessions are.
- **Workshop-Wahl** (`lib/screens/wahl_screen.dart`, guests) — two tabs:
*Wünsche* (`GET /wahl/guest/overview`, tap workshops in order, max 3,
`POST /wahl/:id/teilnehmer`) and *Ergebnis* (`GET /wahl/guest/results`