import { z } from "zod"; export const ScheduledTodoTitleSchema = z .string() .trim() .min(1, "Title is required") .max(100, "Title must be 100 characters or fewer"); export const ScheduledTodoDetailsSchema = z.string().max(5_000).optional(); // "HH:MM", 24-hour -- exactly what an gives back, so // the client never needs to reformat before sending it. export const ScheduledTodoTimeDueSchema = z .string() .regex(/^([01]\d|2[0-3]):[0-5]\d$/, "Invalid time"); // Minutes of lead time before timeDue -- 0 ("When due") up to a week. export const ScheduledTodoRemindMinutesBeforeSchema = z.number().int().min(0).max(10_080); const DateStringSchema = z.string().regex(/^\d{4}-\d{2}-\d{2}$/, "Invalid date"); const RecurrenceEndSchema = z.discriminatedUnion("type", [ z.object({ type: z.literal("never") }), z.object({ type: z.literal("until"), date: DateStringSchema }), z.object({ type: z.literal("count"), count: z.number().int().min(1).max(999) }), ]); /** * What the "Add/Edit Scheduled To-Do" dialog submits for the recurring * case -- validated here and only here turned into an RFC 5545 RRULE * string (see lib/rrule-utils.ts). Never trust a client-supplied RRULE * string directly. */ export const RecurrenceInputSchema = z .object({ frequency: z.enum(["DAILY", "WEEKLY", "MONTHLY", "YEARLY"]), interval: z.number().int().min(1).max(365), // 0 = Sunday .. 6 = Saturday, matching Date#getUTCDay(). daysOfWeek: z.array(z.number().int().min(0).max(6)).max(7).optional(), dayOfMonth: z.number().int().min(1).max(31).optional(), month: z.number().int().min(1).max(12).optional(), end: RecurrenceEndSchema, }) .superRefine((value, ctx) => { if (value.frequency === "WEEKLY" && !value.daysOfWeek?.length) { ctx.addIssue({ code: "custom", path: ["daysOfWeek"], message: "Pick at least one day of the week", }); } if ((value.frequency === "MONTHLY" || value.frequency === "YEARLY") && !value.dayOfMonth) { ctx.addIssue({ code: "custom", path: ["dayOfMonth"], message: "Day of month is required" }); } if (value.frequency === "YEARLY" && !value.month) { ctx.addIssue({ code: "custom", path: ["month"], message: "Month is required" }); } }); export type RecurrenceInput = z.infer;