diff options
| author | dujinkim <dujin.kim@dtsolution.co.kr> | 2025-06-27 01:16:20 +0000 |
|---|---|---|
| committer | dujinkim <dujin.kim@dtsolution.co.kr> | 2025-06-27 01:16:20 +0000 |
| commit | e9897d416b3e7327bbd4d4aef887eee37751ae82 (patch) | |
| tree | bd20ce6eadf9b21755bd7425492d2d31c7700a0e /lib/evaluation/table/periodic-evaluations-toolbar-actions.tsx | |
| parent | 3bf1952c1dad9d479bb8b22031b06a7434d37c37 (diff) | |
(대표님) 20250627 오전 10시 작업사항
Diffstat (limited to 'lib/evaluation/table/periodic-evaluations-toolbar-actions.tsx')
| -rw-r--r-- | lib/evaluation/table/periodic-evaluations-toolbar-actions.tsx | 218 |
1 files changed, 218 insertions, 0 deletions
diff --git a/lib/evaluation/table/periodic-evaluations-toolbar-actions.tsx b/lib/evaluation/table/periodic-evaluations-toolbar-actions.tsx new file mode 100644 index 00000000..2d2bebc1 --- /dev/null +++ b/lib/evaluation/table/periodic-evaluations-toolbar-actions.tsx @@ -0,0 +1,218 @@ +"use client" + +import * as React from "react" +import { type Table } from "@tanstack/react-table" +import { + Plus, + Send, + Users, + Download, + RefreshCw, + FileText, + MessageSquare +} from "lucide-react" +import { toast } from "sonner" +import { useRouter } from "next/navigation" + +import { Button } from "@/components/ui/button" +import { + DropdownMenu, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuSeparator, + DropdownMenuTrigger, +} from "@/components/ui/dropdown-menu" +import { + RequestDocumentsDialog, + RequestEvaluationDialog, +} from "./periodic-evaluation-action-dialogs" +import { PeriodicEvaluationView } from "@/db/schema" +import { exportTableToExcel } from "@/lib/export" + +interface PeriodicEvaluationsTableToolbarActionsProps { + table: Table<PeriodicEvaluationView> + onRefresh?: () => void +} + +export function PeriodicEvaluationsTableToolbarActions({ + table, + onRefresh +}: PeriodicEvaluationsTableToolbarActionsProps) { + const [isLoading, setIsLoading] = React.useState(false) + const [createEvaluationDialogOpen, setCreateEvaluationDialogOpen] = React.useState(false) + const [requestDocumentsDialogOpen, setRequestDocumentsDialogOpen] = React.useState(false) + const [requestEvaluationDialogOpen, setRequestEvaluationDialogOpen] = React.useState(false) + const router = useRouter() + + // 선택된 행들 + const selectedRows = table.getFilteredSelectedRowModel().rows + const hasSelection = selectedRows.length > 0 + const selectedEvaluations = selectedRows.map(row => row.original) + + // 선택된 항목들의 상태 분석 + const selectedStats = React.useMemo(() => { + const pendingSubmission = selectedEvaluations.filter(e => e.status === "PENDING_SUBMISSION").length + const submitted = selectedEvaluations.filter(e => e.status === "SUBMITTED").length + const inReview = selectedEvaluations.filter(e => e.status === "IN_REVIEW").length + const reviewCompleted = selectedEvaluations.filter(e => e.status === "REVIEW_COMPLETED").length + const finalized = selectedEvaluations.filter(e => e.status === "FINALIZED").length + + // 협력업체에게 자료 요청 가능: PENDING_SUBMISSION 상태 + const canRequestDocuments = pendingSubmission > 0 + + // 평가자에게 평가 요청 가능: SUBMITTED 상태 (제출됐지만 아직 평가 시작 안됨) + const canRequestEvaluation = submitted > 0 + + return { + pendingSubmission, + submitted, + inReview, + reviewCompleted, + finalized, + canRequestDocuments, + canRequestEvaluation, + total: selectedEvaluations.length + } + }, [selectedEvaluations]) + + // ---------------------------------------------------------------- + // 신규 정기평가 생성 (자동) + // ---------------------------------------------------------------- + const handleAutoGenerate = async () => { + setIsLoading(true) + try { + // TODO: 평가대상에서 자동 생성 API 호출 + toast.success("정기평가가 자동으로 생성되었습니다.") + router.refresh() + } catch (error) { + console.error('Error auto generating periodic evaluations:', error) + toast.error("자동 생성 중 오류가 발생했습니다.") + } finally { + setIsLoading(false) + } + } + + // ---------------------------------------------------------------- + // 신규 정기평가 생성 (수동) + // ---------------------------------------------------------------- + const handleManualCreate = () => { + setCreateEvaluationDialogOpen(true) + } + + // ---------------------------------------------------------------- + // 다이얼로그 성공 핸들러 + // ---------------------------------------------------------------- + const handleActionSuccess = () => { + table.resetRowSelection() + onRefresh?.() + router.refresh() + } + + return ( + <> + <div className="flex items-center gap-2"> + + {/* 유틸리티 버튼들 */} + <div className="flex items-center gap-1 border-l pl-2 ml-2"> + <Button + variant="outline" + size="sm" + onClick={() => + exportTableToExcel(table, { + filename: "periodic-evaluations", + excludeColumns: ["select", "actions"], + }) + } + className="gap-2" + > + <Download className="size-4" aria-hidden="true" /> + <span className="hidden sm:inline">내보내기</span> + </Button> + </div> + + {/* 선택된 항목 액션 버튼들 */} + {hasSelection && ( + <div className="flex items-center gap-1 border-l pl-2 ml-2"> + {/* 협력업체 자료 요청 버튼 */} + {selectedStats.canRequestDocuments && ( + <Button + variant="outline" + size="sm" + className="gap-2 text-blue-600 border-blue-200 hover:bg-blue-50" + onClick={() => setRequestDocumentsDialogOpen(true)} + disabled={isLoading} + > + <FileText className="size-4" aria-hidden="true" /> + <span className="hidden sm:inline"> + 자료 요청 ({selectedStats.pendingSubmission}) + </span> + </Button> + )} + + {/* 평가자 평가 요청 버튼 */} + {selectedStats.canRequestEvaluation && ( + <Button + variant="outline" + size="sm" + className="gap-2 text-green-600 border-green-200 hover:bg-green-50" + onClick={() => setRequestEvaluationDialogOpen(true)} + disabled={isLoading} + > + <Users className="size-4" aria-hidden="true" /> + <span className="hidden sm:inline"> + 평가 요청 ({selectedStats.submitted}) + </span> + </Button> + )} + + {/* 알림 발송 버튼 (선택사항) */} + <Button + variant="outline" + size="sm" + className="gap-2" + onClick={() => { + // TODO: 선택된 평가에 대한 알림 발송 + toast.info("알림이 발송되었습니다.") + }} + disabled={isLoading} + > + <MessageSquare className="size-4" aria-hidden="true" /> + <span className="hidden sm:inline"> + 알림 발송 ({selectedStats.total}) + </span> + </Button> + </div> + )} + </div> + + + {/* 협력업체 자료 요청 다이얼로그 */} + <RequestDocumentsDialog + open={requestDocumentsDialogOpen} + onOpenChange={setRequestDocumentsDialogOpen} + evaluations={selectedEvaluations} + onSuccess={handleActionSuccess} + /> + + {/* 평가자 평가 요청 다이얼로그 */} + <RequestEvaluationDialog + open={requestEvaluationDialogOpen} + onOpenChange={setRequestEvaluationDialogOpen} + evaluations={selectedEvaluations} + onSuccess={handleActionSuccess} + /> + + {/* 선택 정보 표시 (디버깅용 - 필요시 주석 해제) */} + {/* {hasSelection && ( + <div className="text-xs text-muted-foreground mt-2"> + 선택된 {selectedRows.length}개 항목: + 제출대기 {selectedStats.pendingSubmission}개, + 제출완료 {selectedStats.submitted}개, + 검토중 {selectedStats.inReview}개, + 검토완료 {selectedStats.reviewCompleted}개, + 최종확정 {selectedStats.finalized}개 + </div> + )} */} + </> + ) +}
\ No newline at end of file |
