플로우 위젯 체크박스 선택 버그 수정 - 인덱스 기반에서 Primary Key 기반으로 변경 #268
|
|
@ -130,7 +130,7 @@ export function FlowWidget({
|
|||
const [stepData, setStepData] = useState<any[]>([]);
|
||||
const [stepDataColumns, setStepDataColumns] = useState<string[]>([]);
|
||||
const [stepDataLoading, setStepDataLoading] = useState(false);
|
||||
const [selectedRows, setSelectedRows] = useState<Set<number>>(new Set());
|
||||
const [selectedRows, setSelectedRows] = useState<Set<string>>(new Set()); // Primary Key 값으로 선택 관리
|
||||
const [columnLabels, setColumnLabels] = useState<Record<string, string>>({}); // 컬럼명 -> 라벨 매핑
|
||||
|
||||
// 🆕 검색 필터 관련 상태
|
||||
|
|
@ -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<number>;
|
||||
let newSelected: Set<string>;
|
||||
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 (
|
||||
<Checkbox
|
||||
checked={selectedRows.has(index)}
|
||||
onCheckedChange={() => 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({
|
|||
<div
|
||||
key={actualIndex}
|
||||
className={`bg-card rounded-md border p-3 transition-colors ${
|
||||
selectedRows.has(actualIndex) ? "bg-primary/5 border-primary/30" : ""
|
||||
selectedRows.has(getRowKey(row)) ? "bg-primary/5 border-primary/30" : ""
|
||||
}`}
|
||||
>
|
||||
{allowDataMove && (
|
||||
<div className="mb-2 flex items-center justify-between border-b pb-2">
|
||||
<span className="text-muted-foreground text-xs font-medium">선택</span>
|
||||
<Checkbox
|
||||
checked={selectedRows.has(actualIndex)}
|
||||
onCheckedChange={() => toggleRowSelection(actualIndex)}
|
||||
checked={selectedRows.has(getRowKey(row))}
|
||||
onCheckedChange={() => toggleRowSelection(row)}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
|
@ -1919,13 +1934,13 @@ export function FlowWidget({
|
|||
return (
|
||||
<TableRow
|
||||
key={`${group.groupKey}-${itemIndex}`}
|
||||
className={`h-16 transition-colors hover:bg-muted/50 ${selectedRows.has(actualIndex) ? "bg-primary/5" : ""}`}
|
||||
className={`h-16 transition-colors hover:bg-muted/50 ${selectedRows.has(getRowKey(row)) ? "bg-primary/5" : ""}`}
|
||||
>
|
||||
{allowDataMove && (
|
||||
<TableCell className="bg-background sticky left-0 z-10 border-b px-6 py-3 text-center">
|
||||
<Checkbox
|
||||
checked={selectedRows.has(actualIndex)}
|
||||
onCheckedChange={() => toggleRowSelection(actualIndex)}
|
||||
checked={selectedRows.has(getRowKey(row))}
|
||||
onCheckedChange={() => toggleRowSelection(row)}
|
||||
/>
|
||||
</TableCell>
|
||||
)}
|
||||
|
|
|
|||
Loading…
Reference in New Issue