summaryrefslogtreecommitdiff
path: root/lib/b-rfq/final/final-rfq-detail-toolbar-actions.tsx
diff options
context:
space:
mode:
authordujinkim <dujin.kim@dtsolution.co.kr>2025-07-08 11:23:40 +0000
committerdujinkim <dujin.kim@dtsolution.co.kr>2025-07-08 11:23:40 +0000
commitb84621f9b2b7161a5ad4f0b194264e9df3e65dbf (patch)
treece5ec30b3d1e5104a3a2d942c71973779436783b /lib/b-rfq/final/final-rfq-detail-toolbar-actions.tsx
parent97936ddf280c56a4f122dedcb8dc389d0d2e63a2 (diff)
(대표님) 20250708 미반영분 커밋
Diffstat (limited to 'lib/b-rfq/final/final-rfq-detail-toolbar-actions.tsx')
-rw-r--r--lib/b-rfq/final/final-rfq-detail-toolbar-actions.tsx201
1 files changed, 201 insertions, 0 deletions
diff --git a/lib/b-rfq/final/final-rfq-detail-toolbar-actions.tsx b/lib/b-rfq/final/final-rfq-detail-toolbar-actions.tsx
new file mode 100644
index 00000000..d8be4f7b
--- /dev/null
+++ b/lib/b-rfq/final/final-rfq-detail-toolbar-actions.tsx
@@ -0,0 +1,201 @@
+"use client"
+
+import * as React from "react"
+import { type Table } from "@tanstack/react-table"
+import { useRouter } from "next/navigation"
+import { toast } from "sonner"
+import { Button } from "@/components/ui/button"
+import {
+ Mail,
+ CheckCircle2,
+ Loader,
+ Award,
+ RefreshCw
+} from "lucide-react"
+import { FinalRfqDetailView } from "@/db/schema"
+
+interface FinalRfqDetailTableToolbarActionsProps {
+ table: Table<FinalRfqDetailView>
+ rfqId?: number
+ onRefresh?: () => void // 데이터 새로고침 콜백
+}
+
+export function FinalRfqDetailTableToolbarActions({
+ table,
+ rfqId,
+ onRefresh
+}: FinalRfqDetailTableToolbarActionsProps) {
+ const router = useRouter()
+
+ // 선택된 행들 가져오기
+ const selectedRows = table.getFilteredSelectedRowModel().rows
+ const selectedDetails = selectedRows.map((row) => row.original)
+ const selectedCount = selectedRows.length
+
+ // 상태 관리
+ const [isEmailSending, setIsEmailSending] = React.useState(false)
+ const [isSelecting, setIsSelecting] = React.useState(false)
+
+ // RFQ 발송 핸들러 (로직 없음)
+ const handleBulkRfqSend = async () => {
+ if (selectedCount === 0) {
+ toast.error("발송할 RFQ를 선택해주세요.")
+ return
+ }
+
+ setIsEmailSending(true)
+
+ try {
+ // TODO: 실제 RFQ 발송 로직 구현
+ await new Promise(resolve => setTimeout(resolve, 2000)) // 임시 딜레이
+
+ toast.success(`${selectedCount}개의 최종 RFQ가 발송되었습니다.`)
+
+ // 선택 해제
+ table.toggleAllRowsSelected(false)
+
+ // 데이터 새로고침
+ if (onRefresh) {
+ onRefresh()
+ }
+
+ } catch (error) {
+ console.error("RFQ sending error:", error)
+ toast.error("최종 RFQ 발송 중 오류가 발생했습니다.")
+ } finally {
+ setIsEmailSending(false)
+ }
+ }
+
+ // 최종 선정 핸들러 (로직 없음)
+ const handleFinalSelection = async () => {
+ if (selectedCount === 0) {
+ toast.error("최종 선정할 벤더를 선택해주세요.")
+ return
+ }
+
+ if (selectedCount > 1) {
+ toast.error("최종 선정은 1개의 벤더만 가능합니다.")
+ return
+ }
+
+ setIsSelecting(true)
+
+ try {
+ // TODO: 실제 최종 선정 로직 구현
+ await new Promise(resolve => setTimeout(resolve, 1500)) // 임시 딜레이
+
+ const selectedVendor = selectedDetails[0]
+ toast.success(`${selectedVendor.vendorName}이(가) 최종 선정되었습니다.`)
+
+ // 선택 해제
+ table.toggleAllRowsSelected(false)
+
+ // 데이터 새로고침
+ if (onRefresh) {
+ onRefresh()
+ }
+
+ // 계약서 페이지로 이동 (필요시)
+ if (rfqId) {
+ setTimeout(() => {
+ toast.info("계약서 작성 페이지로 이동합니다.")
+ // router.push(`/evcp/contracts/${rfqId}`)
+ }, 1500)
+ }
+
+ } catch (error) {
+ console.error("Final selection error:", error)
+ toast.error("최종 선정 중 오류가 발생했습니다.")
+ } finally {
+ setIsSelecting(false)
+ }
+ }
+
+ // 발송 가능한 RFQ 필터링 (DRAFT 상태)
+ const sendableRfqs = selectedDetails.filter(
+ detail => detail.finalRfqStatus === "DRAFT"
+ )
+ const sendableCount = sendableRfqs.length
+
+ // 선정 가능한 벤더 필터링 (견적 접수 상태)
+ const selectableVendors = selectedDetails.filter(
+ detail => detail.finalRfqStatus === "Quotation Received"
+ )
+ const selectableCount = selectableVendors.length
+
+ // 전체 벤더 중 견적 접수 완료된 벤더 수
+ const allVendors = table.getRowModel().rows.map(row => row.original)
+ const quotationReceivedCount = allVendors.filter(
+ vendor => vendor.finalRfqStatus === "Quotation Received"
+ ).length
+
+ return (
+ <div className="flex items-center gap-2">
+ {/** 선택된 항목이 있을 때만 표시되는 액션들 */}
+ {selectedCount > 0 && (
+ <>
+ {/* RFQ 발송 버튼 */}
+ <Button
+ variant="outline"
+ size="sm"
+ onClick={handleBulkRfqSend}
+ className="h-8"
+ disabled={isEmailSending || sendableCount === 0}
+ title={sendableCount === 0 ? "발송 가능한 RFQ가 없습니다 (DRAFT 상태만 가능)" : `${sendableCount}개의 최종 RFQ 발송`}
+ >
+ {isEmailSending ? (
+ <Loader className="mr-2 h-4 w-4 animate-spin" />
+ ) : (
+ <Mail className="mr-2 h-4 w-4" />
+ )}
+ 최종 RFQ 발송 ({sendableCount}/{selectedCount})
+ </Button>
+
+ {/* 최종 선정 버튼 */}
+ <Button
+ variant="default"
+ size="sm"
+ onClick={handleFinalSelection}
+ className="h-8"
+ disabled={isSelecting || selectedCount !== 1 || selectableCount === 0}
+ title={
+ selectedCount !== 1
+ ? "최종 선정은 1개의 벤더만 선택해주세요"
+ : selectableCount === 0
+ ? "견적 접수가 완료된 벤더만 선정 가능합니다"
+ : "선택된 벤더를 최종 선정"
+ }
+ >
+ {isSelecting ? (
+ <Loader className="mr-2 h-4 w-4 animate-spin" />
+ ) : (
+ <Award className="mr-2 h-4 w-4" />
+ )}
+ 최종 선정
+ </Button>
+ </>
+ )}
+
+ {/* 정보 표시 (선택이 없을 때) */}
+ {selectedCount === 0 && quotationReceivedCount > 0 && (
+ <div className="text-sm text-muted-foreground">
+ 견적 접수 완료: {quotationReceivedCount}개 벤더
+ </div>
+ )}
+
+ {/* 새로고침 버튼 */}
+ {onRefresh && (
+ <Button
+ variant="ghost"
+ size="sm"
+ onClick={onRefresh}
+ className="h-8"
+ title="데이터 새로고침"
+ >
+ <RefreshCw className="h-4 w-4" />
+ </Button>
+ )}
+ </div>
+ )
+} \ No newline at end of file