changemaker.lite/api/src/modules/meeting-planner/meeting-planner.schemas.ts
admin f57a6d07f5 Fix poll vote submission failure and add pridecorner.ca nginx routing
Users could not submit scheduling poll votes when an invalid or partial
email was entered — Zod rejected empty strings and non-email text with a
generic validation error. Added client-side email validation in both
SchedulingPollPage and SchedulingPollWidget, plus z.preprocess() on the
backend to coerce empty strings to undefined. Also added pridecorner.ca
to all nginx server blocks and added generate_nginx_configs() to
config.sh so template-based configs are generated during setup.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 14:15:26 -07:00

88 lines
3.6 KiB
TypeScript

import { z } from 'zod';
import { SchedulingPollStatus, PollVoteValue } from '@prisma/client';
export const createPollSchema = z.object({
title: z.string().min(1, 'Title is required').max(200),
description: z.string().max(2000).optional(),
location: z.string().max(500).optional(),
timezone: z.string().default('America/Edmonton'),
allowAnonymous: z.boolean().optional().default(true),
notifyOnVote: z.boolean().optional().default(true),
votingDeadline: z.string().datetime().optional(),
options: z.array(z.object({
date: z.string().regex(/^\d{4}-\d{2}-\d{2}$/, 'Date must be YYYY-MM-DD'),
startTime: z.string().regex(/^\d{2}:\d{2}$/, 'Start time must be HH:MM'),
endTime: z.string().regex(/^\d{2}:\d{2}$/, 'End time must be HH:MM'),
})).min(1, 'At least 1 option required').max(20, 'Maximum 20 options'),
});
export const updatePollSchema = z.object({
title: z.string().min(1).max(200).optional(),
description: z.string().max(2000).nullable().optional(),
location: z.string().max(500).nullable().optional(),
timezone: z.string().optional(),
allowAnonymous: z.boolean().optional(),
notifyOnVote: z.boolean().optional(),
votingDeadline: z.string().datetime().nullable().optional(),
status: z.nativeEnum(SchedulingPollStatus).optional(),
});
export const addOptionsSchema = z.object({
options: z.array(z.object({
date: z.string().regex(/^\d{4}-\d{2}-\d{2}$/, 'Date must be YYYY-MM-DD'),
startTime: z.string().regex(/^\d{2}:\d{2}$/, 'Start time must be HH:MM'),
endTime: z.string().regex(/^\d{2}:\d{2}$/, 'End time must be HH:MM'),
})).min(1).max(20),
});
export const submitVotesSchema = z.object({
voterName: z.string().min(1, 'Name is required').max(100),
voterEmail: z.preprocess(
(val) => (typeof val === 'string' && val.trim() === '' ? undefined : val),
z.string().email('Please enter a valid email address').max(200).optional(),
),
voterToken: z.string().optional(),
votes: z.array(z.object({
optionId: z.string().min(1),
value: z.nativeEnum(PollVoteValue),
})).min(1, 'At least one vote required'),
});
export const submitCommentSchema = z.object({
authorName: z.string().min(1, 'Name is required').max(100),
content: z.string().min(1, 'Comment is required').max(2000),
});
export const finalizePollSchema = z.object({
optionId: z.string().min(1, 'Option ID is required'),
});
export const convertToShiftSchema = z.object({
maxVolunteers: z.number().int().min(1).default(10),
isPublic: z.boolean().optional().default(true),
cutId: z.string().optional(),
});
export const updateOptionSchema = z.object({
date: z.string().regex(/^\d{4}-\d{2}-\d{2}$/, 'Date must be YYYY-MM-DD').optional(),
startTime: z.string().regex(/^\d{2}:\d{2}$/, 'Start time must be HH:MM').optional(),
endTime: z.string().regex(/^\d{2}:\d{2}$/, 'End time must be HH:MM').optional(),
});
export const listPollsSchema = z.object({
page: z.coerce.number().int().positive().default(1),
limit: z.coerce.number().int().positive().max(100).default(20),
search: z.string().optional(),
status: z.nativeEnum(SchedulingPollStatus).optional(),
});
export type CreatePollInput = z.infer<typeof createPollSchema>;
export type UpdatePollInput = z.infer<typeof updatePollSchema>;
export type AddOptionsInput = z.infer<typeof addOptionsSchema>;
export type SubmitVotesInput = z.infer<typeof submitVotesSchema>;
export type SubmitCommentInput = z.infer<typeof submitCommentSchema>;
export type FinalizePollInput = z.infer<typeof finalizePollSchema>;
export type ConvertToShiftInput = z.infer<typeof convertToShiftSchema>;
export type UpdateOptionInput = z.infer<typeof updateOptionSchema>;
export type ListPollsInput = z.infer<typeof listPollsSchema>;