탭안에있는 화면 검색필터링 기능
This commit is contained in:
@@ -6,6 +6,7 @@ import { Input } from "@/components/ui/input";
|
||||
import { Settings, Filter, Layers, X, Check, ChevronsUpDown } from "lucide-react";
|
||||
import { useTableOptions } from "@/contexts/TableOptionsContext";
|
||||
import { useTableSearchWidgetHeight } from "@/contexts/TableSearchWidgetHeightContext";
|
||||
import { useActiveTab } from "@/contexts/ActiveTabContext";
|
||||
import { ColumnVisibilityPanel } from "@/components/screen/table-options/ColumnVisibilityPanel";
|
||||
import { FilterPanel } from "@/components/screen/table-options/FilterPanel";
|
||||
import { GroupingPanel } from "@/components/screen/table-options/GroupingPanel";
|
||||
@@ -49,8 +50,9 @@ interface TableSearchWidgetProps {
|
||||
}
|
||||
|
||||
export function TableSearchWidget({ component, screenId, onHeightChange }: TableSearchWidgetProps) {
|
||||
const { registeredTables, selectedTableId, setSelectedTableId, getTable } = useTableOptions();
|
||||
const { registeredTables, selectedTableId, setSelectedTableId, getTable, getActiveTabTables } = useTableOptions();
|
||||
const { isPreviewMode } = useScreenPreview(); // 미리보기 모드 확인
|
||||
const { getAllActiveTabIds, activeTabs } = useActiveTab(); // 활성 탭 정보
|
||||
|
||||
// 높이 관리 context (실제 화면에서만 사용)
|
||||
let setWidgetHeight:
|
||||
@@ -63,6 +65,9 @@ export function TableSearchWidget({ component, screenId, onHeightChange }: Table
|
||||
// Context가 없으면 (디자이너 모드) 무시
|
||||
setWidgetHeight = undefined;
|
||||
}
|
||||
|
||||
// 탭별 필터 값 저장 (탭 ID -> 필터 값)
|
||||
const [tabFilterValues, setTabFilterValues] = useState<Record<string, Record<string, any>>>({});
|
||||
|
||||
const [columnVisibilityOpen, setColumnVisibilityOpen] = useState(false);
|
||||
const [filterOpen, setFilterOpen] = useState(false);
|
||||
@@ -88,38 +93,48 @@ export function TableSearchWidget({ component, screenId, onHeightChange }: Table
|
||||
// Map을 배열로 변환
|
||||
const allTableList = Array.from(registeredTables.values());
|
||||
|
||||
// 대상 패널 위치에 따라 테이블 필터링 (tableId 패턴 기반)
|
||||
// 현재 활성 탭 ID 목록
|
||||
const activeTabIds = useMemo(() => getAllActiveTabIds(), [activeTabs]);
|
||||
|
||||
// 대상 패널 위치 + 활성 탭에 따라 테이블 필터링
|
||||
const tableList = useMemo(() => {
|
||||
// "auto"면 모든 테이블 반환
|
||||
if (targetPanelPosition === "auto") {
|
||||
return allTableList;
|
||||
}
|
||||
|
||||
// 테이블 ID 패턴으로 필터링
|
||||
// card-display-XXX: 좌측 패널 (카드 디스플레이)
|
||||
// datatable-XXX, table-list-XXX: 우측 패널 (테이블 리스트)
|
||||
const filteredTables = allTableList.filter(table => {
|
||||
const tableId = table.tableId.toLowerCase();
|
||||
|
||||
if (targetPanelPosition === "left") {
|
||||
// 좌측 패널 대상: card-display만
|
||||
return tableId.includes("card-display") || tableId.includes("card");
|
||||
} else if (targetPanelPosition === "right") {
|
||||
// 우측 패널 대상: datatable, table-list 등 (card-display 제외)
|
||||
const isCardDisplay = tableId.includes("card-display") || tableId.includes("card");
|
||||
return !isCardDisplay;
|
||||
}
|
||||
|
||||
return true;
|
||||
// 1단계: 활성 탭 기반 필터링
|
||||
// - 활성 탭에 속한 테이블만 표시
|
||||
// - 탭에 속하지 않은 테이블(parentTabId가 없는)도 포함
|
||||
let filteredByTab = allTableList.filter(table => {
|
||||
// 탭에 속하지 않는 테이블은 항상 표시
|
||||
if (!table.parentTabId) return true;
|
||||
// 활성 탭에 속한 테이블만 표시
|
||||
return activeTabIds.includes(table.parentTabId);
|
||||
});
|
||||
|
||||
// 필터링된 결과가 없으면 모든 테이블 반환 (폴백)
|
||||
if (filteredTables.length === 0) {
|
||||
return allTableList;
|
||||
// 2단계: 대상 패널 위치에 따라 추가 필터링
|
||||
if (targetPanelPosition !== "auto") {
|
||||
filteredByTab = filteredByTab.filter(table => {
|
||||
const tableId = table.tableId.toLowerCase();
|
||||
|
||||
if (targetPanelPosition === "left") {
|
||||
// 좌측 패널 대상: card-display만
|
||||
return tableId.includes("card-display") || tableId.includes("card");
|
||||
} else if (targetPanelPosition === "right") {
|
||||
// 우측 패널 대상: datatable, table-list 등 (card-display 제외)
|
||||
const isCardDisplay = tableId.includes("card-display") || tableId.includes("card");
|
||||
return !isCardDisplay;
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
return filteredTables;
|
||||
}, [allTableList, targetPanelPosition]);
|
||||
// 필터링된 결과가 없으면 탭 기반 필터링 결과만 반환
|
||||
if (filteredByTab.length === 0) {
|
||||
return allTableList.filter(table =>
|
||||
!table.parentTabId || activeTabIds.includes(table.parentTabId)
|
||||
);
|
||||
}
|
||||
|
||||
return filteredByTab;
|
||||
}, [allTableList, targetPanelPosition, activeTabIds]);
|
||||
|
||||
// currentTable은 tableList(필터링된 목록)에서 가져와야 함
|
||||
const currentTable = useMemo(() => {
|
||||
@@ -151,6 +166,34 @@ export function TableSearchWidget({ component, screenId, onHeightChange }: Table
|
||||
}
|
||||
}, [tableList, selectedTableId, autoSelectFirstTable, setSelectedTableId, targetPanelPosition]);
|
||||
|
||||
// 현재 선택된 테이블의 탭 ID (탭별 필터 저장용)
|
||||
const currentTableTabId = currentTable?.parentTabId;
|
||||
|
||||
// 탭별 필터 값 저장 키 생성
|
||||
const getTabFilterStorageKey = (tableName: string, tabId?: string) => {
|
||||
const baseKey = screenId
|
||||
? `table_filter_values_${tableName}_screen_${screenId}`
|
||||
: `table_filter_values_${tableName}`;
|
||||
return tabId ? `${baseKey}_tab_${tabId}` : baseKey;
|
||||
};
|
||||
|
||||
// 탭 변경 시 이전 탭의 필터 값 저장 + 새 탭의 필터 값 복원
|
||||
useEffect(() => {
|
||||
if (!currentTable?.tableName) return;
|
||||
|
||||
// 현재 필터 값이 있으면 탭별로 저장
|
||||
if (Object.keys(filterValues).length > 0 && currentTableTabId) {
|
||||
const storageKey = getTabFilterStorageKey(currentTable.tableName, currentTableTabId);
|
||||
localStorage.setItem(storageKey, JSON.stringify(filterValues));
|
||||
|
||||
// 메모리 캐시에도 저장
|
||||
setTabFilterValues(prev => ({
|
||||
...prev,
|
||||
[currentTableTabId]: filterValues
|
||||
}));
|
||||
}
|
||||
}, [currentTableTabId, currentTable?.tableName]);
|
||||
|
||||
// 현재 테이블의 저장된 필터 불러오기 (동적 모드) 또는 고정 필터 적용 (고정 모드)
|
||||
useEffect(() => {
|
||||
if (!currentTable?.tableName) return;
|
||||
@@ -165,14 +208,32 @@ export function TableSearchWidget({ component, screenId, onHeightChange }: Table
|
||||
width: f.width || 200,
|
||||
}));
|
||||
setActiveFilters(activeFiltersList);
|
||||
|
||||
// 탭별 저장된 필터 값 복원
|
||||
if (currentTableTabId) {
|
||||
const storageKey = getTabFilterStorageKey(currentTable.tableName, currentTableTabId);
|
||||
const savedValues = localStorage.getItem(storageKey);
|
||||
if (savedValues) {
|
||||
try {
|
||||
const parsedValues = JSON.parse(savedValues);
|
||||
setFilterValues(parsedValues);
|
||||
// 즉시 필터 적용
|
||||
setTimeout(() => applyFilters(parsedValues), 100);
|
||||
} catch {
|
||||
setFilterValues({});
|
||||
}
|
||||
} else {
|
||||
setFilterValues({});
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// 동적 모드: 화면별로 독립적인 필터 설정 불러오기
|
||||
const storageKey = screenId
|
||||
? `table_filters_${currentTable.tableName}_screen_${screenId}`
|
||||
// 동적 모드: 화면별 + 탭별로 독립적인 필터 설정 불러오기
|
||||
const filterConfigKey = screenId
|
||||
? `table_filters_${currentTable.tableName}_screen_${screenId}${currentTableTabId ? `_tab_${currentTableTabId}` : ''}`
|
||||
: `table_filters_${currentTable.tableName}`;
|
||||
const savedFilters = localStorage.getItem(storageKey);
|
||||
const savedFilters = localStorage.getItem(filterConfigKey);
|
||||
|
||||
if (savedFilters) {
|
||||
try {
|
||||
@@ -193,16 +254,39 @@ export function TableSearchWidget({ component, screenId, onHeightChange }: Table
|
||||
operator: "contains",
|
||||
value: "",
|
||||
filterType: f.filterType,
|
||||
width: f.width || 200, // 저장된 너비 포함
|
||||
width: f.width || 200,
|
||||
}));
|
||||
|
||||
setActiveFilters(activeFiltersList);
|
||||
|
||||
// 탭별 저장된 필터 값 복원
|
||||
if (currentTableTabId) {
|
||||
const valuesStorageKey = getTabFilterStorageKey(currentTable.tableName, currentTableTabId);
|
||||
const savedValues = localStorage.getItem(valuesStorageKey);
|
||||
if (savedValues) {
|
||||
try {
|
||||
const parsedValues = JSON.parse(savedValues);
|
||||
setFilterValues(parsedValues);
|
||||
// 즉시 필터 적용
|
||||
setTimeout(() => applyFilters(parsedValues), 100);
|
||||
} catch {
|
||||
setFilterValues({});
|
||||
}
|
||||
} else {
|
||||
setFilterValues({});
|
||||
}
|
||||
} else {
|
||||
setFilterValues({});
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("저장된 필터 불러오기 실패:", error);
|
||||
}
|
||||
} else {
|
||||
// 필터 설정이 없으면 초기화
|
||||
setFilterValues({});
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [currentTable?.tableName, filterMode, screenId, JSON.stringify(presetFilters)]);
|
||||
}, [currentTable?.tableName, filterMode, screenId, currentTableTabId, JSON.stringify(presetFilters)]);
|
||||
|
||||
// select 옵션 초기 로드 (한 번만 실행, 이후 유지)
|
||||
useEffect(() => {
|
||||
@@ -300,6 +384,12 @@ export function TableSearchWidget({ component, screenId, onHeightChange }: Table
|
||||
|
||||
setFilterValues(newValues);
|
||||
|
||||
// 탭별 필터 값 저장
|
||||
if (currentTable?.tableName && currentTableTabId) {
|
||||
const storageKey = getTabFilterStorageKey(currentTable.tableName, currentTableTabId);
|
||||
localStorage.setItem(storageKey, JSON.stringify(newValues));
|
||||
}
|
||||
|
||||
// 실시간 검색: 값 변경 시 즉시 필터 적용
|
||||
applyFilters(newValues);
|
||||
};
|
||||
@@ -365,6 +455,12 @@ export function TableSearchWidget({ component, screenId, onHeightChange }: Table
|
||||
setFilterValues({});
|
||||
setSelectedLabels({});
|
||||
currentTable?.onFilterChange([]);
|
||||
|
||||
// 탭별 저장된 필터 값도 초기화
|
||||
if (currentTable?.tableName && currentTableTabId) {
|
||||
const storageKey = getTabFilterStorageKey(currentTable.tableName, currentTableTabId);
|
||||
localStorage.removeItem(storageKey);
|
||||
}
|
||||
};
|
||||
|
||||
// 필터 입력 필드 렌더링
|
||||
|
||||
Reference in New Issue
Block a user