33 lines
1.4 KiB
TypeScript
33 lines
1.4 KiB
TypeScript
import z from "zod";
|
|
|
|
const dateRe = /^\d{4}-\d{2}-\d{2}$/ // YYYY-MM-DD
|
|
const timeRe = /^(?:[01]\d|2[0-3]):[0-5]\d$/ // HH:mm (24h)\
|
|
|
|
export function parseLocalDateTime(dateStr: string, timeStr: string) {
|
|
// Construct a Date in the user's local timezone
|
|
const [y, m, d] = dateStr.split("-").map(Number)
|
|
const [hh, mm] = timeStr.split(":").map(Number)
|
|
return new Date(y, m - 1, d, hh, mm, 0, 0)
|
|
}
|
|
|
|
export const calendarEventSchema = z.object({
|
|
name: z.string().min(2, "Please enter at least 2 characters").max(100),
|
|
startDate: z.string().regex(dateRe, "Use YYYY-MM-DD"),
|
|
startTime: z.string().regex(timeRe, "Use HH:mm (24h)"),
|
|
endDate: z.string().regex(dateRe, "Use YYYY-MM-DD"),
|
|
endTime: z.string().regex(timeRe, "Use HH:mm (24h)"),
|
|
location: z.string().max(200).default(""),
|
|
color: z.string().regex(/^#([0-9A-Fa-f]{6})$/, "Use a hex color like #AABBCC"),
|
|
description: z.string().max(2000).default(""),
|
|
id: z.number().int().nonnegative().nullable().default(null),
|
|
}).superRefine((vals, ctx) => {
|
|
const start = parseLocalDateTime(vals.startDate, vals.startTime)
|
|
const end = parseLocalDateTime(vals.endDate, vals.endTime)
|
|
if (!(end > start)) {
|
|
ctx.addIssue({
|
|
code: z.ZodIssueCode.custom,
|
|
message: "End must be after start",
|
|
path: ["endTime"], // attach to a visible field
|
|
})
|
|
}
|
|
}) |