import { z } from 'zod' import { router, memberProcedure, adminProcedure } from '../trpc' const TerminInput = z.object({ titel: z.string().min(3), datum: z.string(), // ISO date string "YYYY-MM-DD" uhrzeit: z.string().optional(), endeDatum: z.string().optional().nullable(), endeUhrzeit: z.string().optional().nullable(), ort: z.string().optional(), adresse: z.string().optional(), typ: z.enum(['Pruefung', 'Versammlung', 'Kurs', 'Event', 'Sonstiges']), beschreibung: z.string().optional(), maxTeilnehmer: z.number().int().positive().optional().nullable(), }) export const termineRouter = router({ /** * List all termine for org */ list: memberProcedure .input( z.object({ upcoming: z.boolean().optional(), nurAngemeldet: z.boolean().optional(), }) ) .query(async ({ ctx, input }) => { const member = await ctx.prisma.member.findFirst({ where: { userId: ctx.session.user.id, orgId: ctx.orgId }, }) const today = new Date() today.setHours(0, 0, 0, 0) const termine = await ctx.prisma.termin.findMany({ where: { orgId: ctx.orgId, ...(input.upcoming && { datum: { gte: today } }), ...(!input.upcoming && input.upcoming !== undefined && { datum: { lt: today } }), ...(input.nurAngemeldet && member && { anmeldungen: { some: { memberId: member.id } }, }), }, include: { anmeldungen: { select: { memberId: true }, }, }, orderBy: { datum: input.upcoming ? 'asc' : 'desc' }, }) return termine.map((t) => ({ ...t, isAngemeldet: member ? t.anmeldungen.some((a) => a.memberId === member.id) : false, teilnehmerAnzahl: t.anmeldungen.length, anmeldungen: undefined, })) }), /** * Get single Termin */ byId: memberProcedure .input(z.object({ id: z.string() })) .query(async ({ ctx, input }) => { const member = await ctx.prisma.member.findFirst({ where: { userId: ctx.session.user.id, orgId: ctx.orgId }, }) const termin = await ctx.prisma.termin.findFirstOrThrow({ where: { id: input.id, orgId: ctx.orgId }, include: { anmeldungen: { include: { member: { select: { name: true, betrieb: true } }, }, }, }, }) const isAngemeldet = member ? termin.anmeldungen.some((a) => a.memberId === member.id) : false return { ...termin, isAngemeldet, teilnehmerAnzahl: termin.anmeldungen.length, // Only expose member list to admins anmeldungen: ctx.role === 'admin' ? termin.anmeldungen : [], } }), /** * Anmelden / Abmelden */ toggleAnmeldung: memberProcedure .input(z.object({ terminId: z.string() })) .mutation(async ({ ctx, input }) => { const member = await ctx.prisma.member.findFirstOrThrow({ where: { userId: ctx.session.user.id, orgId: ctx.orgId }, }) const termin = await ctx.prisma.termin.findFirstOrThrow({ where: { id: input.terminId, orgId: ctx.orgId }, include: { anmeldungen: true }, }) const existing = termin.anmeldungen.find( (a) => a.memberId === member.id ) if (existing) { // Abmelden await ctx.prisma.terminAnmeldung.delete({ where: { id: existing.id } }) return { angemeldet: false } } else { // Check capacity if ( termin.maxTeilnehmer && termin.anmeldungen.length >= termin.maxTeilnehmer ) { throw new Error('Maximale Teilnehmerzahl erreicht') } await ctx.prisma.terminAnmeldung.create({ data: { terminId: input.terminId, memberId: member.id }, }) return { angemeldet: true } } }), /** * Create Termin (admin only) */ create: adminProcedure.input(TerminInput).mutation(async ({ ctx, input }) => { const termin = await ctx.prisma.termin.create({ data: { orgId: ctx.orgId, ...input, datum: new Date(input.datum), endeDatum: input.endeDatum ? new Date(input.endeDatum) : null, }, }) return termin }), /** * Update Termin (admin only) */ update: adminProcedure .input(z.object({ id: z.string(), data: TerminInput.partial() })) .mutation(async ({ ctx, input }) => { await ctx.prisma.termin.updateMany({ where: { id: input.id, orgId: ctx.orgId }, data: { ...input.data, ...(input.data.datum && { datum: new Date(input.data.datum) }), ...(input.data.endeDatum && { endeDatum: new Date(input.data.endeDatum) }), }, }) return { success: true } }), /** * Delete Termin (admin only) */ delete: adminProcedure .input(z.object({ id: z.string() })) .mutation(async ({ ctx, input }) => { await ctx.prisma.termin.deleteMany({ where: { id: input.id, orgId: ctx.orgId }, }) return { success: true } }), })