summaryrefslogtreecommitdiff
path: root/lib/bidding/list
diff options
context:
space:
mode:
authordujinkim <dujin.kim@dtsolution.co.kr>2025-09-16 09:20:58 +0000
committerdujinkim <dujin.kim@dtsolution.co.kr>2025-09-16 09:20:58 +0000
commit6c11fccc84f4c84fa72ee01f9caad9f76f35cea2 (patch)
treefa88d10ea7d21fe6b59ed0c1569856a73d56547a /lib/bidding/list
parent14e3990aba7e1ad1cdd0965cbd167c50230cbfbf (diff)
(대표님, 최겸) 계약, 업로드 관련, 메뉴처리, 입찰, 프리쿼트, rfqLast관련, tbeLast관련
Diffstat (limited to 'lib/bidding/list')
-rw-r--r--lib/bidding/list/biddings-table-toolbar-actions.tsx100
-rw-r--r--lib/bidding/list/biddings-transmission-dialog.tsx159
2 files changed, 224 insertions, 35 deletions
diff --git a/lib/bidding/list/biddings-table-toolbar-actions.tsx b/lib/bidding/list/biddings-table-toolbar-actions.tsx
index 2b7a9d7d..ed5538c6 100644
--- a/lib/bidding/list/biddings-table-toolbar-actions.tsx
+++ b/lib/bidding/list/biddings-table-toolbar-actions.tsx
@@ -2,12 +2,12 @@
import * as React from "react"
import { type Table } from "@tanstack/react-table"
-import {
- Plus, Send, Gavel, Download, FileSpreadsheet,
- Eye, Clock, CheckCircle
+import {
+ Plus, Send, Download, FileSpreadsheet
} from "lucide-react"
import { toast } from "sonner"
import { useRouter } from "next/navigation"
+import { useSession } from "next-auth/react"
import { exportTableToExcel } from "@/lib/export"
import { Button } from "@/components/ui/button"
import {
@@ -19,6 +19,7 @@ import {
} from "@/components/ui/dropdown-menu"
import { BiddingListItem } from "@/db/schema"
import { CreateBiddingDialog } from "./create-bidding-dialog"
+import { TransmissionDialog } from "./biddings-transmission-dialog"
interface BiddingsTableToolbarActionsProps {
table: Table<BiddingListItem>
@@ -28,7 +29,11 @@ interface BiddingsTableToolbarActionsProps {
export function BiddingsTableToolbarActions({ table, paymentTermsOptions, incotermsOptions }: BiddingsTableToolbarActionsProps) {
const router = useRouter()
+ const { data: session } = useSession()
const [isExporting, setIsExporting] = React.useState(false)
+ const [isTransmissionDialogOpen, setIsTransmissionDialogOpen] = React.useState(false)
+
+ const userId = session?.user?.id ? Number(session.user.id) : 1
// 선택된 입찰들
const selectedBiddings = React.useMemo(() => {
@@ -38,6 +43,9 @@ export function BiddingsTableToolbarActions({ table, paymentTermsOptions, incote
.map(row => row.original)
}, [table.getFilteredSelectedRowModel().rows])
+ // 업체선정이 완료된 입찰만 전송 가능
+ const canTransmit = selectedBiddings.length === 1 && selectedBiddings[0].status === 'vendor_selected'
+
const handleExport = async () => {
try {
setIsExporting(true)
@@ -54,47 +62,69 @@ export function BiddingsTableToolbarActions({ table, paymentTermsOptions, incote
}
return (
- <div className="flex items-center gap-2">
- {/* 신규 생성 */}
- <CreateBiddingDialog
- paymentTermsOptions={paymentTermsOptions}
- incotermsOptions={incotermsOptions}
- />
+ <>
+ <div className="flex items-center gap-2">
+ {/* 신규 생성 */}
+ <CreateBiddingDialog
+ paymentTermsOptions={paymentTermsOptions}
+ incotermsOptions={incotermsOptions}
+ />
- {/* 개찰 (입찰 오픈) */}
- {/* {openEligibleBiddings.length > 0 && (
- <Button
- variant="outline"
+ {/* 전송하기 (업체선정 완료된 입찰만) */}
+ <Button
+ variant="default"
size="sm"
- onClick={handleBiddingOpen}
+ onClick={() => setIsTransmissionDialogOpen(true)}
+ disabled={!canTransmit}
+ className="gap-2"
>
- <Gavel className="mr-2 h-4 w-4" />
- 개찰 ({openEligibleBiddings.length})
+ <Send className="size-4" aria-hidden="true" />
+ <span className="hidden sm:inline">전송하기</span>
</Button>
- )} */}
- {/* Export */}
- <DropdownMenu>
- <DropdownMenuTrigger asChild>
+ {/* 개찰 (입찰 오픈) */}
+ {/* {openEligibleBiddings.length > 0 && (
<Button
variant="outline"
size="sm"
- className="gap-2"
- disabled={isExporting}
+ onClick={handleBiddingOpen}
>
- <Download className="size-4" aria-hidden="true" />
- <span className="hidden sm:inline">
- {isExporting ? "내보내는 중..." : "Export"}
- </span>
+ <Gavel className="mr-2 h-4 w-4" />
+ 개찰 ({openEligibleBiddings.length})
</Button>
- </DropdownMenuTrigger>
- <DropdownMenuContent align="end">
- <DropdownMenuItem onClick={handleExport} disabled={isExporting}>
- <FileSpreadsheet className="mr-2 size-4" />
- <span>입찰 목록 내보내기</span>
- </DropdownMenuItem>
- </DropdownMenuContent>
- </DropdownMenu>
- </div>
+ )} */}
+
+ {/* Export */}
+ <DropdownMenu>
+ <DropdownMenuTrigger asChild>
+ <Button
+ variant="outline"
+ size="sm"
+ className="gap-2"
+ disabled={isExporting}
+ >
+ <Download className="size-4" aria-hidden="true" />
+ <span className="hidden sm:inline">
+ {isExporting ? "내보내는 중..." : "Export"}
+ </span>
+ </Button>
+ </DropdownMenuTrigger>
+ <DropdownMenuContent align="end">
+ <DropdownMenuItem onClick={handleExport} disabled={isExporting}>
+ <FileSpreadsheet className="mr-2 size-4" />
+ <span>입찰 목록 내보내기</span>
+ </DropdownMenuItem>
+ </DropdownMenuContent>
+ </DropdownMenu>
+ </div>
+
+ {/* 전송 다이얼로그 */}
+ <TransmissionDialog
+ open={isTransmissionDialogOpen}
+ onOpenChange={setIsTransmissionDialogOpen}
+ bidding={selectedBiddings[0]}
+ userId={userId}
+ />
+ </>
)
} \ No newline at end of file
diff --git a/lib/bidding/list/biddings-transmission-dialog.tsx b/lib/bidding/list/biddings-transmission-dialog.tsx
new file mode 100644
index 00000000..d307ec9d
--- /dev/null
+++ b/lib/bidding/list/biddings-transmission-dialog.tsx
@@ -0,0 +1,159 @@
+"use client"
+
+import * as React from "react"
+import {
+ Send, CheckCircle, FileText, Truck
+} from "lucide-react"
+import { toast } from "sonner"
+import { Button } from "@/components/ui/button"
+import {
+ Dialog,
+ DialogContent,
+ DialogDescription,
+ DialogFooter,
+ DialogHeader,
+ DialogTitle,
+} from "@/components/ui/dialog"
+import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"
+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
+ bidding: BiddingListItem | undefined
+ userId: number
+}
+
+export function TransmissionDialog({ open, onOpenChange, bidding, userId }: TransmissionDialogProps) {
+ const [isLoading, setIsLoading] = React.useState(false)
+
+ if (!bidding) return null
+
+ 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)
+
+ toast.success('계약서 생성이 완료되었습니다.')
+ onOpenChange(false)
+ } catch (error) {
+ console.error('handleToContract error:', error)
+ toast.error(`계약서 생성에 실패했습니다: ${error}`)
+ } finally {
+ setIsLoading(false)
+ console.log('=== END handleToContract ===')
+ }
+ }
+
+ const handleToPO = async () => {
+ try {
+ setIsLoading(true)
+ await transmitToPO(bidding.id)
+ toast.success('PO 전송이 완료되었습니다.')
+ onOpenChange(false)
+ } catch (error) {
+ toast.error(`PO 전송에 실패했습니다: ${error}`)
+ } finally {
+ setIsLoading(false)
+ }
+ }
+
+ return (
+ <Dialog open={open} onOpenChange={onOpenChange}>
+ <DialogContent className="sm:max-w-[600px]">
+ <DialogHeader>
+ <DialogTitle className="flex items-center gap-2">
+ <Send className="w-5 h-5" />
+ 입찰 전송
+ </DialogTitle>
+ <DialogDescription>
+ 선택된 입찰을 계약서 또는 PO로 전송합니다.
+ </DialogDescription>
+ </DialogHeader>
+
+ <div className="space-y-6">
+ {/* 입찰 정보 */}
+ <Card>
+ <CardHeader className="pb-3">
+ <CardTitle className="text-base">입찰 정보</CardTitle>
+ </CardHeader>
+ <CardContent className="space-y-2">
+ <div className="grid grid-cols-2 gap-4">
+ <div>
+ <Label className="text-sm font-medium">입찰번호</Label>
+ <p className="text-sm text-muted-foreground">{bidding.biddingNumber}</p>
+ </div>
+ <div>
+ <Label className="text-sm font-medium">입찰명</Label>
+ <p className="text-sm text-muted-foreground">{bidding.title}</p>
+ </div>
+ <div>
+ <Label className="text-sm font-medium">계약구분</Label>
+ <p className="text-sm text-muted-foreground">{bidding.contractType}</p>
+ </div>
+ <div>
+ <Label className="text-sm font-medium">예산</Label>
+ <p className="text-sm text-muted-foreground">
+ {bidding.budget ? `${bidding.budget.toLocaleString()} ${bidding.currency}` : '-'}
+ </p>
+ </div>
+ </div>
+ </CardContent>
+ </Card>
+
+ {/* 선정된 업체 정보 (임시로 표시) */}
+ <Card>
+ <CardHeader className="pb-3">
+ <CardTitle className="text-base">선정된 업체</CardTitle>
+ </CardHeader>
+ <CardContent>
+ <div className="flex items-center gap-2">
+ <CheckCircle className="w-4 h-4 text-green-600" />
+ <span className="text-sm">업체 선정이 완료되었습니다.</span>
+ </div>
+ <p className="text-xs text-muted-foreground mt-2">
+ 자세한 업체 정보는 전송 후 확인할 수 있습니다.
+ </p>
+ </CardContent>
+ </Card>
+ </div>
+
+ <DialogFooter className="flex gap-2">
+ <Button variant="outline" onClick={() => onOpenChange(false)}>
+ 취소
+ </Button>
+ <Button
+ variant="outline"
+ onClick={handleToContract}
+ disabled={isLoading}
+ className="gap-2"
+ >
+ <FileText className="w-4 h-4" />
+ TO Contract
+ </Button>
+ <Button
+ onClick={handleToPO}
+ disabled={isLoading}
+ className="gap-2"
+ >
+ <Truck className="w-4 h-4" />
+ TO PO
+ </Button>
+ </DialogFooter>
+ </DialogContent>
+ </Dialog>
+ )
+}