summaryrefslogtreecommitdiff
path: root/lib/evaluation/table/periodic-evaluations-toolbar-actions.tsx
diff options
context:
space:
mode:
authordujinkim <dujin.kim@dtsolution.co.kr>2025-07-07 01:44:45 +0000
committerdujinkim <dujin.kim@dtsolution.co.kr>2025-07-07 01:44:45 +0000
commit90f79a7a691943a496f67f01c1e493256070e4de (patch)
tree37275fde3ae08c2bca384fbbc8eb378de7e39230 /lib/evaluation/table/periodic-evaluations-toolbar-actions.tsx
parentfbb3b7f05737f9571b04b0a8f4f15c0928de8545 (diff)
(대표님) 변경사항 20250707 10시 43분 - unstaged 변경사항 추가
Diffstat (limited to 'lib/evaluation/table/periodic-evaluations-toolbar-actions.tsx')
-rw-r--r--lib/evaluation/table/periodic-evaluations-toolbar-actions.tsx179
1 files changed, 106 insertions, 73 deletions
diff --git a/lib/evaluation/table/periodic-evaluations-toolbar-actions.tsx b/lib/evaluation/table/periodic-evaluations-toolbar-actions.tsx
index 2d2bebc1..bb63a1fd 100644
--- a/lib/evaluation/table/periodic-evaluations-toolbar-actions.tsx
+++ b/lib/evaluation/table/periodic-evaluations-toolbar-actions.tsx
@@ -1,5 +1,3 @@
-"use client"
-
import * as React from "react"
import { type Table } from "@tanstack/react-table"
import {
@@ -9,7 +7,8 @@ import {
Download,
RefreshCw,
FileText,
- MessageSquare
+ MessageSquare,
+ CheckCircle2
} from "lucide-react"
import { toast } from "sonner"
import { useRouter } from "next/navigation"
@@ -28,6 +27,7 @@ import {
} from "./periodic-evaluation-action-dialogs"
import { PeriodicEvaluationView } from "@/db/schema"
import { exportTableToExcel } from "@/lib/export"
+import { FinalizeEvaluationDialog } from "./periodic-evaluation-finalize-dialogs"
interface PeriodicEvaluationsTableToolbarActionsProps {
table: Table<PeriodicEvaluationView>
@@ -42,20 +42,66 @@ export function PeriodicEvaluationsTableToolbarActions({
const [createEvaluationDialogOpen, setCreateEvaluationDialogOpen] = React.useState(false)
const [requestDocumentsDialogOpen, setRequestDocumentsDialogOpen] = React.useState(false)
const [requestEvaluationDialogOpen, setRequestEvaluationDialogOpen] = React.useState(false)
+ const [finalizeEvaluationDialogOpen, setFinalizeEvaluationDialogOpen] = React.useState(false)
const router = useRouter()
// 선택된 행들
const selectedRows = table.getFilteredSelectedRowModel().rows
const hasSelection = selectedRows.length > 0
- const selectedEvaluations = selectedRows.map(row => row.original)
- // 선택된 항목들의 상태 분석
+ // ✅ selectedEvaluations를 useMemo로 안정화 (VendorsTable 방식과 동일)
+ const selectedEvaluations = React.useMemo(() => {
+ return selectedRows.map(row => row.original)
+ }, [selectedRows])
+
+ // ✅ 각 상태별 평가들을 개별적으로 메모이제이션 (VendorsTable 방식과 동일)
+ const pendingSubmissionEvaluations = React.useMemo(() => {
+ return table
+ .getFilteredSelectedRowModel()
+ .rows
+ .map(row => row.original)
+ .filter(e => e.status === "PENDING_SUBMISSION");
+ }, [table.getFilteredSelectedRowModel().rows]);
+
+ const submittedEvaluations = React.useMemo(() => {
+ return table
+ .getFilteredSelectedRowModel()
+ .rows
+ .map(row => row.original)
+ .filter(e => e.status === "SUBMITTED" || e.status === "PENDING_SUBMISSION");
+ }, [table.getFilteredSelectedRowModel().rows]);
+
+ const inReviewEvaluations = React.useMemo(() => {
+ return table
+ .getFilteredSelectedRowModel()
+ .rows
+ .map(row => row.original)
+ .filter(e => e.status === "IN_REVIEW");
+ }, [table.getFilteredSelectedRowModel().rows]);
+
+ const reviewCompletedEvaluations = React.useMemo(() => {
+ return table
+ .getFilteredSelectedRowModel()
+ .rows
+ .map(row => row.original)
+ .filter(e => e.status === "REVIEW_COMPLETED");
+ }, [table.getFilteredSelectedRowModel().rows]);
+
+ const finalizedEvaluations = React.useMemo(() => {
+ return table
+ .getFilteredSelectedRowModel()
+ .rows
+ .map(row => row.original)
+ .filter(e => e.status === "FINALIZED");
+ }, [table.getFilteredSelectedRowModel().rows]);
+
+ // ✅ 선택된 항목들의 상태 분석 - 안정화된 개별 배열들 사용
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
+ const pendingSubmission = pendingSubmissionEvaluations.length
+ const submitted = submittedEvaluations.length
+ const inReview = inReviewEvaluations.length
+ const reviewCompleted = reviewCompletedEvaluations.length
+ const finalized = finalizedEvaluations.length
// 협력업체에게 자료 요청 가능: PENDING_SUBMISSION 상태
const canRequestDocuments = pendingSubmission > 0
@@ -63,6 +109,9 @@ export function PeriodicEvaluationsTableToolbarActions({
// 평가자에게 평가 요청 가능: SUBMITTED 상태 (제출됐지만 아직 평가 시작 안됨)
const canRequestEvaluation = submitted > 0
+ // 평가 확정 가능: REVIEW_COMPLETED 상태
+ const canFinalizeEvaluation = reviewCompleted > 0
+
return {
pendingSubmission,
submitted,
@@ -71,42 +120,37 @@ export function PeriodicEvaluationsTableToolbarActions({
finalized,
canRequestDocuments,
canRequestEvaluation,
+ canFinalizeEvaluation,
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)
- }
-
+ }, [
+ pendingSubmissionEvaluations.length,
+ submittedEvaluations.length,
+ inReviewEvaluations.length,
+ reviewCompletedEvaluations.length,
+ finalizedEvaluations.length,
+ selectedEvaluations.length
+ ])
+
+
// ----------------------------------------------------------------
// 다이얼로그 성공 핸들러
// ----------------------------------------------------------------
- const handleActionSuccess = () => {
+ const handleActionSuccess = React.useCallback(() => {
table.resetRowSelection()
onRefresh?.()
router.refresh()
- }
+ }, [table, onRefresh, router])
+
+ // ----------------------------------------------------------------
+ // 내보내기 핸들러
+ // ----------------------------------------------------------------
+ const handleExport = React.useCallback(() => {
+ exportTableToExcel(table, {
+ filename: "periodic-evaluations",
+ excludeColumns: ["select", "actions"],
+ })
+ }, [table])
return (
<>
@@ -117,12 +161,7 @@ export function PeriodicEvaluationsTableToolbarActions({
<Button
variant="outline"
size="sm"
- onClick={() =>
- exportTableToExcel(table, {
- filename: "periodic-evaluations",
- excludeColumns: ["select", "actions"],
- })
- }
+ onClick={handleExport}
className="gap-2"
>
<Download className="size-4" aria-hidden="true" />
@@ -165,27 +204,25 @@ export function PeriodicEvaluationsTableToolbarActions({
</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>
+ {/* 평가 확정 버튼 */}
+ {selectedStats.canFinalizeEvaluation && (
+ <Button
+ variant="outline"
+ size="sm"
+ className="gap-2 text-purple-600 border-purple-200 hover:bg-purple-50"
+ onClick={() => setFinalizeEvaluationDialogOpen(true)}
+ disabled={isLoading}
+ >
+ <CheckCircle2 className="size-4" aria-hidden="true" />
+ <span className="hidden sm:inline">
+ 평가 확정 ({selectedStats.reviewCompleted})
+ </span>
+ </Button>
+ )}
</div>
)}
</div>
-
{/* 협력업체 자료 요청 다이얼로그 */}
<RequestDocumentsDialog
open={requestDocumentsDialogOpen}
@@ -202,17 +239,13 @@ export function PeriodicEvaluationsTableToolbarActions({
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>
- )} */}
+ {/* 평가 확정 다이얼로그 */}
+ <FinalizeEvaluationDialog
+ open={finalizeEvaluationDialogOpen}
+ onOpenChange={setFinalizeEvaluationDialogOpen}
+ evaluations={reviewCompletedEvaluations}
+ onSuccess={handleActionSuccess}
+ />
</>
)
-} \ No newline at end of file
+}