summaryrefslogtreecommitdiff
path: root/lib/b-rfq/initial/initial-rfq-detail-toolbar-actions.tsx
diff options
context:
space:
mode:
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.tsx301
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