import { z } from 'zod'; import { BaseDomainEventPayloadSchema, uuidSchema } from './commonEventPayloadSchemas'; const appointmentIdSchema = uuidSchema('Appointment ID'); const ticketIdSchema = uuidSchema('Ticket ID'); const scheduleBlockIdSchema = uuidSchema('Schedule Block ID'); const scheduleEntryIdSchema = uuidSchema('Schedule Entry ID'); const teamIdSchema = uuidSchema('Team ID'); const userIdSchema = uuidSchema('User ID'); const appointmentRequestIdSchema = uuidSchema('Appointment Request ID'); const serviceIdSchema = uuidSchema('Service ID'); const assigneeTypeSchema = z.enum(['user', 'team']).describe('Assignee type'); const partySchema = z.enum(['customer', 'agent']).describe('No-show party'); export const appointmentCreatedEventPayloadSchema = BaseDomainEventPayloadSchema.extend({ appointmentId: appointmentIdSchema, ticketId: ticketIdSchema.optional(), startAt: z.string().datetime(), endAt: z.string().datetime(), timezone: z.string().min(1), assigneeId: z.string().uuid().optional(), assigneeType: assigneeTypeSchema.optional(), createdByUserId: userIdSchema.optional(), createdAt: z.string().datetime().optional(), location: z.string().optional(), }).describe('Payload for APPOINTMENT_CREATED'); export type AppointmentCreatedEventPayload = z.infer; export const appointmentRescheduledEventPayloadSchema = BaseDomainEventPayloadSchema.extend({ appointmentId: appointmentIdSchema, ticketId: ticketIdSchema.optional(), previousStartAt: z.string().datetime(), previousEndAt: z.string().datetime(), newStartAt: z.string().datetime(), newEndAt: z.string().datetime(), timezone: z.string().min(1), rescheduledAt: z.string().datetime().optional(), reason: z.string().optional(), }).describe('Payload for APPOINTMENT_RESCHEDULED'); export type AppointmentRescheduledEventPayload = z.infer; export const appointmentCanceledEventPayloadSchema = BaseDomainEventPayloadSchema.extend({ appointmentId: appointmentIdSchema, ticketId: ticketIdSchema.optional(), canceledAt: z.string().datetime().optional(), reason: z.string().optional(), }).describe('Payload for APPOINTMENT_CANCELED'); export type AppointmentCanceledEventPayload = z.infer; export const appointmentCompletedEventPayloadSchema = BaseDomainEventPayloadSchema.extend({ appointmentId: appointmentIdSchema, ticketId: ticketIdSchema.optional(), completedAt: z.string().datetime().optional(), outcome: z.string().optional(), }).describe('Payload for APPOINTMENT_COMPLETED'); export type AppointmentCompletedEventPayload = z.infer; export const appointmentNoShowEventPayloadSchema = BaseDomainEventPayloadSchema.extend({ appointmentId: appointmentIdSchema, ticketId: ticketIdSchema.optional(), markedAt: z.string().datetime().optional(), party: partySchema, }).describe('Payload for APPOINTMENT_NO_SHOW'); export type AppointmentNoShowEventPayload = z.infer; export const appointmentAssignedEventPayloadSchema = BaseDomainEventPayloadSchema.extend({ appointmentId: appointmentIdSchema, ticketId: ticketIdSchema.optional(), previousAssigneeId: z.string().uuid().optional(), previousAssigneeType: assigneeTypeSchema.optional(), newAssigneeId: z.string().uuid(), newAssigneeType: assigneeTypeSchema, assignedAt: z.string().datetime().optional(), }).describe('Payload for APPOINTMENT_ASSIGNED'); export type AppointmentAssignedEventPayload = z.infer; const ownerTypeSchema = z.enum(['user', 'team']).describe('Owner type'); export const scheduleBlockCreatedEventPayloadSchema = BaseDomainEventPayloadSchema.extend({ scheduleBlockId: scheduleBlockIdSchema, ownerId: z.string().uuid(), ownerType: ownerTypeSchema, startAt: z.string().datetime(), endAt: z.string().datetime(), timezone: z.string().min(1), createdAt: z.string().datetime().optional(), reason: z.string().optional(), }).describe('Payload for SCHEDULE_BLOCK_CREATED'); export type ScheduleBlockCreatedEventPayload = z.infer; export const scheduleBlockDeletedEventPayloadSchema = BaseDomainEventPayloadSchema.extend({ scheduleBlockId: scheduleBlockIdSchema, deletedAt: z.string().datetime().optional(), reason: z.string().optional(), }).describe('Payload for SCHEDULE_BLOCK_DELETED'); export type ScheduleBlockDeletedEventPayload = z.infer; export const capacityThresholdReachedEventPayloadSchema = BaseDomainEventPayloadSchema.extend({ teamId: teamIdSchema, date: z.string().min(1).describe('Local date (YYYY-MM-DD)'), capacityLimit: z.number().nonnegative(), currentBooked: z.number().nonnegative(), triggeredAt: z.string().datetime().optional(), }).describe('Payload for CAPACITY_THRESHOLD_REACHED'); export type CapacityThresholdReachedEventPayload = z.infer; export const technicianDispatchedEventPayloadSchema = BaseDomainEventPayloadSchema.extend({ appointmentId: appointmentIdSchema, ticketId: ticketIdSchema.optional(), technicianUserId: userIdSchema, dispatchedByUserId: userIdSchema.optional(), dispatchedAt: z.string().datetime().optional(), }).describe('Payload for TECHNICIAN_DISPATCHED'); export type TechnicianDispatchedEventPayload = z.infer; export const technicianEnRouteEventPayloadSchema = BaseDomainEventPayloadSchema.extend({ appointmentId: appointmentIdSchema, ticketId: ticketIdSchema.optional(), technicianUserId: userIdSchema, startedAt: z.string().datetime().optional(), eta: z.string().datetime().optional(), }).describe('Payload for TECHNICIAN_EN_ROUTE'); export type TechnicianEnRouteEventPayload = z.infer; export const technicianArrivedEventPayloadSchema = BaseDomainEventPayloadSchema.extend({ appointmentId: appointmentIdSchema, ticketId: ticketIdSchema.optional(), technicianUserId: userIdSchema, arrivedAt: z.string().datetime().optional(), location: z.string().optional(), }).describe('Payload for TECHNICIAN_ARRIVED'); export type TechnicianArrivedEventPayload = z.infer; export const technicianCheckedOutEventPayloadSchema = BaseDomainEventPayloadSchema.extend({ appointmentId: appointmentIdSchema, ticketId: ticketIdSchema.optional(), technicianUserId: userIdSchema, checkedOutAt: z.string().datetime().optional(), workSummary: z.string().optional(), }).describe('Payload for TECHNICIAN_CHECKED_OUT'); export type TechnicianCheckedOutEventPayload = z.infer; // --------------------------------------------------------------------------- // Legacy scheduling events (still used in harness fixtures) // --------------------------------------------------------------------------- export const scheduleEntryEventPayloadSchema = BaseDomainEventPayloadSchema.extend({ entryId: scheduleEntryIdSchema, userId: userIdSchema, changes: z.record(z.unknown()).optional(), }).describe('Payload for schedule entry events (SCHEDULE_ENTRY_*)'); export type ScheduleEntryEventPayload = z.infer; export const scheduleEntryCreatedEventPayloadSchema = scheduleEntryEventPayloadSchema.describe( 'Payload for SCHEDULE_ENTRY_CREATED' ); export const scheduleEntryUpdatedEventPayloadSchema = scheduleEntryEventPayloadSchema.describe( 'Payload for SCHEDULE_ENTRY_UPDATED' ); export const scheduleEntryDeletedEventPayloadSchema = scheduleEntryEventPayloadSchema.describe( 'Payload for SCHEDULE_ENTRY_DELETED' ); export const appointmentRequestEventPayloadSchema = BaseDomainEventPayloadSchema.extend({ appointmentRequestId: appointmentRequestIdSchema, serviceId: serviceIdSchema, serviceName: z.string().min(1), requestedDate: z.string().min(1), requestedTime: z.string().min(1), requestedDuration: z.number().int().positive(), isAuthenticated: z.boolean(), requesterEmail: z.string().email(), requesterName: z.string().nullable().optional(), requesterPhone: z.string().nullable().optional(), clientId: z.string().uuid().optional(), contactId: z.string().uuid().optional(), clientUserId: z.string().uuid().optional(), preferredAssignedUserId: z.string().uuid().optional(), companyName: z.string().optional(), ticketId: z.string().uuid().optional(), description: z.string().optional(), approvedByUserId: z.string().uuid().optional(), assignedUserId: z.string().uuid().optional(), scheduleEntryId: z.string().uuid().optional(), declineReason: z.string().optional(), }).describe('Payload for appointment request events (APPOINTMENT_REQUEST_*)'); export type AppointmentRequestEventPayload = z.infer; export const appointmentRequestCreatedEventPayloadSchema = appointmentRequestEventPayloadSchema.describe( 'Payload for APPOINTMENT_REQUEST_CREATED' ); export const appointmentRequestApprovedEventPayloadSchema = appointmentRequestEventPayloadSchema.describe( 'Payload for APPOINTMENT_REQUEST_APPROVED' ); export const appointmentRequestDeclinedEventPayloadSchema = appointmentRequestEventPayloadSchema.describe( 'Payload for APPOINTMENT_REQUEST_DECLINED' ); export const appointmentRequestCancelledEventPayloadSchema = appointmentRequestEventPayloadSchema.describe( 'Payload for APPOINTMENT_REQUEST_CANCELLED' );