# 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: ```bash 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, open/close them, add workshops, list participants + Force-Zuteilung, run the assignment, view the result table, export the CSV (browser download). - **Dateien** (`files_admin_screen.dart`): upload with a visibility tier (native ``), list. Needs a configured Nextcloud/S3 on the backend or the upload returns 500. - 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.dart` — `Api` (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.dart` → `browser_web.dart` / `browser_stub.dart`) so `flutter test` compiles on the Dart VM. - `lib/chat_socket.dart` — `/chat` WebSocket wrapper. - `lib/main.dart` — `AppScope` (an `InheritedNotifier`) exposes `AppScope.of(context)`; `_AuthGate` switches Login/Home. No third-party state-management package.