diff options
Diffstat (limited to 'lib/bidding/list')
| -rw-r--r-- | lib/bidding/list/biddings-page-header.tsx | 4 | ||||
| -rw-r--r-- | lib/bidding/list/biddings-table-columns.tsx | 21 | ||||
| -rw-r--r-- | lib/bidding/list/biddings-table-toolbar-actions.tsx | 6 | ||||
| -rw-r--r-- | lib/bidding/list/biddings-table.tsx | 17 | ||||
| -rw-r--r-- | lib/bidding/list/biddings-transmission-dialog.tsx | 14 | ||||
| -rw-r--r-- | lib/bidding/list/create-bidding-dialog.tsx | 249 | ||||
| -rw-r--r-- | lib/bidding/list/edit-bidding-sheet.tsx | 37 |
7 files changed, 270 insertions, 78 deletions
diff --git a/lib/bidding/list/biddings-page-header.tsx b/lib/bidding/list/biddings-page-header.tsx index 7fa9a39c..64e588c3 100644 --- a/lib/bidding/list/biddings-page-header.tsx +++ b/lib/bidding/list/biddings-page-header.tsx @@ -19,13 +19,13 @@ export function BiddingsPageHeader() { {/* 우측: 액션 버튼들 */} <div className="flex items-center gap-2"> - <Button + {/* <Button variant="outline" onClick={() => router.push('/evcp/biddings/analytics')} > <TrendingUp className="mr-2 h-4 w-4" /> 분석 보기 - </Button> + </Button> */} <Button variant="outline" diff --git a/lib/bidding/list/biddings-table-columns.tsx b/lib/bidding/list/biddings-table-columns.tsx index 5240b134..7f0b8e40 100644 --- a/lib/bidding/list/biddings-table-columns.tsx +++ b/lib/bidding/list/biddings-table-columns.tsx @@ -293,12 +293,23 @@ export function getBiddingsColumns({ setRowAction }: GetColumnsProps): ColumnDef }, { - accessorKey: "contractPeriod", + id: "contractPeriod", header: ({ column }) => <DataTableColumnHeaderSimple column={column} title="계약기간" />, - cell: ({ row }) => ( - <span className="truncate max-w-[100px]">{row.original.contractPeriod || '-'}</span> - ), - size: 100, + cell: ({ row }) => { + const startDate = row.original.contractStartDate + const endDate = row.original.contractEndDate + + if (!startDate || !endDate) { + return <span className="text-muted-foreground">-</span> + } + + return ( + <div className="text-xs max-w-[120px] truncate" title={`${formatDate(startDate, "KR")} ~ ${formatDate(endDate, "KR")}`}> + {formatDate(startDate, "KR")} ~ {formatDate(endDate, "KR")} + </div> + ) + }, + size: 120, meta: { excelHeader: "계약기간" }, }, ] diff --git a/lib/bidding/list/biddings-table-toolbar-actions.tsx b/lib/bidding/list/biddings-table-toolbar-actions.tsx index ed5538c6..702396ae 100644 --- a/lib/bidding/list/biddings-table-toolbar-actions.tsx +++ b/lib/bidding/list/biddings-table-toolbar-actions.tsx @@ -23,11 +23,9 @@ import { TransmissionDialog } from "./biddings-transmission-dialog" interface BiddingsTableToolbarActionsProps { table: Table<BiddingListItem> - paymentTermsOptions: Array<{code: string, description: string}> - incotermsOptions: Array<{code: string, description: string}> } -export function BiddingsTableToolbarActions({ table, paymentTermsOptions, incotermsOptions }: BiddingsTableToolbarActionsProps) { +export function BiddingsTableToolbarActions({ table }: BiddingsTableToolbarActionsProps) { const router = useRouter() const { data: session } = useSession() const [isExporting, setIsExporting] = React.useState(false) @@ -66,8 +64,6 @@ export function BiddingsTableToolbarActions({ table, paymentTermsOptions, incote <div className="flex items-center gap-2"> {/* 신규 생성 */} <CreateBiddingDialog - paymentTermsOptions={paymentTermsOptions} - incotermsOptions={incotermsOptions} /> {/* 전송하기 (업체선정 완료된 입찰만) */} diff --git a/lib/bidding/list/biddings-table.tsx b/lib/bidding/list/biddings-table.tsx index 2a8f98c3..2ecfaa73 100644 --- a/lib/bidding/list/biddings-table.tsx +++ b/lib/bidding/list/biddings-table.tsx @@ -12,7 +12,7 @@ import { useDataTable } from "@/hooks/use-data-table" import { DataTable } from "@/components/data-table/data-table" import { DataTableAdvancedToolbar } from "@/components/data-table/data-table-advanced-toolbar" import { getBiddingsColumns } from "./biddings-table-columns" -import { getBiddings, getBiddingStatusCounts, getActivePaymentTerms, getActiveIncoterms, getBiddingTypeCounts, getBiddingManagerCounts, getBiddingMonthlyStats } from "@/lib/bidding/service" +import { getBiddings, getBiddingStatusCounts } from "@/lib/bidding/service" import { BiddingListItem } from "@/db/schema" import { BiddingsTableToolbarActions } from "./biddings-table-toolbar-actions" import { @@ -28,26 +28,17 @@ interface BiddingsTableProps { promises: Promise< [ Awaited<ReturnType<typeof getBiddings>>, - Awaited<ReturnType<typeof getBiddingStatusCounts>>, - Awaited<ReturnType<typeof getBiddingTypeCounts>>, // 추가 - Awaited<ReturnType<typeof getBiddingManagerCounts>>, // 추가 - Awaited<ReturnType<typeof getBiddingMonthlyStats>>, // 추가 - Awaited<ReturnType<typeof getActivePaymentTerms>>, - Awaited<ReturnType<typeof getActiveIncoterms>> + Awaited<ReturnType<typeof getBiddingStatusCounts>> ] > } export function BiddingsTable({ promises }: BiddingsTableProps) { - const [biddingsResult, statusCounts, typeCounts, managerCounts, monthlyStats, paymentTermsResult, incotermsResult] = React.use(promises) + const [biddingsResult, statusCounts] = React.use(promises) // biddingsResult에서 data와 pageCount 추출 const { data, pageCount } = biddingsResult - const paymentTermsOptions = paymentTermsResult.success && 'data' in paymentTermsResult ? paymentTermsResult.data || [] : [] - const incotermsOptions = incotermsResult.success && 'data' in incotermsResult ? incotermsResult.data || [] : [] - console.log(paymentTermsOptions,"paymentTermsOptions") - console.log(incotermsOptions,"incotermsOptions") const [isCompact, setIsCompact] = React.useState<boolean>(false) const [specMeetingDialogOpen, setSpecMeetingDialogOpen] = React.useState(false) const [prDocumentsDialogOpen, setPrDocumentsDialogOpen] = React.useState(false) @@ -179,8 +170,6 @@ export function BiddingsTable({ promises }: BiddingsTableProps) { > <BiddingsTableToolbarActions table={table} - paymentTermsOptions={paymentTermsOptions} - incotermsOptions={incotermsOptions} /> </DataTableAdvancedToolbar> </DataTable> diff --git a/lib/bidding/list/biddings-transmission-dialog.tsx b/lib/bidding/list/biddings-transmission-dialog.tsx index d307ec9d..035ab583 100644 --- a/lib/bidding/list/biddings-transmission-dialog.tsx +++ b/lib/bidding/list/biddings-transmission-dialog.tsx @@ -19,10 +19,6 @@ import { Label } from "@/components/ui/label" import { BiddingListItem } from "@/db/schema"
import { transmitToContract, transmitToPO } from "@/lib/bidding/actions"
-console.log('=== Module loaded ===')
-console.log('transmitToContract imported:', typeof transmitToContract)
-console.log('transmitToPO imported:', typeof transmitToPO)
-
interface TransmissionDialogProps {
open: boolean
onOpenChange: (open: boolean) => void
@@ -38,23 +34,15 @@ export function TransmissionDialog({ open, onOpenChange, bidding, userId }: Tran const handleToContract = async () => {
try {
setIsLoading(true)
- console.log('=== START handleToContract ===')
console.log('bidding.id', bidding.id)
console.log('userId', userId)
- console.log('transmitToContract function:', typeof transmitToContract)
-
- console.log('About to call transmitToContract...')
- const result = await transmitToContract(bidding.id, userId)
- console.log('transmitToContract result:', result)
-
+ await transmitToContract(bidding.id, userId)
toast.success('계약서 생성이 완료되었습니다.')
onOpenChange(false)
} catch (error) {
- console.error('handleToContract error:', error)
toast.error(`계약서 생성에 실패했습니다: ${error}`)
} finally {
setIsLoading(false)
- console.log('=== END handleToContract ===')
}
}
diff --git a/lib/bidding/list/create-bidding-dialog.tsx b/lib/bidding/list/create-bidding-dialog.tsx index 4fc4fd7b..a25dd363 100644 --- a/lib/bidding/list/create-bidding-dialog.tsx +++ b/lib/bidding/list/create-bidding-dialog.tsx @@ -67,7 +67,8 @@ import { } from "@/components/ui/file-list" import { Checkbox } from "@/components/ui/checkbox" -import { createBidding, type CreateBiddingInput, getActivePaymentTerms, getActiveIncoterms } from "@/lib/bidding/service" +import { createBidding, type CreateBiddingInput } from "@/lib/bidding/service" +import { getIncotermsForSelection, getPaymentTermsForSelection, getPlaceOfShippingForSelection, getPlaceOfDestinationForSelection } from "@/lib/procurement-select/service" import { createBiddingSchema, type CreateBiddingSchema @@ -127,12 +128,7 @@ interface PRItemInfo { const TAB_ORDER = ["basic", "contract", "schedule", "conditions", "details", "manager"] as const type TabType = typeof TAB_ORDER[number] -interface CreateBiddingDialogProps { - paymentTermsOptions?: Array<{code: string, description: string}> - incotermsOptions?: Array<{code: string, description: string}> -} - -export function CreateBiddingDialog({ paymentTermsOptions = [], incotermsOptions = [] }: CreateBiddingDialogProps) { +export function CreateBiddingDialog() { const router = useRouter() const [isSubmitting, setIsSubmitting] = React.useState(false) const { data: session } = useSession() @@ -141,6 +137,13 @@ export function CreateBiddingDialog({ paymentTermsOptions = [], incotermsOptions const [showSuccessDialog, setShowSuccessDialog] = React.useState(false) // 추가 const [createdBiddingId, setCreatedBiddingId] = React.useState<number | null>(null) // 추가 + // Procurement 데이터 상태들 + const [paymentTermsOptions, setPaymentTermsOptions] = React.useState<Array<{code: string, description: string}>>([]) + const [incotermsOptions, setIncotermsOptions] = React.useState<Array<{code: string, description: string}>>([]) + const [shippingPlaces, setShippingPlaces] = React.useState<Array<{code: string, description: string}>>([]) + const [destinationPlaces, setDestinationPlaces] = React.useState<Array<{code: string, description: string}>>([]) + const [procurementLoading, setProcurementLoading] = React.useState(false) + // 사양설명회 정보 상태 const [specMeetingInfo, setSpecMeetingInfo] = React.useState<SpecificationMeetingInfo>({ meetingDate: "", @@ -157,8 +160,24 @@ export function CreateBiddingDialog({ paymentTermsOptions = [], incotermsOptions meetingFiles: [], // 사양설명회 첨부파일 }) - // PR 아이템들 상태 - const [prItems, setPrItems] = React.useState<PRItemInfo[]>([]) + // PR 아이템들 상태 - 기본적으로 하나의 빈 아이템 생성 + const [prItems, setPrItems] = React.useState<PRItemInfo[]>([ + { + id: `pr-default`, + prNumber: "", + itemCode: "", + itemInfo: "", + quantity: "", + quantityUnit: "EA", + totalWeight: "", + weightUnit: "KG", + materialDescription: "", + hasSpecDocument: false, + requestedDeliveryDate: "", + specFiles: [], + isRepresentative: true, // 첫 번째 아이템은 대표 아이템 + } + ]) // 파일 첨부를 위해 선택된 아이템 ID const [selectedItemForFile, setSelectedItemForFile] = React.useState<string | null>(null) @@ -175,6 +194,69 @@ export function CreateBiddingDialog({ paymentTermsOptions = [], incotermsOptions sparePartOptions: "", }) + // Procurement 데이터 로드 함수들 + const loadPaymentTerms = React.useCallback(async () => { + setProcurementLoading(true); + try { + const data = await getPaymentTermsForSelection(); + setPaymentTermsOptions(data); + } catch (error) { + console.error("Failed to load payment terms:", error); + toast.error("결제조건 목록을 불러오는데 실패했습니다."); + } finally { + setProcurementLoading(false); + } + }, []); + + const loadIncoterms = React.useCallback(async () => { + setProcurementLoading(true); + try { + const data = await getIncotermsForSelection(); + setIncotermsOptions(data); + } catch (error) { + console.error("Failed to load incoterms:", error); + toast.error("운송조건 목록을 불러오는데 실패했습니다."); + } finally { + setProcurementLoading(false); + } + }, []); + + const loadShippingPlaces = React.useCallback(async () => { + setProcurementLoading(true); + try { + const data = await getPlaceOfShippingForSelection(); + setShippingPlaces(data); + } catch (error) { + console.error("Failed to load shipping places:", error); + toast.error("선적지 목록을 불러오는데 실패했습니다."); + } finally { + setProcurementLoading(false); + } + }, []); + + const loadDestinationPlaces = React.useCallback(async () => { + setProcurementLoading(true); + try { + const data = await getPlaceOfDestinationForSelection(); + setDestinationPlaces(data); + } catch (error) { + console.error("Failed to load destination places:", error); + toast.error("하역지 목록을 불러오는데 실패했습니다."); + } finally { + setProcurementLoading(false); + } + }, []); + + // 다이얼로그 열릴 때 procurement 데이터 로드 + React.useEffect(() => { + if (open) { + loadPaymentTerms(); + loadIncoterms(); + loadShippingPlaces(); + loadDestinationPlaces(); + } + }, [open, loadPaymentTerms, loadIncoterms, loadShippingPlaces, loadDestinationPlaces]) + // 사양설명회 파일 추가 const addMeetingFiles = (files: File[]) => { @@ -211,7 +293,8 @@ export function CreateBiddingDialog({ paymentTermsOptions = [], incotermsOptions contractType: "general", biddingType: "equipment", awardCount: "single", - contractPeriod: "", + contractStartDate: "", + contractEndDate: "", submissionStartDate: "", submissionEndDate: "", @@ -268,9 +351,10 @@ export function CreateBiddingDialog({ paymentTermsOptions = [], incotermsOptions isValid: formValues.contractType && formValues.biddingType && formValues.awardCount && - formValues.contractPeriod.trim() !== "" && + formValues.contractStartDate && + formValues.contractEndDate && formValues.currency, - hasErrors: !!(formErrors.contractType || formErrors.biddingType || formErrors.awardCount || formErrors.contractPeriod || formErrors.currency) + hasErrors: !!(formErrors.contractType || formErrors.biddingType || formErrors.awardCount || formErrors.contractStartDate || formErrors.contractEndDate || formErrors.currency) }, schedule: { isValid: formValues.submissionStartDate && @@ -289,7 +373,7 @@ export function CreateBiddingDialog({ paymentTermsOptions = [], incotermsOptions hasErrors: false }, details: { - isValid: true, // 세부내역은 선택사항 + isValid: prItems.length > 0, hasErrors: false }, manager: { @@ -369,6 +453,12 @@ export function CreateBiddingDialog({ paymentTermsOptions = [], incotermsOptions // PR 아이템 제거 const removePRItem = (id: string) => { + // 최소 하나의 아이템은 유지해야 함 + if (prItems.length <= 1) { + toast.error("최소 하나의 품목이 필요합니다.") + return + } + setPrItems(prev => { const filteredItems = prev.filter(item => item.id !== id) // 만약 대표 아이템을 삭제했다면, 첫 번째 아이템을 대표로 설정 @@ -443,7 +533,9 @@ export function CreateBiddingDialog({ paymentTermsOptions = [], incotermsOptions toast.error("제출 시작일시와 마감일시를 입력해주세요") } } else if (activeTab === "conditions") { - toast.error("입찰 조건을 모두 입력해주세요 (지급조건, 세금조건, 운송조건, 계약납품일, 선적지, 도착지)") + toast.error("입찰 조건을 모두 입력해주세요 (지급조건, 세금조건, 운송조건, 계약납품일, 선적지, 하역지)") + } else if (activeTab === "details") { + toast.error("품목정보, 수량/단위 또는 중량/중량단위를 입력해주세요") } return } @@ -524,7 +616,8 @@ export function CreateBiddingDialog({ paymentTermsOptions = [], incotermsOptions contractType: "general", biddingType: "equipment", awardCount: "single", - contractPeriod: "", + contractStartDate: "", + contractEndDate: "", submissionStartDate: "", submissionEndDate: "", hasSpecificationMeeting: false, @@ -556,7 +649,23 @@ export function CreateBiddingDialog({ paymentTermsOptions = [], incotermsOptions isRequired: false, meetingFiles: [], }) - setPrItems([]) + setPrItems([ + { + id: `pr-default`, + prNumber: "", + itemCode: "", + itemInfo: "", + quantity: "", + quantityUnit: "EA", + totalWeight: "", + weightUnit: "KG", + materialDescription: "", + hasSpecDocument: false, + requestedDeliveryDate: "", + specFiles: [], + isRepresentative: true, // 첫 번째 아이템은 대표 아이템 + } + ]) setSelectedItemForFile(null) setBiddingConditions({ paymentTerms: "", @@ -705,6 +814,9 @@ export function CreateBiddingDialog({ paymentTermsOptions = [], incotermsOptions }`} > 세부내역 + {!tabValidation.details.isValid && ( + <span className="absolute -top-1 -right-1 h-2 w-2 bg-red-500 rounded-full"></span> + )} </button> <button type="button" @@ -927,18 +1039,34 @@ export function CreateBiddingDialog({ paymentTermsOptions = [], incotermsOptions )} /> - {/* 계약기간 */} + {/* 계약 시작일 */} <FormField control={form.control} - name="contractPeriod" + name="contractStartDate" render={({ field }) => ( <FormItem> - <FormLabel> - 계약기간 <span className="text-red-500">*</span> - </FormLabel> + <FormLabel>계약 시작일 <span className="text-red-500">*</span></FormLabel> + <FormControl> + <Input + type="date" + {...field} + /> + </FormControl> + <FormMessage /> + </FormItem> + )} + /> + + {/* 계약 종료일 */} + <FormField + control={form.control} + name="contractEndDate" + render={({ field }) => ( + <FormItem> + <FormLabel>계약 종료일 <span className="text-red-500">*</span></FormLabel> <FormControl> <Input - placeholder="예: 계약일로부터 60일" + type="date" {...field} /> </FormControl> @@ -1403,26 +1531,58 @@ export function CreateBiddingDialog({ paymentTermsOptions = [], incotermsOptions <div className="space-y-2"> <label className="text-sm font-medium">선적지 <span className="text-red-500">*</span></label> - <Input - placeholder="예: 부산항, 인천항" + <Select value={biddingConditions.shippingPort} - onChange={(e) => setBiddingConditions(prev => ({ + onValueChange={(value) => setBiddingConditions(prev => ({ ...prev, - shippingPort: e.target.value + shippingPort: value }))} - /> + > + <SelectTrigger> + <SelectValue placeholder="선적지 선택" /> + </SelectTrigger> + <SelectContent> + {shippingPlaces.length > 0 ? ( + shippingPlaces.map((place) => ( + <SelectItem key={place.code} value={place.code}> + {place.code} {place.description && `(${place.description})`} + </SelectItem> + )) + ) : ( + <SelectItem value="loading" disabled> + 데이터를 불러오는 중... + </SelectItem> + )} + </SelectContent> + </Select> </div> <div className="space-y-2"> - <label className="text-sm font-medium">도착지 <span className="text-red-500">*</span></label> - <Input - placeholder="예: 현장 직납, 창고 납품" + <label className="text-sm font-medium">하역지 <span className="text-red-500">*</span></label> + <Select value={biddingConditions.destinationPort} - onChange={(e) => setBiddingConditions(prev => ({ + onValueChange={(value) => setBiddingConditions(prev => ({ ...prev, - destinationPort: e.target.value + destinationPort: value }))} - /> + > + <SelectTrigger> + <SelectValue placeholder="하역지 선택" /> + </SelectTrigger> + <SelectContent> + {destinationPlaces.length > 0 ? ( + destinationPlaces.map((place) => ( + <SelectItem key={place.code} value={place.code}> + {place.code} {place.description && `(${place.description})`} + </SelectItem> + )) + ) : ( + <SelectItem value="loading" disabled> + 데이터를 불러오는 중... + </SelectItem> + )} + </SelectContent> + </Select> </div> </div> @@ -1463,7 +1623,10 @@ export function CreateBiddingDialog({ paymentTermsOptions = [], incotermsOptions <div> <CardTitle>세부내역 관리</CardTitle> <p className="text-sm text-muted-foreground mt-1"> - PR 아이템 또는 수기 아이템을 추가하여 입찰 세부내역을 관리하세요 + 최소 하나의 품목을 입력해야 합니다 + </p> + <p className="text-xs text-amber-600 mt-1"> + 수량/단위 또는 중량/중량단위를 선택해서 입력하세요 </p> </div> <Button @@ -1487,7 +1650,7 @@ export function CreateBiddingDialog({ paymentTermsOptions = [], incotermsOptions <TableHead className="w-[60px]">대표</TableHead> <TableHead className="w-[120px]">PR 번호</TableHead> <TableHead className="w-[120px]">품목코드</TableHead> - <TableHead>품목정보</TableHead> + <TableHead>품목정보 *</TableHead> <TableHead className="w-[80px]">수량</TableHead> <TableHead className="w-[80px]">단위</TableHead> <TableHead className="w-[80px]">중량</TableHead> @@ -1526,7 +1689,7 @@ export function CreateBiddingDialog({ paymentTermsOptions = [], incotermsOptions </TableCell> <TableCell> <Input - placeholder="품목정보" + placeholder="품목정보 *" value={item.itemInfo} onChange={(e) => updatePRItem(item.id, { itemInfo: e.target.value })} className="h-8" @@ -1535,6 +1698,7 @@ export function CreateBiddingDialog({ paymentTermsOptions = [], incotermsOptions <TableCell> <Input type="number" + min="0" placeholder="수량" value={item.quantity} onChange={(e) => updatePRItem(item.id, { quantity: e.target.value })} @@ -1562,6 +1726,7 @@ export function CreateBiddingDialog({ paymentTermsOptions = [], incotermsOptions <TableCell> <Input type="number" + min="0" placeholder="중량" value={item.totalWeight} onChange={(e) => updatePRItem(item.id, { totalWeight: e.target.value })} @@ -1590,6 +1755,7 @@ export function CreateBiddingDialog({ paymentTermsOptions = [], incotermsOptions value={item.requestedDeliveryDate} onChange={(e) => updatePRItem(item.id, { requestedDeliveryDate: e.target.value })} className="h-8" + placeholder="납품요청일" /> </TableCell> <TableCell> @@ -1612,7 +1778,9 @@ export function CreateBiddingDialog({ paymentTermsOptions = [], incotermsOptions variant="outline" size="sm" onClick={() => removePRItem(item.id)} + disabled={prItems.length <= 1} className="h-8 w-8 p-0" + title={prItems.length <= 1 ? "최소 하나의 품목이 필요합니다" : "품목 삭제"} > <Trash2 className="h-4 w-4" /> </Button> @@ -1978,7 +2146,14 @@ export function CreateBiddingDialog({ paymentTermsOptions = [], incotermsOptions )} </span> )} - {activeTab === "details" && "세부내역 아이템을 관리하세요 (선택사항)"} + {activeTab === "details" && ( + <span> + 최소 하나의 품목을 입력하세요 + {!tabValidation.details.isValid && ( + <span className="text-red-500 ml-2">• 필수 항목이 누락되었습니다</span> + )} + </span> + )} {activeTab === "manager" && "담당자 정보를 확인하고 입찰을 생성하세요"} </div> diff --git a/lib/bidding/list/edit-bidding-sheet.tsx b/lib/bidding/list/edit-bidding-sheet.tsx index c76ec2a2..dc24d0cf 100644 --- a/lib/bidding/list/edit-bidding-sheet.tsx +++ b/lib/bidding/list/edit-bidding-sheet.tsx @@ -82,7 +82,8 @@ export function EditBiddingSheet({ contractType: "general", biddingType: "equipment", awardCount: "single", - contractPeriod: "", + contractStartDate: "", + contractEndDate: "", preQuoteDate: "", biddingRegistrationDate: "", @@ -126,7 +127,8 @@ export function EditBiddingSheet({ contractType: bidding.contractType || "general", biddingType: bidding.biddingType || "equipment", awardCount: bidding.awardCount || "single", - contractPeriod: bidding.contractPeriod || "", + contractStartDate: formatDate(bidding.contractStartDate, "kr"), + contractEndDate: formatDate(bidding.contractEndDate, "kr"), preQuoteDate: formatDate(bidding.preQuoteDate, "kr"), biddingRegistrationDate: formatDate(bidding.biddingRegistrationDate, "kr"), @@ -356,6 +358,37 @@ export function EditBiddingSheet({ /> </div> + {/* 계약 기간 */} + <div className="grid grid-cols-2 gap-4"> + <FormField + control={form.control} + name="contractStartDate" + render={({ field }) => ( + <FormItem> + <FormLabel>계약 시작일</FormLabel> + <FormControl> + <Input type="date" {...field} /> + </FormControl> + <FormMessage /> + </FormItem> + )} + /> + + <FormField + control={form.control} + name="contractEndDate" + render={({ field }) => ( + <FormItem> + <FormLabel>계약 종료일</FormLabel> + <FormControl> + <Input type="date" {...field} /> + </FormControl> + <FormMessage /> + </FormItem> + )} + /> + </div> + <FormField control={form.control} name="status" |
