import { createSearchParamsCache, parseAsArrayOf, parseAsInteger, parseAsString, parseAsStringEnum, } from "nuqs/server" import * as z from "zod" import { getFiltersStateParser, getSortingStateParser } from "@/lib/parsers" import { techVendors, TechVendor, TechVendorContact, TechVendorItemsView, VENDOR_TYPES } from "@/db/schema/techVendors"; export const searchParamsCache = createSearchParamsCache({ // 공통 플래그 flags: parseAsArrayOf(z.enum(["advancedTable", "floatingBar"])).withDefault( [] ), // 페이징 page: parseAsInteger.withDefault(1), perPage: parseAsInteger.withDefault(10), // 정렬 (techVendors 테이블에 맞춰 TechVendor 타입 지정) sort: getSortingStateParser().withDefault([ { id: "createdAt", desc: true }, // createdAt 기준 내림차순 ]), // 고급 필터 filters: getFiltersStateParser().withDefault([]), joinOperator: parseAsStringEnum(["and", "or"]).withDefault("and"), // 검색 키워드 search: parseAsString.withDefault(""), // ----------------------------------------------------------------- // 기술영업 협력업체에 특화된 검색 필드 // ----------------------------------------------------------------- // 상태 (ACTIVE, INACTIVE, BLACKLISTED 등) 중에서 선택 status: parseAsStringEnum(["ACTIVE", "INACTIVE", "BLACKLISTED", "PENDING_REVIEW", "IN_REVIEW", "REJECTED"]), // 협력업체명 검색 vendorName: parseAsString.withDefault(""), // 국가 검색 country: parseAsString.withDefault(""), // 예) 코드 검색 vendorCode: parseAsString.withDefault(""), // 필요하다면 이메일 검색 / 웹사이트 검색 등 추가 가능 email: parseAsString.withDefault(""), website: parseAsString.withDefault(""), }); export const searchParamsContactCache = createSearchParamsCache({ // 공통 플래그 flags: parseAsArrayOf(z.enum(["advancedTable", "floatingBar"])).withDefault( [] ), // 페이징 page: parseAsInteger.withDefault(1), perPage: parseAsInteger.withDefault(10), // 정렬 sort: getSortingStateParser().withDefault([ { id: "createdAt", desc: true }, // createdAt 기준 내림차순 ]), // 고급 필터 filters: getFiltersStateParser().withDefault([]), joinOperator: parseAsStringEnum(["and", "or"]).withDefault("and"), // 검색 키워드 search: parseAsString.withDefault(""), // 특정 필드 검색 contactName: parseAsString.withDefault(""), contactPosition: parseAsString.withDefault(""), contactEmail: parseAsString.withDefault(""), contactPhone: parseAsString.withDefault(""), }); export const searchParamsItemCache = createSearchParamsCache({ // 공통 플래그 flags: parseAsArrayOf(z.enum(["advancedTable", "floatingBar"])).withDefault( [] ), // 페이징 page: parseAsInteger.withDefault(1), perPage: parseAsInteger.withDefault(10), // 정렬 sort: getSortingStateParser().withDefault([ { id: "createdAt", desc: true }, // createdAt 기준 내림차순 ]), // 고급 필터 filters: getFiltersStateParser().withDefault([]), joinOperator: parseAsStringEnum(["and", "or"]).withDefault("and"), // 검색 키워드 search: parseAsString.withDefault(""), // 특정 필드 검색 itemName: parseAsString.withDefault(""), itemCode: parseAsString.withDefault(""), }); // 기술영업 벤더 기본 정보 업데이트 스키마 export const updateTechVendorSchema = z.object({ vendorName: z.string().min(1, "업체명은 필수 입력사항입니다"), vendorCode: z.string().optional(), address: z.string().optional(), country: z.string().optional(), phone: z.string().optional(), email: z.string().email("유효한 이메일 주소를 입력해주세요").optional(), website: z.string().url("유효한 URL을 입력해주세요").optional(), status: z.enum(techVendors.status.enumValues).optional(), userId: z.number().optional(), comment: z.string().optional(), }); // 연락처 스키마 const contactSchema = z.object({ id: z.number().optional(), contactName: z .string() .min(1, "Contact name is required") .max(255, "Max length 255"), contactPosition: z.string().max(100).optional(), contactEmail: z.string().email("Invalid email").max(255), contactPhone: z.string().max(50).optional(), isPrimary: z.boolean().default(false).optional() }); // 기술영업 벤더 생성 스키마 export const createTechVendorSchema = z .object({ vendorName: z .string() .min(1, "Vendor name is required") .max(255, "Max length 255"), email: z.string().email("Invalid email").max(255), // 나머지 optional 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(), website: z.string().url("유효하지 않은 URL입니다. https:// 혹은 http:// 로 시작하는 주소를 입력해주세요.").max(255).optional(), files: z.any().optional(), status: z.enum(techVendors.status.enumValues).default("ACTIVE"), techVendorType: z.enum(VENDOR_TYPES).default("조선"), 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(), taxId: z.string().min(1, { message: "사업자등록번호를 입력해주세요" }), items: z.string().min(1, { message: "공급품목을 입력해주세요" }), contacts: z .array(contactSchema) .nonempty("At least one contact is required.") }) .superRefine((data, ctx) => { 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) 업체일 경우 필수입니다.", }) } } }); // 연락처 생성 스키마 export const createTechVendorContactSchema = z.object({ vendorId: z.number(), contactName: z.string() .min(1, "Contact name is required") .max(255, "Max length 255"), contactPosition: z.string().max(100, "Max length 100"), contactEmail: z.string().email(), contactPhone: z.string().max(50, "Max length 50").optional(), isPrimary: z.boolean(), }); // 연락처 업데이트 스키마 export const updateTechVendorContactSchema = z.object({ contactName: z.string() .min(1, "Contact name is required") .max(255, "Max length 255"), contactPosition: z.string().max(100, "Max length 100").optional(), contactEmail: z.string().email().optional(), contactPhone: z.string().max(50, "Max length 50").optional(), isPrimary: z.boolean().optional(), }); // 아이템 생성 스키마 export const createTechVendorItemSchema = z.object({ vendorId: z.number(), itemCode: z.string().max(100, "Max length 100"), itemName: z.string().min(1, "Item name is required").max(255, "Max length 255"), }); // 아이템 업데이트 스키마 export const updateTechVendorItemSchema = z.object({ itemName: z.string().optional(), itemCode: z.string().max(100, "Max length 100"), }); // 타입 내보내기 export type GetTechVendorsSchema = Awaited> export type GetTechVendorContactsSchema = Awaited> export type GetTechVendorItemsSchema = Awaited> export type UpdateTechVendorSchema = z.infer export type CreateTechVendorSchema = z.infer export type CreateTechVendorContactSchema = z.infer export type UpdateTechVendorContactSchema = z.infer export type CreateTechVendorItemSchema = z.infer export type UpdateTechVendorItemSchema = z.infer