diff options
| author | dujinkim <dujin.kim@dtsolution.co.kr> | 2025-11-12 10:42:36 +0000 |
|---|---|---|
| committer | dujinkim <dujin.kim@dtsolution.co.kr> | 2025-11-12 10:42:36 +0000 |
| commit | 8642ee064ddf96f1db2b948b4cc8bbbd6cfee820 (patch) | |
| tree | 36bd57d147ba929f1d72918d1fb91ad2c4778624 /lib/bidding/receive/biddings-receive-table.tsx | |
| parent | 57ea2f740abf1c7933671561cfe0e421fb5ef3fc (diff) | |
(최겸) 구매 일반계약, 입찰 수정
Diffstat (limited to 'lib/bidding/receive/biddings-receive-table.tsx')
| -rw-r--r-- | lib/bidding/receive/biddings-receive-table.tsx | 149 |
1 files changed, 124 insertions, 25 deletions
diff --git a/lib/bidding/receive/biddings-receive-table.tsx b/lib/bidding/receive/biddings-receive-table.tsx index 88fade40..873f3fa4 100644 --- a/lib/bidding/receive/biddings-receive-table.tsx +++ b/lib/bidding/receive/biddings-receive-table.tsx @@ -8,6 +8,9 @@ import type { DataTableFilterField,
DataTableRowAction,
} from "@/types/table"
+import { Button } from "@/components/ui/button"
+import { Loader2 } from "lucide-react"
+import { toast } from "sonner"
import { useDataTable } from "@/hooks/use-data-table"
import { DataTable } from "@/components/data-table/data-table"
@@ -18,7 +21,8 @@ import { biddingStatusLabels,
contractTypeLabels,
} from "@/db/schema"
-import { SpecificationMeetingDialog, PrDocumentsDialog } from "../list/bidding-detail-dialogs"
+// import { SpecificationMeetingDialog, PrDocumentsDialog } from "../list/bidding-detail-dialogs"
+import { openBiddingAction, earlyOpenBiddingAction } from "@/lib/bidding/actions"
type BiddingReceiveItem = {
id: number
@@ -62,11 +66,13 @@ export function BiddingsReceiveTable({ promises }: BiddingsReceiveTableProps) { const { data, pageCount } = biddingsResult
const [isCompact, setIsCompact] = React.useState<boolean>(false)
- const [specMeetingDialogOpen, setSpecMeetingDialogOpen] = React.useState(false)
- const [prDocumentsDialogOpen, setPrDocumentsDialogOpen] = React.useState(false)
- const [selectedBidding, setSelectedBidding] = React.useState<BiddingReceiveItem | null>(null)
+ // const [specMeetingDialogOpen, setSpecMeetingDialogOpen] = React.useState(false)
+ // const [prDocumentsDialogOpen, setPrDocumentsDialogOpen] = React.useState(false)
+ // const [selectedBidding, setSelectedBidding] = React.useState<BiddingReceiveItem | null>(null)
const [rowAction, setRowAction] = React.useState<DataTableRowAction<BiddingReceiveItem> | null>(null)
+ const [isOpeningBidding, setIsOpeningBidding] = React.useState(false)
+ const [isEarlyOpeningBidding, setIsEarlyOpeningBidding] = React.useState(false)
const router = useRouter()
const { data: session } = useSession()
@@ -86,10 +92,6 @@ export function BiddingsReceiveTable({ promises }: BiddingsReceiveTableProps) { // 상세 페이지로 이동
router.push(`/evcp/bid/${rowAction.row.original.id}`)
break
- case "open_bidding":
- // 개찰하기 (추후 구현)
- console.log('개찰하기:', rowAction.row.original)
- break
default:
break
}
@@ -100,19 +102,16 @@ export function BiddingsReceiveTable({ promises }: BiddingsReceiveTableProps) { {
id: "biddingNumber",
label: "입찰번호",
- type: "text",
placeholder: "입찰번호를 입력하세요",
},
{
id: "prNumber",
label: "P/R번호",
- type: "text",
placeholder: "P/R번호를 입력하세요",
},
{
id: "title",
label: "입찰명",
- type: "text",
placeholder: "입찰명을 입력하세요",
},
]
@@ -151,6 +150,7 @@ export function BiddingsReceiveTable({ promises }: BiddingsReceiveTableProps) { filterFields,
enablePinning: true,
enableAdvancedFilter: true,
+ enableRowSelection: true,
initialState: {
sorting: [{ id: "createdAt", desc: true }],
columnPinning: { right: ["actions"] },
@@ -164,17 +164,96 @@ export function BiddingsReceiveTable({ promises }: BiddingsReceiveTableProps) { setIsCompact(compact)
}, [])
- const handleSpecMeetingDialogClose = React.useCallback(() => {
- setSpecMeetingDialogOpen(false)
- setRowAction(null)
- setSelectedBidding(null)
- }, [])
+ // const handleSpecMeetingDialogClose = React.useCallback(() => {
+ // setSpecMeetingDialogOpen(false)
+ // setRowAction(null)
+ // setSelectedBidding(null)
+ // }, [])
- const handlePrDocumentsDialogClose = React.useCallback(() => {
- setPrDocumentsDialogOpen(false)
- setRowAction(null)
- setSelectedBidding(null)
- }, [])
+ // const handlePrDocumentsDialogClose = React.useCallback(() => {
+ // setPrDocumentsDialogOpen(false)
+ // setRowAction(null)
+ // setSelectedBidding(null)
+ // }, [])
+
+ // 선택된 행 가져오기
+ const selectedRows = table.getFilteredSelectedRowModel().rows
+ const selectedBiddingForAction = selectedRows.length > 0 ? selectedRows[0].original : null
+
+ // 조기개찰 가능 여부 확인
+ const canEarlyOpen = React.useMemo(() => {
+ if (!selectedBiddingForAction) return false
+
+ const now = new Date()
+ const submissionEndDate = selectedBiddingForAction.submissionEndDate
+
+ // 참여협력사가 1명 이상이어야 함
+ if (selectedBiddingForAction.participantParticipated < 1) return false
+
+ // 입찰서 제출기간 내여야 함
+ if (!submissionEndDate || now > submissionEndDate) return false
+
+ // 미제출 협력사가 0이어야 함
+ if (selectedBiddingForAction.participantPending > 0) return false
+
+ // 참여협력사 + 포기협력사 = 참여예정협력사 여야 함
+ const participatedOrDeclined = selectedBiddingForAction.participantParticipated + selectedBiddingForAction.participantDeclined
+ return participatedOrDeclined === selectedBiddingForAction.participantExpected
+ }, [selectedBiddingForAction])
+
+ // 개찰 가능 여부 확인
+ const canOpen = React.useMemo(() => {
+ if (!selectedBiddingForAction) return false
+
+ // 참여협력사가 1명 이상이어야 함
+ if (selectedBiddingForAction.participantParticipated < 1) return false
+
+ const now = new Date()
+ const submissionEndDate = selectedBiddingForAction.submissionEndDate
+
+ // 입찰서 제출기간이 종료되어야 함
+ return submissionEndDate && now > submissionEndDate
+ }, [selectedBiddingForAction])
+
+ const handleEarlyOpenBidding = React.useCallback(async () => {
+ if (!selectedBiddingForAction) return
+
+ setIsEarlyOpeningBidding(true)
+ try {
+ const result = await earlyOpenBiddingAction(selectedBiddingForAction.id)
+ if (result.success) {
+ toast.success("조기개찰이 완료되었습니다.")
+ // 데이터 리프레시
+ window.location.reload()
+ } else {
+ toast.error(result.message || "조기개찰에 실패했습니다.")
+ }
+ } catch (error) {
+ toast.error("조기개찰 중 오류가 발생했습니다.")
+ } finally {
+ setIsEarlyOpeningBidding(false)
+ }
+ }, [selectedBiddingForAction])
+
+ const handleOpenBidding = React.useCallback(async () => {
+ if (!selectedBiddingForAction) return
+
+ setIsOpeningBidding(true)
+ try {
+ const result = await openBiddingAction(selectedBiddingForAction.id)
+ if (result.success) {
+ toast.success("개찰이 완료되었습니다.")
+ // 데이터 리프레시
+ window.location.reload()
+ } else {
+ toast.error(result.message || "개찰에 실패했습니다.")
+ }
+ } catch (error) {
+ toast.error("개찰 중 오류가 발생했습니다.")
+ } finally {
+ setIsOpeningBidding(false)
+ }
+ }, [selectedBiddingForAction])
return (
<>
@@ -190,22 +269,42 @@ export function BiddingsReceiveTable({ promises }: BiddingsReceiveTableProps) { compactStorageKey="biddingsReceiveTableCompact"
onCompactChange={handleCompactChange}
>
+ <div className="flex items-center gap-2">
+ <Button
+ variant="outline"
+ size="sm"
+ onClick={handleEarlyOpenBidding}
+ disabled={!selectedBiddingForAction || !canEarlyOpen || isEarlyOpeningBidding}
+ >
+ {isEarlyOpeningBidding && <Loader2 className="mr-2 h-4 w-4 animate-spin" />}
+ 조기개찰
+ </Button>
+ <Button
+ variant="outline"
+ size="sm"
+ onClick={handleOpenBidding}
+ disabled={!selectedBiddingForAction || !canOpen || isOpeningBidding}
+ >
+ {isOpeningBidding && <Loader2 className="mr-2 h-4 w-4 animate-spin" />}
+ 개찰
+ </Button>
+ </div>
</DataTableAdvancedToolbar>
</DataTable>
{/* 사양설명회 다이얼로그 */}
- <SpecificationMeetingDialog
+ {/* <SpecificationMeetingDialog
open={specMeetingDialogOpen}
onOpenChange={handleSpecMeetingDialogClose}
bidding={selectedBidding}
- />
+ /> */}
{/* PR 문서 다이얼로그 */}
- <PrDocumentsDialog
+ {/* <PrDocumentsDialog
open={prDocumentsDialogOpen}
onOpenChange={handlePrDocumentsDialogClose}
bidding={selectedBidding}
- />
+ /> */}
</>
)
}
|
