124 lines
5.1 KiB
TypeScript
124 lines
5.1 KiB
TypeScript
import { z } from 'zod';
|
|
|
|
// Simplified approval level schema - only requires email and tatHours
|
|
// Backend will enrich with user details (approverId, approverName, levelName)
|
|
const simplifiedApprovalLevelSchema = z.object({
|
|
email: z.string().email('Valid email is required'),
|
|
tatHours: z.number().positive('TAT hours must be positive'),
|
|
isFinalApprover: z.boolean().optional(),
|
|
// Optional fields that backend will auto-populate if not provided
|
|
levelNumber: z.number().int().min(1).max(10).optional(),
|
|
levelName: z.string().optional(),
|
|
approverId: z.string().uuid().optional(),
|
|
approverEmail: z.string().email().optional(),
|
|
approverName: z.string().optional(),
|
|
});
|
|
|
|
// Simplified spectator schema - only requires email
|
|
const simplifiedSpectatorSchema = z.object({
|
|
email: z.string().email('Valid email is required').optional(),
|
|
// Optional fields that backend will auto-populate if not provided
|
|
userId: z.string().uuid().optional(),
|
|
userEmail: z.string().email().optional(),
|
|
userName: z.string().optional(),
|
|
participantType: z.enum(['INITIATOR', 'APPROVER', 'SPECTATOR'] as const).optional(),
|
|
canComment: z.boolean().optional(),
|
|
canViewDocuments: z.boolean().optional(),
|
|
canDownloadDocuments: z.boolean().optional(),
|
|
notificationEnabled: z.boolean().optional(),
|
|
});
|
|
|
|
export const createWorkflowSchema = z.object({
|
|
templateType: z.enum(['CUSTOM', 'TEMPLATE']),
|
|
title: z.string().min(1, 'Title is required').max(500, 'Title too long'),
|
|
description: z.string().min(1, 'Description is required'),
|
|
priority: z.enum(['STANDARD', 'EXPRESS'] as const),
|
|
approvalLevels: z.array(simplifiedApprovalLevelSchema)
|
|
.min(1, 'At least one approval level is required')
|
|
.max(10, 'Maximum 10 approval levels allowed'),
|
|
participants: z.array(simplifiedSpectatorSchema).optional(),
|
|
spectators: z.array(simplifiedSpectatorSchema).optional(), // Alias for participants
|
|
// Additional frontend compatibility fields
|
|
approverCount: z.number().optional(),
|
|
approvers: z.array(z.any()).optional(),
|
|
priorityUi: z.string().optional(),
|
|
templateId: z.string().optional(),
|
|
ccList: z.array(z.any()).optional(),
|
|
isDraft: z.boolean().optional(),
|
|
});
|
|
|
|
export const updateWorkflowSchema = z.object({
|
|
title: z.string().min(1).max(500).optional(),
|
|
description: z.string().min(1).optional(),
|
|
priority: z.enum(['STANDARD', 'EXPRESS'] as const).optional(),
|
|
status: z.enum(['DRAFT', 'PENDING', 'APPROVED', 'REJECTED', 'CLOSED', 'PAUSED'] as const).optional(),
|
|
conclusionRemark: z.string().optional(),
|
|
// For draft updates - allow updating approval levels and participants
|
|
approvalLevels: z.array(z.object({
|
|
levelNumber: z.number().int().min(1).max(10),
|
|
levelName: z.string().optional(),
|
|
approverId: z.string().uuid(),
|
|
approverEmail: z.string().email(),
|
|
approverName: z.string().min(1),
|
|
tatHours: z.number().positive(),
|
|
isFinalApprover: z.boolean().optional(),
|
|
})).optional(),
|
|
participants: z.array(z.object({
|
|
userId: z.string().uuid(),
|
|
userEmail: z.string().email(),
|
|
userName: z.string().min(1),
|
|
participantType: z.enum(['INITIATOR', 'APPROVER', 'SPECTATOR'] as const),
|
|
canComment: z.boolean().optional(),
|
|
canViewDocuments: z.boolean().optional(),
|
|
canDownloadDocuments: z.boolean().optional(),
|
|
notificationEnabled: z.boolean().optional(),
|
|
})).optional(),
|
|
deleteDocumentIds: z.array(z.string().uuid()).optional(),
|
|
isDraft: z.boolean().optional(),
|
|
});
|
|
|
|
// Helper to validate UUID or requestNumber format
|
|
// Supports both old format (REQ-YYYY-NNNNN) and new format (REQ-YYYY-MM-XXXX)
|
|
const workflowIdValidator = z.string().refine(
|
|
(val) => {
|
|
// Check if it's a valid UUID
|
|
const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
|
|
if (uuidRegex.test(val)) {
|
|
return true;
|
|
}
|
|
// Check if it's a valid requestNumber format
|
|
// Old format: REQ-YYYY-NNNNN (e.g., REQ-2025-12057) - 5+ digits after year
|
|
// New format: REQ-YYYY-MM-XXXX (e.g., REQ-2025-11-0001) - 2-digit month, 4-digit counter
|
|
const oldFormatRegex = /^REQ-\d{4}-\d{5,}$/i; // Old: REQ-2025-12057
|
|
const newFormatRegex = /^REQ-\d{4}-\d{2}-\d{4}$/i; // New: REQ-2025-11-0001
|
|
if (oldFormatRegex.test(val) || newFormatRegex.test(val)) {
|
|
return true;
|
|
}
|
|
return false;
|
|
},
|
|
{
|
|
message: 'Invalid workflow ID - must be a valid UUID or requestNumber (e.g., REQ-2025-11-0001 or REQ-2025-12057)',
|
|
}
|
|
);
|
|
|
|
export const workflowParamsSchema = z.object({
|
|
id: workflowIdValidator,
|
|
});
|
|
|
|
export const workflowQuerySchema = z.object({
|
|
page: z.string().transform(Number).pipe(z.number().int().min(1)).optional(),
|
|
limit: z.string().transform(Number).pipe(z.number().int().min(1).max(100)).optional(),
|
|
status: z.enum(['DRAFT', 'PENDING', 'APPROVED', 'REJECTED', 'CLOSED', 'PAUSED'] as const).optional(),
|
|
priority: z.enum(['STANDARD', 'EXPRESS'] as const).optional(),
|
|
sortBy: z.string().optional(),
|
|
sortOrder: z.enum(['ASC', 'DESC'] as const).optional(),
|
|
});
|
|
|
|
export const validateCreateWorkflow = (data: any) => {
|
|
return createWorkflowSchema.parse(data);
|
|
};
|
|
|
|
export const validateUpdateWorkflow = (data: any) => {
|
|
return updateWorkflowSchema.parse(data);
|
|
};
|