검색필터 고장

This commit is contained in:
kjs 2025-12-24 14:46:51 +09:00
parent 5102eec46f
commit 5daef415ad
6 changed files with 183 additions and 116 deletions

View File

@ -542,10 +542,12 @@ export const InteractiveScreenViewerDynamic: React.FC<InteractiveScreenViewerPro
if (response.success) {
// 리피터 데이터 저장 이벤트 발생 (UnifiedRepeater 컴포넌트가 리스닝)
window.dispatchEvent(new CustomEvent("repeaterSave", {
detail: { parentId: response.data?.id || formData.id }
}));
window.dispatchEvent(
new CustomEvent("repeaterSave", {
detail: { parentId: response.data?.id || formData.id },
}),
);
toast.success("데이터가 성공적으로 저장되었습니다.");
} else {
toast.error(response.message || "저장에 실패했습니다.");

View File

@ -45,9 +45,6 @@ export const UnifiedList = forwardRef<HTMLDivElement, UnifiedListProps>((props,
[config.columns],
);
// 디버깅: config.cardConfig 확인
console.log("📋 UnifiedList config.cardConfig:", config.cardConfig);
// TableListComponent에 전달할 component 객체 생성
const componentObj = useMemo(
() => ({

View File

@ -1,27 +1,11 @@
import React, {
createContext,
useContext,
useState,
useCallback,
useMemo,
ReactNode,
} from "react";
import {
TableRegistration,
TableOptionsContextValue,
} from "@/types/table-options";
import React, { createContext, useContext, useState, useCallback, useMemo, ReactNode } from "react";
import { TableRegistration, TableOptionsContextValue } from "@/types/table-options";
import { useActiveTab } from "./ActiveTabContext";
const TableOptionsContext = createContext<TableOptionsContextValue | undefined>(
undefined
);
const TableOptionsContext = createContext<TableOptionsContextValue | undefined>(undefined);
export const TableOptionsProvider: React.FC<{ children: ReactNode }> = ({
children,
}) => {
const [registeredTables, setRegisteredTables] = useState<
Map<string, TableRegistration>
>(new Map());
export const TableOptionsProvider: React.FC<{ children: ReactNode }> = ({ children }) => {
const [registeredTables, setRegisteredTables] = useState<Map<string, TableRegistration>>(new Map());
const [selectedTableId, setSelectedTableId] = useState<string | null>(null);
/**
@ -43,7 +27,7 @@ export const TableOptionsProvider: React.FC<{ children: ReactNode }> = ({
/**
*
* :
* :
* 1. selectedTableId를
* 2. unregister가 selectedTableId를
*/
@ -54,13 +38,13 @@ export const TableOptionsProvider: React.FC<{ children: ReactNode }> = ({
newMap.delete(tableId);
return newMap;
});
// 🚫 selectedTableId를 변경하지 않음
// 이유: useEffect 재실행 시 cleanup → register 순서로 호출되는데,
// cleanup에서 selectedTableId를 null로 만들면 필터 설정이 초기화됨
// 다른 테이블이 선택되어야 하면 TableSearchWidget에서 자동 선택함
},
[] // 의존성 없음 - 무한 루프 방지
[], // 의존성 없음 - 무한 루프 방지
);
/**
@ -70,7 +54,7 @@ export const TableOptionsProvider: React.FC<{ children: ReactNode }> = ({
(tableId: string) => {
return registeredTables.get(tableId);
},
[registeredTables]
[registeredTables],
);
/**
@ -99,25 +83,26 @@ export const TableOptionsProvider: React.FC<{ children: ReactNode }> = ({
const getActiveTabTables = useCallback(() => {
const allTables = Array.from(registeredTables.values());
const activeTabIds = activeTabContext.getAllActiveTabIds();
// 활성 탭이 없으면 탭에 속하지 않은 테이블만 반환
if (activeTabIds.length === 0) {
return allTables.filter(table => !table.parentTabId);
return allTables.filter((table) => !table.parentTabId);
}
// 활성 탭에 속한 테이블 + 탭에 속하지 않은 테이블
return allTables.filter(table =>
!table.parentTabId || activeTabIds.includes(table.parentTabId)
);
return allTables.filter((table) => !table.parentTabId || activeTabIds.includes(table.parentTabId));
}, [registeredTables, activeTabContext]);
/**
*
*/
const getTablesForTab = useCallback((tabId: string) => {
const allTables = Array.from(registeredTables.values());
return allTables.filter(table => table.parentTabId === tabId);
}, [registeredTables]);
const getTablesForTab = useCallback(
(tabId: string) => {
const allTables = Array.from(registeredTables.values());
return allTables.filter((table) => table.parentTabId === tabId);
},
[registeredTables],
);
return (
<TableOptionsContext.Provider
@ -142,10 +127,12 @@ export const TableOptionsProvider: React.FC<{ children: ReactNode }> = ({
* Context Hook
*/
export const useTableOptions = () => {
console.log("🔍🔍🔍 [useTableOptions] Hook 호출됨");
const context = useContext(TableOptionsContext);
console.log("🔍 [useTableOptions] context 확인:", { hasContext: !!context });
if (!context) {
console.error("❌ [useTableOptions] Context가 없습니다! TableOptionsProvider 외부에서 호출됨");
throw new Error("useTableOptions must be used within TableOptionsProvider");
}
return context;
};

View File

@ -720,17 +720,53 @@ export const DynamicComponentRenderer: React.FC<DynamicComponentRendererProps> =
};
// 렌더러가 클래스인지 함수인지 확인
if (
const isClass =
typeof NewComponentRenderer === "function" &&
NewComponentRenderer.prototype &&
NewComponentRenderer.prototype.render
) {
NewComponentRenderer.prototype.render;
if (componentType === "table-search-widget") {
console.log("🔍 [DynamicComponentRenderer] table-search-widget 렌더링 분기:", {
isClass,
hasPrototype: !!NewComponentRenderer.prototype,
hasRender: !!NewComponentRenderer.prototype?.render,
componentName: NewComponentRenderer.name,
componentProp: rendererProps.component,
screenId: rendererProps.screenId,
});
}
if (isClass) {
// 클래스 기반 렌더러 (AutoRegisteringComponentRenderer 상속)
const rendererInstance = new NewComponentRenderer(rendererProps);
return rendererInstance.render();
} else {
// 함수형 컴포넌트
// refreshKey를 React key로 전달하여 컴포넌트 리마운트 강제
// 🔧 디버깅: table-search-widget인 경우 직접 호출 후 반환
if (componentType === "table-search-widget") {
console.log("🔧🔧🔧 [DynamicComponentRenderer] TableSearchWidget 직접 호출 반환");
console.log("🔧 [DynamicComponentRenderer] NewComponentRenderer 함수 확인:", {
name: NewComponentRenderer.name,
toString: NewComponentRenderer.toString().substring(0, 200),
});
try {
const result = NewComponentRenderer(rendererProps);
console.log("🔧 [DynamicComponentRenderer] TableSearchWidget 결과 상세:", {
resultType: typeof result,
type: result?.type?.name || result?.type || "unknown",
propsKeys: result?.props ? Object.keys(result.props) : [],
propsStyle: result?.props?.style,
propsChildren: typeof result?.props?.children,
});
// 직접 호출 결과를 반환
return result;
} catch (directCallError) {
console.error("❌ [DynamicComponentRenderer] TableSearchWidget 직접 호출 실패:", directCallError);
}
}
return <NewComponentRenderer key={refreshKey} {...rendererProps} />;
}
}

View File

@ -342,14 +342,19 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
const newSearchValues: Record<string, any> = {};
filters.forEach((filter) => {
if (filter.value) {
newSearchValues[filter.columnName] = filter.value;
// operator 정보도 함께 전달 (백엔드에서 equals/contains 구분)
newSearchValues[filter.columnName] = {
value: filter.value,
operator: filter.operator || "contains",
};
}
});
// console.log("🔍 [TableListComponent] filters → searchValues:", {
// filters: filters.length,
// searchValues: newSearchValues,
// });
console.log("🔍 [TableListComponent] filters → searchValues:", {
filtersCount: filters.length,
filters: filters.map((f) => ({ col: f.columnName, op: f.operator, val: f.value })),
searchValues: newSearchValues,
});
setSearchValues(newSearchValues);
setCurrentPage(1); // 필터 변경 시 첫 페이지로

View File

@ -50,7 +50,24 @@ interface TableSearchWidgetProps {
}
export function TableSearchWidget({ component, screenId, onHeightChange }: TableSearchWidgetProps) {
const { registeredTables, selectedTableId, setSelectedTableId, getTable, getActiveTabTables } = useTableOptions();
console.log("🎯🎯🎯 [TableSearchWidget] 함수 시작!", { componentId: component?.id, screenId });
// 🔧 직접 useTableOptions 호출 (에러 발생 시 catch하지 않고 그대로 throw)
const tableOptionsContext = useTableOptions();
console.log("✅ [TableSearchWidget] useTableOptions 성공", { hasContext: !!tableOptionsContext });
const { registeredTables, selectedTableId, setSelectedTableId, getTable, getActiveTabTables } = tableOptionsContext;
// 등록된 테이블 확인 로그
console.log("🔍 [TableSearchWidget] 등록된 테이블:", {
count: registeredTables.size,
tables: Array.from(registeredTables.entries()).map(([id, t]) => ({
id,
tableName: t.tableName,
hasOnFilterChange: typeof t.onFilterChange === "function",
})),
selectedTableId,
});
const { isPreviewMode } = useScreenPreview(); // 미리보기 모드 확인
const { getAllActiveTabIds, activeTabs } = useActiveTab(); // 활성 탭 정보
@ -65,7 +82,7 @@ export function TableSearchWidget({ component, screenId, onHeightChange }: Table
// Context가 없으면 (디자이너 모드) 무시
setWidgetHeight = undefined;
}
// 탭별 필터 값 저장 (탭 ID -> 필터 값)
const [tabFilterValues, setTabFilterValues] = useState<Record<string, Record<string, any>>>({});
@ -92,16 +109,16 @@ export function TableSearchWidget({ component, screenId, onHeightChange }: Table
// Map을 배열로 변환
const allTableList = Array.from(registeredTables.values());
// 현재 활성 탭 ID 목록
const activeTabIds = useMemo(() => getAllActiveTabIds(), [activeTabs]);
// 대상 패널 위치 + 활성 탭에 따라 테이블 필터링
const tableList = useMemo(() => {
// 1단계: 활성 탭 기반 필터링
// - 활성 탭에 속한 테이블만 표시
// - 탭에 속하지 않은 테이블(parentTabId가 없는)도 포함
let filteredByTab = allTableList.filter(table => {
let filteredByTab = allTableList.filter((table) => {
// 탭에 속하지 않는 테이블은 항상 표시
if (!table.parentTabId) return true;
// 활성 탭에 속한 테이블만 표시
@ -110,9 +127,9 @@ export function TableSearchWidget({ component, screenId, onHeightChange }: Table
// 2단계: 대상 패널 위치에 따라 추가 필터링
if (targetPanelPosition !== "auto") {
filteredByTab = filteredByTab.filter(table => {
filteredByTab = filteredByTab.filter((table) => {
const tableId = table.tableId.toLowerCase();
if (targetPanelPosition === "left") {
// 좌측 패널 대상: card-display만
return tableId.includes("card-display") || tableId.includes("card");
@ -121,16 +138,14 @@ export function TableSearchWidget({ component, screenId, onHeightChange }: Table
const isCardDisplay = tableId.includes("card-display") || tableId.includes("card");
return !isCardDisplay;
}
return true;
});
}
// 필터링된 결과가 없으면 탭 기반 필터링 결과만 반환
if (filteredByTab.length === 0) {
return allTableList.filter(table =>
!table.parentTabId || activeTabIds.includes(table.parentTabId)
);
return allTableList.filter((table) => !table.parentTabId || activeTabIds.includes(table.parentTabId));
}
return filteredByTab;
@ -141,18 +156,18 @@ export function TableSearchWidget({ component, screenId, onHeightChange }: Table
console.log("🔍 [TableSearchWidget] currentTable 계산:", {
selectedTableId,
tableListLength: tableList.length,
tableList: tableList.map(t => ({ id: t.tableId, name: t.tableName, parentTabId: t.parentTabId }))
tableList: tableList.map((t) => ({ id: t.tableId, name: t.tableName, parentTabId: t.parentTabId })),
});
if (!selectedTableId) return undefined;
// 먼저 tableList(필터링된 목록)에서 찾기
const tableFromList = tableList.find(t => t.tableId === selectedTableId);
const tableFromList = tableList.find((t) => t.tableId === selectedTableId);
if (tableFromList) {
console.log("✅ [TableSearchWidget] 테이블 찾음 (tableList):", tableFromList.tableName);
return tableFromList;
}
// tableList에 없으면 전체에서 찾기 (폴백)
const tableFromAll = getTable(selectedTableId);
console.log("🔄 [TableSearchWidget] 테이블 찾음 (전체):", tableFromAll?.tableName);
@ -161,10 +176,10 @@ export function TableSearchWidget({ component, screenId, onHeightChange }: Table
// 🆕 활성 탭 ID 문자열 (변경 감지용)
const activeTabIdsStr = useMemo(() => activeTabIds.join(","), [activeTabIds]);
// 🆕 이전 활성 탭 ID 추적 (탭 전환 감지용)
const prevActiveTabIdsRef = useRef<string>(activeTabIdsStr);
// 대상 패널의 첫 번째 테이블 자동 선택
useEffect(() => {
if (!autoSelectFirstTable || tableList.length === 0) {
@ -177,21 +192,21 @@ export function TableSearchWidget({ component, screenId, onHeightChange }: Table
console.log("🔄 [TableSearchWidget] 탭 전환 감지:", {
이전탭: prevActiveTabIdsRef.current,
현재탭: activeTabIdsStr,
가용테이블: tableList.map(t => ({ id: t.tableId, tableName: t.tableName, parentTabId: t.parentTabId })),
현재선택테이블: selectedTableId
가용테이블: tableList.map((t) => ({ id: t.tableId, tableName: t.tableName, parentTabId: t.parentTabId })),
현재선택테이블: selectedTableId,
});
prevActiveTabIdsRef.current = activeTabIdsStr;
// 🆕 탭 전환 시: 해당 탭에 속한 테이블 중 첫 번째 강제 선택
const activeTabTable = tableList.find(t => t.parentTabId && activeTabIds.includes(t.parentTabId));
const activeTabTable = tableList.find((t) => t.parentTabId && activeTabIds.includes(t.parentTabId));
const targetTable = activeTabTable || tableList[0];
if (targetTable) {
console.log("✅ [TableSearchWidget] 탭 전환으로 테이블 강제 선택:", {
테이블ID: targetTable.tableId,
테이블명: targetTable.tableName,
탭ID: targetTable.parentTabId,
이전테이블: selectedTableId
이전테이블: selectedTableId,
});
setSelectedTableId(targetTable.tableId);
}
@ -199,30 +214,38 @@ export function TableSearchWidget({ component, screenId, onHeightChange }: Table
}
// 현재 선택된 테이블이 대상 패널에 있는지 확인
const isCurrentTableInTarget = selectedTableId && tableList.some(t => t.tableId === selectedTableId);
const isCurrentTableInTarget = selectedTableId && tableList.some((t) => t.tableId === selectedTableId);
// 현재 선택된 테이블이 대상 패널에 없으면 첫 번째 테이블 선택
if (!selectedTableId || !isCurrentTableInTarget) {
const activeTabTable = tableList.find(t => t.parentTabId && activeTabIds.includes(t.parentTabId));
const activeTabTable = tableList.find((t) => t.parentTabId && activeTabIds.includes(t.parentTabId));
const targetTable = activeTabTable || tableList[0];
if (targetTable && targetTable.tableId !== selectedTableId) {
console.log("✅ [TableSearchWidget] 테이블 자동 선택 (초기):", {
테이블ID: targetTable.tableId,
테이블명: targetTable.tableName,
탭ID: targetTable.parentTabId
탭ID: targetTable.parentTabId,
});
setSelectedTableId(targetTable.tableId);
}
}
}, [tableList, selectedTableId, autoSelectFirstTable, setSelectedTableId, targetPanelPosition, activeTabIdsStr, activeTabIds]);
}, [
tableList,
selectedTableId,
autoSelectFirstTable,
setSelectedTableId,
targetPanelPosition,
activeTabIdsStr,
activeTabIds,
]);
// 현재 선택된 테이블의 탭 ID (탭별 필터 저장용)
const currentTableTabId = currentTable?.parentTabId;
// 탭별 필터 값 저장 키 생성
const getTabFilterStorageKey = (tableName: string, tabId?: string) => {
const baseKey = screenId
const baseKey = screenId
? `table_filter_values_${tableName}_screen_${screenId}`
: `table_filter_values_${tableName}`;
return tabId ? `${baseKey}_tab_${tabId}` : baseKey;
@ -231,16 +254,16 @@ export function TableSearchWidget({ component, screenId, onHeightChange }: Table
// 탭 변경 시 이전 탭의 필터 값 저장 + 새 탭의 필터 값 복원
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 => ({
setTabFilterValues((prev) => ({
...prev,
[currentTableTabId]: filterValues
[currentTableTabId]: filterValues,
}));
}
}, [currentTableTabId, currentTable?.tableName]);
@ -252,7 +275,7 @@ export function TableSearchWidget({ component, screenId, onHeightChange }: Table
currentTableTabId,
filterMode,
selectedTableId,
컬럼수: currentTable?.columns?.length
컬럼수: currentTable?.columns?.length,
});
if (!currentTable?.tableName) return;
@ -266,7 +289,7 @@ export function TableSearchWidget({ component, screenId, onHeightChange }: Table
width: f.width || 200,
}));
setActiveFilters(activeFiltersList);
// 탭별 저장된 필터 값 복원
if (currentTableTabId) {
const storageKey = getTabFilterStorageKey(currentTable.tableName, currentTableTabId);
@ -289,7 +312,7 @@ export function TableSearchWidget({ component, screenId, onHeightChange }: Table
// 동적 모드: 화면별로 독립적인 필터 설정 불러오기
// 참고: FilterPanel.tsx에서도 screenId만 사용하여 저장하므로 키가 일치해야 함
const filterConfigKey = screenId
const filterConfigKey = screenId
? `table_filters_${currentTable.tableName}_screen_${screenId}`
: `table_filters_${currentTable.tableName}`;
const savedFilters = localStorage.getItem(filterConfigKey);
@ -298,7 +321,7 @@ export function TableSearchWidget({ component, screenId, onHeightChange }: Table
filterConfigKey,
savedFilters: savedFilters ? `${savedFilters.substring(0, 100)}...` : null,
screenId,
tableName: currentTable.tableName
tableName: currentTable.tableName,
});
if (savedFilters) {
@ -327,11 +350,11 @@ export function TableSearchWidget({ component, screenId, onHeightChange }: Table
filterConfigKey,
총필터수: parsed.length,
활성화필터수: activeFiltersList.length,
활성화필터: activeFiltersList.map(f => f.columnName)
활성화필터: activeFiltersList.map((f) => f.columnName),
});
setActiveFilters(activeFiltersList);
// 탭별 저장된 필터 값 복원
if (currentTableTabId) {
const valuesStorageKey = getTabFilterStorageKey(currentTable.tableName, currentTableTabId);
@ -361,7 +384,7 @@ export function TableSearchWidget({ component, screenId, onHeightChange }: Table
// 필터 설정이 없으면 activeFilters와 filterValues 모두 초기화
console.log("⚠️ [TableSearchWidget] 저장된 필터 설정 없음 - 필터 초기화:", {
tableName: currentTable.tableName,
filterConfigKey
filterConfigKey,
});
setActiveFilters([]);
setFilterValues({});
@ -482,21 +505,26 @@ export function TableSearchWidget({ component, screenId, onHeightChange }: Table
const filtersWithValues = activeFilters
.map((filter) => {
let filterValue = values[filter.columnName];
// 날짜 범위 객체를 처리
if (filter.filterType === "date" && filterValue && typeof filterValue === "object" && (filterValue.from || filterValue.to)) {
if (
filter.filterType === "date" &&
filterValue &&
typeof filterValue === "object" &&
(filterValue.from || filterValue.to)
) {
// 날짜 범위 객체를 문자열 형식으로 변환 (백엔드 재시작 불필요)
const formatDate = (date: Date) => {
const year = date.getFullYear();
const month = String(date.getMonth() + 1).padStart(2, '0');
const day = String(date.getDate()).padStart(2, '0');
const month = String(date.getMonth() + 1).padStart(2, "0");
const day = String(date.getDate()).padStart(2, "0");
return `${year}-${month}-${day}`;
};
// "YYYY-MM-DD|YYYY-MM-DD" 형식으로 변환
const fromStr = filterValue.from ? formatDate(filterValue.from) : "";
const toStr = filterValue.to ? formatDate(filterValue.to) : "";
if (fromStr && toStr) {
// 둘 다 있으면 파이프로 연결
filterValue = `${fromStr}|${toStr}`;
@ -510,12 +538,12 @@ export function TableSearchWidget({ component, screenId, onHeightChange }: Table
filterValue = "";
}
}
// 다중선택 배열을 처리 (파이프로 연결된 문자열로 변환)
if (filter.filterType === "select" && Array.isArray(filterValue)) {
filterValue = filterValue.join("|");
}
return {
...filter,
value: filterValue || "",
@ -529,7 +557,23 @@ export function TableSearchWidget({ component, screenId, onHeightChange }: Table
return true;
});
currentTable?.onFilterChange(filtersWithValues);
console.log("🔍 [TableSearchWidget] applyFilters 호출:", {
currentTableId: currentTable?.tableId,
currentTableName: currentTable?.tableName,
hasOnFilterChange: !!currentTable?.onFilterChange,
filtersCount: filtersWithValues.length,
filters: filtersWithValues.map((f) => ({
col: f.columnName,
op: f.operator,
val: f.value,
})),
});
if (currentTable?.onFilterChange) {
currentTable.onFilterChange(filtersWithValues);
} else {
console.warn("⚠️ [TableSearchWidget] onFilterChange가 없음!", { currentTable });
}
};
// 필터 초기화
@ -537,7 +581,7 @@ export function TableSearchWidget({ component, screenId, onHeightChange }: Table
setFilterValues({});
setSelectedLabels({});
currentTable?.onFilterChange([]);
// 탭별 저장된 필터 값도 초기화
if (currentTable?.tableName && currentTableTabId) {
const storageKey = getTabFilterStorageKey(currentTable.tableName, currentTableTabId);
@ -557,7 +601,7 @@ export function TableSearchWidget({ component, screenId, onHeightChange }: Table
<div style={{ width: `${width}px` }}>
<ModernDatePicker
label={column?.columnLabel || filter.columnName}
value={value ? (typeof value === 'string' ? { from: new Date(value), to: new Date(value) } : value) : {}}
value={value ? (typeof value === "string" ? { from: new Date(value), to: new Date(value) } : value) : {}}
onChange={(dateRange) => {
if (dateRange.from && dateRange.to) {
// 기간이 선택되면 from과 to를 모두 저장
@ -584,7 +628,7 @@ export function TableSearchWidget({ component, screenId, onHeightChange }: Table
);
case "select": {
let options = selectOptions[filter.columnName] || [];
const options = selectOptions[filter.columnName] || [];
// 중복 제거 (value 기준)
const uniqueOptions = options.reduce(
@ -598,13 +642,13 @@ export function TableSearchWidget({ component, screenId, onHeightChange }: Table
);
// 항상 다중선택 모드
const selectedValues: string[] = Array.isArray(value) ? value : (value ? [value] : []);
const selectedValues: string[] = Array.isArray(value) ? value : value ? [value] : [];
// 선택된 값들의 라벨 표시
const getDisplayText = () => {
if (selectedValues.length === 0) return column?.columnLabel || "선택";
if (selectedValues.length === 1) {
const opt = uniqueOptions.find(o => o.value === selectedValues[0]);
const opt = uniqueOptions.find((o) => o.value === selectedValues[0]);
return opt?.label || selectedValues[0];
}
return `${selectedValues.length}개 선택됨`;
@ -615,7 +659,7 @@ export function TableSearchWidget({ component, screenId, onHeightChange }: Table
if (checked) {
newValues = [...selectedValues, optionValue];
} else {
newValues = selectedValues.filter(v => v !== optionValue);
newValues = selectedValues.filter((v) => v !== optionValue);
}
handleFilterChange(filter.columnName, newValues.length > 0 ? newValues : "");
};
@ -628,7 +672,7 @@ export function TableSearchWidget({ component, screenId, onHeightChange }: Table
role="combobox"
className={cn(
"h-9 min-h-9 justify-between text-xs font-normal focus:ring-0 focus:outline-none focus-visible:ring-0 focus-visible:ring-offset-0 sm:text-sm",
selectedValues.length === 0 && "text-muted-foreground"
selectedValues.length === 0 && "text-muted-foreground",
)}
style={{ width: `${width}px`, height: "36px", minHeight: "36px", outline: "none", boxShadow: "none" }}
>
@ -636,11 +680,7 @@ export function TableSearchWidget({ component, screenId, onHeightChange }: Table
<ChevronsUpDown className="ml-2 h-4 w-4 shrink-0 opacity-50" />
</Button>
</PopoverTrigger>
<PopoverContent
className="p-0"
style={{ width: `${width}px` }}
align="start"
>
<PopoverContent className="p-0" style={{ width: `${width}px` }} align="start">
<div className="max-h-60 overflow-auto">
{uniqueOptions.length === 0 ? (
<div className="text-muted-foreground px-3 py-2 text-xs"> </div>
@ -649,7 +689,7 @@ export function TableSearchWidget({ component, screenId, onHeightChange }: Table
{uniqueOptions.map((option, index) => (
<div
key={`${filter.columnName}-multi-${option.value}-${index}`}
className="flex items-center space-x-2 rounded-sm px-2 py-1.5 hover:bg-accent cursor-pointer"
className="hover:bg-accent flex cursor-pointer items-center space-x-2 rounded-sm px-2 py-1.5"
onClick={() => handleMultiSelectChange(option.value, !selectedValues.includes(option.value))}
>
<Checkbox
@ -668,7 +708,7 @@ export function TableSearchWidget({ component, screenId, onHeightChange }: Table
<Button
variant="ghost"
size="sm"
className="w-full h-7 text-xs"
className="h-7 w-full text-xs"
onClick={() => handleFilterChange(filter.columnName, "")}
>