From 8a865ac1f487185677bc7a38f9d1a6fc47ccde27 Mon Sep 17 00:00:00 2001 From: leeheejin Date: Fri, 16 Jan 2026 14:29:19 +0900 Subject: [PATCH 1/2] =?UTF-8?q?=EC=9D=B4=EC=83=81=ED=95=9C=20=EB=B6=80?= =?UTF-8?q?=EB=B6=84=20=EC=88=98=EC=A0=95=20=ED=94=BC=EB=B2=97=EA=B7=B8?= =?UTF-8?q?=EB=A6=AC=EB=93=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../pivot-grid/PivotGridComponent.tsx | 77 ++++++++++++++----- 1 file changed, 56 insertions(+), 21 deletions(-) diff --git a/frontend/lib/registry/components/pivot-grid/PivotGridComponent.tsx b/frontend/lib/registry/components/pivot-grid/PivotGridComponent.tsx index bdc00019..ccfbde88 100644 --- a/frontend/lib/registry/components/pivot-grid/PivotGridComponent.tsx +++ b/frontend/lib/registry/components/pivot-grid/PivotGridComponent.tsx @@ -526,11 +526,16 @@ export const PivotGridComponent: React.FC = ({ }); return result; - }, [filteredData, fields, pivotState.expandedRowPaths, pivotState.expandedColumnPaths]); + }, [ + filteredData, + fields, + JSON.stringify(pivotState.expandedRowPaths), + JSON.stringify(pivotState.expandedColumnPaths) + ]); // πŸ†• 초기 λ‘œλ“œ μ‹œ 첫 레벨 μžλ™ ν™•μž₯ useEffect(() => { - if (pivotResult && pivotResult.flatRows.length > 0) { + if (pivotResult && pivotResult.flatRows.length > 0 && !isInitialExpanded) { console.log("πŸ”Ά ν”Όλ²— κ²°κ³Ό 생성됨:", { flatRowsCount: pivotResult.flatRows.length, expandedRowPaths: pivotState.expandedRowPaths.length, @@ -542,10 +547,10 @@ export const PivotGridComponent: React.FC = ({ console.log("πŸ”Ά 첫 레벨 ν–‰ (level 0, hasChildren):", firstLevelRows.map(r => ({ path: r.path, caption: r.caption }))); - // 초기 ν™•μž₯이 μ•ˆ λ˜μ–΄ 있고, 첫 레벨 행이 있으면 μžλ™ ν™•μž₯ - if (!isInitialExpanded && firstLevelRows.length > 0) { + // 첫 레벨 행이 있으면 μžλ™ ν™•μž₯ + if (firstLevelRows.length > 0) { const firstLevelPaths = firstLevelRows.map(row => row.path); - console.log("πŸ”Ά 초기 μžλ™ ν™•μž₯ μ‹€ν–‰:", firstLevelPaths); + console.log("πŸ”Ά 초기 μžλ™ ν™•μž₯ μ‹€ν–‰ (ν•œ 번만):", firstLevelPaths); setPivotState(prev => ({ ...prev, expandedRowPaths: firstLevelPaths, @@ -553,7 +558,7 @@ export const PivotGridComponent: React.FC = ({ setIsInitialExpanded(true); } } - }, [pivotResult, isInitialExpanded, pivotState.expandedRowPaths.length]); + }, [pivotResult, isInitialExpanded]); // 쑰건뢀 μ„œμ‹μš© 전체 κ°’ μˆ˜μ§‘ const allCellValues = useMemo(() => { @@ -749,31 +754,61 @@ export const PivotGridComponent: React.FC = ({ [onExpandChange] ); - // 전체 ν™•μž₯ + // 전체 ν™•μž₯ (μž¬κ·€μ μœΌλ‘œ λͺ¨λ“  레벨 ν™•μž₯) const handleExpandAll = useCallback(() => { - if (!pivotResult) return; + if (!pivotResult) { + console.log("❌ [handleExpandAll] pivotResultκ°€ μ—†μŒ"); + return; + } + // πŸ†• μž¬κ·€μ μœΌλ‘œ λͺ¨λ“  κ°€λŠ₯ν•œ 경둜 생성 const allRowPaths: string[][] = []; - pivotResult.flatRows.forEach((row) => { - if (row.hasChildren) { - allRowPaths.push(row.path); + const rowFields = fields.filter((f) => f.area === "row" && f.visible !== false); + + // λ°μ΄ν„°μ—μ„œ λͺ¨λ“  κ³ μœ ν•œ 경둜 μΆ”μΆœ + const pathSet = new Set(); + filteredData.forEach((item) => { + for (let depth = 1; depth <= rowFields.length; depth++) { + const path = rowFields.slice(0, depth).map((f) => String(item[f.field] ?? "")); + const pathKey = JSON.stringify(path); + pathSet.add(pathKey); } }); + // Set을 λ°°μ—΄λ‘œ λ³€ν™˜ + pathSet.forEach((pathKey) => { + allRowPaths.push(JSON.parse(pathKey)); + }); + + console.log("πŸ”· [handleExpandAll] ν™•μž₯ν•  ν–‰:", { + totalRows: pivotResult.flatRows.length, + rowsWithChildren: allRowPaths.length, + paths: allRowPaths.slice(0, 5), // 처음 5개만 둜그 + }); + setPivotState((prev) => ({ ...prev, expandedRowPaths: allRowPaths, expandedColumnPaths: [], })); - }, [pivotResult]); + }, [pivotResult, fields, filteredData]); // 전체 μΆ•μ†Œ const handleCollapseAll = useCallback(() => { - setPivotState((prev) => ({ - ...prev, - expandedRowPaths: [], - expandedColumnPaths: [], - })); + console.log("πŸ”· [handleCollapseAll] 전체 μΆ•μ†Œ μ‹€ν–‰"); + + setPivotState((prev) => { + console.log("πŸ”· [handleCollapseAll] 이전 μƒνƒœ:", { + expandedRowPaths: prev.expandedRowPaths.length, + expandedColumnPaths: prev.expandedColumnPaths.length, + }); + + return { + ...prev, + expandedRowPaths: [], + expandedColumnPaths: [], + }; + }); }, []); // μ…€ 클릭 @@ -1391,8 +1426,8 @@ export const PivotGridComponent: React.FC = ({ variant="ghost" size="sm" className="h-7 px-2" - onClick={handleExpandAll} - title="전체 ν™•μž₯" + onClick={handleCollapseAll} + title="전체 μΆ•μ†Œ" > @@ -1401,8 +1436,8 @@ export const PivotGridComponent: React.FC = ({ variant="ghost" size="sm" className="h-7 px-2" - onClick={handleCollapseAll} - title="전체 μΆ•μ†Œ" + onClick={handleExpandAll} + title="전체 ν™•μž₯" > -- 2.43.0 From 02eee979ea306061a118bb0c491e6e013e2ce3f0 Mon Sep 17 00:00:00 2001 From: leeheejin Date: Fri, 16 Jan 2026 15:17:49 +0900 Subject: [PATCH 2/2] =?UTF-8?q?=EA=B3=A0=EC=B9=98=EA=B8=B0=20=EC=99=84?= =?UTF-8?q?=EB=A3=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../pivot-grid/PivotGridComponent.tsx | 53 ++++++++--- .../pivot-grid/components/FieldPanel.tsx | 88 ++++++++++++++++--- 2 files changed, 117 insertions(+), 24 deletions(-) diff --git a/frontend/lib/registry/components/pivot-grid/PivotGridComponent.tsx b/frontend/lib/registry/components/pivot-grid/PivotGridComponent.tsx index ccfbde88..c30472d8 100644 --- a/frontend/lib/registry/components/pivot-grid/PivotGridComponent.tsx +++ b/frontend/lib/registry/components/pivot-grid/PivotGridComponent.tsx @@ -432,10 +432,20 @@ export const PivotGridComponent: React.FC = ({ // ν•„ν„° μ˜μ—­ ν•„λ“œ const filterFields = useMemo( - () => - fields + () => { + const result = fields .filter((f) => f.area === "filter" && f.visible !== false) - .sort((a, b) => (a.areaIndex || 0) - (b.areaIndex || 0)), + .sort((a, b) => (a.areaIndex || 0) - (b.areaIndex || 0)); + + console.log("πŸ”· [filterFields] ν•„ν„° ν•„λ“œ 계산:", { + totalFields: fields.length, + filterFieldsCount: result.length, + filterFieldNames: result.map(f => f.field), + allFieldAreas: fields.map(f => ({ field: f.field, area: f.area, visible: f.visible })), + }); + + return result; + }, [fields] ); @@ -715,7 +725,15 @@ export const PivotGridComponent: React.FC = ({ // ν•„λ“œ λ³€κ²½ const handleFieldsChange = useCallback( (newFields: PivotFieldConfig[]) => { + console.log("πŸ”· [handleFieldsChange] ν•„λ“œ λ³€κ²½:", { + totalFields: newFields.length, + filterFields: newFields.filter(f => f.area === "filter").length, + filterFieldNames: newFields.filter(f => f.area === "filter").map(f => f.field), + changedFields: newFields.filter(f => f.area === "filter"), + }); + console.log("πŸ”· [handleFieldsChange] setFields 호좜 μ „"); setFields(newFields); + console.log("πŸ”· [handleFieldsChange] setFields 호좜 ν›„"); }, [] ); @@ -1023,10 +1041,12 @@ export const PivotGridComponent: React.FC = ({ console.log("ν”Όλ²— μƒνƒœκ°€ μ €μž₯λ˜μ—ˆμŠ΅λ‹ˆλ‹€."); }, [saveStateToStorage]); - // μƒνƒœ μ΄ˆκΈ°ν™” + // μƒνƒœ μ΄ˆκΈ°ν™” (ν™•μž₯/μΆ•μ†Œ, μ •λ ¬, ν•„ν„°λ§Œ μ΄ˆκΈ°ν™”, ν•„λ“œ 섀정은 μœ μ§€) const handleResetState = useCallback(() => { + // 둜컬 μŠ€ν† λ¦¬μ§€μ—μ„œ μƒνƒœ 제거 localStorage.removeItem(stateStorageKey); - setFields(initialFields); + + // ν™•μž₯/μΆ•μ†Œ, μ •λ ¬, ν•„ν„° μƒνƒœλ§Œ μ΄ˆκΈ°ν™” setPivotState({ expandedRowPaths: [], expandedColumnPaths: [], @@ -1037,7 +1057,10 @@ export const PivotGridComponent: React.FC = ({ setColumnWidths({}); setSelectedCell(null); setSelectionRange(null); - }, [stateStorageKey, initialFields]); + + // πŸ†• ν•„λ“œ 섀정은 μœ μ§€ (initialFields둜 λ˜λŒλ¦¬μ§€ μ•ŠμŒ) + console.log("πŸ”· ν”Όλ²— μƒνƒœκ°€ μ΄ˆκΈ°ν™”λ˜μ—ˆμŠ΅λ‹ˆλ‹€ (ν•„λ“œ 섀정은 μœ μ§€)"); + }, [stateStorageKey]); // ν•„λ“œ 숨기기/ν‘œμ‹œ μƒνƒœ const [hiddenFields, setHiddenFields] = useState>(new Set()); @@ -1617,19 +1640,25 @@ export const PivotGridComponent: React.FC = ({ } /> diff --git a/frontend/lib/registry/components/pivot-grid/components/FieldPanel.tsx b/frontend/lib/registry/components/pivot-grid/components/FieldPanel.tsx index fed43afb..08dca70e 100644 --- a/frontend/lib/registry/components/pivot-grid/components/FieldPanel.tsx +++ b/frontend/lib/registry/components/pivot-grid/components/FieldPanel.tsx @@ -25,6 +25,7 @@ import { horizontalListSortingStrategy, useSortable, } from "@dnd-kit/sortable"; +import { useDroppable } from "@dnd-kit/core"; import { CSS } from "@dnd-kit/utilities"; import { cn } from "@/lib/utils"; import { PivotFieldConfig, PivotAreaType } from "../types"; @@ -244,22 +245,31 @@ const DroppableArea: React.FC = ({ const areaFields = fields.filter((f) => f.area === area && f.visible !== false); const fieldIds = areaFields.map((f) => `${area}-${f.field}`); + // πŸ†• λ“œλ‘­ κ°€λŠ₯ μ˜μ—­ μ„€μ • + const { setNodeRef, isOver: isOverDroppable } = useDroppable({ + id: area, // "filter", "column", "row", "data" + }); + + const finalIsOver = isOver || isOverDroppable; + return (
{/* μ˜μ—­ 헀더 */} -
+
{icon} {title} {areaFields.length > 0 && ( - + {areaFields.length} )} @@ -267,11 +277,16 @@ const DroppableArea: React.FC = ({ {/* ν•„λ“œ λͺ©λ‘ */} -
+
{areaFields.length === 0 ? ( - - ν•„λ“œλ₯Ό μ—¬κΈ°λ‘œ λ“œλž˜κ·Έ - +
+ + ← ν•„λ“œλ₯Ό μ—¬κΈ°λ‘œ λ“œλž˜κ·Έν•˜μ„Έμš” + +
) : ( areaFields.map((field) => ( = ({ return; } - // λ“œλ‘­ μ˜μ—­ 감지 + // λ“œλ‘­ μ˜μ—­ 감지 (μ˜μ—­ 자체의 IDλ₯Ό μš°μ„  확인) const overId = over.id as string; + + // 1. overIdκ°€ μ˜μ—­ 자체인 경우 (filter, column, row, data) + if (["filter", "column", "row", "data"].includes(overId)) { + setOverArea(overId as PivotAreaType); + console.log("πŸ”· [handleDragOver] μ˜μ—­ 감지:", overId); + return; + } + + // 2. overIdκ°€ ν•„λ“œμΈ 경우 (예: row-part_name) const targetArea = overId.split("-")[0] as PivotAreaType; if (["filter", "column", "row", "data"].includes(targetArea)) { setOverArea(targetArea); + console.log("πŸ”· [handleDragOver] ν•„λ“œ μ˜μ—­ 감지:", targetArea); } }; // λ“œλž˜κ·Έ μ’…λ£Œ const handleDragEnd = (event: DragEndEvent) => { const { active, over } = event; + const currentOverArea = overArea; // handleDragOverμ—μ„œ κ°μ§€ν•œ μ˜μ—­ μ €μž₯ setActiveId(null); setOverArea(null); - if (!over) return; + if (!over) { + console.log("πŸ”· [FieldPanel] λ“œλ‘­ λŒ€μƒ μ—†μŒ"); + return; + } const activeId = active.id as string; const overId = over.id as string; + console.log("πŸ”· [FieldPanel] λ“œλž˜κ·Έ μ’…λ£Œ:", { + activeId, + overId, + detectedOverArea: currentOverArea, + }); + // ν•„λ“œ 정보 νŒŒμ‹± const [sourceArea, sourceField] = activeId.split("-") as [ PivotAreaType, string ]; - const [targetArea] = overId.split("-") as [PivotAreaType, string]; + + // targetArea κ²°μ •: handleDragOverμ—μ„œ κ°μ§€ν•œ μ˜μ—­ μš°μ„  μ‚¬μš© + let targetArea: PivotAreaType; + if (currentOverArea) { + targetArea = currentOverArea; + } else if (["filter", "column", "row", "data"].includes(overId)) { + targetArea = overId as PivotAreaType; + } else { + targetArea = overId.split("-")[0] as PivotAreaType; + } + + console.log("πŸ”· [FieldPanel] νŒŒμ‹± κ²°κ³Ό:", { + sourceArea, + sourceField, + targetArea, + usedOverArea: !!currentOverArea, + }); // 같은 μ˜μ—­ λ‚΄ μ •λ ¬ if (sourceArea === targetArea) { @@ -396,6 +447,12 @@ export const FieldPanel: React.FC = ({ // λ‹€λ₯Έ μ˜μ—­μœΌλ‘œ 이동 if (["filter", "column", "row", "data"].includes(targetArea)) { + console.log("πŸ”· [FieldPanel] μ˜μ—­ 이동:", { + field: sourceField, + from: sourceArea, + to: targetArea, + }); + const newFields = fields.map((f) => { if (f.field === sourceField && f.area === sourceArea) { return { @@ -406,6 +463,13 @@ export const FieldPanel: React.FC = ({ } return f; }); + + console.log("πŸ”· [FieldPanel] λ³€κ²½λœ ν•„λ“œ:", { + totalFields: newFields.length, + filterFields: newFields.filter(f => f.area === "filter").length, + changedField: newFields.find(f => f.field === sourceField), + }); + onFieldsChange(newFields); } }; -- 2.43.0