website/lib/schema/appointments.ts

85 lines
3.3 KiB
TypeScript
Raw Normal View History

import { z } from "zod";
// Selected Slot Schema (for new API format)
export const selectedSlotSchema = z.object({
day: z.number().int().min(0).max(6),
time_slot: z.enum(["morning", "afternoon", "evening"]),
});
export type SelectedSlotInput = z.infer<typeof selectedSlotSchema>;
// Create Appointment Schema (updated to use selected_slots)
export const createAppointmentSchema = z.object({
first_name: z.string().min(1, "First name is required").max(100, "First name must be 100 characters or less"),
last_name: z.string().min(1, "Last name is required").max(100, "Last name must be 100 characters or less"),
email: z.string().email("Invalid email address").max(100, "Email must be 100 characters or less"),
selected_slots: z
.array(selectedSlotSchema)
.min(1, "At least one time slot must be selected"),
phone: z.string().max(100, "Phone must be 100 characters or less").optional(),
reason: z.string().max(100, "Reason must be 100 characters or less").optional(),
// Legacy fields (optional, for backward compatibility - but should not be sent)
preferred_dates: z
.array(z.string().regex(/^\d{4}-\d{2}-\d{2}$/, "Date must be in YYYY-MM-DD format"))
.optional(),
preferred_time_slots: z
.array(z.enum(["morning", "afternoon", "evening"]))
.optional(),
}).refine(
(data) => data.selected_slots && data.selected_slots.length > 0,
{
message: "At least one time slot must be selected",
path: ["selected_slots"],
}
);
export type CreateAppointmentInput = z.infer<typeof createAppointmentSchema>;
// Schedule Appointment Schema (Admin only)
// Supports two scheduling methods:
// 1. Direct datetime: scheduled_datetime + scheduled_duration
// 2. Date and slot: date_str + time_slot + scheduled_duration
export const scheduleAppointmentSchema = z.object({
scheduled_datetime: z.string().datetime("Invalid datetime format").optional(),
scheduled_duration: z.number().int().positive().optional(),
date_str: z.string().regex(/^\d{4}-\d{2}-\d{2}$/, "Date must be in YYYY-MM-DD format").optional(),
time_slot: z.enum(["morning", "afternoon", "evening"]).optional(),
create_jitsi_meeting: z.boolean().optional(),
jitsi_custom_config: z.string().optional(),
}).refine(
(data) => {
// Either scheduled_datetime OR (date_str + time_slot) must be provided
return data.scheduled_datetime || (data.date_str && data.time_slot);
},
{
message: "Either scheduled_datetime or both date_str and time_slot must be provided",
path: ["scheduled_datetime"],
}
);
export type ScheduleAppointmentInput = z.infer<typeof scheduleAppointmentSchema>;
// Reject Appointment Schema (Admin only)
export const rejectAppointmentSchema = z.object({
rejection_reason: z.string().optional(),
});
export type RejectAppointmentInput = z.infer<typeof rejectAppointmentSchema>;
// Update Admin Availability Schema (updated to use availability_schedule)
export const updateAvailabilitySchema = z.object({
availability_schedule: z
.record(z.string(), z.array(z.enum(["morning", "afternoon", "evening"])))
.refine(
(schedule) => Object.keys(schedule).length > 0,
{ message: "At least one day must have availability" }
),
// Legacy field (optional, for backward compatibility)
available_days: z
.array(z.number().int().min(0).max(6))
.optional(),
});
export type UpdateAvailabilityInput = z.infer<typeof updateAvailabilitySchema>;