diff --git a/src/wahl/dto/update-wahl.dto.ts b/src/wahl/dto/update-wahl.dto.ts new file mode 100644 index 0000000..5d3a1bc --- /dev/null +++ b/src/wahl/dto/update-wahl.dto.ts @@ -0,0 +1,7 @@ +import { IsBoolean, IsOptional } from 'class-validator'; + +export class UpdateWahlDto { + @IsOptional() + @IsBoolean() + isOpen?: boolean; +} diff --git a/src/wahl/wahl.controller.ts b/src/wahl/wahl.controller.ts index 332b9a2..8dd49c1 100644 --- a/src/wahl/wahl.controller.ts +++ b/src/wahl/wahl.controller.ts @@ -3,6 +3,7 @@ import { Controller, Get, Param, + Patch, Post, Query, Req, @@ -14,6 +15,7 @@ import { Response } from 'express'; import { WahlService } from './wahl.service'; import { ZuteilungService } from './zuteilung.service'; import { CreateWahlDto } from './dto/create-wahl.dto'; +import { UpdateWahlDto } from './dto/update-wahl.dto'; import { CreateWorkshopDto } from './dto/create-workshop.dto'; import { SubmitTeilnehmerDto } from './dto/submit-teilnehmer.dto'; import { CreateForceZuteilungDto } from './dto/create-force-zuteilung.dto'; @@ -45,6 +47,20 @@ export class WahlController { return this.wahl.listWahlen(kcId); } + @Patch(':wahlId') + @UseGuards(AuthGuard(['authentik', 'team']), RolesGuard) + @Roles(Role.LEITUNGSTEAM) + updateWahl(@Param('wahlId') wahlId: string, @Body() dto: UpdateWahlDto) { + return this.wahl.updateWahl(wahlId, { isOpen: dto.isOpen }); + } + + @Get(':wahlId/teilnehmer') + @UseGuards(AuthGuard(['authentik', 'team']), RolesGuard) + @Roles(Role.LEITUNGSTEAM) + listTeilnehmer(@Param('wahlId') wahlId: string) { + return this.wahl.listTeilnehmer(wahlId); + } + @Post(':wahlId/workshops') @UseGuards(AuthGuard(['authentik', 'team']), RolesGuard) @Roles(Role.LEITUNGSTEAM) diff --git a/src/wahl/wahl.service.ts b/src/wahl/wahl.service.ts index 9223234..f1858ae 100644 --- a/src/wahl/wahl.service.ts +++ b/src/wahl/wahl.service.ts @@ -22,6 +22,33 @@ export class WahlService { return this.prisma.wahl.findMany({ where: { kcId } }); } + async updateWahl(wahlId: string, data: { isOpen?: boolean }) { + await this.getWahlOrThrow(wahlId); + const wahl = await this.prisma.wahl.update({ where: { id: wahlId }, data }); + await this.sync.capture('Wahl', SyncOperation.UPDATE, wahl.id, wahl); + return wahl; + } + + /// LT view of who took part in a Wahl, with their priorities and any + /// existing Force-Zuteilung. + async listTeilnehmer(wahlId: string) { + await this.getWahlOrThrow(wahlId); + const rows = await this.prisma.teilnehmer.findMany({ + where: { wahlId }, + orderBy: { guestAccount: { lastName: 'asc' } }, + include: { + guestAccount: { select: { firstName: true, lastName: true } }, + forceZuteilung: { select: { workshopId: true } }, + }, + }); + return rows.map((t) => ({ + id: t.id, + name: `${t.guestAccount.firstName} ${t.guestAccount.lastName}`.trim(), + prioritaeten: (t.prioritaeten as string[] | null) ?? [], + forcedWorkshopId: t.forceZuteilung?.workshopId ?? null, + })); + } + /// Guest-facing view: open Wahlen for the guest's KC, each with its /// workshops and the guest's own current priorities (null if not submitted). async guestOverview(kcId: string, guestAccountId: string) {