diff options
Diffstat (limited to 'lib/evaluation-target-list/table/evaluation-targets-toolbar-actions.tsx')
| -rw-r--r-- | lib/evaluation-target-list/table/evaluation-targets-toolbar-actions.tsx | 119 |
1 files changed, 86 insertions, 33 deletions
diff --git a/lib/evaluation-target-list/table/evaluation-targets-toolbar-actions.tsx b/lib/evaluation-target-list/table/evaluation-targets-toolbar-actions.tsx index 82b7c97c..8bc5254c 100644 --- a/lib/evaluation-target-list/table/evaluation-targets-toolbar-actions.tsx +++ b/lib/evaluation-target-list/table/evaluation-targets-toolbar-actions.tsx @@ -51,16 +51,69 @@ export function EvaluationTargetsTableToolbarActions({ // 선택된 행들 const selectedRows = table.getFilteredSelectedRowModel().rows const hasSelection = selectedRows.length > 0 - const selectedTargets = selectedRows.map(row => row.original) - // 선택된 항목들의 상태 분석 + // ✅ 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 = selectedTargets.filter(t => t.status === "PENDING").length - const confirmed = selectedTargets.filter(t => t.status === "CONFIRMED").length - const excluded = selectedTargets.filter(t => t.status === "EXCLUDED").length - const consensusTrue = selectedTargets.filter(t => t.consensusStatus === true).length - const consensusFalse = selectedTargets.filter(t => t.consensusStatus === false).length - const consensusNull = selectedTargets.filter(t => t.consensusStatus === null).length + 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, @@ -73,12 +126,19 @@ export function EvaluationTargetsTableToolbarActions({ canExclude: pending > 0, canRequestReview: pending > 0 } - }, [selectedTargets]) + }, [ + pendingTargets.length, + confirmedTargets.length, + excludedTargets.length, + consensusTrueTargets.length, + consensusFalseTargets.length, + consensusNullTargets.length + ]) // ---------------------------------------------------------------- // 신규 평가 대상 생성 (자동) // ---------------------------------------------------------------- - const handleAutoGenerate = async () => { + const handleAutoGenerate = React.useCallback(async () => { setIsLoading(true) try { // TODO: 발주실적에서 자동 추출 API 호출 @@ -90,23 +150,33 @@ export function EvaluationTargetsTableToolbarActions({ } finally { setIsLoading(false) } - } + }, [router]) // ---------------------------------------------------------------- // 신규 평가 대상 생성 (수동) // ---------------------------------------------------------------- - const handleManualCreate = () => { + const handleManualCreate = React.useCallback(() => { setManualCreateDialogOpen(true) - } + }, []) // ---------------------------------------------------------------- // 다이얼로그 성공 핸들러 // ---------------------------------------------------------------- - const handleActionSuccess = () => { + 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]) return ( <> @@ -141,12 +211,7 @@ export function EvaluationTargetsTableToolbarActions({ <Button variant="outline" size="sm" - onClick={() => - exportTableToExcel(table, { - filename: "vendor-target-list", - excludeColumns: ["select", "actions"], - }) - } + onClick={handleExport} className="gap-2" > <Download className="size-4" aria-hidden="true" /> @@ -237,18 +302,6 @@ export function EvaluationTargetsTableToolbarActions({ targets={selectedTargets} onSuccess={handleActionSuccess} /> - - {/* 선택 정보 표시 */} - {/* {hasSelection && ( - <div className="text-xs text-muted-foreground"> - 선택된 {selectedRows.length}개 항목: - 대기중 {selectedStats.pending}개, - 확정 {selectedStats.confirmed}개, - 제외 {selectedStats.excluded}개 - {selectedStats.consensusTrue > 0 && ` | 의견일치 ${selectedStats.consensusTrue}개`} - {selectedStats.consensusFalse > 0 && ` | 의견불일치 ${selectedStats.consensusFalse}개`} - </div> - )} */} </> ) }
\ No newline at end of file |
