diff --git a/backend/prisma/seed-dev.js b/backend/prisma/seed-dev.js new file mode 100644 index 0000000..22f71c3 --- /dev/null +++ b/backend/prisma/seed-dev.js @@ -0,0 +1,35 @@ +/* Minimal dev seed: one KC + Gemeinde + open Wahl with workshops. + Run: node prisma/seed-dev.js (backend/.env must point at the dev DB) */ +const { PrismaClient } = require('@prisma/client'); +const prisma = new PrismaClient(); + +async function main() { + const kc = await prisma.kc.upsert({ + where: { inviteCode: 'DEV123' }, + update: {}, + create: { name: 'KC Dev 2026', inviteCode: 'DEV123' }, + }); + const gem = await prisma.gemeinde.upsert({ + where: { kcId_name: { kcId: kc.id, name: 'Mustergemeinde' } }, + update: {}, + create: { kcId: kc.id, name: 'Mustergemeinde' }, + }); + let wahl = await prisma.wahl.findFirst({ where: { kcId: kc.id } }); + if (!wahl) { + wahl = await prisma.wahl.create({ + data: { kcId: kc.id, name: 'Samstag Teil 1', datumsSchluessel: '2026-06-13', teil: '1' }, + }); + await prisma.workshop.createMany({ + data: [ + { wahlId: wahl.id, name: 'Töpfern', kapazitaet: 12, minTeilnehmer: 4 }, + { wahlId: wahl.id, name: 'Fußball', kapazitaet: 20, minTeilnehmer: 6 }, + { wahlId: wahl.id, name: 'Bandworkshop', kapazitaet: 8, minTeilnehmer: 3 }, + ], + }); + } + console.log('KC', kc.id, 'invite', kc.inviteCode); + console.log('Gemeinde', gem.id); + console.log('Wahl', wahl.id); +} + +main().finally(() => prisma.$disconnect()); diff --git a/backend/src/wahl/wahl.controller.ts b/backend/src/wahl/wahl.controller.ts index f222678..e60e74b 100644 --- a/backend/src/wahl/wahl.controller.ts +++ b/backend/src/wahl/wahl.controller.ts @@ -69,6 +69,15 @@ export class WahlController { 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')) diff --git a/backend/src/wahl/wahl.service.ts b/backend/src/wahl/wahl.service.ts index 290c698..c2e7153 100644 --- a/backend/src/wahl/wahl.service.ts +++ b/backend/src/wahl/wahl.service.ts @@ -22,6 +22,40 @@ export class WahlService { return this.prisma.wahl.findMany({ where: { kcId } }); } + /// 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) { + const [kc, wahlen] = await Promise.all([ + this.prisma.kc.findUnique({ where: { id: kcId }, select: { id: true, name: true } }), + this.prisma.wahl.findMany({ + where: { kcId, isOpen: true }, + orderBy: { createdAt: 'asc' }, + include: { + workshops: { + select: { id: true, name: true, kapazitaet: true }, + orderBy: { name: 'asc' }, + }, + teilnehmer: { + where: { guestAccountId }, + select: { prioritaeten: true }, + }, + }, + }), + ]); + + return { + kc, + wahlen: wahlen.map((w) => ({ + id: w.id, + name: w.name, + datumsSchluessel: w.datumsSchluessel, + teil: w.teil, + workshops: w.workshops, + meinePrioritaeten: (w.teilnehmer[0]?.prioritaeten as string[] | undefined) ?? null, + })), + }; + } + async createWorkshop( wahlId: string, name: string,