44 lines
1.5 KiB
TypeScript
44 lines
1.5 KiB
TypeScript
import { z } from "zod";
|
|
|
|
// "YYYY-MM-DD" -- exactly what an <input type="date"> gives back, so the
|
|
// client never needs to reformat before sending it. Lexicographic string
|
|
// comparison is a valid chronological comparison for this shape.
|
|
const DateStringSchema = z.string().regex(/^\d{4}-\d{2}-\d{2}$/, "Invalid date");
|
|
|
|
export const SummaryDateRangeSchema = z.enum([
|
|
"today",
|
|
"thisWeek",
|
|
"thisMonth",
|
|
"lastMonth",
|
|
"custom",
|
|
]);
|
|
|
|
export const SummaryOrganizeBySchema = z.enum(["byDate", "byCategory"]);
|
|
|
|
export const SummaryRequestSchema = z
|
|
.object({
|
|
dateRange: SummaryDateRangeSchema,
|
|
// Only read when dateRange is "custom" -- enforced conditionally below.
|
|
customStart: DateStringSchema.optional(),
|
|
customEnd: DateStringSchema.optional(),
|
|
organizeBy: SummaryOrganizeBySchema,
|
|
})
|
|
.superRefine((value, ctx) => {
|
|
if (value.dateRange !== "custom") return;
|
|
if (!value.customStart) {
|
|
ctx.addIssue({ code: "custom", path: ["customStart"], message: "A start date is required." });
|
|
}
|
|
if (!value.customEnd) {
|
|
ctx.addIssue({ code: "custom", path: ["customEnd"], message: "An end date is required." });
|
|
}
|
|
if (value.customStart && value.customEnd && value.customStart > value.customEnd) {
|
|
ctx.addIssue({
|
|
code: "custom",
|
|
path: ["customStart"],
|
|
message: "The start date must be on or before the end date.",
|
|
});
|
|
}
|
|
});
|
|
|
|
export type SummaryRequest = z.infer<typeof SummaryRequestSchema>;
|