task-h
This commit is contained in:
@@ -1,5 +1,43 @@
|
||||
import * as z from 'zod';
|
||||
|
||||
function coerceOptionalNumber(message: string) {
|
||||
return z.preprocess((value) => {
|
||||
if (value === '' || value === null || value === undefined) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
if (typeof value === 'number') {
|
||||
return value;
|
||||
}
|
||||
|
||||
if (typeof value === 'string') {
|
||||
const trimmed = value.trim();
|
||||
|
||||
if (!trimmed) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return Number(trimmed);
|
||||
}
|
||||
|
||||
return value;
|
||||
}, z.number().refine((value) => !Number.isNaN(value), message).optional());
|
||||
}
|
||||
|
||||
function coerceRequiredNumber(message: string) {
|
||||
return z.preprocess((value) => {
|
||||
if (typeof value === 'number') {
|
||||
return value;
|
||||
}
|
||||
|
||||
if (typeof value === 'string') {
|
||||
return Number(value.trim());
|
||||
}
|
||||
|
||||
return value;
|
||||
}, z.number().refine((value) => !Number.isNaN(value), message));
|
||||
}
|
||||
|
||||
export const quotationSchema = z.object({
|
||||
enquiryId: z.string().optional().nullable(),
|
||||
customerId: z.string().min(1, 'Please select a customer'),
|
||||
@@ -12,14 +50,11 @@ export const quotationSchema = z.object({
|
||||
attention: z.string().optional(),
|
||||
branchId: z.string().optional().nullable(),
|
||||
currency: z.string().min(1, 'Please select a currency'),
|
||||
exchangeRate: z.number().nullable().optional(),
|
||||
exchangeRate: coerceOptionalNumber('Exchange rate must be a number'),
|
||||
status: z.string().min(1, 'Please select a status'),
|
||||
chancePercent: z
|
||||
.number()
|
||||
.nullable()
|
||||
.optional()
|
||||
chancePercent: coerceOptionalNumber('Chance percent must be a number')
|
||||
.refine(
|
||||
(value) => value === null || value === undefined || (value >= 0 && value <= 100),
|
||||
(value) => value === undefined || (value >= 0 && value <= 100),
|
||||
'Chance percent must be between 0 and 100'
|
||||
),
|
||||
isHotProject: z.boolean().default(false),
|
||||
@@ -27,9 +62,9 @@ export const quotationSchema = z.object({
|
||||
reference: z.string().optional(),
|
||||
notes: z.string().optional(),
|
||||
salesmanId: z.string().optional().nullable(),
|
||||
discount: z.number().nullable().optional(),
|
||||
discount: coerceOptionalNumber('Discount must be a number'),
|
||||
discountType: z.string().optional().nullable(),
|
||||
taxRate: z.number().nullable().optional(),
|
||||
taxRate: coerceOptionalNumber('Tax rate must be a number'),
|
||||
isActive: z.boolean().default(true),
|
||||
sentVia: z.string().optional().nullable(),
|
||||
revisionRemark: z.string().optional().nullable()
|
||||
@@ -38,14 +73,26 @@ export const quotationSchema = z.object({
|
||||
export const quotationItemSchema = z.object({
|
||||
productType: z.string().min(1, 'Please select a product type'),
|
||||
description: z.string().min(2, 'Description must be at least 2 characters'),
|
||||
quantity: z.number().positive('Quantity must be greater than 0'),
|
||||
quantity: coerceRequiredNumber('Quantity must be a number').refine(
|
||||
(value) => value > 0,
|
||||
'Quantity must be greater than 0'
|
||||
),
|
||||
unit: z.string().optional(),
|
||||
unitPrice: z.number().min(0, 'Unit price must be 0 or greater'),
|
||||
discount: z.number().min(0).nullable().optional(),
|
||||
unitPrice: coerceRequiredNumber('Unit price must be a number').refine(
|
||||
(value) => value >= 0,
|
||||
'Unit price must be 0 or greater'
|
||||
),
|
||||
discount: coerceOptionalNumber('Discount must be a number').refine(
|
||||
(value) => value === undefined || value >= 0,
|
||||
'Discount must be 0 or greater'
|
||||
),
|
||||
discountType: z.string().nullable().optional(),
|
||||
taxRate: z.number().min(0).nullable().optional(),
|
||||
taxRate: coerceOptionalNumber('Tax rate must be a number').refine(
|
||||
(value) => value === undefined || value >= 0,
|
||||
'Tax rate must be 0 or greater'
|
||||
),
|
||||
notes: z.string().optional(),
|
||||
sortOrder: z.number().nullable().optional()
|
||||
sortOrder: coerceOptionalNumber('Sort order must be a number')
|
||||
});
|
||||
|
||||
export const quotationCustomerSchema = z.object({
|
||||
@@ -58,13 +105,13 @@ export const quotationTopicSchema = z.object({
|
||||
id: z.string().optional(),
|
||||
topicType: z.string().min(1, 'Please select a topic type'),
|
||||
title: z.string().min(2, 'Title must be at least 2 characters'),
|
||||
sortOrder: z.number().nullable().optional(),
|
||||
sortOrder: coerceOptionalNumber('Sort order must be a number'),
|
||||
items: z
|
||||
.array(
|
||||
z.object({
|
||||
id: z.string().optional(),
|
||||
content: z.string().min(1, 'Topic item content is required'),
|
||||
sortOrder: z.number().nullable().optional()
|
||||
sortOrder: coerceOptionalNumber('Sort order must be a number')
|
||||
})
|
||||
)
|
||||
.min(1, 'At least one topic item is required')
|
||||
@@ -86,7 +133,7 @@ export const quotationAttachmentSchema = z.object({
|
||||
fileName: z.string().min(1, 'Stored file name is required'),
|
||||
originalFileName: z.string().min(1, 'Original file name is required'),
|
||||
filePath: z.string().min(1, 'File path is required'),
|
||||
fileSize: z.number().nullable().optional(),
|
||||
fileSize: coerceOptionalNumber('File size must be a number'),
|
||||
fileType: z.string().nullable().optional(),
|
||||
description: z.string().optional()
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user