diff options
Diffstat (limited to 'lib/vendors/table/vendors-table-toolbar-actions.tsx')
| -rw-r--r-- | lib/vendors/table/vendors-table-toolbar-actions.tsx | 154 |
1 files changed, 128 insertions, 26 deletions
diff --git a/lib/vendors/table/vendors-table-toolbar-actions.tsx b/lib/vendors/table/vendors-table-toolbar-actions.tsx index 3cb2c552..1c788911 100644 --- a/lib/vendors/table/vendors-table-toolbar-actions.tsx +++ b/lib/vendors/table/vendors-table-toolbar-actions.tsx @@ -2,7 +2,7 @@ import * as React from "react" import { type Table } from "@tanstack/react-table" -import { Download, Upload, Check, BuildingIcon } from "lucide-react" +import { Download, FileSpreadsheet, Upload, Check, BuildingIcon, FileText } from "lucide-react" import { toast } from "sonner" import { exportTableToExcel } from "@/lib/export" @@ -11,25 +11,29 @@ import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, + DropdownMenuSeparator, DropdownMenuTrigger, } from "@/components/ui/dropdown-menu" -import { Vendor } from "@/db/schema/vendors" +import { VendorWithType } from "@/db/schema/vendors" import { ApproveVendorsDialog } from "./approve-vendor-dialog" import { RequestPQVendorsDialog } from "./request-vendor-pg-dialog" import { RequestProjectPQDialog } from "./request-project-pq-dialog" import { SendVendorsDialog } from "./send-vendor-dialog" import { RequestVendorsInvestigateDialog } from "./request-vendor-investigate-dialog" import { RequestInfoDialog } from "./request-additional-Info-dialog" +import { RequestContractDialog } from "./request-basicContract-dialog" +import { exportVendorsWithRelatedData } from "./vendor-all-export" interface VendorsTableToolbarActionsProps { - table: Table<Vendor> + table: Table<VendorWithType> } export function VendorsTableToolbarActions({ table }: VendorsTableToolbarActionsProps) { + const [isExporting, setIsExporting] = React.useState(false); // 파일 input을 숨기고, 버튼 클릭 시 참조해 클릭하는 방식 const fileInputRef = React.useRef<HTMLInputElement>(null) - // 선택된 벤더 중 PENDING_REVIEW 상태인 벤더만 필터링 + // 선택된 협력업체 중 PENDING_REVIEW 상태인 협력업체만 필터링 const pendingReviewVendors = React.useMemo(() => { return table .getFilteredSelectedRowModel() @@ -38,7 +42,7 @@ export function VendorsTableToolbarActions({ table }: VendorsTableToolbarActions .filter(vendor => vendor.status === "PENDING_REVIEW"); }, [table.getFilteredSelectedRowModel().rows]); - // 선택된 벤더 중 IN_REVIEW 상태인 벤더만 필터링 + // 선택된 협력업체 중 IN_REVIEW 상태인 협력업체만 필터링 const inReviewVendors = React.useMemo(() => { return table .getFilteredSelectedRowModel() @@ -71,7 +75,7 @@ export function VendorsTableToolbarActions({ table }: VendorsTableToolbarActions .filter(vendor => vendor.status === "PQ_APPROVED"); }, [table.getFilteredSelectedRowModel().rows]); - // 프로젝트 PQ를 보낼 수 있는 벤더 상태 필터링 + // 프로젝트 PQ를 보낼 수 있는 협력업체 상태 필터링 const projectPQEligibleVendors = React.useMemo(() => { return table .getFilteredSelectedRowModel() @@ -81,10 +85,66 @@ export function VendorsTableToolbarActions({ table }: VendorsTableToolbarActions ["PENDING_REVIEW", "IN_REVIEW", "IN_PQ", "PQ_APPROVED", "APPROVED", "READY_TO_SEND", "ACTIVE"].includes(vendor.status) ); }, [table.getFilteredSelectedRowModel().rows]); + + // 선택된 모든 벤더 가져오기 + const selectedVendors = React.useMemo(() => { + return table + .getFilteredSelectedRowModel() + .rows + .map(row => row.original); + }, [table.getFilteredSelectedRowModel().rows]); + + // 테이블의 모든 벤더 가져오기 (필터링된 결과) + const allFilteredVendors = React.useMemo(() => { + return table + .getFilteredRowModel() + .rows + .map(row => row.original); + }, [table.getFilteredRowModel().rows]); + + // 선택된 벤더 통합 내보내기 함수 실행 + const handleSelectedExport = async () => { + if (selectedVendors.length === 0) { + toast.warning("내보낼 협력업체를 선택해주세요."); + return; + } + + try { + setIsExporting(true); + toast.info(`선택된 ${selectedVendors.length}개 업체의 정보를 내보내는 중입니다...`); + await exportVendorsWithRelatedData(selectedVendors, "selected-vendors-detailed"); + toast.success(`${selectedVendors.length}개 업체 정보 내보내기가 완료되었습니다.`); + } catch (error) { + console.error("상세 정보 내보내기 오류:", error); + toast.error("상세 정보 내보내기 중 오류가 발생했습니다."); + } finally { + setIsExporting(false); + } + }; + + // 모든 벤더 통합 내보내기 함수 실행 + const handleAllFilteredExport = async () => { + if (allFilteredVendors.length === 0) { + toast.warning("내보낼 협력업체가 없습니다."); + return; + } + + try { + setIsExporting(true); + toast.info(`총 ${allFilteredVendors.length}개 업체의 정보를 내보내는 중입니다...`); + await exportVendorsWithRelatedData(allFilteredVendors, "all-vendors-detailed"); + toast.success(`${allFilteredVendors.length}개 업체 정보 내보내기가 완료되었습니다.`); + } catch (error) { + console.error("상세 정보 내보내기 오류:", error); + toast.error("상세 정보 내보내기 중 오류가 발생했습니다."); + } finally { + setIsExporting(false); + } + }; return ( <div className="flex items-center gap-2"> - {/* 승인 다이얼로그: PENDING_REVIEW 상태인 벤더가 있을 때만 표시 */} + {/* 승인 다이얼로그: PENDING_REVIEW 상태인 협력업체가 있을 때만 표시 */} {pendingReviewVendors.length > 0 && ( <ApproveVendorsDialog vendors={pendingReviewVendors} @@ -92,7 +152,7 @@ export function VendorsTableToolbarActions({ table }: VendorsTableToolbarActions /> )} - {/* 일반 PQ 요청: IN_REVIEW 상태인 벤더가 있을 때만 표시 */} + {/* 일반 PQ 요청: IN_REVIEW 상태인 협력업체가 있을 때만 표시 */} {inReviewVendors.length > 0 && ( <RequestPQVendorsDialog vendors={inReviewVendors} @@ -100,7 +160,7 @@ export function VendorsTableToolbarActions({ table }: VendorsTableToolbarActions /> )} - {/* 프로젝트 PQ 요청: 적격 상태의 벤더가 있을 때만 표시 */} + {/* 프로젝트 PQ 요청: 적격 상태의 협력업체가 있을 때만 표시 */} {projectPQEligibleVendors.length > 0 && ( <RequestProjectPQDialog vendors={projectPQEligibleVendors} @@ -109,13 +169,13 @@ export function VendorsTableToolbarActions({ table }: VendorsTableToolbarActions )} {approvedVendors.length > 0 && ( - <RequestInfoDialog + <RequestContractDialog vendors={approvedVendors} onSuccess={() => table.toggleAllRowsSelected(false)} /> )} - {sendVendors.length > 0 && ( + {pqApprovedVendors.length > 0 && ( <RequestInfoDialog vendors={sendVendors} onSuccess={() => table.toggleAllRowsSelected(false)} @@ -129,21 +189,63 @@ export function VendorsTableToolbarActions({ table }: VendorsTableToolbarActions /> )} - {/** 4) Export 버튼 */} - <Button - variant="outline" - size="sm" - onClick={() => - exportTableToExcel(table, { - filename: "vendors", - excludeColumns: ["select", "actions"], - }) - } - className="gap-2" - > - <Download className="size-4" aria-hidden="true" /> - <span className="hidden sm:inline">Export</span> - </Button> + {/* 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={() => + exportTableToExcel(table, { + filename: "vendors", + excludeColumns: ["select", "actions"], + }) + } + disabled={isExporting} + > + <FileText className="mr-2 size-4" /> + <span>현재 테이블 데이터 내보내기</span> + </DropdownMenuItem> + + <DropdownMenuSeparator /> + + {/* 선택된 벤더만 상세 내보내기 */} + <DropdownMenuItem + onClick={handleSelectedExport} + disabled={selectedVendors.length === 0 || isExporting} + > + <FileSpreadsheet className="mr-2 size-4" /> + <span>선택한 업체 상세 정보 내보내기</span> + {selectedVendors.length > 0 && ( + <span className="ml-1 text-xs text-muted-foreground">({selectedVendors.length}개)</span> + )} + </DropdownMenuItem> + + {/* 모든 필터링된 벤더 상세 내보내기 */} + <DropdownMenuItem + onClick={handleAllFilteredExport} + disabled={allFilteredVendors.length === 0 || isExporting} + > + <Download className="mr-2 size-4" /> + <span>모든 업체 상세 정보 내보내기</span> + {allFilteredVendors.length > 0 && ( + <span className="ml-1 text-xs text-muted-foreground">({allFilteredVendors.length}개)</span> + )} + </DropdownMenuItem> + </DropdownMenuContent> + </DropdownMenu> </div> ) }
\ No newline at end of file |
