diff --git a/frontend/lib/registry/components/table-list/TableListComponent.tsx b/frontend/lib/registry/components/table-list/TableListComponent.tsx index 78abf111..366aa05b 100644 --- a/frontend/lib/registry/components/table-list/TableListComponent.tsx +++ b/frontend/lib/registry/components/table-list/TableListComponent.tsx @@ -41,7 +41,7 @@ import { Lock, } from "lucide-react"; import * as XLSX from "xlsx"; -import { FileText, ChevronRightIcon } from "lucide-react"; +import { FileText, ChevronRightIcon, Search } from "lucide-react"; import { Checkbox } from "@/components/ui/checkbox"; import { cn } from "@/lib/utils"; import { toast } from "sonner"; @@ -455,6 +455,7 @@ export const TableListComponent: React.FC = ({ // πŸ†• 컬럼 헀더 ν•„ν„° μƒνƒœ (μƒλ‹¨μ—μ„œ μ„ μ–Έ) const [headerFilters, setHeaderFilters] = useState>>({}); + const [headerLikeFilters, setHeaderLikeFilters] = useState>({}); // LIKE κ²€μƒ‰μš© const [openFilterColumn, setOpenFilterColumn] = useState(null); // πŸ†• Filter Builder (κ³ κΈ‰ ν•„ν„°) κ΄€λ ¨ μƒνƒœ - filteredData보닀 λ¨Όμ € μ •μ˜ν•΄μ•Ό 함 @@ -488,6 +489,22 @@ export const TableListComponent: React.FC = ({ }); } + // 2-1. πŸ†• LIKE 검색 ν•„ν„° 적용 + if (Object.keys(headerLikeFilters).length > 0) { + result = result.filter((row) => { + return Object.entries(headerLikeFilters).every(([columnName, searchText]) => { + if (!searchText || searchText.trim() === "") return true; + + // μ—¬λŸ¬ κ°€λŠ₯ν•œ 컬럼λͺ… μ‹œλ„ + const cellValue = row[columnName] ?? row[columnName.toLowerCase()] ?? row[columnName.toUpperCase()]; + const cellStr = cellValue !== null && cellValue !== undefined ? String(cellValue).toLowerCase() : ""; + + // LIKE 검색 (λŒ€μ†Œλ¬Έμž λ¬΄μ‹œ) + return cellStr.includes(searchText.toLowerCase()); + }); + }); + } + // 3. πŸ†• Filter Builder 적용 if (filterGroups.length > 0) { result = result.filter((row) => { @@ -541,7 +558,7 @@ export const TableListComponent: React.FC = ({ } return result; - }, [data, splitPanelPosition, splitPanelContext?.addedItemIds, headerFilters, filterGroups]); + }, [data, splitPanelPosition, splitPanelContext?.addedItemIds, headerFilters, headerLikeFilters, filterGroups]); const [currentPage, setCurrentPage] = useState(1); const [totalPages, setTotalPages] = useState(0); @@ -2935,6 +2952,7 @@ export const TableListComponent: React.FC = ({ headerFilters: Object.fromEntries( Object.entries(headerFilters).map(([key, set]) => [key, Array.from(set as Set)]), ), + headerLikeFilters, // LIKE 검색 ν•„ν„° μ €μž₯ pageSize: localPageSize, timestamp: Date.now(), }; @@ -2955,6 +2973,7 @@ export const TableListComponent: React.FC = ({ frozenColumnCount, showGridLines, headerFilters, + headerLikeFilters, localPageSize, ]); @@ -2991,6 +3010,9 @@ export const TableListComponent: React.FC = ({ }); setHeaderFilters(filters); } + if (state.headerLikeFilters) { + setHeaderLikeFilters(state.headerLikeFilters); + } } catch (error) { console.error("❌ ν…Œμ΄λΈ” μƒνƒœ 볡원 μ‹€νŒ¨:", error); } @@ -5737,7 +5759,7 @@ export const TableListComponent: React.FC = ({ }} className={cn( "hover:bg-primary/20 ml-1 rounded p-0.5 transition-colors", - headerFilters[column.columnName]?.size > 0 && "text-primary bg-primary/10", + (headerFilters[column.columnName]?.size > 0 || headerLikeFilters[column.columnName]) && "text-primary bg-primary/10", )} title="ν•„ν„°" > @@ -5745,7 +5767,7 @@ export const TableListComponent: React.FC = ({ e.stopPropagation()} > @@ -5754,16 +5776,42 @@ export const TableListComponent: React.FC = ({ ν•„ν„°: {columnLabels[column.columnName] || column.displayName} - {headerFilters[column.columnName]?.size > 0 && ( + {(headerFilters[column.columnName]?.size > 0 || headerLikeFilters[column.columnName]) && ( )} -
+ {/* LIKE 검색 μž…λ ₯ ν•„λ“œ */} +
+ + { + setHeaderLikeFilters((prev) => ({ + ...prev, + [column.columnName]: e.target.value, + })); + }} + className="border-input bg-background placeholder:text-muted-foreground h-7 w-full rounded-md border pl-7 pr-2 text-xs focus:outline-none focus:ring-1 focus:ring-primary" + onClick={(e) => e.stopPropagation()} + /> +
+ {/* ꡬ뢄선 */} +
λ˜λŠ” κ°’ 선택:
+
{columnUniqueValues[column.columnName]?.slice(0, 50).map((val) => { const isSelected = headerFilters[column.columnName]?.has(val); return (