diff --git a/frontend/components/screen/widgets/FlowWidget.tsx b/frontend/components/screen/widgets/FlowWidget.tsx index 50ad0343..5564a14d 100644 --- a/frontend/components/screen/widgets/FlowWidget.tsx +++ b/frontend/components/screen/widgets/FlowWidget.tsx @@ -130,7 +130,7 @@ export function FlowWidget({ const [stepData, setStepData] = useState([]); const [stepDataColumns, setStepDataColumns] = useState([]); const [stepDataLoading, setStepDataLoading] = useState(false); - const [selectedRows, setSelectedRows] = useState>(new Set()); + const [selectedRows, setSelectedRows] = useState>(new Set()); // Primary Key 값으로 선택 관리 const [columnLabels, setColumnLabels] = useState>({}); // 컬럼명 -> 라벨 매핑 // 🆕 검색 필터 관련 상태 @@ -753,25 +753,35 @@ export function FlowWidget({ } }; - // 체크박스 토글 - const toggleRowSelection = (rowIndex: number) => { + // Primary Key 컬럼명 (플로우 정의에서 가져오거나 기본값 id) + const primaryKeyColumn = flowData?.primaryKey || "id"; + + // 행의 Primary Key 값 가져오기 + const getRowKey = useCallback((row: any): string => { + const keyValue = row[primaryKeyColumn] || row.id; + return String(keyValue); + }, [primaryKeyColumn]); + + // 체크박스 토글 (Primary Key 기반) + const toggleRowSelection = (row: any) => { // 프리뷰 모드에서는 행 선택 차단 if (isPreviewMode) { return; } + const rowKey = getRowKey(row); const newSelected = new Set(selectedRows); - if (newSelected.has(rowIndex)) { - newSelected.delete(rowIndex); + if (newSelected.has(rowKey)) { + newSelected.delete(rowKey); } else { - newSelected.add(rowIndex); + newSelected.add(rowKey); } setSelectedRows(newSelected); - // 선택된 데이터를 상위로 전달 - const selectedData = Array.from(newSelected).map((index) => stepData[index]); + // 선택된 데이터를 상위로 전달 (stepData에서 선택된 행들 찾기) + const selectedData = stepData.filter((r) => newSelected.has(getRowKey(r))); console.log("🌊 FlowWidget - 체크박스 토글, 상위로 전달:", { - rowIndex, + rowKey, newSelectedSize: newSelected.size, selectedData, selectedStepId, @@ -780,18 +790,18 @@ export function FlowWidget({ onSelectedDataChange?.(selectedData, selectedStepId); }; - // 전체 선택/해제 + // 전체 선택/해제 (Primary Key 기반) const toggleAllRows = () => { - let newSelected: Set; + let newSelected: Set; if (selectedRows.size === stepData.length) { newSelected = new Set(); } else { - newSelected = new Set(stepData.map((_, index) => index)); + newSelected = new Set(stepData.map((row) => getRowKey(row))); } setSelectedRows(newSelected); // 선택된 데이터를 상위로 전달 - const selectedData = Array.from(newSelected).map((index) => stepData[index]); + const selectedData = stepData.filter((row) => newSelected.has(getRowKey(row))); onSelectedDataChange?.(selectedData, selectedStepId); }; @@ -951,36 +961,41 @@ export function FlowWidget({ return formatValue(value); }, []); - // 🆕 전체 선택 핸들러 + // 🆕 전체 선택 핸들러 (Primary Key 기반) const handleSelectAll = useCallback((checked: boolean) => { if (checked) { - const allIndices = new Set(sortedDisplayData.map((_, idx) => idx)); - setSelectedRows(allIndices); + const allKeys = new Set(sortedDisplayData.map((row) => getRowKey(row))); + setSelectedRows(allKeys); + // 선택된 데이터를 상위로 전달 + onSelectedDataChange?.(sortedDisplayData, selectedStepId); } else { setSelectedRows(new Set()); + onSelectedDataChange?.([], selectedStepId); } - }, [sortedDisplayData]); + }, [sortedDisplayData, getRowKey, onSelectedDataChange, selectedStepId]); // 🆕 행 클릭 핸들러 const handleRowClick = useCallback((row: any) => { // 필요 시 행 클릭 로직 추가 }, []); - // 🆕 체크박스 셀 렌더링 - const renderCheckboxCell = useCallback((row: any, index: number) => { + // 🆕 체크박스 셀 렌더링 (Primary Key 기반) + // index 파라미터는 SingleTableWithSticky 인터페이스 호환을 위해 유지하지만 사용하지 않음 + const renderCheckboxCell = useCallback((row: any, _index: number) => { + const rowKey = getRowKey(row); return ( toggleRowSelection(index)} + checked={selectedRows.has(rowKey)} + onCheckedChange={() => toggleRowSelection(row)} /> ); - }, [selectedRows, toggleRowSelection]); + }, [selectedRows, toggleRowSelection, getRowKey]); - // 🆕 Excel 내보내기 + // 🆕 Excel 내보내기 (Primary Key 기반) const exportToExcel = useCallback(() => { try { const exportData = selectedRows.size > 0 - ? sortedDisplayData.filter((_, idx) => selectedRows.has(idx)) + ? sortedDisplayData.filter((row) => selectedRows.has(getRowKey(row))) : sortedDisplayData; if (exportData.length === 0) { @@ -1010,13 +1025,13 @@ export function FlowWidget({ console.error("Excel 내보내기 오류:", error); toast.error("Excel 내보내기에 실패했습니다."); } - }, [sortedDisplayData, selectedRows, stepDataColumns, columnLabels, flowName]); + }, [sortedDisplayData, selectedRows, stepDataColumns, columnLabels, flowName, getRowKey]); // 🆕 PDF 내보내기 (html2canvas 사용으로 한글 지원) const exportToPdf = useCallback(async () => { try { const exportData = selectedRows.size > 0 - ? sortedDisplayData.filter((_, idx) => selectedRows.has(idx)) + ? sortedDisplayData.filter((row) => selectedRows.has(getRowKey(row))) : sortedDisplayData; if (exportData.length === 0) { @@ -1175,13 +1190,13 @@ export function FlowWidget({ console.error("PDF 내보내기 오류:", error); toast.error("PDF 내보내기에 실패했습니다.", { id: "pdf-export" }); } - }, [sortedDisplayData, selectedRows, stepDataColumns, columnLabels, flowName]); + }, [sortedDisplayData, selectedRows, stepDataColumns, columnLabels, flowName, getRowKey]); - // 🆕 복사 기능 + // 🆕 복사 기능 (Primary Key 기반) const handleCopy = useCallback(() => { try { const copyData = selectedRows.size > 0 - ? sortedDisplayData.filter((_, idx) => selectedRows.has(idx)) + ? sortedDisplayData.filter((row) => selectedRows.has(getRowKey(row))) : []; if (copyData.length === 0) { @@ -1203,7 +1218,7 @@ export function FlowWidget({ console.error("복사 오류:", error); toast.error("복사에 실패했습니다."); } - }, [sortedDisplayData, selectedRows, stepDataColumns, columnLabels]); + }, [sortedDisplayData, selectedRows, stepDataColumns, columnLabels, getRowKey]); // 🆕 통합 검색 실행 const executeGlobalSearch = useCallback((term: string) => { @@ -1828,15 +1843,15 @@ export function FlowWidget({
{allowDataMove && (
선택 toggleRowSelection(actualIndex)} + checked={selectedRows.has(getRowKey(row))} + onCheckedChange={() => toggleRowSelection(row)} />
)} @@ -1919,13 +1934,13 @@ export function FlowWidget({ return ( {allowDataMove && ( toggleRowSelection(actualIndex)} + checked={selectedRows.has(getRowKey(row))} + onCheckedChange={() => toggleRowSelection(row)} /> )}