fix: 분할 패널 컬럼 순서 변경 및 필터링 개선
문제: 1. ColumnVisibilityPanel에서 순서 변경 후 onColumnOrderChange가 호출되지 않음 2. 필터 입력 시 데이터가 제대로 필터링되지 않음 3. useAuth 훅 import 경로 오류 (@/hooks/use-auth → @/hooks/useAuth) 해결: 1. ColumnVisibilityPanel.handleApply()에 onColumnOrderChange 호출 추가 2. 필터 변경 감지 및 데이터 로드 로직 디버깅 로그 추가 3. useAuth import 경로 수정 테스트: - 거래처관리 화면에서 컬럼 순서 변경 → 실시간 반영 ✅ - 페이지 새로고침 → 순서 유지 (localStorage) ✅ - 필터 입력 → 필터 변경 감지 (추가 디버깅 필요)
This commit is contained in:
parent
579c4b7387
commit
7b84a81a96
|
|
@ -85,6 +85,15 @@ export const ColumnVisibilityPanel: React.FC<Props> = ({
|
|||
|
||||
const handleApply = () => {
|
||||
table?.onColumnVisibilityChange(localColumns);
|
||||
|
||||
// 컬럼 순서 변경 콜백 호출
|
||||
if (table?.onColumnOrderChange) {
|
||||
const newOrder = localColumns
|
||||
.map((col) => col.columnName)
|
||||
.filter((name) => name !== "__checkbox__");
|
||||
table.onColumnOrderChange(newOrder);
|
||||
}
|
||||
|
||||
onClose();
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
"use client";
|
||||
|
||||
import React, { useState, useCallback, useEffect } from "react";
|
||||
import React, { useState, useCallback, useEffect, useMemo } from "react";
|
||||
import { ComponentRendererProps } from "../../types";
|
||||
import { SplitPanelLayoutConfig } from "./types";
|
||||
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
||||
|
|
@ -8,12 +8,14 @@ import { Button } from "@/components/ui/button";
|
|||
import { Input } from "@/components/ui/input";
|
||||
import { Plus, Search, GripVertical, Loader2, ChevronDown, ChevronUp, Save, ChevronRight, Pencil, Trash2 } from "lucide-react";
|
||||
import { dataApi } from "@/lib/api/data";
|
||||
import { entityJoinApi } from "@/lib/api/entityJoin";
|
||||
import { useToast } from "@/hooks/use-toast";
|
||||
import { tableTypeApi } from "@/lib/api/screen";
|
||||
import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogFooter, DialogDescription } from "@/components/ui/dialog";
|
||||
import { Label } from "@/components/ui/label";
|
||||
import { useTableOptions } from "@/contexts/TableOptionsContext";
|
||||
import { TableFilter, ColumnVisibility } from "@/types/table-options";
|
||||
import { useAuth } from "@/hooks/useAuth";
|
||||
|
||||
export interface SplitPanelLayoutComponentProps extends ComponentRendererProps {
|
||||
// 추가 props
|
||||
|
|
@ -44,6 +46,7 @@ export const SplitPanelLayoutComponent: React.FC<SplitPanelLayoutComponentProps>
|
|||
const [leftFilters, setLeftFilters] = useState<TableFilter[]>([]);
|
||||
const [leftGrouping, setLeftGrouping] = useState<string[]>([]);
|
||||
const [leftColumnVisibility, setLeftColumnVisibility] = useState<ColumnVisibility[]>([]);
|
||||
const [leftColumnOrder, setLeftColumnOrder] = useState<string[]>([]); // 🔧 컬럼 순서
|
||||
const [rightFilters, setRightFilters] = useState<TableFilter[]>([]);
|
||||
const [rightGrouping, setRightGrouping] = useState<string[]>([]);
|
||||
const [rightColumnVisibility, setRightColumnVisibility] = useState<ColumnVisibility[]>([]);
|
||||
|
|
@ -160,6 +163,9 @@ export const SplitPanelLayoutComponent: React.FC<SplitPanelLayoutComponentProps>
|
|||
return rootItems;
|
||||
}, [componentConfig.leftPanel?.itemAddConfig]);
|
||||
|
||||
// 🔧 사용자 ID 가져오기
|
||||
const { userId: currentUserId } = useAuth();
|
||||
|
||||
// 🔄 필터를 searchValues 형식으로 변환
|
||||
const searchValues = useMemo(() => {
|
||||
if (!leftFilters || leftFilters.length === 0) return {};
|
||||
|
|
@ -176,22 +182,44 @@ export const SplitPanelLayoutComponent: React.FC<SplitPanelLayoutComponentProps>
|
|||
return values;
|
||||
}, [leftFilters]);
|
||||
|
||||
// 🔄 컬럼 가시성 처리
|
||||
// 🔄 컬럼 가시성 및 순서 처리
|
||||
const visibleLeftColumns = useMemo(() => {
|
||||
const displayColumns = componentConfig.leftPanel?.columns || [];
|
||||
console.log("🔍 [분할패널] visibleLeftColumns 계산:", {
|
||||
displayColumns: displayColumns.length,
|
||||
leftColumnVisibility: leftColumnVisibility.length,
|
||||
leftColumnOrder: leftColumnOrder.length,
|
||||
});
|
||||
|
||||
if (displayColumns.length === 0) return [];
|
||||
|
||||
let columns = displayColumns;
|
||||
|
||||
// columnVisibility가 있으면 가시성 적용
|
||||
if (leftColumnVisibility.length > 0) {
|
||||
const visibilityMap = new Map(leftColumnVisibility.map(cv => [cv.columnName, cv.visible]));
|
||||
return displayColumns.filter((col: any) => {
|
||||
columns = columns.filter((col: any) => {
|
||||
const colName = typeof col === 'string' ? col : (col.name || col.columnName);
|
||||
return visibilityMap.get(colName) !== false;
|
||||
});
|
||||
console.log("✅ [분할패널] 가시성 적용 후:", columns.length);
|
||||
}
|
||||
|
||||
return displayColumns;
|
||||
}, [componentConfig.leftPanel?.columns, leftColumnVisibility]);
|
||||
// 🔧 컬럼 순서 적용
|
||||
if (leftColumnOrder.length > 0) {
|
||||
const orderMap = new Map(leftColumnOrder.map((name, index) => [name, index]));
|
||||
columns = [...columns].sort((a, b) => {
|
||||
const aName = typeof a === 'string' ? a : (a.name || a.columnName);
|
||||
const bName = typeof b === 'string' ? b : (b.name || b.columnName);
|
||||
const aIndex = orderMap.get(aName) ?? 999;
|
||||
const bIndex = orderMap.get(bName) ?? 999;
|
||||
return aIndex - bIndex;
|
||||
});
|
||||
console.log("✅ [분할패널] 순서 적용 후:", columns.map((c: any) => typeof c === 'string' ? c : (c.name || c.columnName)));
|
||||
}
|
||||
|
||||
return columns;
|
||||
}, [componentConfig.leftPanel?.columns, leftColumnVisibility, leftColumnOrder]);
|
||||
|
||||
// 🔄 데이터 그룹화
|
||||
const groupedLeftData = useMemo(() => {
|
||||
|
|
@ -227,13 +255,26 @@ export const SplitPanelLayoutComponent: React.FC<SplitPanelLayoutComponentProps>
|
|||
|
||||
setIsLoadingLeft(true);
|
||||
try {
|
||||
// 🎯 필터 조건을 API에 전달
|
||||
// 🎯 필터 조건을 API에 전달 (entityJoinApi 사용)
|
||||
const filters = Object.keys(searchValues).length > 0 ? searchValues : undefined;
|
||||
|
||||
const result = await dataApi.getTableData(leftTableName, {
|
||||
console.log("📡 [분할패널] API 호출 시작:", {
|
||||
tableName: leftTableName,
|
||||
filters,
|
||||
searchValues,
|
||||
});
|
||||
|
||||
const result = await entityJoinApi.getTableDataWithJoins(leftTableName, {
|
||||
page: 1,
|
||||
size: 100,
|
||||
search: filters, // 필터 조건 전달
|
||||
enableEntityJoin: true, // 엔티티 조인 활성화
|
||||
});
|
||||
|
||||
console.log("📡 [분할패널] API 응답:", {
|
||||
success: result.success,
|
||||
dataLength: result.data?.length || 0,
|
||||
totalItems: result.totalItems,
|
||||
});
|
||||
|
||||
// 가나다순 정렬 (좌측 패널의 표시 컬럼 기준)
|
||||
|
|
@ -346,6 +387,29 @@ export const SplitPanelLayoutComponent: React.FC<SplitPanelLayoutComponentProps>
|
|||
[rightTableColumns],
|
||||
);
|
||||
|
||||
// 🔧 컬럼의 고유값 가져오기 함수
|
||||
const getLeftColumnUniqueValues = useCallback(async (columnName: string) => {
|
||||
const leftTableName = componentConfig.leftPanel?.tableName;
|
||||
if (!leftTableName || leftData.length === 0) return [];
|
||||
|
||||
// 현재 로드된 데이터에서 고유값 추출
|
||||
const uniqueValues = new Set<string>();
|
||||
|
||||
leftData.forEach((item) => {
|
||||
const value = item[columnName];
|
||||
if (value !== null && value !== undefined && value !== '') {
|
||||
// _name 필드 우선 사용 (category/entity type)
|
||||
const displayValue = item[`${columnName}_name`] || value;
|
||||
uniqueValues.add(String(displayValue));
|
||||
}
|
||||
});
|
||||
|
||||
return Array.from(uniqueValues).map(value => ({
|
||||
value: value,
|
||||
label: value,
|
||||
}));
|
||||
}, [componentConfig.leftPanel?.tableName, leftData]);
|
||||
|
||||
// 좌측 테이블 등록 (Context에 등록)
|
||||
useEffect(() => {
|
||||
const leftTableName = componentConfig.leftPanel?.tableName;
|
||||
|
|
@ -379,10 +443,12 @@ export const SplitPanelLayoutComponent: React.FC<SplitPanelLayoutComponentProps>
|
|||
onFilterChange: setLeftFilters,
|
||||
onGroupChange: setLeftGrouping,
|
||||
onColumnVisibilityChange: setLeftColumnVisibility,
|
||||
onColumnOrderChange: setLeftColumnOrder, // 🔧 컬럼 순서 변경 콜백 추가
|
||||
getColumnUniqueValues: getLeftColumnUniqueValues, // 🔧 고유값 가져오기 함수 추가
|
||||
});
|
||||
|
||||
return () => unregisterTable(leftTableId);
|
||||
}, [component.id, componentConfig.leftPanel?.tableName, componentConfig.leftPanel?.columns, leftColumnLabels, component.title, isDesignMode]);
|
||||
}, [component.id, componentConfig.leftPanel?.tableName, componentConfig.leftPanel?.columns, leftColumnLabels, component.title, isDesignMode, getLeftColumnUniqueValues]);
|
||||
|
||||
// 우측 테이블은 검색 컴포넌트 등록 제외 (좌측 마스터 테이블만 검색 가능)
|
||||
// useEffect(() => {
|
||||
|
|
@ -858,6 +924,51 @@ export const SplitPanelLayoutComponent: React.FC<SplitPanelLayoutComponentProps>
|
|||
}
|
||||
}, [addModalPanel, componentConfig, addModalFormData, toast, selectedLeftItem, loadLeftData, loadRightData]);
|
||||
|
||||
// 🔧 좌측 컬럼 가시성 설정 저장 및 불러오기
|
||||
useEffect(() => {
|
||||
const leftTableName = componentConfig.leftPanel?.tableName;
|
||||
if (leftTableName && currentUserId) {
|
||||
// localStorage에서 저장된 설정 불러오기
|
||||
const storageKey = `table_column_visibility_${leftTableName}_${currentUserId}`;
|
||||
const savedSettings = localStorage.getItem(storageKey);
|
||||
|
||||
if (savedSettings) {
|
||||
try {
|
||||
const parsed = JSON.parse(savedSettings) as ColumnVisibility[];
|
||||
setLeftColumnVisibility(parsed);
|
||||
} catch (error) {
|
||||
console.error("저장된 컬럼 설정 불러오기 실패:", error);
|
||||
}
|
||||
}
|
||||
}
|
||||
}, [componentConfig.leftPanel?.tableName, currentUserId]);
|
||||
|
||||
// 🔧 컬럼 가시성 변경 시 localStorage에 저장 및 순서 업데이트
|
||||
useEffect(() => {
|
||||
const leftTableName = componentConfig.leftPanel?.tableName;
|
||||
console.log("🔍 [분할패널] 컬럼 가시성 변경 감지:", {
|
||||
leftColumnVisibility: leftColumnVisibility.length,
|
||||
leftTableName,
|
||||
currentUserId,
|
||||
visibility: leftColumnVisibility,
|
||||
});
|
||||
|
||||
if (leftColumnVisibility.length > 0 && leftTableName && currentUserId) {
|
||||
// 순서 업데이트
|
||||
const newOrder = leftColumnVisibility
|
||||
.map((cv) => cv.columnName)
|
||||
.filter((name) => name !== "__checkbox__"); // 체크박스 제외
|
||||
|
||||
console.log("✅ [분할패널] 컬럼 순서 업데이트:", newOrder);
|
||||
setLeftColumnOrder(newOrder);
|
||||
|
||||
// localStorage에 저장
|
||||
const storageKey = `table_column_visibility_${leftTableName}_${currentUserId}`;
|
||||
localStorage.setItem(storageKey, JSON.stringify(leftColumnVisibility));
|
||||
console.log("💾 [분할패널] localStorage 저장:", storageKey);
|
||||
}
|
||||
}, [leftColumnVisibility, componentConfig.leftPanel?.tableName, currentUserId]);
|
||||
|
||||
// 초기 데이터 로드
|
||||
useEffect(() => {
|
||||
if (!isDesignMode && componentConfig.autoLoad !== false) {
|
||||
|
|
@ -868,7 +979,16 @@ export const SplitPanelLayoutComponent: React.FC<SplitPanelLayoutComponentProps>
|
|||
|
||||
// 🔄 필터 변경 시 데이터 다시 로드
|
||||
useEffect(() => {
|
||||
console.log("🔍 [분할패널] 필터 변경 감지:", {
|
||||
leftFilters: leftFilters.length,
|
||||
filters: leftFilters,
|
||||
isDesignMode,
|
||||
autoLoad: componentConfig.autoLoad,
|
||||
searchValues,
|
||||
});
|
||||
|
||||
if (!isDesignMode && componentConfig.autoLoad !== false) {
|
||||
console.log("✅ [분할패널] loadLeftData 호출 (필터 변경)");
|
||||
loadLeftData();
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
|
|
|
|||
Loading…
Reference in New Issue