import { BiddingListView, biddings, type Bidding } from "@/db/schema" import { createSearchParamsCache, parseAsArrayOf, parseAsInteger, parseAsString, parseAsStringEnum, } from "nuqs/server" import * as z from "zod" import { getFiltersStateParser, getSortingStateParser } from "@/lib/parsers" export const searchParamsCache = createSearchParamsCache({ flags: parseAsArrayOf(z.enum(["advancedTable", "floatingBar"])).withDefault([]), page: parseAsInteger.withDefault(1), perPage: parseAsInteger.withDefault(10), sort: getSortingStateParser().withDefault([ { id: "createdAt", desc: true }, ]), // 기본 필터 biddingNumber: parseAsString.withDefault(""), status: parseAsArrayOf(z.enum(biddings.status.enumValues)).withDefault([]), biddingType: parseAsArrayOf(z.enum(biddings.biddingType.enumValues)).withDefault([]), contractType: parseAsArrayOf(z.enum(biddings.contractType.enumValues)).withDefault([]), managerName: parseAsString.withDefault(""), // 날짜 필터 preQuoteDateFrom: parseAsString.withDefault(""), preQuoteDateTo: parseAsString.withDefault(""), submissionDateFrom: parseAsString.withDefault(""), submissionDateTo: parseAsString.withDefault(""), createdAtFrom: parseAsString.withDefault(""), createdAtTo: parseAsString.withDefault(""), // 가격 필터 budgetMin: parseAsString.withDefault(""), budgetMax: parseAsString.withDefault(""), // Boolean 필터 hasSpecificationMeeting: parseAsString.withDefault(""), // "true" | "false" | "" hasPrDocument: parseAsString.withDefault(""), // "true" | "false" | "" // 고급 필터 filters: getFiltersStateParser().withDefault([]), joinOperator: parseAsStringEnum(["and", "or"]).withDefault("and"), search: parseAsString.withDefault(""), }) export const createBiddingSchema = z.object({ // ❌ 제거: biddingNumber (자동 생성) // ❌ 제거: preQuoteDate (나중에 자동 기록) // ❌ 제거: biddingRegistrationDate (시스템에서 자동 기록) revision: z.number().int().min(0).default(0), // ✅ 프로젝트 정보 (새로 추가) projectId: z.number().min(1, "프로젝트를 선택해주세요"), // 필수 projectName: z.string().optional(), // ProjectSelector에서 자동 설정 // ✅ 필수 필드들 itemName: z.string().min(1, "품목명은 필수입니다"), title: z.string().min(1, "입찰명은 필수입니다"), description: z.string().optional(), content: z.string().optional(), // ✅ 계약 정보 (필수) contractType: z.enum(biddings.contractType.enumValues, { required_error: "계약구분을 선택해주세요" }), biddingType: z.enum(biddings.biddingType.enumValues, { required_error: "입찰유형을 선택해주세요" }), awardCount: z.enum(biddings.awardCount.enumValues, { required_error: "낙찰수를 선택해주세요" }), contractPeriod: z.string().min(1, "계약기간은 필수입니다"), // ✅ 일정 (제출기간 필수) submissionStartDate: z.string().min(1, "제출시작일시는 필수입니다"), submissionEndDate: z.string().min(1, "제출마감일시는 필수입니다"), evaluationDate: z.string().optional(), // 회의 및 문서 hasSpecificationMeeting: z.boolean().default(false), hasPrDocument: z.boolean().default(false), prNumber: z.string().optional(), // ✅ 가격 정보 (통화 필수) currency: z.string().min(1, "통화를 선택해주세요").default("KRW"), budget: z.string().optional(), targetPrice: z.string().optional(), finalBidPrice: z.string().optional(), // 상태 및 담당자 status: z.enum(biddings.status.enumValues).default("bidding_generated"), isPublic: z.boolean().default(false), managerName: z.string().optional(), managerEmail: z.string().email().optional().or(z.literal("")), managerPhone: z.string().optional(), // 메타 remarks: z.string().optional(), // 입찰 조건 (선택사항이지만, 설정할 경우 필수 항목들이 있음) biddingConditions: z.object({ paymentTerms: z.string().min(1, "지급조건은 필수입니다"), taxConditions: z.string().min(1, "세금조건은 필수입니다"), incoterms: z.string().min(1, "운송조건은 필수입니다"), proposedDeliveryDate: z.string().optional(), proposedShippingPort: z.string().optional(), proposedDestinationPort: z.string().optional(), priceAdjustmentApplicable: z.boolean().default(false), specialConditions: z.string().optional(), sparePartRequirement: z.string().optional(), additionalNotes: z.string().optional(), }).optional(), }).refine((data) => { // 제출 기간 검증: 시작일이 마감일보다 이전이어야 함 if (data.submissionStartDate && data.submissionEndDate) { const startDate = new Date(data.submissionStartDate) const endDate = new Date(data.submissionEndDate) return startDate < endDate } return true }, { message: "제출시작일시가 제출마감일시보다 늦을 수 없습니다", path: ["submissionEndDate"] }) export const updateBiddingSchema = z.object({ biddingNumber: z.string().min(1, "입찰번호는 필수입니다").optional(), revision: z.number().int().min(0).optional(), projectId: z.number().min(1).optional(), projectName: z.string().optional(), itemName: z.string().min(1, "품목명은 필수입니다").optional(), title: z.string().min(1, "입찰명은 필수입니다").optional(), description: z.string().optional(), content: z.string().optional(), contractType: z.enum(biddings.contractType.enumValues).optional(), biddingType: z.enum(biddings.biddingType.enumValues).optional(), awardCount: z.enum(biddings.awardCount.enumValues).optional(), contractPeriod: z.string().optional(), submissionStartDate: z.string().optional(), submissionEndDate: z.string().optional(), evaluationDate: z.string().optional(), hasSpecificationMeeting: z.boolean().optional(), hasPrDocument: z.boolean().optional(), prNumber: z.string().optional(), currency: z.string().optional(), budget: z.string().optional(), targetPrice: z.string().optional(), finalBidPrice: z.string().optional(), status: z.enum(biddings.status.enumValues).optional(), isPublic: z.boolean().optional(), managerName: z.string().optional(), managerEmail: z.string().email().optional().or(z.literal("")), managerPhone: z.string().optional(), remarks: z.string().optional(), }) export type GetBiddingsSchema = Awaited> export type CreateBiddingSchema = z.infer export type UpdateBiddingSchema = z.infer // === 상세 페이지용 검증 스키마들 === // 내정가 업데이트 스키마 export const updateTargetPriceSchema = z.object({ biddingId: z.number().int().positive('입찰 ID는 필수입니다'), targetPrice: z.number().min(0, '내정가는 0 이상이어야 합니다'), }) // 협력업체 정보 생성 스키마 export const createQuotationVendorSchema = z.object({ biddingId: z.number().int().positive('입찰 ID는 필수입니다'), vendorId: z.number().int().positive('업체 ID는 필수입니다'), vendorName: z.string().min(1, '업체명은 필수입니다'), vendorCode: z.string().min(1, '업체코드는 필수입니다'), contactPerson: z.string().optional(), contactEmail: z.string().email().optional().or(z.literal('')), contactPhone: z.string().optional(), quotationAmount: z.number().min(0, '견적금액은 0 이상이어야 합니다'), currency: z.string().min(1, '통화는 필수입니다').default('KRW'), paymentTerms: z.string().optional(), taxConditions: z.string().optional(), deliveryDate: z.string().optional(), awardRatio: z.number().min(0).max(100, '발주비율은 0-100 사이여야 합니다').optional(), status: z.enum(['pending', 'submitted', 'selected', 'rejected']).default('pending'), }) // 협력업체 정보 업데이트 스키마 export const updateQuotationVendorSchema = z.object({ id: z.number().int().positive('협력업체 ID는 필수입니다'), vendorName: z.string().min(1, '업체명은 필수입니다').optional(), vendorCode: z.string().min(1, '업체코드는 필수입니다').optional(), contactPerson: z.string().optional(), contactEmail: z.string().email().optional().or(z.literal('')), contactPhone: z.string().optional(), quotationAmount: z.number().min(0, '견적금액은 0 이상이어야 합니다').optional(), currency: z.string().min(1, '통화는 필수입니다').optional(), paymentTerms: z.string().optional(), taxConditions: z.string().optional(), deliveryDate: z.string().optional(), awardRatio: z.number().min(0).max(100, '발주비율은 0-100 사이여야 합니다').optional(), status: z.enum(['pending', 'submitted', 'selected', 'rejected']).optional(), }) // 낙찰 선택 스키마 export const selectWinnerSchema = z.object({ biddingId: z.number().int().positive('입찰 ID는 필수입니다'), vendorId: z.number().int().positive('업체 ID는 필수입니다'), awardRatio: z.number().min(0).max(100, '발주비율은 0-100 사이여야 합니다'), }) // 입찰 상태 변경 스키마 export const updateBiddingStatusSchema = z.object({ biddingId: z.number().int().positive('입찰 ID는 필수입니다'), status: z.enum(biddings.status.enumValues, { required_error: '입찰 상태는 필수입니다' }), }) export type UpdateTargetPriceSchema = z.infer export type CreateQuotationVendorSchema = z.infer export type UpdateQuotationVendorSchema = z.infer export type SelectWinnerSchema = z.infer export type UpdateBiddingStatusSchema = z.infer