"use client" import * as React from "react" import { type Table } from "@tanstack/react-table" import { Plus, Check, MessageSquare, X, Download, Upload, RefreshCw, Settings, Trash2 } from "lucide-react" import { toast } from "sonner" import { useRouter } from "next/navigation" import { useSession } from "next-auth/react" import { Button } from "@/components/ui/button" import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuSeparator, DropdownMenuTrigger, } from "@/components/ui/dropdown-menu" import { ManualCreateEvaluationTargetDialog } from "./manual-create-evaluation-target-dialog" import { ConfirmTargetsDialog, ExcludeTargetsDialog, RequestReviewDialog } from "./evaluation-target-action-dialogs" import { DeleteTargetsDialog } from "./delete-targets-dialog" import { EvaluationTargetWithDepartments } from "@/db/schema" import { exportTableToExcel } from "@/lib/export" import { autoGenerateEvaluationTargets } from "../service" // 서버 액션 import import { useAuthRole } from "@/hooks/use-auth-role" interface EvaluationTargetsTableToolbarActionsProps { table: Table onRefresh?: () => void } export function EvaluationTargetsTableToolbarActions({ table, onRefresh }: EvaluationTargetsTableToolbarActionsProps) { const [isLoading, setIsLoading] = React.useState(false) const [manualCreateDialogOpen, setManualCreateDialogOpen] = React.useState(false) const [confirmDialogOpen, setConfirmDialogOpen] = React.useState(false) const [excludeDialogOpen, setExcludeDialogOpen] = React.useState(false) const [reviewDialogOpen, setReviewDialogOpen] = React.useState(false) const [deleteDialogOpen, setDeleteDialogOpen] = React.useState(false) const router = useRouter() const { data: session } = useSession() // 권한 체크 const { hasRole, isLoading: roleLoading } = useAuthRole() const canManageEvaluations = hasRole('정기평가') || hasRole('admin') // 사용자 ID 가져오기 const userId = React.useMemo(() => { return session?.user?.id ? Number(session.user.id) : 1; }, [session]); // 선택된 행들 const selectedRows = table.getFilteredSelectedRowModel().rows const hasSelection = selectedRows.length > 0 // ✅ selectedTargets를 useMemo로 안정화 (VendorsTable 방식과 동일) const selectedTargets = React.useMemo(() => { return selectedRows.map(row => row.original) }, [selectedRows]) // ✅ 각 상태별 타겟들을 개별적으로 메모이제이션 (VendorsTable 방식과 동일) const pendingTargets = React.useMemo(() => { return table .getFilteredSelectedRowModel() .rows .map(row => row.original) .filter(t => t.status === "PENDING"); }, [table.getFilteredSelectedRowModel().rows]); const confirmedTargets = React.useMemo(() => { return table .getFilteredSelectedRowModel() .rows .map(row => row.original) .filter(t => t.status === "CONFIRMED"); }, [table.getFilteredSelectedRowModel().rows]); const excludedTargets = React.useMemo(() => { return table .getFilteredSelectedRowModel() .rows .map(row => row.original) .filter(t => t.status === "EXCLUDED"); }, [table.getFilteredSelectedRowModel().rows]); const consensusTrueTargets = React.useMemo(() => { return table .getFilteredSelectedRowModel() .rows .map(row => row.original) .filter(t => t.consensusStatus === true); }, [table.getFilteredSelectedRowModel().rows]); const consensusFalseTargets = React.useMemo(() => { return table .getFilteredSelectedRowModel() .rows .map(row => row.original) .filter(t => t.consensusStatus === false); }, [table.getFilteredSelectedRowModel().rows]); const consensusNullTargets = React.useMemo(() => { return table .getFilteredSelectedRowModel() .rows .map(row => row.original) .filter(t => t.consensusStatus === null); }, [table.getFilteredSelectedRowModel().rows]); // ✅ 선택된 항목들의 상태 분석 - 안정화된 개별 배열들 사용 const selectedStats = React.useMemo(() => { const pending = pendingTargets.length const confirmed = confirmedTargets.length const excluded = excludedTargets.length const consensusTrue = consensusTrueTargets.length const consensusFalse = consensusFalseTargets.length const consensusNull = consensusNullTargets.length return { pending, confirmed, excluded, consensusTrue, consensusFalse, consensusNull, canConfirm: pending > 0 && consensusTrue > 0, canExclude: pending > 0, canRequestReview: pending > 0, canDelete: pending > 0 // 삭제는 PENDING 상태인 것만 가능 } }, [ pendingTargets.length, confirmedTargets.length, excludedTargets.length, consensusTrueTargets.length, consensusFalseTargets.length, consensusNullTargets.length ]) // ---------------------------------------------------------------- // 신규 평가 대상 생성 (자동) // ---------------------------------------------------------------- const handleAutoGenerate = React.useCallback(async () => { setIsLoading(true) try { // 현재 년도를 기준으로 평가 대상 자동 생성 const currentYear = new Date().getFullYear() const result = await autoGenerateEvaluationTargets(currentYear, userId) if (result.success) { if (result.generatedCount === 0) { toast.info(result.message, { description: result.skippedCount ? `이미 존재하는 평가 대상: ${result.skippedCount}개` : undefined }) } else { toast.success(result.message, { description: result.details ? `해양: ${result.details.shipTargets}개, 조선: ${result.details.plantTargets}개 생성${result.details.duplicateSkipped > 0 ? `, 중복 건너뜀: ${result.details.duplicateSkipped}개` : ''}` : undefined }) } onRefresh?.() router.refresh() } else { toast.error(result.error || "자동 생성 중 오류가 발생했습니다.") } } catch (error) { console.error('Error auto generating targets:', error) toast.error("자동 생성 중 오류가 발생했습니다.") } finally { setIsLoading(false) } }, [router, onRefresh, userId]) // ---------------------------------------------------------------- // 신규 평가 대상 생성 (수동) // ---------------------------------------------------------------- const handleManualCreate = React.useCallback(() => { setManualCreateDialogOpen(true) }, []) // ---------------------------------------------------------------- // 다이얼로그 성공 핸들러 // ---------------------------------------------------------------- const handleActionSuccess = React.useCallback(() => { table.resetRowSelection() onRefresh?.() router.refresh() }, [table, onRefresh, router]) // ---------------------------------------------------------------- // 내보내기 핸들러 // ---------------------------------------------------------------- const handleExport = React.useCallback(() => { exportTableToExcel(table, { filename: "vendor-target-list", excludeColumns: ["select", "actions"], }) }, [table]) // 권한이 없거나 로딩 중인 경우 내보내기 버튼만 표시 if (roleLoading) { return (
) } return ( <>
{/* 신규 생성 드롭다운 - 정기평가 권한이 있는 경우만 표시 */} {canManageEvaluations && ( 자동 생성 (발주실적 기반) 수동 생성 )} {/* 유틸리티 버튼들 */}
{/* 선택된 항목 액션 버튼들 - 정기평가 권한이 있는 경우만 표시 */} {canManageEvaluations && hasSelection && (
{/* 확정 버튼 */} {selectedStats.canConfirm && ( )} {/* 제외 버튼 */} {selectedStats.canExclude && ( )} {/* 삭제 버튼 */} {selectedStats.canDelete && ( )} {/* 의견 요청 버튼 */} {selectedStats.canRequestReview && ( )}
)} {/* 권한이 없는 경우 안내 메시지 (선택사항) */} {!canManageEvaluations && hasSelection && (
평가 관리 권한이 필요합니다
)}
{/* 다이얼로그들 - 권한이 있는 경우만 렌더링 */} {canManageEvaluations && ( <> {/* 수동 생성 다이얼로그 */} {/* 확정 컨펌 다이얼로그 */} {/* 제외 컨펌 다이얼로그 */} {/* 의견 요청 다이얼로그 */} {/* 삭제 컨펌 다이얼로그 */} )} ) }