diff options
| author | dujinkim <dujin.kim@dtsolution.co.kr> | 2025-06-17 09:02:32 +0000 |
|---|---|---|
| committer | dujinkim <dujin.kim@dtsolution.co.kr> | 2025-06-17 09:02:32 +0000 |
| commit | 7a1524ba54f43d0f2a19e4bca2c6a2e0b01c5ef1 (patch) | |
| tree | daa214d404c7fc78b32419a028724e5671a6c7a4 /lib/b-rfq/initial/initial-rfq-detail-toolbar-actions.tsx | |
| parent | fa6a6093014c5d60188edfc9c4552e81c4b97bd1 (diff) | |
(대표님) 20250617 18시 작업사항
Diffstat (limited to 'lib/b-rfq/initial/initial-rfq-detail-toolbar-actions.tsx')
| -rw-r--r-- | lib/b-rfq/initial/initial-rfq-detail-toolbar-actions.tsx | 301 |
1 files changed, 206 insertions, 95 deletions
diff --git a/lib/b-rfq/initial/initial-rfq-detail-toolbar-actions.tsx b/lib/b-rfq/initial/initial-rfq-detail-toolbar-actions.tsx index 981659d5..639d338d 100644 --- a/lib/b-rfq/initial/initial-rfq-detail-toolbar-actions.tsx +++ b/lib/b-rfq/initial/initial-rfq-detail-toolbar-actions.tsx @@ -1,109 +1,220 @@ -// initial-rfq-detail-toolbar-actions.tsx "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 { - Download, - Mail, - RefreshCw, - Settings, - Trash2, - FileText +import { + Download, + Mail, + RefreshCw, + Settings, + Trash2, + FileText, + CheckCircle2, + Loader } from "lucide-react" +import { AddInitialRfqDialog } from "./add-initial-rfq-dialog" +import { DeleteInitialRfqDialog } from "./delete-initial-rfq-dialog" +import { InitialRfqDetailView } from "@/db/schema" +import { sendBulkInitialRfqEmails } from "../service" interface InitialRfqDetailTableToolbarActionsProps { - table: Table<any> - rfqId?: number + table: Table<InitialRfqDetailView> + rfqId?: number + onRefresh?: () => void // 데이터 새로고침 콜백 } export function InitialRfqDetailTableToolbarActions({ - table, - rfqId + table, + rfqId, + onRefresh }: InitialRfqDetailTableToolbarActionsProps) { - - // 선택된 행들 가져오기 - const selectedRows = table.getFilteredSelectedRowModel().rows - const selectedDetails = selectedRows.map((row) => row.original) - const selectedCount = selectedRows.length - - const handleBulkEmail = () => { - console.log("Bulk email to selected vendors:", selectedDetails) - // 벌크 이메일 로직 구현 - } - - const handleBulkDelete = () => { - console.log("Bulk delete selected items:", selectedDetails) - // 벌크 삭제 로직 구현 - table.toggleAllRowsSelected(false) - } - - const handleExport = () => { - console.log("Export data:", selectedCount > 0 ? selectedDetails : "all data") - // 데이터 엑스포트 로직 구현 - } - - const handleRefresh = () => { - window.location.reload() - } - - return ( - <div className="flex items-center gap-2"> - {/** 선택된 항목이 있을 때만 표시되는 액션들 */} - {selectedCount > 0 && ( + const router = useRouter() + + // 선택된 행들 가져오기 + const selectedRows = table.getFilteredSelectedRowModel().rows + const selectedDetails = selectedRows.map((row) => row.original) + const selectedCount = selectedRows.length + + // 상태 관리 + const [showDeleteDialog, setShowDeleteDialog] = React.useState(false) + const [isEmailSending, setIsEmailSending] = React.useState(false) + + const handleBulkEmail = async () => { + if (selectedCount === 0) return + + setIsEmailSending(true) + + try { + const initialRfqIds = selectedDetails.map(detail => detail.initialRfqId); + + const result = await sendBulkInitialRfqEmails({ + initialRfqIds, + language: "en" // 기본 영어, 필요시 사용자 설정으로 변경 + }) + + if (result.success) { + toast.success(result.message) + + // 에러가 있다면 별도 알림 + if (result.errors && result.errors.length > 0) { + setTimeout(() => { + toast.warning(`일부 오류 발생: ${result.errors?.join(', ')}`) + }, 1000) + } + + // 선택 해제 + table.toggleAllRowsSelected(false) + + // 데이터 새로고침 + if (onRefresh) { + onRefresh() + } + } else { + toast.error(result.message || "RFQ 발송에 실패했습니다.") + } + + } catch (error) { + console.error("Email sending error:", error) + toast.error("RFQ 발송 중 오류가 발생했습니다.") + } finally { + setIsEmailSending(false) + } + } + + const handleBulkDelete = () => { + // DRAFT가 아닌 상태의 RFQ 확인 + const nonDraftRfqs = selectedDetails.filter( + detail => detail.initialRfqStatus !== "DRAFT" + ) + + if (nonDraftRfqs.length > 0) { + const statusMessages = { + "Init. RFQ Sent": "이미 발송된", + "S/L Decline": "Short List 거절 처리된", + "Init. RFQ Answered": "답변 완료된" + } + + const nonDraftStatuses = [...new Set(nonDraftRfqs.map(rfq => rfq.initialRfqStatus))] + const statusText = nonDraftStatuses + .map(status => statusMessages[status as keyof typeof statusMessages] || status) + .join(", ") + + toast.error( + `${statusText} RFQ는 삭제할 수 없습니다. DRAFT 상태의 RFQ만 삭제 가능합니다.` + ) + return + } + + setShowDeleteDialog(true) + } + + // S/L 확정 버튼 클릭 + const handleSlConfirm = () => { + if (rfqId) { + router.push(`/evcp/b-rfq/${rfqId}`) + } + } + + // 초기 RFQ 추가 성공 시 처리 + const handleAddSuccess = () => { + // 선택 해제 + table.toggleAllRowsSelected(false) + + // 데이터 새로고침 + if (onRefresh) { + onRefresh() + } else { + // fallback으로 페이지 새로고침 + setTimeout(() => { + window.location.reload() + }, 1000) + } + } + + // 삭제 성공 시 처리 + const handleDeleteSuccess = () => { + // 선택 해제 + table.toggleAllRowsSelected(false) + setShowDeleteDialog(false) + + // 데이터 새로고침 + if (onRefresh) { + onRefresh() + } + } + + // 선택된 항목 중 첫 번째를 기본값으로 사용 + const defaultValues = selectedCount > 0 ? selectedDetails[0] : undefined + + const canDelete = selectedDetails.every(detail => detail.initialRfqStatus === "DRAFT") + const draftCount = selectedDetails.filter(detail => detail.initialRfqStatus === "DRAFT").length + + + return ( <> - <Button - variant="outline" - size="sm" - onClick={handleBulkEmail} - className="h-8" - > - <Mail className="mr-2 h-4 w-4" /> - 이메일 발송 ({selectedCount}) - </Button> - - <Button - variant="outline" - size="sm" - onClick={handleBulkDelete} - className="h-8 text-red-600 hover:text-red-700" - > - <Trash2 className="mr-2 h-4 w-4" /> - 삭제 ({selectedCount}) - </Button> + <div className="flex items-center gap-2"> + {/** 선택된 항목이 있을 때만 표시되는 액션들 */} + {selectedCount > 0 && ( + <> + <Button + variant="outline" + size="sm" + onClick={handleBulkEmail} + className="h-8" + disabled={isEmailSending} + > + {isEmailSending ? <Loader className="mr-2 h-4 w-4 animate-spin" /> : <Mail className="mr-2 h-4 w-4" />} + RFQ 발송 ({selectedCount}) + </Button> + + <Button + variant="outline" + size="sm" + onClick={handleBulkDelete} + className="h-8 text-red-600 hover:text-red-700" + disabled={!canDelete || selectedCount === 0} + title={!canDelete ? "DRAFT 상태의 RFQ만 삭제할 수 있습니다" : ""} + > + <Trash2 className="mr-2 h-4 w-4" /> + 삭제 ({draftCount}/{selectedCount}) + </Button> + </> + )} + + {/* S/L 확정 버튼 */} + {rfqId && ( + <Button + variant="default" + size="sm" + onClick={handleSlConfirm} + className="h-8" + > + <CheckCircle2 className="mr-2 h-4 w-4" /> + S/L 확정 + </Button> + )} + + {/* 초기 RFQ 추가 버튼 */} + {rfqId && ( + <AddInitialRfqDialog + rfqId={rfqId} + onSuccess={handleAddSuccess} + defaultValues={defaultValues} + /> + )} + </div> + + {/* 삭제 다이얼로그 */} + <DeleteInitialRfqDialog + open={showDeleteDialog} + onOpenChange={setShowDeleteDialog} + initialRfqs={selectedDetails} + showTrigger={false} + onSuccess={handleDeleteSuccess} + /> </> - )} - - {/** 항상 표시되는 액션들 */} - <Button - variant="outline" - size="sm" - onClick={handleExport} - className="h-8" - > - <Download className="mr-2 h-4 w-4" /> - 엑스포트 - </Button> - - <Button - variant="outline" - size="sm" - onClick={handleRefresh} - className="h-8" - > - <RefreshCw className="mr-2 h-4 w-4" /> - 새로고침 - </Button> - - <Button - variant="outline" - size="sm" - className="h-8" - > - <Settings className="mr-2 h-4 w-4" /> - 설정 - </Button> - </div> - ) -} + ) +}
\ No newline at end of file |
