GET /api/wahl/guest/overview (guest JWT) returns the open Wahlen for the guest's KC, each with its workshops and the guest's own current priorities (null if not yet submitted) — everything the client needs to render the Wahl form without any LT-only endpoint. Verified end to end against a local Postgres (guest login -> overview -> submit -> re-fetch). prisma/seed-dev.js: minimal dev fixture (one KC "DEV123" + Gemeinde + open Wahl with three workshops). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
117 lines
3.8 KiB
TypeScript
117 lines
3.8 KiB
TypeScript
import {
|
|
Body,
|
|
Controller,
|
|
Get,
|
|
Param,
|
|
Post,
|
|
Query,
|
|
Req,
|
|
Res,
|
|
UseGuards,
|
|
} from '@nestjs/common';
|
|
import { AuthGuard } from '@nestjs/passport';
|
|
import { Response } from 'express';
|
|
import { WahlService } from './wahl.service';
|
|
import { ZuteilungService } from './zuteilung.service';
|
|
import { CreateWahlDto } from './dto/create-wahl.dto';
|
|
import { CreateWorkshopDto } from './dto/create-workshop.dto';
|
|
import { SubmitTeilnehmerDto } from './dto/submit-teilnehmer.dto';
|
|
import { CreateForceZuteilungDto } from './dto/create-force-zuteilung.dto';
|
|
import { Roles } from '../common/roles.decorator';
|
|
import { RolesGuard } from '../common/roles.guard';
|
|
import { Role } from '../common/role.enum';
|
|
import { GuestAuthenticatedRequest } from '../auth/authenticated-request';
|
|
|
|
@Controller('wahl')
|
|
export class WahlController {
|
|
constructor(
|
|
private readonly wahl: WahlService,
|
|
private readonly zuteilung: ZuteilungService,
|
|
) {}
|
|
|
|
/// Wahl-/Workshop-/Force-Zuteilung-Verwaltung ist ausschließlich Sache des
|
|
/// Leitungsteams (global über alle KCs, siehe RolesGuard).
|
|
@Post()
|
|
@UseGuards(AuthGuard('authentik'), 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)
|
|
@Roles(Role.LEITUNGSTEAM)
|
|
listWahlen(@Query('kcId') kcId: string) {
|
|
return this.wahl.listWahlen(kcId);
|
|
}
|
|
|
|
@Post(':wahlId/workshops')
|
|
@UseGuards(AuthGuard('authentik'), 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)
|
|
@Roles(Role.LEITUNGSTEAM)
|
|
listWorkshops(@Param('wahlId') wahlId: string) {
|
|
return this.wahl.listWorkshops(wahlId);
|
|
}
|
|
|
|
@Post(':wahlId/force-zuteilung')
|
|
@UseGuards(AuthGuard('authentik'), RolesGuard)
|
|
@Roles(Role.LEITUNGSTEAM)
|
|
createForceZuteilung(
|
|
@Param('wahlId') wahlId: string,
|
|
@Body() dto: CreateForceZuteilungDto,
|
|
) {
|
|
return this.wahl.createForceZuteilung(wahlId, dto.teilnehmerId, dto.workshopId);
|
|
}
|
|
|
|
/// Everything a guest needs to fill in the Wahl: open Wahlen for their KC,
|
|
/// each with its workshops and the guest's own current priorities (if any).
|
|
@Get('guest/overview')
|
|
@UseGuards(AuthGuard('guest'))
|
|
guestOverview(@Req() req: GuestAuthenticatedRequest) {
|
|
const guest = req.user!;
|
|
return this.wahl.guestOverview(guest.kcId, guest.guestId);
|
|
}
|
|
|
|
/// Guests submit their own workshop preferences (guest JWT, not Authentik).
|
|
@Post(':wahlId/teilnehmer')
|
|
@UseGuards(AuthGuard('guest'))
|
|
submitTeilnehmer(
|
|
@Param('wahlId') wahlId: string,
|
|
@Body() dto: SubmitTeilnehmerDto,
|
|
@Req() req: GuestAuthenticatedRequest,
|
|
) {
|
|
const guest = req.user!;
|
|
return this.wahl.submitTeilnehmer(wahlId, guest.guestId, guest.kcId, dto.prioritaeten);
|
|
}
|
|
|
|
@Post(':wahlId/zuteilung/run')
|
|
@UseGuards(AuthGuard('authentik'), RolesGuard)
|
|
@Roles(Role.LEITUNGSTEAM)
|
|
runZuteilung(@Param('wahlId') wahlId: string) {
|
|
return this.zuteilung.run(wahlId);
|
|
}
|
|
|
|
@Get(':wahlId/zuteilung')
|
|
@UseGuards(AuthGuard('authentik'), RolesGuard)
|
|
@Roles(Role.LEITUNGSTEAM)
|
|
getZuteilung(@Param('wahlId') wahlId: string) {
|
|
return this.zuteilung.getResults(wahlId);
|
|
}
|
|
|
|
@Get(':wahlId/zuteilung/csv')
|
|
@UseGuards(AuthGuard('authentik'), RolesGuard)
|
|
@Roles(Role.LEITUNGSTEAM)
|
|
async exportCsv(@Param('wahlId') wahlId: string, @Res() res: Response) {
|
|
const csv = await this.zuteilung.exportCsv(wahlId);
|
|
res.setHeader('Content-Type', 'text/csv; charset=utf-8');
|
|
res.setHeader('Content-Disposition', `attachment; filename="zuteilung-${wahlId}.csv"`);
|
|
res.send(csv);
|
|
}
|
|
}
|