From 0b588fa4b7155fd63f5d3b7bf828e836eb20abec Mon Sep 17 00:00:00 2001 From: linus Date: Thu, 10 Sep 2026 08:57:05 +0200 Subject: [PATCH] feat(backend): guest-facing Wahl result endpoint GET /api/wahl/guest/results (guest JWT) returns, per Wahl the guest took part in, their assignment: status PENDING (algorithm not run yet) / ASSIGNED (workshopName + wunschRang) / UNASSIGNED (no capacity left). Verified both PENDING and ASSIGNED paths against local Postgres. Co-Authored-By: Claude Sonnet 5 --- backend/src/wahl/wahl.controller.ts | 8 +++++++ backend/src/wahl/wahl.service.ts | 36 +++++++++++++++++++++++++++++ 2 files changed, 44 insertions(+) diff --git a/backend/src/wahl/wahl.controller.ts b/backend/src/wahl/wahl.controller.ts index e60e74b..6498857 100644 --- a/backend/src/wahl/wahl.controller.ts +++ b/backend/src/wahl/wahl.controller.ts @@ -78,6 +78,14 @@ export class WahlController { return this.wahl.guestOverview(guest.kcId, guest.guestId); } + /// The guest's own assignment result per Wahl they took part in. + @Get('guest/results') + @UseGuards(AuthGuard('guest')) + guestResults(@Req() req: GuestAuthenticatedRequest) { + const guest = req.user!; + return this.wahl.guestResults(guest.kcId, guest.guestId); + } + /// Guests submit their own workshop preferences (guest JWT, not Authentik). @Post(':wahlId/teilnehmer') @UseGuards(AuthGuard('guest')) diff --git a/backend/src/wahl/wahl.service.ts b/backend/src/wahl/wahl.service.ts index c2e7153..9223234 100644 --- a/backend/src/wahl/wahl.service.ts +++ b/backend/src/wahl/wahl.service.ts @@ -56,6 +56,42 @@ export class WahlService { }; } + /// Guest-facing result view: for every Wahl in the guest's KC where they + /// took part, their assignment (workshop name + wish rank), or a pending + /// marker if the algorithm has not run for them yet. + async guestResults(kcId: string, guestAccountId: string) { + const teilnahmen = await this.prisma.teilnehmer.findMany({ + where: { guestAccountId, wahl: { kcId } }, + orderBy: { wahl: { createdAt: 'asc' } }, + select: { + wahl: { select: { id: true, name: true, datumsSchluessel: true, teil: true } }, + zuteilung: { select: { workshopId: true, wunschRang: true, isForced: true } }, + }, + }); + + const workshopIds = teilnahmen + .map((t) => t.zuteilung?.workshopId) + .filter((id): id is string => !!id); + const workshops = workshopIds.length + ? await this.prisma.workshop.findMany({ + where: { id: { in: workshopIds } }, + select: { id: true, name: true }, + }) + : []; + const nameById = new Map(workshops.map((w) => [w.id, w.name])); + + return teilnahmen.map((t) => { + const z = t.zuteilung; + return { + wahl: t.wahl, + status: !z ? 'PENDING' : z.workshopId ? 'ASSIGNED' : 'UNASSIGNED', + workshopName: z?.workshopId ? (nameById.get(z.workshopId) ?? null) : null, + wunschRang: z?.wunschRang ?? null, + isForced: z?.isForced ?? false, + }; + }); + } + async createWorkshop( wahlId: string, name: string,