Files
KC-APP/client/app
linusandClaude Sonnet 5 a4ae7549ae feat: LT Wahl controls (open/close, Force-Zuteilung, CSV) + file upload
Backend:
- PATCH /api/wahl/:wahlId (isOpen) to open/close a Wahl.
- GET /api/wahl/:wahlId/teilnehmer (LT): participants with their priorities
  and any existing Force-Zuteilung.
- Verified against local Postgres: PATCH toggles isOpen, teilnehmer list
  returns, CSV export works. (A stale dev server on :3000 masked this at
  first — real routes are fine.)

Client (client/app/):
- Wahl detail: open/close switch, participant list with a "Zuteilen"
  (Force-Zuteilung) action, CSV export via a browser download
  (browser.downloadText).
- files_admin_screen.dart: LT file upload — browser.pickFile() +
  visibility picker -> multipart POST /api/files/:kcId; list existing
  files. Reachable from KcDetailScreen.
- browser_web.dart gains pickFile()/downloadText() (native <input file> +
  Blob), with throwing stubs for the VM.
- Dropped the file_picker package again (heavy transitive deps, and the
  native web input is enough); disk on this box is nearly full.

flutter analyze/test/build web green; backend npm test 56.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-10 10:19:35 +02:00
..

KC-App client (Flutter)

Single Flutter codebase for the KC-App platform. Web is the only target enabled so far (flutter config --enable-web); Android/iOS/desktop can be added later with flutter create --platforms=... in this directory — the lib/ code is platform-agnostic.

Run

The Authentik redirect URI is http://localhost:3000/v1/auth/callback, so the app must be reached on :3000 — i.e. served by the backend, not flutter run's own dev server. Build it and let NestJS serve it:

flutter pub get
flutter build web            # backend serves client/app/build/web at /
# then run the backend (npm run start:dev in ../../backend) and open :3000

For pure UI work without the OIDC flow, flutter run -d chrome --dart-define=API_BASE=http://localhost:3000/api still works (guest / local Teamer login only).

Dart-defines

define default
API_BASE http://localhost:3000/api
OIDC_ISSUER https://sso.konfi-castle.com/application/o/konfi-castle-app/
OIDC_CLIENT_ID the konfi-castle public client id
OIDC_REDIRECT_URI http://localhost:3000/v1/auth/callback

What's implemented

  • Login (lib/screens/login_screen.dart) — three tabs:
    • Konfi / Gast: KC invite code + first/last name → POST /auth/guest.
    • Leitungsteam / Verantwortliche: "Mit Konfi-Castle-ID anmelden" starts the Authentik Authorization Code + PKCE flow (lib/oidc.dart); below it, the local Gemeinde-Teamer password form (POST /auth/team-login).
    • Einladung: redeem a Teamer invite token → POST /auth/teamer/register.
  • OIDC: discovery + S256 challenge, ?code= handled on bootstrap, access + refresh token persisted (shared_preferences / localStorage), expired access token refreshed on restart. GET /auth/me resolves the role.
  • Home (lib/screens/home_screen.dart) — identity card + navigation.
  • Verwaltung (lib/screens/admin_screen.dart, Leitungsteam only) — list/create KCs; per KC:
    • Gemeinden (list/create); each opens Teamer-Verwaltung (teamer_admin_screen.dart): local Teamer accounts + group-link / email invites.
    • Workshop-Wahlen (wahl_admin_screen.dart): create Wahlen, add workshops, run the assignment, view the result table.
    • pending Verantwortlichen self-registrations (approve / reject).
  • Als Verantwortliche/r registrieren (verantwortliche_register_screen.dart) — shown on the home screen to a logged-in Authentik user without a membership: enter a KC invite code, pick a Gemeinde, submit; a Leitungsteam member then approves.
  • 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 — PENDING / ASSIGNED with workshop + wish rank / UNASSIGNED).
  • Dateien (lib/screens/files_screen.dart) — GET /files/:kcId, filtered server-side by the caller's visibility tier.
  • Chat (lib/screens/chat_screen.dart + lib/chat_socket.dart) — channel list, REST history, then a live /chat WebSocket connection (chat:join / chat:send / chat:message) with a compose bar.

Architecture

  • lib/api.dartApi (thin REST wrapper + models) and AppState (ChangeNotifier: session, login/logout, token persistence).
  • lib/oidc.dart — Authentik PKCE flow. Browser-only bits (sessionStorage, redirect, window.location) sit behind a conditional import (browser.dartbrowser_web.dart / browser_stub.dart) so flutter test compiles on the Dart VM.
  • lib/chat_socket.dart/chat WebSocket wrapper.
  • lib/main.dartAppScope (an InheritedNotifier<AppState>) exposes AppScope.of(context); _AuthGate switches Login/Home. No third-party state-management package.