summaryrefslogtreecommitdiff
path: root/lib/evaluation-target-list/table/evaluation-targets-toolbar-actions.tsx
diff options
context:
space:
mode:
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.tsx119
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