diff options
Diffstat (limited to 'lib/tech-vendors/validations.ts')
| -rw-r--r-- | lib/tech-vendors/validations.ts | 783 |
1 files changed, 386 insertions, 397 deletions
diff --git a/lib/tech-vendors/validations.ts b/lib/tech-vendors/validations.ts index 618ad22e..d217bee0 100644 --- a/lib/tech-vendors/validations.ts +++ b/lib/tech-vendors/validations.ts @@ -1,398 +1,387 @@ -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";
-
-// TechVendorPossibleItem 타입 정의
-export interface TechVendorPossibleItem {
- id: number;
- vendorId: number;
- vendorCode: string | null;
- vendorEmail: string | null;
- itemCode: string;
- workType: string | null;
- shipTypes: string | null;
- itemList: string | null;
- subItemList: string | null;
- createdAt: Date;
- updatedAt: Date;
- // 조인된 정보
- techVendorType?: "조선" | "해양TOP" | "해양HULL";
-}
-
-export const searchParamsCache = createSearchParamsCache({
- // 공통 플래그
- flags: parseAsArrayOf(z.enum(["advancedTable", "floatingBar"])).withDefault(
- []
- ),
-
- // 페이징
- page: parseAsInteger.withDefault(1),
- perPage: parseAsInteger.withDefault(10),
-
- // 정렬 (techVendors 테이블에 맞춰 TechVendor 타입 지정)
- sort: getSortingStateParser<TechVendor>().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"]),
-
- // 협력업체명 검색
- vendorName: parseAsString.withDefault(""),
-
- // 국가 검색
- country: parseAsString.withDefault(""),
-
- // 예) 코드 검색
- vendorCode: parseAsString.withDefault(""),
-
- // 벤더 타입 필터링 (다중 선택 가능)
- vendorType: parseAsStringEnum(["ship", "top", "hull"]),
-
- // workTypes 필터링 (다중 선택 가능)
- workTypes: parseAsArrayOf(parseAsStringEnum([
- // 조선 workTypes
- "기장", "전장", "선실", "배관", "철의", "선체",
- // 해양TOP workTypes
- "TM", "TS", "TE", "TP",
- // 해양HULL workTypes
- "HA", "HE", "HH", "HM", "NC", "HO", "HP"
- ])).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<TechVendorContact>().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<TechVendorItemsView>().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 searchParamsPossibleItemsCache = createSearchParamsCache({
- // 공통 플래그
- flags: parseAsArrayOf(z.enum(["advancedTable", "floatingBar"])).withDefault(
- []
- ),
-
- // 페이징
- page: parseAsInteger.withDefault(1),
- perPage: parseAsInteger.withDefault(10),
-
- // 정렬
- sort: getSortingStateParser<TechVendorPossibleItem>().withDefault([
- { id: "createdAt", desc: true },
- ]),
-
- // 고급 필터
- filters: getFiltersStateParser().withDefault([]),
- joinOperator: parseAsStringEnum(["and", "or"]).withDefault("and"),
-
- // 검색 키워드
- search: parseAsString.withDefault(""),
-
- // 개별 필터 필드들
- itemCode: parseAsString.withDefault(""),
- workType: parseAsString.withDefault(""),
- itemList: parseAsString.withDefault(""),
- shipTypes: parseAsString.withDefault(""),
- subItemList: parseAsString.withDefault(""),
-});
-
-// 기술영업 벤더 기본 정보 업데이트 스키마
-export const updateTechVendorSchema = z.object({
- vendorName: z.string().min(1, "업체명은 필수 입력사항입니다"),
- vendorCode: z.string().optional(),
- address: z.string().optional(),
- country: z.string().optional(),
- countryEng: z.string().optional(),
- countryFab: z.string().optional(),
- phone: z.string().optional(),
- email: z.string().email("유효한 이메일 주소를 입력해주세요").optional(),
- website: z.string().optional(),
- techVendorType: z.union([
- z.array(z.enum(VENDOR_TYPES)).min(1, "최소 하나의 벤더 타입을 선택해주세요"),
- z.string().min(1, "벤더 타입을 선택해주세요")
- ]).optional(),
- status: z.enum(techVendors.status.enumValues).optional(),
- // 에이전트 정보
- agentName: z.string().optional(),
- agentEmail: z.string().email("유효한 이메일 주소를 입력해주세요").optional().or(z.literal("")),
- agentPhone: z.string().optional(),
- // 대표자 정보
- representativeName: z.string().optional(),
- representativeEmail: z.string().email("유효한 이메일 주소를 입력해주세요").optional().or(z.literal("")),
- representativePhone: z.string().optional(),
- representativeBirth: z.string().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),
- contactCountry: z.string().max(100).optional(),
- 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().max(255).optional(),
-
- files: z.any().optional(),
- status: z.enum(techVendors.status.enumValues).default("ACTIVE"),
- techVendorType: z.union([
- z.array(z.enum(VENDOR_TYPES)).min(1, "최소 하나의 벤더 타입을 선택해주세요"),
- z.string().min(1, "벤더 타입을 선택해주세요")
- ]).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(),
- contactCountry: z.string().max(100, "Max length 100").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(),
- contactCountry: z.string().max(100, "Max length 100").optional(),
- isPrimary: z.boolean().optional(),
-});
-
-// 아이템 생성 스키마
-export const createTechVendorItemSchema = z.object({
- vendorId: z.number(),
- itemCode: z.string().max(100, "Max length 100"),
- itemList: z.string().min(1, "Item list is required").max(255, "Max length 255"),
-});
-
-// 아이템 업데이트 스키마
-export const updateTechVendorItemSchema = z.object({
- itemList: z.string().optional(),
- itemCode: z.string().max(100, "Max length 100"),
-});
-
-// Possible Items 생성 스키마
-export const createTechVendorPossibleItemSchema = z.object({
- vendorId: z.number(),
- itemCode: z.string().min(1, "아이템 코드는 필수입니다"),
- workType: z.string().optional(),
- shipTypes: z.string().optional(),
- itemList: z.string().optional(),
- subItemList: z.string().optional(),
-});
-
-// Possible Items 업데이트 스키마
-export const updateTechVendorPossibleItemSchema = createTechVendorPossibleItemSchema.extend({
- id: z.number(),
-});
-
-export const searchParamsRfqHistoryCache = createSearchParamsCache({
- // 공통 플래그
- flags: parseAsArrayOf(z.enum(["advancedTable", "floatingBar"])).withDefault(
- []
- ),
-
- // 페이징
- page: parseAsInteger.withDefault(1),
- perPage: parseAsInteger.withDefault(10),
-
- // 정렬 (RFQ 히스토리에 맞춰)
- sort: getSortingStateParser<{
- id: number;
- rfqCode: string | null;
- description: string | null;
- projectCode: string | null;
- projectName: string | null;
- projectType: string | null; // 프로젝트 타입 추가
- status: string;
- totalAmount: string | null;
- currency: string | null;
- dueDate: Date | null;
- createdAt: Date;
- quotationCode: string | null;
- submittedAt: Date | null;
- }>().withDefault([
- { id: "createdAt", desc: true },
- ]),
-
- // 고급 필터
- filters: getFiltersStateParser().withDefault([]),
- joinOperator: parseAsStringEnum(["and", "or"]).withDefault("and"),
-
- // 검색 키워드
- search: parseAsString.withDefault(""),
-
- // RFQ 히스토리 특화 필드
- rfqCode: parseAsString.withDefault(""),
- description: parseAsString.withDefault(""),
- projectCode: parseAsString.withDefault(""),
- projectName: parseAsString.withDefault(""),
- projectType: parseAsStringEnum(["SHIP", "TOP", "HULL"]), // 프로젝트 타입 필터 추가
- status: parseAsStringEnum(["DRAFT", "PUBLISHED", "EVALUATION", "AWARDED"]),
-});
-
-// 타입 내보내기
-export type GetTechVendorsSchema = Awaited<ReturnType<typeof searchParamsCache.parse>>
-export type GetTechVendorContactsSchema = Awaited<ReturnType<typeof searchParamsContactCache.parse>>
-export type GetTechVendorItemsSchema = Awaited<ReturnType<typeof searchParamsItemCache.parse>>
-export type GetTechVendorPossibleItemsSchema = Awaited<ReturnType<typeof searchParamsPossibleItemsCache.parse>>
-export type GetTechVendorRfqHistorySchema = Awaited<ReturnType<typeof searchParamsRfqHistoryCache.parse>>
-
-export type UpdateTechVendorSchema = z.infer<typeof updateTechVendorSchema>
-export type CreateTechVendorSchema = z.infer<typeof createTechVendorSchema>
-export type CreateTechVendorContactSchema = z.infer<typeof createTechVendorContactSchema>
-export type UpdateTechVendorContactSchema = z.infer<typeof updateTechVendorContactSchema>
-export type CreateTechVendorItemSchema = z.infer<typeof createTechVendorItemSchema>
-export type UpdateTechVendorItemSchema = z.infer<typeof updateTechVendorItemSchema>
-export type CreateTechVendorPossibleItemSchema = z.infer<typeof createTechVendorPossibleItemSchema>
+import { + createSearchParamsCache, + parseAsArrayOf, + parseAsInteger, + parseAsString, + parseAsStringEnum, +} from "nuqs/server" +import * as z from "zod" + +import { getFiltersStateParser, getSortingStateParser } from "@/lib/parsers" +import { techVendors, TechVendor, TechVendorContact, VENDOR_TYPES } from "@/db/schema/techVendors"; + +// TechVendorPossibleItem 타입 정의 - 새 스키마에 맞게 수정 +export interface TechVendorPossibleItem { + id: number; + vendorId: number; + shipbuildingItemId: number | null; + offshoreTopItemId: number | null; + offshoreHullItemId: number | null; + createdAt: Date; + updatedAt: Date; + + // 조인된 정보 (어떤 타입의 아이템인지에 따라) + itemCode?: string; + workType?: string | null; + shipTypes?: string | null; + itemList?: string | null; + subItemList?: string | null; + techVendorType?: "조선" | "해양TOP" | "해양HULL"; +} + +export const searchParamsCache = createSearchParamsCache({ + // 공통 플래그 + flags: parseAsArrayOf(z.enum(["advancedTable", "floatingBar"])).withDefault( + [] + ), + + // 페이징 + page: parseAsInteger.withDefault(1), + perPage: parseAsInteger.withDefault(10), + + // 정렬 (techVendors 테이블에 맞춰 TechVendor 타입 지정) + sort: getSortingStateParser<TechVendor>().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"]), + + // 협력업체명 검색 + vendorName: parseAsString.withDefault(""), + + // 국가 검색 + country: parseAsString.withDefault(""), + + // 예) 코드 검색 + vendorCode: parseAsString.withDefault(""), + + // 벤더 타입 필터링 (다중 선택 가능) + vendorType: parseAsStringEnum(["ship", "top", "hull"]), + + // workTypes 필터링 (다중 선택 가능) + workTypes: parseAsArrayOf(parseAsStringEnum([ + // 조선 workTypes + "기장", "전장", "선실", "배관", "철의", "선체", + // 해양TOP workTypes + "TM", "TS", "TE", "TP", + // 해양HULL workTypes + "HA", "HE", "HH", "HM", "NC", "HO", "HP" + ])).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<TechVendorContact>().withDefault([ + { id: "createdAt", desc: true }, // createdAt 기준 내림차순 + ]), + + // 고급 필터 + filters: getFiltersStateParser().withDefault([]), + joinOperator: parseAsStringEnum(["and", "or"]).withDefault("and"), + + // 검색 키워드 + search: parseAsString.withDefault(""), + + // 특정 필드 검색 + contactName: parseAsString.withDefault(""), + contactPosition: parseAsString.withDefault(""), + contactTitle: 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<TechVendorPossibleItem>().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 searchParamsPossibleItemsCache = createSearchParamsCache({ + // 공통 플래그 + flags: parseAsArrayOf(z.enum(["advancedTable", "floatingBar"])).withDefault( + [] + ), + + // 페이징 + page: parseAsInteger.withDefault(1), + perPage: parseAsInteger.withDefault(10), + + // 정렬 + sort: getSortingStateParser<TechVendorPossibleItem>().withDefault([ + { id: "createdAt", desc: true }, + ]), + + // 고급 필터 + filters: getFiltersStateParser().withDefault([]), + joinOperator: parseAsStringEnum(["and", "or"]).withDefault("and"), + + // 검색 키워드 + search: parseAsString.withDefault(""), + + // 개별 필터 필드들 + itemCode: parseAsString.withDefault(""), + workType: parseAsString.withDefault(""), + itemList: parseAsString.withDefault(""), + shipTypes: parseAsString.withDefault(""), + subItemList: parseAsString.withDefault(""), +}); + +// 기술영업 벤더 기본 정보 업데이트 스키마 +export const updateTechVendorSchema = z.object({ + vendorName: z.string().min(1, "업체명은 필수 입력사항입니다"), + vendorCode: z.string().optional(), + address: z.string().optional(), + country: z.string().optional(), + countryEng: z.string().optional(), + countryFab: z.string().optional(), + phone: z.string().optional(), + email: z.string().email("유효한 이메일 주소를 입력해주세요").optional(), + website: z.string().optional(), + techVendorType: z.union([ + z.array(z.enum(VENDOR_TYPES)).min(1, "최소 하나의 벤더 타입을 선택해주세요"), + z.string().min(1, "벤더 타입을 선택해주세요") + ]).optional(), + status: z.enum(techVendors.status.enumValues).optional(), + // 에이전트 정보 + agentName: z.string().optional(), + agentEmail: z.string().email("유효한 이메일 주소를 입력해주세요").optional().or(z.literal("")), + agentPhone: z.string().optional(), + // 대표자 정보 + representativeName: z.string().optional(), + representativeEmail: z.string().email("유효한 이메일 주소를 입력해주세요").optional().or(z.literal("")), + representativePhone: z.string().optional(), + representativeBirth: z.string().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), + contactCountry: z.string().max(100).optional(), + contactPhone: z.string().max(50).optional(), + contactTitle: z.string().max(100).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().max(255).optional(), + + files: z.any().optional(), + status: z.enum(techVendors.status.enumValues).default("ACTIVE"), + techVendorType: z.union([ + z.array(z.enum(VENDOR_TYPES)).min(1, "최소 하나의 벤더 타입을 선택해주세요"), + z.string().min(1, "벤더 타입을 선택해주세요") + ]).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"), + contactTitle: z.string().max(100, "Max length 100").optional(), + contactEmail: z.string().email(), + contactPhone: z.string().max(50, "Max length 50").optional(), + contactCountry: z.string().max(100, "Max length 100").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(), + contactTitle: z.string().max(100, "Max length 100").optional(), + contactEmail: z.string().email().optional(), + contactPhone: z.string().max(50, "Max length 50").optional(), + contactCountry: z.string().max(100, "Max length 100").optional(), + isPrimary: z.boolean().optional(), +}); + +// Possible Items 생성 스키마 - 새 스키마에 맞게 수정 +export const createTechVendorPossibleItemSchema = z.object({ + vendorId: z.number(), + shipbuildingItemId: z.number().optional(), + offshoreTopItemId: z.number().optional(), + offshoreHullItemId: z.number().optional(), +}); + +// Possible Items 업데이트 스키마 +export const updateTechVendorPossibleItemSchema = createTechVendorPossibleItemSchema.extend({ + id: z.number(), +}); + +export const searchParamsRfqHistoryCache = createSearchParamsCache({ + // 공통 플래그 + flags: parseAsArrayOf(z.enum(["advancedTable", "floatingBar"])).withDefault( + [] + ), + + // 페이징 + page: parseAsInteger.withDefault(1), + perPage: parseAsInteger.withDefault(10), + + // 정렬 (RFQ 히스토리에 맞춰) + sort: getSortingStateParser<{ + id: number; + rfqCode: string | null; + description: string | null; + projectCode: string | null; + projectName: string | null; + projectType: string | null; // 프로젝트 타입 추가 + status: string; + totalAmount: string | null; + currency: string | null; + dueDate: Date | null; + createdAt: Date; + quotationCode: string | null; + submittedAt: Date | null; + }>().withDefault([ + { id: "createdAt", desc: true }, + ]), + + // 고급 필터 + filters: getFiltersStateParser().withDefault([]), + joinOperator: parseAsStringEnum(["and", "or"]).withDefault("and"), + + // 검색 키워드 + search: parseAsString.withDefault(""), + + // RFQ 히스토리 특화 필드 + rfqCode: parseAsString.withDefault(""), + description: parseAsString.withDefault(""), + projectCode: parseAsString.withDefault(""), + projectName: parseAsString.withDefault(""), + projectType: parseAsStringEnum(["SHIP", "TOP", "HULL"]), // 프로젝트 타입 필터 추가 + status: parseAsStringEnum(["DRAFT", "PUBLISHED", "EVALUATION", "AWARDED"]), +}); + +// 타입 내보내기 +export type GetTechVendorsSchema = Awaited<ReturnType<typeof searchParamsCache.parse>> +export type GetTechVendorContactsSchema = Awaited<ReturnType<typeof searchParamsContactCache.parse>> +export type GetTechVendorItemsSchema = Awaited<ReturnType<typeof searchParamsItemCache.parse>> +export type GetTechVendorPossibleItemsSchema = Awaited<ReturnType<typeof searchParamsPossibleItemsCache.parse>> +export type GetTechVendorRfqHistorySchema = Awaited<ReturnType<typeof searchParamsRfqHistoryCache.parse>> + +export type UpdateTechVendorSchema = z.infer<typeof updateTechVendorSchema> +export type CreateTechVendorSchema = z.infer<typeof createTechVendorSchema> +export type CreateTechVendorContactSchema = z.infer<typeof createTechVendorContactSchema> +export type UpdateTechVendorContactSchema = z.infer<typeof updateTechVendorContactSchema> +export type CreateTechVendorPossibleItemSchema = z.infer<typeof createTechVendorPossibleItemSchema> export type UpdateTechVendorPossibleItemSchema = z.infer<typeof updateTechVendorPossibleItemSchema>
\ No newline at end of file |
