diff options
| author | dujinkim <dujin.kim@dtsolution.co.kr> | 2025-06-27 01:16:20 +0000 |
|---|---|---|
| committer | dujinkim <dujin.kim@dtsolution.co.kr> | 2025-06-27 01:16:20 +0000 |
| commit | e9897d416b3e7327bbd4d4aef887eee37751ae82 (patch) | |
| tree | bd20ce6eadf9b21755bd7425492d2d31c7700a0e /lib/vendors/validations.ts | |
| parent | 3bf1952c1dad9d479bb8b22031b06a7434d37c37 (diff) | |
(대표님) 20250627 오전 10시 작업사항
Diffstat (limited to 'lib/vendors/validations.ts')
| -rw-r--r-- | lib/vendors/validations.ts | 146 |
1 files changed, 111 insertions, 35 deletions
diff --git a/lib/vendors/validations.ts b/lib/vendors/validations.ts index 7ba54ccf..07eaae83 100644 --- a/lib/vendors/validations.ts +++ b/lib/vendors/validations.ts @@ -10,6 +10,7 @@ import * as z from "zod" import { getFiltersStateParser, getSortingStateParser } from "@/lib/parsers" import { Vendor, VendorContact, VendorItemsView, VendorMaterialsView, vendors, VendorWithType } from "@/db/schema/vendors"; import { rfqs } from "@/db/schema/rfq" +import { countryDialCodes } from "@/components/signup/join-form"; export const searchParamsCache = createSearchParamsCache({ @@ -155,97 +156,172 @@ const contactSchema = z.object({ const vendorStatusEnum = z.enum(vendors.status.enumValues) // CREATE 시: 일부 필드는 필수, 일부는 optional +// Phone validation helper function +const validatePhoneByCountry = (phone: string, country: string): boolean => { + if (!phone || !country) return false; + + // Remove spaces, hyphens, and parentheses for validation + const cleanPhone = phone.replace(/[\s\-\(\)]/g, ''); + + switch (country) { + case 'KR': // South Korea + // Should start with +82 and have 10-11 digits after country code + return /^\+82[1-9]\d{7,9}$/.test(cleanPhone) || /^0[1-9]\d{7,9}$/.test(cleanPhone); + + case 'US': // United States + case 'CA': // Canada + // Should start with +1 and have exactly 10 digits after + return /^\+1[2-9]\d{9}$/.test(cleanPhone); + + case 'JP': // Japan + // Should start with +81 and have 10-11 digits after country code + return /^\+81[1-9]\d{8,9}$/.test(cleanPhone); + + case 'CN': // China + // Should start with +86 and have 11 digits after country code + return /^\+86[1-9]\d{9}$/.test(cleanPhone); + + case 'GB': // United Kingdom + // Should start with +44 and have 10-11 digits after country code + return /^\+44[1-9]\d{8,9}$/.test(cleanPhone); + + case 'DE': // Germany + // Should start with +49 and have 10-12 digits after country code + return /^\+49[1-9]\d{9,11}$/.test(cleanPhone); + + case 'FR': // France + // Should start with +33 and have 9 digits after country code + return /^\+33[1-9]\d{8}$/.test(cleanPhone); + + default: + // For other countries, just check if it starts with + and has reasonable length + return /^\+\d{10,15}$/.test(cleanPhone); + } +}; + +// Enhanced createVendorSchema with phone validation export const createVendorSchema = z .object({ - vendorName: z .string() .min(1, "Vendor name is required") .max(255, "Max length 255"), - vendorTypeId: z.number({ required_error: "업체유형을 선택해주세요" }), - + + vendorTypeId: z.number({ required_error: "업체유형을 선택해주세요" }), + email: z.string().email("Invalid email").max(255), - // 나머지 optional + + // Updated phone validation - now required and must include country code + phone: z.string() + .min(1, "전화번호는 필수입니다") + .max(50, "Max length 50"), + + // Other fields remain the same vendorCode: z.string().max(100, "Max length 100").optional(), address: z.string().optional(), country: z.string() - .min(1, "국가 선택은 필수입니다.") - .max(100, "Max length 100"), - phone: z.string().max(50, "Max length 50").optional(), + .min(1, "국가 선택은 필수입니다.") + .max(100, "Max length 100"), website: z.string().url("유효하지 않은 URL입니다. https:// 혹은 http:// 로 시작하는 주소를 입력해주세요.").max(255).optional(), - + attachedFiles: z.any() - .refine( - val => { - // Validate that files exist and there's at least one file - return val && - (Array.isArray(val) ? val.length > 0 : - val instanceof FileList ? val.length > 0 : - val && typeof val === 'object' && 'length' in val && val.length > 0); - }, - { message: "첨부 파일은 필수입니다." } - ), + .refine( + val => { + return val && + (Array.isArray(val) ? val.length > 0 : + val instanceof FileList ? val.length > 0 : + val && typeof val === 'object' && 'length' in val && val.length > 0); + }, + { message: "첨부 파일은 필수입니다." } + ), + status: vendorStatusEnum.default("PENDING_REVIEW"), - + representativeName: z.union([z.string().max(255), z.literal("")]).optional(), representativeBirth: z.union([z.string().max(20), z.literal("")]).optional(), representativeEmail: z.union([z.string().email("Invalid email").max(255), z.literal("")]).optional(), representativePhone: z.union([z.string().max(50), z.literal("")]).optional(), corporateRegistrationNumber: z.union([z.string().max(100), z.literal("")]).optional(), taxId: z.string().min(1, { message: "사업자등록번호를 입력해주세요" }), - + items: z.string().min(1, { message: "공급품목을 입력해주세요" }), - + contacts: z - .array(contactSchema) - .nonempty("At least one contact is required."), - - // ... (기타 필드) + .array(contactSchema) + .nonempty("At least one contact is required."), }) .superRefine((data, ctx) => { + // Validate main phone number with country code + if (data.phone && data.country) { + if (!validatePhoneByCountry(data.phone, data.country)) { + const countryDialCode = countryDialCodes[data.country] || "+XX"; + ctx.addIssue({ + code: "custom", + path: ["phone"], + message: `올바른 전화번호 형식이 아닙니다. ${countryDialCode}로 시작하는 국제 전화번호를 입력해주세요. (예: ${countryDialCode}XXXXXXXXX)`, + }); + } + } + + // Validate representative phone for Korean companies if (data.country === "KR") { - // 1) 대표자 정보가 누락되면 각각 에러 발생 if (!data.representativeName) { ctx.addIssue({ code: "custom", path: ["representativeName"], message: "대표자 이름은 한국(KR) 업체일 경우 필수입니다.", - }) + }); } if (!data.representativeBirth) { ctx.addIssue({ code: "custom", path: ["representativeBirth"], message: "대표자 생년월일은 한국(KR) 업체일 경우 필수입니다.", - }) + }); } if (!data.representativeEmail) { ctx.addIssue({ code: "custom", path: ["representativeEmail"], message: "대표자 이메일은 한국(KR) 업체일 경우 필수입니다.", - }) + }); } if (!data.representativePhone) { ctx.addIssue({ code: "custom", path: ["representativePhone"], message: "대표자 전화번호는 한국(KR) 업체일 경우 필수입니다.", - }) + }); + } else if (!validatePhoneByCountry(data.representativePhone, "KR")) { + ctx.addIssue({ + code: "custom", + path: ["representativePhone"], + message: "올바른 한국 전화번호 형식이 아닙니다. +82로 시작하거나 010으로 시작하는 번호를 입력해주세요.", + }); } if (!data.corporateRegistrationNumber) { ctx.addIssue({ code: "custom", path: ["corporateRegistrationNumber"], message: "법인등록번호는 한국(KR) 업체일 경우 필수입니다.", - }) + }); } - - } - } -) + // Validate contact phone numbers + data.contacts?.forEach((contact, index) => { + if (contact.contactPhone && data.country) { + if (!validatePhoneByCountry(contact.contactPhone, data.country)) { + const countryDialCode = countryDialCodes[data.country] || "+XX"; + ctx.addIssue({ + code: "custom", + path: ["contacts", index, "contactPhone"], + message: `올바른 전화번호 형식이 아닙니다. ${countryDialCode}로 시작하는 국제 전화번호를 입력해주세요.`, + }); + } + } + }); + }); export const createVendorContactSchema = z.object({ vendorId: z.number(), contactName: z.string() |
