import { z } from "zod"; import { CARGO_TYPES } from "@/modules/quote/validation"; import { normalizeDimUnit, normalizeWeightUnit, weightUnitAmbiguousMessage, } from "@/modules/quote/unit-converter"; const US_ZIP_REGEX = /^\d{5}(-\d{4})?$/; const addressDraftSchema = z.object({ street: z.string().min(1, "请填写街道地址"), city: z.string().min(1, "请填写城市"), state: z.string().length(2, "请填写 2 位州码"), zip: z .string() .refine((v) => v === "" || US_ZIP_REGEX.test(v), "邮编格式无效") .optional() .default(""), }); const apiWeightUnitSchema = z .string({ errorMap: () => ({ message: "请选择有效的重量单位" }) }) .superRefine((s, ctx) => { const ambiguous = weightUnitAmbiguousMessage(s); if (ambiguous) { ctx.addIssue({ code: z.ZodIssueCode.custom, message: ambiguous }); return; } if (normalizeWeightUnit(s) === null) { ctx.addIssue({ code: z.ZodIssueCode.custom, message: "请选择有效的重量单位(lb / kg / t)", }); } }) .transform((s) => normalizeWeightUnit(s)!); const apiDimUnitSchema = z .string({ errorMap: () => ({ message: "请选择有效的尺寸单位" }) }) .refine( (s) => normalizeDimUnit(s) !== null, "请选择有效的尺寸单位(in / cm / m)", ) .transform((s) => normalizeDimUnit(s)!); const positiveFinite = (label: string) => z .number({ invalid_type_error: `请填写${label}` }) .finite(`请填写有效的${label}`) .positive(`请填写有效的${label}`); const cargoSchema = z.object({ weight: z.object({ value: positiveFinite("重量"), unit: apiWeightUnitSchema, }), dimensions: z.object({ length: positiveFinite("长度"), width: positiveFinite("宽度"), height: positiveFinite("高度"), unit: apiDimUnitSchema, }), pallet_count: z .number({ invalid_type_error: "请填写托盘数" }) .finite("请填写有效的托盘数") .int("请填写有效的托盘数"), cargo_type: z.enum(CARGO_TYPES), }); const candidateSchema = z.object({ option_id: z.string().min(1), display_label: z.string().min(1), formatted_address: z.string().min(1), street: z.string().min(1), city: z.string().min(1), state: z.string().length(2), zip: z.string().optional().default(""), selectable: z.boolean().optional(), unavailable_reason: z.string().optional(), }); export const hostCandidatesBodySchema = z.object({ pickup_address: addressDraftSchema, delivery_address: addressDraftSchema, cargo: cargoSchema, }); export const hostSubmitBodySchema = z.object({ host_session_id: z.string().uuid("会话标识无效"), pickup_candidate: candidateSchema, delivery_candidate: candidateSchema, });