refactor: centralize event dialog form state

This commit is contained in:
2026-04-09 17:41:26 -04:00
parent 911e5735a4
commit 12f2fd95dc
7 changed files with 344 additions and 188 deletions

84
src/lib/event-form.ts Normal file
View File

@@ -0,0 +1,84 @@
import { isAfter, parseISO } from "date-fns";
import { z } from "zod";
import type { CalendarEvent } from "@/lib/types";
export interface EventFormValues {
title: string;
description: string;
location: string;
url: string;
start: string;
end: string;
allDay: boolean;
recurrenceRule?: string;
}
const eventFormSchema = z
.object({
title: z.string().trim().min(1, "Title is required."),
description: z.string(),
location: z.string(),
url: z.preprocess((value) => value ?? "", z.string().trim()),
start: z.string().trim().min(1, "Start date is required."),
end: z.string(),
allDay: z.boolean(),
recurrenceRule: z.string().optional(),
})
.superRefine((value, ctx) => {
if (value.url) {
const urlValidation = z.string().url().safeParse(value.url);
if (!urlValidation.success) {
ctx.addIssue({
code: z.ZodIssueCode.custom,
path: ["url"],
message: "Enter a valid URL.",
});
}
}
if (value.end) {
const startDate = parseISO(value.start);
const endDate = parseISO(value.end);
if (Number.isNaN(startDate.getTime()) || Number.isNaN(endDate.getTime())) {
ctx.addIssue({
code: z.ZodIssueCode.custom,
path: ["end"],
message: "End date must be valid.",
});
} else if (!isAfter(endDate, startDate)) {
ctx.addIssue({
code: z.ZodIssueCode.custom,
path: ["end"],
message: "End date must be after the start date.",
});
}
}
});
export const getDefaultEventFormValues = (): EventFormValues => ({
title: "",
description: "",
location: "",
url: "",
start: "",
end: "",
allDay: false,
recurrenceRule: undefined,
});
export const getEventFormValuesFromEvent = (
event?: Partial<CalendarEvent>,
): EventFormValues => ({
...getDefaultEventFormValues(),
title: event?.title || "",
description: event?.description || "",
location: event?.location || "",
url: event?.url || "",
start: event?.start || "",
end: event?.end || "",
allDay: event?.allDay || false,
recurrenceRule: event?.recurrenceRule || undefined,
});
export const validateEventFormValues = (values: EventFormValues) =>
eventFormSchema.safeParse(values);