import { z } from 'zod'; import { BaseDomainEventPayloadSchema, changesSchema, updatedFieldsSchema, uuidSchema, } from './commonEventPayloadSchemas'; /** * Ticket event payloads for workflow runtime v2 simulation and validation. * * Notes: * - Payloads follow PRD conventions: tenantId + occurredAt required; actor fields optional. * - These are intentionally higher-level and stable; they can be expanded/versioned as needed. */ const ticketIdSchema = uuidSchema('Ticket ID'); const userIdSchema = uuidSchema('User ID'); const contactIdSchema = uuidSchema('Contact ID'); const messageIdSchema = uuidSchema('Message ID'); const noteIdSchema = uuidSchema('Note ID'); const timeEntryIdSchema = uuidSchema('Time Entry ID'); const assigneeTypeSchema = z.enum(['user', 'team']).describe('Assignee type'); export const ticketCreatedEventPayloadSchema = BaseDomainEventPayloadSchema.extend({ ticketId: ticketIdSchema, createdByUserId: userIdSchema.optional().describe('User who created the ticket'), actorUserId: userIdSchema.optional().describe('Actor User ID (preferred)'), createdAt: z.string().optional().describe('Created timestamp (ISO 8601)'), updatedFields: updatedFieldsSchema, changes: changesSchema, }).describe('Payload for TICKET_CREATED'); export type TicketCreatedEventPayload = z.infer; export const ticketAssignedEventPayloadSchema = BaseDomainEventPayloadSchema.extend({ ticketId: ticketIdSchema, assignedToUserId: userIdSchema.optional().describe('Deprecated: user assigned to the ticket'), assignedByUserId: userIdSchema.optional().describe('User who performed the assignment'), previousAssigneeId: z.string().uuid().optional(), previousAssigneeType: assigneeTypeSchema.optional(), newAssigneeId: z.string().uuid().optional(), newAssigneeType: assigneeTypeSchema.optional(), assignedAt: z.string().optional().describe('Assigned timestamp (ISO 8601)'), updatedFields: updatedFieldsSchema, changes: changesSchema, }).describe('Payload for TICKET_ASSIGNED'); export type TicketAssignedEventPayload = z.infer; export const ticketClosedEventPayloadSchema = BaseDomainEventPayloadSchema.extend({ ticketId: ticketIdSchema, closedByUserId: userIdSchema.optional().describe('User who closed the ticket'), closedAt: z.string().optional().describe('Closed timestamp (ISO 8601)'), reason: z.string().optional().describe('Optional close reason'), updatedFields: updatedFieldsSchema, changes: changesSchema, }).describe('Payload for TICKET_CLOSED'); export type TicketClosedEventPayload = z.infer; export const ticketUpdatedEventPayloadSchema = BaseDomainEventPayloadSchema.extend({ ticketId: ticketIdSchema, updatedByUserId: userIdSchema.optional().describe('User who updated the ticket'), updatedFields: updatedFieldsSchema, changes: changesSchema, }).describe('Payload for TICKET_UPDATED'); export type TicketUpdatedEventPayload = z.infer; export const ticketResponseStateChangedEventPayloadSchema = BaseDomainEventPayloadSchema.extend({ ticketId: ticketIdSchema, previousResponseState: z.string().optional().describe('Previous response state'), newResponseState: z.string().optional().describe('New response state'), }).describe('Payload for TICKET_RESPONSE_STATE_CHANGED'); export type TicketResponseStateChangedEventPayload = z.infer< typeof ticketResponseStateChangedEventPayloadSchema >; export const ticketStatusChangedEventPayloadSchema = BaseDomainEventPayloadSchema.extend({ ticketId: ticketIdSchema, previousStatusId: z.string().min(1).describe('Previous status ID'), newStatusId: z.string().min(1).describe('New status ID'), reason: z.string().optional(), changedAt: z.string().datetime().optional().describe('Timestamp when status changed (ISO 8601)'), }).describe('Payload for TICKET_STATUS_CHANGED'); export type TicketStatusChangedEventPayload = z.infer; export const ticketPriorityChangedEventPayloadSchema = BaseDomainEventPayloadSchema.extend({ ticketId: ticketIdSchema, previousPriorityId: z.string().min(1).describe('Previous priority ID'), newPriorityId: z.string().min(1).describe('New priority ID'), reason: z.string().optional(), changedAt: z.string().datetime().optional().describe('Timestamp when priority changed (ISO 8601)'), }).describe('Payload for TICKET_PRIORITY_CHANGED'); export type TicketPriorityChangedEventPayload = z.infer; export const ticketUnassignedEventPayloadSchema = BaseDomainEventPayloadSchema.extend({ ticketId: ticketIdSchema, previousAssigneeId: z.string().uuid().describe('Previous assignee ID'), previousAssigneeType: assigneeTypeSchema.describe('Previous assignee type'), newAssigneeId: z.string().uuid().optional().describe('New assignee ID (typically absent after unassignment)'), newAssigneeType: assigneeTypeSchema.optional().describe('New assignee type (typically absent after unassignment)'), unassignedAt: z.string().datetime().optional(), reason: z.string().optional(), }).describe('Payload for TICKET_UNASSIGNED'); export type TicketUnassignedEventPayload = z.infer; export const ticketReopenedEventPayloadSchema = BaseDomainEventPayloadSchema.extend({ ticketId: ticketIdSchema, previousStatusId: z.string().min(1).describe('Previous status ID'), newStatusId: z.string().min(1).describe('New status ID'), reopenedAt: z.string().datetime().optional(), reason: z.string().optional(), }).describe('Payload for TICKET_REOPENED'); export type TicketReopenedEventPayload = z.infer; export const ticketMergedEventPayloadSchema = BaseDomainEventPayloadSchema.extend({ sourceTicketId: ticketIdSchema.describe('Source ticket ID'), targetTicketId: ticketIdSchema.describe('Target ticket ID'), mergedAt: z.string().datetime().optional(), reason: z.string().optional(), }).describe('Payload for TICKET_MERGED'); export type TicketMergedEventPayload = z.infer; export const ticketSplitEventPayloadSchema = BaseDomainEventPayloadSchema.extend({ originalTicketId: ticketIdSchema.describe('Original ticket ID'), newTicketIds: z.array(ticketIdSchema).min(1).describe('New ticket IDs'), splitAt: z.string().datetime().optional(), reason: z.string().optional(), }).describe('Payload for TICKET_SPLIT'); export type TicketSplitEventPayload = z.infer; export const ticketTagsChangedEventPayloadSchema = BaseDomainEventPayloadSchema.extend({ ticketId: ticketIdSchema, addedTagIds: z.array(z.string().uuid()).optional(), removedTagIds: z.array(z.string().uuid()).optional(), changedAt: z.string().datetime().optional(), }).describe('Payload for TICKET_TAGS_CHANGED'); export type TicketTagsChangedEventPayload = z.infer; export const ticketQueueChangedEventPayloadSchema = BaseDomainEventPayloadSchema.extend({ ticketId: ticketIdSchema, previousBoardId: z.string().uuid().optional(), newBoardId: z.string().uuid().optional(), changedAt: z.string().datetime().optional(), }).describe('Payload for TICKET_QUEUE_CHANGED'); export type TicketQueueChangedEventPayload = z.infer; export const ticketEscalatedEventPayloadSchema = BaseDomainEventPayloadSchema.extend({ ticketId: ticketIdSchema, fromQueueId: z.string().uuid().optional(), toQueueId: z.string().uuid().optional(), escalatedAt: z.string().datetime().optional(), reason: z.string().optional(), }).describe('Payload for TICKET_ESCALATED'); export type TicketEscalatedEventPayload = z.infer; const visibilitySchema = z.enum(['public', 'internal']).describe('Message visibility'); const messageChannelSchema = z.enum(['email', 'portal', 'ui', 'api']).describe('Message channel'); const authorTypeSchema = z.enum(['user', 'contact']).describe('Author type'); export const ticketMessageAddedEventPayloadSchema = BaseDomainEventPayloadSchema.extend({ ticketId: ticketIdSchema, messageId: messageIdSchema, visibility: visibilitySchema, authorId: z.string().uuid().describe('Author ID (user/contact)'), authorType: authorTypeSchema, channel: messageChannelSchema, createdAt: z.string().datetime().optional(), attachmentsCount: z.number().int().nonnegative().optional(), }).describe('Payload for TICKET_MESSAGE_ADDED'); export type TicketMessageAddedEventPayload = z.infer; export const ticketCustomerRepliedEventPayloadSchema = BaseDomainEventPayloadSchema.extend({ ticketId: ticketIdSchema, messageId: messageIdSchema, contactId: contactIdSchema, channel: messageChannelSchema, receivedAt: z.string().datetime().optional(), attachmentsCount: z.number().int().nonnegative().optional(), }).describe('Payload for TICKET_CUSTOMER_REPLIED'); export type TicketCustomerRepliedEventPayload = z.infer; export const ticketInternalNoteAddedEventPayloadSchema = BaseDomainEventPayloadSchema.extend({ ticketId: ticketIdSchema, noteId: noteIdSchema, createdAt: z.string().datetime().optional(), }).describe('Payload for TICKET_INTERNAL_NOTE_ADDED'); export type TicketInternalNoteAddedEventPayload = z.infer< typeof ticketInternalNoteAddedEventPayloadSchema >; export const ticketTimeEntryAddedEventPayloadSchema = BaseDomainEventPayloadSchema.extend({ ticketId: ticketIdSchema, timeEntryId: timeEntryIdSchema, minutes: z.number().int().positive(), billable: z.boolean(), createdAt: z.string().datetime().optional(), }).describe('Payload for TICKET_TIME_ENTRY_ADDED'); export type TicketTimeEntryAddedEventPayload = z.infer; const slaStageSchema = z.enum(['response', 'resolution', 'custom']).describe('SLA stage'); export const ticketSlaStageEnteredEventPayloadSchema = BaseDomainEventPayloadSchema.extend({ ticketId: ticketIdSchema, slaPolicyId: z.string().uuid(), stage: slaStageSchema, enteredAt: z.string().datetime().optional(), targetAt: z.string().datetime().optional(), }).describe('Payload for TICKET_SLA_STAGE_ENTERED'); export type TicketSlaStageEnteredEventPayload = z.infer; export const ticketSlaStageMetEventPayloadSchema = BaseDomainEventPayloadSchema.extend({ ticketId: ticketIdSchema, slaPolicyId: z.string().uuid(), stage: slaStageSchema, metAt: z.string().datetime().optional(), targetAt: z.string().datetime().optional(), }).describe('Payload for TICKET_SLA_STAGE_MET'); export type TicketSlaStageMetEventPayload = z.infer; export const ticketSlaStageBreachedEventPayloadSchema = BaseDomainEventPayloadSchema.extend({ ticketId: ticketIdSchema, slaPolicyId: z.string().uuid(), stage: slaStageSchema, breachedAt: z.string().datetime().optional(), targetAt: z.string().datetime().optional(), overdueBySeconds: z.number().int().nonnegative().optional(), }).describe('Payload for TICKET_SLA_STAGE_BREACHED'); export type TicketSlaStageBreachedEventPayload = z.infer< typeof ticketSlaStageBreachedEventPayloadSchema >; export const ticketApprovalRequestedEventPayloadSchema = BaseDomainEventPayloadSchema.extend({ ticketId: ticketIdSchema, approvalRequestId: z.string().uuid(), approvalType: z.string().min(1), requestedByUserId: userIdSchema.optional(), requestedAt: z.string().datetime().optional(), notes: z.string().optional(), }).describe('Payload for TICKET_APPROVAL_REQUESTED'); export type TicketApprovalRequestedEventPayload = z.infer; export const ticketApprovalGrantedEventPayloadSchema = BaseDomainEventPayloadSchema.extend({ ticketId: ticketIdSchema, approvalRequestId: z.string().uuid(), approvedByUserId: userIdSchema.optional(), approvedAt: z.string().datetime().optional(), conditions: z.unknown().optional(), }).describe('Payload for TICKET_APPROVAL_GRANTED'); export type TicketApprovalGrantedEventPayload = z.infer; export const ticketApprovalRejectedEventPayloadSchema = BaseDomainEventPayloadSchema.extend({ ticketId: ticketIdSchema, approvalRequestId: z.string().uuid(), rejectedByUserId: userIdSchema.optional(), rejectedAt: z.string().datetime().optional(), reason: z.string().optional(), }).describe('Payload for TICKET_APPROVAL_REJECTED'); export type TicketApprovalRejectedEventPayload = z.infer; // --------------------------------------------------------------------------- // Legacy ticket events (still used in harness fixtures) // --------------------------------------------------------------------------- export const ticketCommentAddedEventPayloadSchema = BaseDomainEventPayloadSchema.extend({ ticketId: ticketIdSchema, commentId: z.string().uuid().optional(), visibility: z.enum(['public', 'internal']).optional(), authorId: z.string().uuid().optional(), authorType: z.enum(['user', 'contact']).optional(), createdAt: z.string().datetime().optional(), }).describe('Payload for TICKET_COMMENT_ADDED'); export type TicketCommentAddedEventPayload = z.infer; export const ticketAdditionalAgentAssignedEventPayloadSchema = BaseDomainEventPayloadSchema.extend({ ticketId: ticketIdSchema, primaryAgentId: userIdSchema, additionalAgentId: userIdSchema, assignedByUserId: userIdSchema.optional(), assignedAt: z.string().datetime().optional(), }).describe('Payload for TICKET_ADDITIONAL_AGENT_ASSIGNED'); export type TicketAdditionalAgentAssignedEventPayload = z.infer;