Bernt Christian Egeland 7 月之前
父节点
当前提交
bb4c7ff585

+ 5 - 5
src/features/customers/Schema/customerSchema.ts

@@ -2,11 +2,11 @@ import { z } from "zod";
 
 
 export const createCustomerSchema = z.object({
 export const createCustomerSchema = z.object({
   name: z.string().min(1, "Name is required"),
   name: z.string().min(1, "Name is required"),
-  email: z.string().email("Invalid email").optional().or(z.literal("")),
-  phone: z.string().optional(),
-  address: z.string().optional(),
-  company: z.string().optional(),
-  notes: z.string().optional(),
+  email: z.string().email("Invalid email").nullish().or(z.literal("")),
+  phone: z.string().nullish(),
+  address: z.string().nullish(),
+  company: z.string().nullish(),
+  notes: z.string().nullish(),
 });
 });
 
 
 export const updateCustomerSchema = createCustomerSchema.partial().extend({
 export const updateCustomerSchema = createCustomerSchema.partial().extend({

+ 6 - 3
src/features/inspections/Schema/inspectionSchema.ts

@@ -3,13 +3,16 @@ import { z } from "zod";
 export const createInspectionSchema = z.object({
 export const createInspectionSchema = z.object({
   vehicleId: z.string().min(1, "Vehicle is required"),
   vehicleId: z.string().min(1, "Vehicle is required"),
   templateId: z.string().min(1, "Template is required"),
   templateId: z.string().min(1, "Template is required"),
-  mileage: z.coerce.number().int().min(0).optional(),
+  mileage: z.preprocess(
+    (v) => (v === null || v === undefined || v === "" ? undefined : v),
+    z.coerce.number().int().min(0).optional(),
+  ),
 });
 });
 
 
 export const updateInspectionItemSchema = z.object({
 export const updateInspectionItemSchema = z.object({
   condition: z.enum(["pass", "fail", "attention", "not_inspected"]).default("not_inspected"),
   condition: z.enum(["pass", "fail", "attention", "not_inspected"]).default("not_inspected"),
-  notes: z.string().optional(),
-  imageUrls: z.array(z.string()).optional(),
+  notes: z.string().nullish(),
+  imageUrls: z.array(z.string()).nullish(),
 });
 });
 
 
 export type CreateInspectionInput = z.infer<typeof createInspectionSchema>;
 export type CreateInspectionInput = z.infer<typeof createInspectionSchema>;

+ 9 - 9
src/features/inventory/Schema/inventorySchema.ts

@@ -2,19 +2,19 @@ import { z } from "zod";
 
 
 export const createInventoryPartSchema = z.object({
 export const createInventoryPartSchema = z.object({
   name: z.string().min(1, "Part name is required"),
   name: z.string().min(1, "Part name is required"),
-  partNumber: z.string().optional(),
-  description: z.string().optional(),
-  category: z.string().optional(),
+  partNumber: z.string().nullish(),
+  description: z.string().nullish(),
+  category: z.string().nullish(),
   quantity: z.coerce.number().int().min(0, "Quantity must be 0 or more").default(0),
   quantity: z.coerce.number().int().min(0, "Quantity must be 0 or more").default(0),
   minQuantity: z.coerce.number().int().min(0, "Min quantity must be 0 or more").default(0),
   minQuantity: z.coerce.number().int().min(0, "Min quantity must be 0 or more").default(0),
   unitCost: z.coerce.number().min(0, "Unit cost must be 0 or more").default(0),
   unitCost: z.coerce.number().min(0, "Unit cost must be 0 or more").default(0),
   sellPrice: z.coerce.number().min(0, "Sell price must be 0 or more").default(0),
   sellPrice: z.coerce.number().min(0, "Sell price must be 0 or more").default(0),
-  supplier: z.string().optional(),
-  supplierPhone: z.string().optional(),
-  supplierEmail: z.string().optional(),
-  supplierUrl: z.string().url().optional().or(z.literal("")),
-  imageUrl: z.string().url().optional().or(z.literal("")),
-  location: z.string().optional(),
+  supplier: z.string().nullish(),
+  supplierPhone: z.string().nullish(),
+  supplierEmail: z.string().nullish(),
+  supplierUrl: z.string().url().nullish().or(z.literal("")),
+  imageUrl: z.string().url().nullish().or(z.literal("")),
+  location: z.string().nullish(),
 });
 });
 
 
 export const updateInventoryPartSchema = createInventoryPartSchema.partial().extend({
 export const updateInventoryPartSchema = createInventoryPartSchema.partial().extend({

+ 1 - 1
src/features/payments/Schema/paymentSchema.ts

@@ -5,7 +5,7 @@ export const createPaymentSchema = z.object({
   amount: z.coerce.number().min(0.01, "Amount must be greater than 0"),
   amount: z.coerce.number().min(0.01, "Amount must be greater than 0"),
   date: z.string().default(() => new Date().toISOString()),
   date: z.string().default(() => new Date().toISOString()),
   method: z.enum(["cash", "card", "transfer", "other"]).default("other"),
   method: z.enum(["cash", "card", "transfer", "other"]).default("other"),
-  note: z.string().optional(),
+  note: z.string().nullish(),
 });
 });
 
 
 export type CreatePaymentInput = z.infer<typeof createPaymentSchema>;
 export type CreatePaymentInput = z.infer<typeof createPaymentSchema>;

+ 11 - 11
src/features/quotes/Schema/quoteSchema.ts

@@ -1,7 +1,7 @@
 import { z } from "zod";
 import { z } from "zod";
 
 
 export const quotePartSchema = z.object({
 export const quotePartSchema = z.object({
-  partNumber: z.string().optional(),
+  partNumber: z.string().nullish(),
   name: z.string().min(1, "Part name is required"),
   name: z.string().min(1, "Part name is required"),
   quantity: z.coerce.number().min(0).default(1),
   quantity: z.coerce.number().min(0).default(1),
   unitPrice: z.coerce.number().min(0).default(0),
   unitPrice: z.coerce.number().min(0).default(0),
@@ -23,28 +23,28 @@ export const quoteAttachmentSchema = z.object({
   fileType: z.string(),
   fileType: z.string(),
   fileSize: z.number(),
   fileSize: z.number(),
   category: z.enum(["image", "document"]).default("image"),
   category: z.enum(["image", "document"]).default("image"),
-  description: z.string().optional(),
+  description: z.string().nullish(),
   includeInInvoice: z.boolean().default(true),
   includeInInvoice: z.boolean().default(true),
 });
 });
 
 
 export const createQuoteSchema = z.object({
 export const createQuoteSchema = z.object({
   title: z.string().min(1, "Title is required"),
   title: z.string().min(1, "Title is required"),
-  description: z.string().optional(),
+  description: z.string().nullish(),
   status: z.enum(["draft", "sent", "accepted", "rejected", "expired", "converted", "changes_requested"]).default("draft"),
   status: z.enum(["draft", "sent", "accepted", "rejected", "expired", "converted", "changes_requested"]).default("draft"),
-  validUntil: z.string().optional(),
-  customerId: z.string().optional(),
-  vehicleId: z.string().optional(),
-  partItems: z.array(quotePartSchema).optional(),
-  laborItems: z.array(quoteLaborSchema).optional(),
+  validUntil: z.string().nullish(),
+  customerId: z.string().nullish(),
+  vehicleId: z.string().nullish(),
+  partItems: z.array(quotePartSchema).nullish(),
+  laborItems: z.array(quoteLaborSchema).nullish(),
   subtotal: z.coerce.number().min(0).default(0),
   subtotal: z.coerce.number().min(0).default(0),
   taxRate: z.coerce.number().min(0).default(0),
   taxRate: z.coerce.number().min(0).default(0),
   taxAmount: z.coerce.number().min(0).default(0),
   taxAmount: z.coerce.number().min(0).default(0),
-  discountType: z.enum(["none", "percentage", "fixed"]).optional(),
+  discountType: z.enum(["none", "percentage", "fixed"]).nullish(),
   discountValue: z.coerce.number().min(0).default(0),
   discountValue: z.coerce.number().min(0).default(0),
   discountAmount: z.coerce.number().min(0).default(0),
   discountAmount: z.coerce.number().min(0).default(0),
   totalAmount: z.coerce.number().min(0).default(0),
   totalAmount: z.coerce.number().min(0).default(0),
-  notes: z.string().optional(),
-  inspectionId: z.string().optional(),
+  notes: z.string().nullish(),
+  inspectionId: z.string().nullish(),
 });
 });
 
 
 export const updateQuoteSchema = createQuoteSchema.partial().extend({
 export const updateQuoteSchema = createQuoteSchema.partial().extend({

+ 6 - 3
src/features/vehicles/Schema/reminderSchema.ts

@@ -3,9 +3,12 @@ import { z } from "zod";
 export const createReminderSchema = z.object({
 export const createReminderSchema = z.object({
   vehicleId: z.string(),
   vehicleId: z.string(),
   title: z.string().min(1, "Title is required"),
   title: z.string().min(1, "Title is required"),
-  description: z.string().optional(),
-  dueDate: z.string().optional(),
-  dueMileage: z.coerce.number().optional(),
+  description: z.string().nullish(),
+  dueDate: z.string().nullish(),
+  dueMileage: z.preprocess(
+    (v) => (v === null || v === undefined || v === "" ? undefined : v),
+    z.coerce.number().optional(),
+  ),
 });
 });
 
 
 export type CreateReminderInput = z.infer<typeof createReminderSchema>;
 export type CreateReminderInput = z.infer<typeof createReminderSchema>;

+ 22 - 16
src/features/vehicles/Schema/serviceSchema.ts

@@ -1,12 +1,18 @@
 import { z } from "zod";
 import { z } from "zod";
 
 
+// Coerce to number but pass through null/undefined instead of coercing null to 0
+const coerceNumberNullish = z.preprocess(
+  (v) => (v === null || v === undefined || v === "" ? undefined : v),
+  z.coerce.number().optional(),
+);
+
 export const servicePartSchema = z.object({
 export const servicePartSchema = z.object({
-  partNumber: z.string().optional(),
+  partNumber: z.string().nullish(),
   name: z.string().min(1, "Part name is required"),
   name: z.string().min(1, "Part name is required"),
   quantity: z.coerce.number().min(0).default(1),
   quantity: z.coerce.number().min(0).default(1),
   unitPrice: z.coerce.number().min(0).default(0),
   unitPrice: z.coerce.number().min(0).default(0),
   total: z.coerce.number().min(0).default(0),
   total: z.coerce.number().min(0).default(0),
-  inventoryPartId: z.string().optional(),
+  inventoryPartId: z.string().nullish(),
 });
 });
 
 
 export const serviceLaborSchema = z.object({
 export const serviceLaborSchema = z.object({
@@ -22,36 +28,36 @@ export const serviceAttachmentSchema = z.object({
   fileType: z.string(),
   fileType: z.string(),
   fileSize: z.number(),
   fileSize: z.number(),
   category: z.enum(["image", "diagnostic", "document", "video"]).default("diagnostic"),
   category: z.enum(["image", "diagnostic", "document", "video"]).default("diagnostic"),
-  description: z.string().optional(),
+  description: z.string().nullish(),
   includeInInvoice: z.boolean().default(true),
   includeInInvoice: z.boolean().default(true),
 });
 });
 
 
 export const createServiceSchema = z.object({
 export const createServiceSchema = z.object({
   vehicleId: z.string(),
   vehicleId: z.string(),
   title: z.string().min(1, "Title is required"),
   title: z.string().min(1, "Title is required"),
-  description: z.string().optional(),
+  description: z.string().nullish(),
   type: z.enum(["maintenance", "repair", "upgrade", "inspection"]).default("maintenance"),
   type: z.enum(["maintenance", "repair", "upgrade", "inspection"]).default("maintenance"),
   status: z.enum(["pending", "in-progress", "waiting-parts", "completed"]).default("pending"),
   status: z.enum(["pending", "in-progress", "waiting-parts", "completed"]).default("pending"),
   cost: z.coerce.number().min(0).default(0),
   cost: z.coerce.number().min(0).default(0),
-  mileage: z.coerce.number().optional(),
+  mileage: coerceNumberNullish,
   serviceDate: z.string().default(() => new Date().toISOString()),
   serviceDate: z.string().default(() => new Date().toISOString()),
-  shopName: z.string().optional(),
-  techName: z.string().optional(),
-  parts: z.string().optional(),
-  laborHours: z.coerce.number().optional(),
-  diagnosticNotes: z.string().optional(),
-  invoiceNotes: z.string().optional(),
-  partItems: z.array(servicePartSchema).optional(),
-  laborItems: z.array(serviceLaborSchema).optional(),
-  attachments: z.array(serviceAttachmentSchema).optional(),
+  shopName: z.string().nullish(),
+  techName: z.string().nullish(),
+  parts: z.string().nullish(),
+  laborHours: coerceNumberNullish,
+  diagnosticNotes: z.string().nullish(),
+  invoiceNotes: z.string().nullish(),
+  partItems: z.array(servicePartSchema).nullish(),
+  laborItems: z.array(serviceLaborSchema).nullish(),
+  attachments: z.array(serviceAttachmentSchema).nullish(),
   subtotal: z.coerce.number().min(0).default(0),
   subtotal: z.coerce.number().min(0).default(0),
   taxRate: z.coerce.number().min(0).default(0),
   taxRate: z.coerce.number().min(0).default(0),
   taxAmount: z.coerce.number().min(0).default(0),
   taxAmount: z.coerce.number().min(0).default(0),
   totalAmount: z.coerce.number().min(0).default(0),
   totalAmount: z.coerce.number().min(0).default(0),
-  discountType: z.enum(["none", "percentage", "fixed"]).optional(),
+  discountType: z.enum(["none", "percentage", "fixed"]).nullish(),
   discountValue: z.coerce.number().min(0).default(0),
   discountValue: z.coerce.number().min(0).default(0),
   discountAmount: z.coerce.number().min(0).default(0),
   discountAmount: z.coerce.number().min(0).default(0),
-  invoiceNumber: z.string().optional(),
+  invoiceNumber: z.string().nullish(),
 });
 });
 
 
 export const updateServiceSchema = createServiceSchema.partial().extend({
 export const updateServiceSchema = createServiceSchema.partial().extend({

+ 13 - 10
src/features/vehicles/Schema/vehicleSchema.ts

@@ -7,17 +7,20 @@ export const createVehicleSchema = z.object({
     .number()
     .number()
     .min(1900, "Year must be after 1900")
     .min(1900, "Year must be after 1900")
     .max(new Date().getFullYear() + 2, "Year is too far in the future"),
     .max(new Date().getFullYear() + 2, "Year is too far in the future"),
-  vin: z.string().optional(),
-  licensePlate: z.string().optional(),
-  color: z.string().optional(),
+  vin: z.string().nullish(),
+  licensePlate: z.string().nullish(),
+  color: z.string().nullish(),
   mileage: z.coerce.number().min(0).default(0),
   mileage: z.coerce.number().min(0).default(0),
-  fuelType: z.string().optional(),
-  transmission: z.string().optional(),
-  engineSize: z.string().optional(),
-  purchaseDate: z.string().optional(),
-  purchasePrice: z.coerce.number().optional(),
-  imageUrl: z.string().optional(),
-  customerId: z.string().optional(),
+  fuelType: z.string().nullish(),
+  transmission: z.string().nullish(),
+  engineSize: z.string().nullish(),
+  purchaseDate: z.string().nullish(),
+  purchasePrice: z.preprocess(
+    (v) => (v === null || v === undefined || v === "" ? undefined : v),
+    z.coerce.number().optional(),
+  ),
+  imageUrl: z.string().nullish(),
+  customerId: z.string().nullish(),
 });
 });
 
 
 export const updateVehicleSchema = createVehicleSchema.partial().extend({
 export const updateVehicleSchema = createVehicleSchema.partial().extend({

+ 4 - 4
src/features/workboard/Schema/workboardSchema.ts

@@ -6,7 +6,7 @@ export const createTechnicianSchema = z.object({
     .string()
     .string()
     .regex(/^#[0-9a-fA-F]{6}$/, "Must be a valid hex color")
     .regex(/^#[0-9a-fA-F]{6}$/, "Must be a valid hex color")
     .default("#3b82f6"),
     .default("#3b82f6"),
-  memberId: z.string().optional(),
+  memberId: z.string().nullish(),
 });
 });
 
 
 export const updateTechnicianSchema = z.object({
 export const updateTechnicianSchema = z.object({
@@ -24,9 +24,9 @@ export const updateTechnicianSchema = z.object({
 export const createBoardAssignmentSchema = z.object({
 export const createBoardAssignmentSchema = z.object({
   date: z.string().min(1, "Date is required"),
   date: z.string().min(1, "Date is required"),
   technicianId: z.string().min(1, "Technician is required"),
   technicianId: z.string().min(1, "Technician is required"),
-  serviceRecordId: z.string().optional(),
-  inspectionId: z.string().optional(),
-  notes: z.string().optional(),
+  serviceRecordId: z.string().nullish(),
+  inspectionId: z.string().nullish(),
+  notes: z.string().nullish(),
   sortOrder: z.number().int().min(0).default(0),
   sortOrder: z.number().int().min(0).default(0),
 });
 });