feat(client): LT Wahl admin, Teamer admin, Verantwortlichen self-registration
New screens (client/app/lib/screens/): - wahl_admin_screen.dart — per KC: list/create Wahlen; per Wahl: add workshops, run the assignment (POST /wahl/:id/zuteilung/run), view the result table. - teamer_admin_screen.dart — per Gemeinde: list/create local Teamer accounts, create group-link or per-email invites (shows the token). - verantwortliche_register_screen.dart — enter a KC invite code (GET /onboarding/kc/:code), pick a Gemeinde, submit (POST /onboarding/verantwortliche); shown on the home screen to a logged-in Authentik user who has no membership yet. - ui.dart — shared toast / ErrorText / SectionHeader / promptText. KcDetailScreen now links to Wahl admin and each Gemeinde row opens Teamer admin. Backend: widen the wahl + files LT routes to AuthGuard(['authentik','team']) for consistency with the other LT controllers. Rebrand web/index.html + manifest from "kc_app" to "KC-App". Verified against local Postgres with an isLeitungsteam team token: create KC/Gemeinde/Wahl/Workshop, run Zuteilung, create Teamer + invite, resolve an invite code. flutter analyze/test/build web green; backend npm test 56. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -32,35 +32,35 @@ export class WahlController {
|
||||
/// Wahl-/Workshop-/Force-Zuteilung-Verwaltung ist ausschließlich Sache des
|
||||
/// Leitungsteams (global über alle KCs, siehe RolesGuard).
|
||||
@Post()
|
||||
@UseGuards(AuthGuard('authentik'), RolesGuard)
|
||||
@UseGuards(AuthGuard(['authentik', 'team']), RolesGuard)
|
||||
@Roles(Role.LEITUNGSTEAM)
|
||||
createWahl(@Body() dto: CreateWahlDto) {
|
||||
return this.wahl.createWahl(dto.kcId, dto.name, dto.datumsSchluessel, dto.teil);
|
||||
}
|
||||
|
||||
@Get()
|
||||
@UseGuards(AuthGuard('authentik'), RolesGuard)
|
||||
@UseGuards(AuthGuard(['authentik', 'team']), RolesGuard)
|
||||
@Roles(Role.LEITUNGSTEAM)
|
||||
listWahlen(@Query('kcId') kcId: string) {
|
||||
return this.wahl.listWahlen(kcId);
|
||||
}
|
||||
|
||||
@Post(':wahlId/workshops')
|
||||
@UseGuards(AuthGuard('authentik'), RolesGuard)
|
||||
@UseGuards(AuthGuard(['authentik', 'team']), RolesGuard)
|
||||
@Roles(Role.LEITUNGSTEAM)
|
||||
createWorkshop(@Param('wahlId') wahlId: string, @Body() dto: CreateWorkshopDto) {
|
||||
return this.wahl.createWorkshop(wahlId, dto.name, dto.kapazitaet, dto.minTeilnehmer);
|
||||
}
|
||||
|
||||
@Get(':wahlId/workshops')
|
||||
@UseGuards(AuthGuard('authentik'), RolesGuard)
|
||||
@UseGuards(AuthGuard(['authentik', 'team']), RolesGuard)
|
||||
@Roles(Role.LEITUNGSTEAM)
|
||||
listWorkshops(@Param('wahlId') wahlId: string) {
|
||||
return this.wahl.listWorkshops(wahlId);
|
||||
}
|
||||
|
||||
@Post(':wahlId/force-zuteilung')
|
||||
@UseGuards(AuthGuard('authentik'), RolesGuard)
|
||||
@UseGuards(AuthGuard(['authentik', 'team']), RolesGuard)
|
||||
@Roles(Role.LEITUNGSTEAM)
|
||||
createForceZuteilung(
|
||||
@Param('wahlId') wahlId: string,
|
||||
@@ -99,21 +99,21 @@ export class WahlController {
|
||||
}
|
||||
|
||||
@Post(':wahlId/zuteilung/run')
|
||||
@UseGuards(AuthGuard('authentik'), RolesGuard)
|
||||
@UseGuards(AuthGuard(['authentik', 'team']), RolesGuard)
|
||||
@Roles(Role.LEITUNGSTEAM)
|
||||
runZuteilung(@Param('wahlId') wahlId: string) {
|
||||
return this.zuteilung.run(wahlId);
|
||||
}
|
||||
|
||||
@Get(':wahlId/zuteilung')
|
||||
@UseGuards(AuthGuard('authentik'), RolesGuard)
|
||||
@UseGuards(AuthGuard(['authentik', 'team']), RolesGuard)
|
||||
@Roles(Role.LEITUNGSTEAM)
|
||||
getZuteilung(@Param('wahlId') wahlId: string) {
|
||||
return this.zuteilung.getResults(wahlId);
|
||||
}
|
||||
|
||||
@Get(':wahlId/zuteilung/csv')
|
||||
@UseGuards(AuthGuard('authentik'), RolesGuard)
|
||||
@UseGuards(AuthGuard(['authentik', 'team']), RolesGuard)
|
||||
@Roles(Role.LEITUNGSTEAM)
|
||||
async exportCsv(@Param('wahlId') wahlId: string, @Res() res: Response) {
|
||||
const csv = await this.zuteilung.exportCsv(wahlId);
|
||||
|
||||
Reference in New Issue
Block a user