검색 필터기능 수정사항
This commit is contained in:
@@ -7,14 +7,12 @@ import { entityJoinApi } from "@/lib/api/entityJoin";
|
||||
import { codeCache } from "@/lib/caching/codeCache";
|
||||
import { useEntityJoinOptimization } from "@/lib/hooks/useEntityJoinOptimization";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table";
|
||||
import {
|
||||
ChevronLeft,
|
||||
ChevronRight,
|
||||
ChevronsLeft,
|
||||
ChevronsRight,
|
||||
Search,
|
||||
RefreshCw,
|
||||
ArrowUpDown,
|
||||
ArrowUp,
|
||||
@@ -23,6 +21,8 @@ import {
|
||||
} from "lucide-react";
|
||||
import { Checkbox } from "@/components/ui/checkbox";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { AdvancedSearchFilters } from "@/components/screen/filters/AdvancedSearchFilters";
|
||||
import { Separator } from "@/components/ui/separator";
|
||||
|
||||
export interface TableListComponentProps {
|
||||
component: any;
|
||||
@@ -96,10 +96,12 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
|
||||
const [columnLabels, setColumnLabels] = useState<Record<string, string>>({});
|
||||
const [tableLabel, setTableLabel] = useState<string>("");
|
||||
const [localPageSize, setLocalPageSize] = useState<number>(tableConfig.pagination?.pageSize || 20); // 로컬 페이지 크기 상태
|
||||
const [selectedSearchColumn, setSelectedSearchColumn] = useState<string>(""); // 선택된 검색 컬럼
|
||||
const [displayColumns, setDisplayColumns] = useState<ColumnConfig[]>([]); // 🎯 표시할 컬럼 (Entity 조인 적용됨)
|
||||
const [columnMeta, setColumnMeta] = useState<Record<string, { webType?: string; codeCategory?: string }>>({}); // 🎯 컬럼 메타정보 (웹타입, 코드카테고리)
|
||||
|
||||
// 고급 필터 관련 state
|
||||
const [searchValues, setSearchValues] = useState<Record<string, any>>({});
|
||||
|
||||
// 체크박스 상태 관리
|
||||
const [selectedRows, setSelectedRows] = useState<Set<string>>(new Set()); // 선택된 행들의 키 집합
|
||||
const [isAllSelected, setIsAllSelected] = useState(false); // 전체 선택 상태
|
||||
@@ -234,56 +236,66 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
|
||||
const result = await entityJoinApi.getTableDataWithJoins(tableConfig.selectedTable, {
|
||||
page: currentPage,
|
||||
size: localPageSize,
|
||||
search: searchTerm?.trim()
|
||||
? (() => {
|
||||
// 스마트한 단일 컬럼 선택 로직 (서버가 OR 검색을 지원하지 않음)
|
||||
let searchColumn = selectedSearchColumn || sortColumn; // 사용자 선택 컬럼 최우선, 그 다음 정렬된 컬럼
|
||||
search: (() => {
|
||||
// 고급 필터 값이 있으면 우선 사용
|
||||
const hasAdvancedFilters = Object.values(searchValues).some((value) => {
|
||||
if (typeof value === "string") return value.trim() !== "";
|
||||
if (typeof value === "object" && value !== null) {
|
||||
return Object.values(value).some((v) => v !== "" && v !== null && v !== undefined);
|
||||
}
|
||||
return value !== null && value !== undefined && value !== "";
|
||||
});
|
||||
|
||||
if (!searchColumn) {
|
||||
// 1순위: name 관련 컬럼 (가장 검색에 적합)
|
||||
const nameColumns = visibleColumns.filter(
|
||||
(col) =>
|
||||
col.columnName.toLowerCase().includes("name") ||
|
||||
col.columnName.toLowerCase().includes("title") ||
|
||||
col.columnName.toLowerCase().includes("subject"),
|
||||
);
|
||||
if (hasAdvancedFilters) {
|
||||
console.log("🔍 고급 검색 필터 사용:", searchValues);
|
||||
console.log("🔍 고급 검색 필터 상세:", JSON.stringify(searchValues, null, 2));
|
||||
return searchValues;
|
||||
}
|
||||
|
||||
// 2순위: text/varchar 타입 컬럼
|
||||
const textColumns = visibleColumns.filter(
|
||||
(col) => col.dataType === "text" || col.dataType === "varchar",
|
||||
);
|
||||
// 고급 필터가 없으면 기존 단순 검색 사용
|
||||
if (searchTerm?.trim()) {
|
||||
// 스마트한 단일 컬럼 선택 로직 (서버가 OR 검색을 지원하지 않음)
|
||||
let searchColumn = sortColumn; // 정렬된 컬럼 우선
|
||||
|
||||
// 3순위: description 관련 컬럼
|
||||
const descColumns = visibleColumns.filter(
|
||||
(col) =>
|
||||
col.columnName.toLowerCase().includes("desc") ||
|
||||
col.columnName.toLowerCase().includes("comment") ||
|
||||
col.columnName.toLowerCase().includes("memo"),
|
||||
);
|
||||
|
||||
// 우선순위에 따라 선택
|
||||
if (nameColumns.length > 0) {
|
||||
searchColumn = nameColumns[0].columnName;
|
||||
} else if (textColumns.length > 0) {
|
||||
searchColumn = textColumns[0].columnName;
|
||||
} else if (descColumns.length > 0) {
|
||||
searchColumn = descColumns[0].columnName;
|
||||
} else {
|
||||
// 마지막 대안: 첫 번째 컬럼
|
||||
searchColumn = visibleColumns[0]?.columnName || "id";
|
||||
}
|
||||
}
|
||||
|
||||
console.log("🔍 선택된 검색 컬럼:", searchColumn);
|
||||
console.log("🔍 검색어:", searchTerm);
|
||||
console.log(
|
||||
"🔍 사용 가능한 컬럼들:",
|
||||
visibleColumns.map((col) => `${col.columnName}(${col.dataType || "unknown"})`),
|
||||
if (!searchColumn) {
|
||||
// 1순위: name 관련 컬럼 (가장 검색에 적합)
|
||||
const nameColumns = visibleColumns.filter(
|
||||
(col) =>
|
||||
col.columnName.toLowerCase().includes("name") ||
|
||||
col.columnName.toLowerCase().includes("title") ||
|
||||
col.columnName.toLowerCase().includes("subject"),
|
||||
);
|
||||
|
||||
return { [searchColumn]: searchTerm };
|
||||
})()
|
||||
: undefined,
|
||||
// 2순위: text/varchar 타입 컬럼
|
||||
const textColumns = visibleColumns.filter((col) => col.dataType === "text" || col.dataType === "varchar");
|
||||
|
||||
// 3순위: description 관련 컬럼
|
||||
const descColumns = visibleColumns.filter(
|
||||
(col) =>
|
||||
col.columnName.toLowerCase().includes("desc") ||
|
||||
col.columnName.toLowerCase().includes("comment") ||
|
||||
col.columnName.toLowerCase().includes("memo"),
|
||||
);
|
||||
|
||||
// 우선순위에 따라 선택
|
||||
if (nameColumns.length > 0) {
|
||||
searchColumn = nameColumns[0].columnName;
|
||||
} else if (textColumns.length > 0) {
|
||||
searchColumn = textColumns[0].columnName;
|
||||
} else if (descColumns.length > 0) {
|
||||
searchColumn = descColumns[0].columnName;
|
||||
} else {
|
||||
// 마지막 대안: 첫 번째 컬럼
|
||||
searchColumn = visibleColumns[0]?.columnName || "id";
|
||||
}
|
||||
}
|
||||
|
||||
console.log("🔍 기존 검색 방식 사용:", { [searchColumn]: searchTerm });
|
||||
return { [searchColumn]: searchTerm };
|
||||
}
|
||||
|
||||
return undefined;
|
||||
})(),
|
||||
sortBy: sortColumn || undefined,
|
||||
sortOrder: sortDirection,
|
||||
enableEntityJoin: true, // 🎯 Entity 조인 활성화
|
||||
@@ -410,10 +422,23 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
|
||||
}
|
||||
};
|
||||
|
||||
// 검색
|
||||
const handleSearch = (term: string) => {
|
||||
setSearchTerm(term);
|
||||
setCurrentPage(1); // 검색 시 첫 페이지로 이동
|
||||
// 고급 필터 핸들러
|
||||
const handleSearchValueChange = (columnName: string, value: any) => {
|
||||
setSearchValues((prev) => ({
|
||||
...prev,
|
||||
[columnName]: value,
|
||||
}));
|
||||
};
|
||||
|
||||
const handleAdvancedSearch = () => {
|
||||
setCurrentPage(1);
|
||||
fetchTableData();
|
||||
};
|
||||
|
||||
const handleClearAdvancedFilters = () => {
|
||||
setSearchValues({});
|
||||
setCurrentPage(1);
|
||||
fetchTableData();
|
||||
};
|
||||
|
||||
// 새로고침
|
||||
@@ -522,7 +547,16 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
|
||||
if (tableConfig.autoLoad && !isDesignMode) {
|
||||
fetchTableData();
|
||||
}
|
||||
}, [tableConfig.selectedTable, localPageSize, currentPage, searchTerm, sortColumn, sortDirection, columnLabels]);
|
||||
}, [
|
||||
tableConfig.selectedTable,
|
||||
localPageSize,
|
||||
currentPage,
|
||||
searchTerm,
|
||||
sortColumn,
|
||||
sortDirection,
|
||||
columnLabels,
|
||||
searchValues,
|
||||
]);
|
||||
|
||||
// refreshKey 변경 시 테이블 데이터 새로고침
|
||||
useEffect(() => {
|
||||
@@ -577,8 +611,8 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
|
||||
.sort((a, b) => a.order - b.order);
|
||||
}
|
||||
|
||||
// 체크박스가 활성화된 경우 체크박스 컬럼을 추가
|
||||
if (checkboxConfig.enabled) {
|
||||
// 체크박스가 활성화되고 실제 데이터 컬럼이 있는 경우에만 체크박스 컬럼을 추가
|
||||
if (checkboxConfig.enabled && columns.length > 0) {
|
||||
const checkboxColumn: ColumnConfig = {
|
||||
columnName: "__checkbox__",
|
||||
displayName: "",
|
||||
@@ -819,8 +853,8 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* 검색 */}
|
||||
{tableConfig.filter?.enabled && tableConfig.filter?.quickSearch && (
|
||||
{/* 검색 - 기존 방식은 주석처리 */}
|
||||
{/* {tableConfig.filter?.enabled && tableConfig.filter?.quickSearch && (
|
||||
<div className="flex items-center space-x-2">
|
||||
<div className="relative">
|
||||
<Search className="absolute top-1/2 left-2 h-4 w-4 -translate-y-1/2 transform text-gray-400" />
|
||||
@@ -831,7 +865,6 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
|
||||
className="w-64 pl-8"
|
||||
/>
|
||||
</div>
|
||||
{/* 검색 컬럼 선택 드롭다운 */}
|
||||
{tableConfig.filter?.showColumnSelector && (
|
||||
<select
|
||||
value={selectedSearchColumn}
|
||||
@@ -847,7 +880,7 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
|
||||
</select>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
)} */}
|
||||
|
||||
{/* 새로고침 */}
|
||||
<Button variant="outline" size="sm" onClick={handleRefresh} disabled={loading}>
|
||||
@@ -857,6 +890,33 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* 고급 검색 필터 - 항상 표시 (컬럼 정보 기반 자동 생성) */}
|
||||
{tableConfig.filter?.enabled && visibleColumns && visibleColumns.length > 0 && (
|
||||
<>
|
||||
<Separator className="my-2" />
|
||||
<AdvancedSearchFilters
|
||||
filters={tableConfig.filter?.filters || []} // 설정된 필터 사용, 없으면 자동 생성
|
||||
searchValues={searchValues}
|
||||
onSearchValueChange={handleSearchValueChange}
|
||||
onSearch={handleAdvancedSearch}
|
||||
onClearFilters={handleClearAdvancedFilters}
|
||||
tableColumns={visibleColumns.map((col) => ({
|
||||
columnName: col.columnName,
|
||||
webType: columnMeta[col.columnName]?.webType || "text",
|
||||
displayName: columnLabels[col.columnName] || col.displayName || col.columnName,
|
||||
codeCategory: columnMeta[col.columnName]?.codeCategory,
|
||||
isVisible: col.visible,
|
||||
// 추가 메타데이터 전달 (필터 자동 생성용)
|
||||
web_type: columnMeta[col.columnName]?.webType || "text",
|
||||
column_name: col.columnName,
|
||||
column_label: columnLabels[col.columnName] || col.displayName || col.columnName,
|
||||
code_category: columnMeta[col.columnName]?.codeCategory,
|
||||
}))}
|
||||
tableName={tableConfig.selectedTable}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
|
||||
{/* 테이블 컨텐츠 */}
|
||||
<div className={`flex-1 ${localPageSize >= 50 ? "overflow-auto" : "overflow-hidden"}`}>
|
||||
{loading ? (
|
||||
|
||||
@@ -95,20 +95,33 @@ export const TableListConfigPanel: React.FC<TableListConfigPanelProps> = ({
|
||||
fetchTables();
|
||||
}, []);
|
||||
|
||||
// 선택된 테이블의 컬럼 목록 설정 (tableColumns prop 우선 사용)
|
||||
// 선택된 테이블의 컬럼 목록 설정
|
||||
useEffect(() => {
|
||||
console.log("🔍 useEffect 실행됨 - tableColumns:", tableColumns, "length:", tableColumns?.length);
|
||||
if (tableColumns && tableColumns.length > 0) {
|
||||
// tableColumns prop이 있으면 사용
|
||||
console.log(
|
||||
"🔍 useEffect 실행됨 - config.selectedTable:",
|
||||
config.selectedTable,
|
||||
"screenTableName:",
|
||||
screenTableName,
|
||||
);
|
||||
|
||||
// 컴포넌트에 명시적으로 테이블이 선택되었거나, 화면에 연결된 테이블이 있는 경우에만 컬럼 목록 표시
|
||||
const shouldShowColumns = config.selectedTable || (screenTableName && config.columns && config.columns.length > 0);
|
||||
|
||||
if (!shouldShowColumns) {
|
||||
console.log("🔧 컬럼 목록 숨김 - 명시적 테이블 선택 또는 설정된 컬럼이 없음");
|
||||
setAvailableColumns([]);
|
||||
return;
|
||||
}
|
||||
|
||||
// tableColumns prop을 우선 사용하되, 컴포넌트가 명시적으로 설정되었을 때만
|
||||
if (tableColumns && tableColumns.length > 0 && (config.selectedTable || config.columns?.length > 0)) {
|
||||
console.log("🔧 tableColumns prop 사용:", tableColumns);
|
||||
console.log("🔧 첫 번째 컬럼 상세:", tableColumns[0]);
|
||||
const mappedColumns = tableColumns.map((column: any) => ({
|
||||
columnName: column.columnName || column.name,
|
||||
dataType: column.dataType || column.type || "text",
|
||||
label: column.label || column.displayName || column.columnLabel || column.columnName || column.name,
|
||||
}));
|
||||
console.log("🏷️ availableColumns 설정됨:", mappedColumns);
|
||||
console.log("🏷️ 첫 번째 mappedColumn:", mappedColumns[0]);
|
||||
setAvailableColumns(mappedColumns);
|
||||
} else if (config.selectedTable || screenTableName) {
|
||||
// API에서 컬럼 정보 가져오기
|
||||
@@ -144,7 +157,7 @@ export const TableListConfigPanel: React.FC<TableListConfigPanelProps> = ({
|
||||
} else {
|
||||
setAvailableColumns([]);
|
||||
}
|
||||
}, [config.selectedTable, screenTableName, tableColumns]);
|
||||
}, [config.selectedTable, screenTableName, tableColumns, config.columns]);
|
||||
|
||||
// Entity 조인 컬럼 정보 가져오기
|
||||
useEffect(() => {
|
||||
@@ -274,6 +287,72 @@ export const TableListConfigPanel: React.FC<TableListConfigPanelProps> = ({
|
||||
handleChange("columns", columns);
|
||||
};
|
||||
|
||||
// 필터 추가
|
||||
const addFilter = (columnName: string) => {
|
||||
const existingFilter = config.filter?.filters?.find((f) => f.columnName === columnName);
|
||||
if (existingFilter) return;
|
||||
|
||||
const column = availableColumns.find((col) => col.columnName === columnName);
|
||||
if (!column) return;
|
||||
|
||||
// tableColumns에서 해당 컬럼의 메타정보 찾기
|
||||
const tableColumn = tableColumns?.find((tc) => tc.columnName === columnName || tc.column_name === columnName);
|
||||
|
||||
// 컬럼의 데이터 타입과 웹타입에 따라 위젯 타입 결정
|
||||
const inferWidgetType = (dataType: string, webType?: string): string => {
|
||||
// 웹타입이 있으면 우선 사용
|
||||
if (webType) {
|
||||
return webType;
|
||||
}
|
||||
|
||||
// 데이터 타입으로 추론
|
||||
const type = dataType.toLowerCase();
|
||||
if (type.includes("int") || type.includes("numeric") || type.includes("decimal")) return "number";
|
||||
if (type.includes("date") || type.includes("timestamp")) return "date";
|
||||
if (type.includes("bool")) return "boolean";
|
||||
return "text";
|
||||
};
|
||||
|
||||
const widgetType = inferWidgetType(column.dataType, tableColumn?.webType || tableColumn?.web_type);
|
||||
|
||||
const newFilter = {
|
||||
columnName,
|
||||
widgetType,
|
||||
label: column.label || column.columnName,
|
||||
gridColumns: 3,
|
||||
numberFilterMode: "range" as const,
|
||||
// 코드 타입인 경우 코드 카테고리 추가
|
||||
...(widgetType === "code" && {
|
||||
codeCategory: tableColumn?.codeCategory || tableColumn?.code_category,
|
||||
}),
|
||||
// 엔티티 타입인 경우 참조 정보 추가
|
||||
...(widgetType === "entity" && {
|
||||
referenceTable: tableColumn?.referenceTable || tableColumn?.reference_table,
|
||||
referenceColumn: tableColumn?.referenceColumn || tableColumn?.reference_column,
|
||||
displayColumn: tableColumn?.displayColumn || tableColumn?.display_column,
|
||||
}),
|
||||
};
|
||||
|
||||
console.log("🔍 필터 추가:", newFilter);
|
||||
|
||||
const currentFilters = config.filter?.filters || [];
|
||||
handleNestedChange("filter", "filters", [...currentFilters, newFilter]);
|
||||
};
|
||||
|
||||
// 필터 제거
|
||||
const removeFilter = (index: number) => {
|
||||
const currentFilters = config.filter?.filters || [];
|
||||
const updatedFilters = currentFilters.filter((_, i) => i !== index);
|
||||
handleNestedChange("filter", "filters", updatedFilters);
|
||||
};
|
||||
|
||||
// 필터 업데이트
|
||||
const updateFilter = (index: number, key: string, value: any) => {
|
||||
const currentFilters = config.filter?.filters || [];
|
||||
const updatedFilters = currentFilters.map((filter, i) => (i === index ? { ...filter, [key]: value } : filter));
|
||||
handleNestedChange("filter", "filters", updatedFilters);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="space-y-4">
|
||||
<div className="text-sm font-medium">테이블 리스트 설정</div>
|
||||
@@ -620,6 +699,16 @@ export const TableListConfigPanel: React.FC<TableListConfigPanelProps> = ({
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
) : availableColumns.length === 0 ? (
|
||||
<Card>
|
||||
<CardContent className="pt-6">
|
||||
<div className="text-center text-gray-500">
|
||||
<p>컬럼을 추가하려면 먼저 컴포넌트에 테이블을 명시적으로 선택하거나</p>
|
||||
<p className="text-sm">기본 설정 탭에서 테이블을 설정해주세요.</p>
|
||||
<p className="mt-2 text-xs text-blue-600">현재 화면 테이블: {screenTableName}</p>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
) : (
|
||||
<>
|
||||
<Card>
|
||||
@@ -990,54 +1079,141 @@ export const TableListConfigPanel: React.FC<TableListConfigPanelProps> = ({
|
||||
{/* 필터 설정 탭 */}
|
||||
<TabsContent value="filter" className="space-y-4">
|
||||
<ScrollArea className="h-[600px] pr-4">
|
||||
{/* 필터 기능 활성화 */}
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle className="text-base">검색 및 필터</CardTitle>
|
||||
<CardTitle className="text-base">필터 설정</CardTitle>
|
||||
<CardDescription>테이블에서 사용할 검색 필터를 설정하세요</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent className="space-y-4">
|
||||
<div className="flex items-center space-x-2">
|
||||
<Checkbox
|
||||
id="filterEnabled"
|
||||
checked={config.filter?.enabled}
|
||||
checked={config.filter?.enabled || false}
|
||||
onCheckedChange={(checked) => handleNestedChange("filter", "enabled", checked)}
|
||||
/>
|
||||
<Label htmlFor="filterEnabled">필터 기능 사용</Label>
|
||||
</div>
|
||||
|
||||
{config.filter?.enabled && (
|
||||
<>
|
||||
<div className="flex items-center space-x-2">
|
||||
<Checkbox
|
||||
id="quickSearch"
|
||||
checked={config.filter?.quickSearch}
|
||||
onCheckedChange={(checked) => handleNestedChange("filter", "quickSearch", checked)}
|
||||
/>
|
||||
<Label htmlFor="quickSearch">빠른 검색</Label>
|
||||
</div>
|
||||
|
||||
{config.filter?.quickSearch && (
|
||||
<div className="ml-6 flex items-center space-x-2">
|
||||
<Checkbox
|
||||
id="showColumnSelector"
|
||||
checked={config.filter?.showColumnSelector}
|
||||
onCheckedChange={(checked) => handleNestedChange("filter", "showColumnSelector", checked)}
|
||||
/>
|
||||
<Label htmlFor="showColumnSelector">검색 컬럼 선택기 표시</Label>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="flex items-center space-x-2">
|
||||
<Checkbox
|
||||
id="advancedFilter"
|
||||
checked={config.filter?.advancedFilter}
|
||||
onCheckedChange={(checked) => handleNestedChange("filter", "advancedFilter", checked)}
|
||||
/>
|
||||
<Label htmlFor="advancedFilter">고급 필터</Label>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
{/* 필터 목록 */}
|
||||
{config.filter?.enabled && (
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle className="text-base">사용할 필터</CardTitle>
|
||||
<CardDescription>검색에 사용할 컬럼 필터를 추가하고 설정하세요</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent className="space-y-4">
|
||||
{/* 필터 추가 버튼 */}
|
||||
{availableColumns.length > 0 && (
|
||||
<div className="flex flex-wrap gap-2">
|
||||
{availableColumns
|
||||
.filter((col) => !config.filter?.filters?.find((f) => f.columnName === col.columnName))
|
||||
.map((column) => (
|
||||
<Button
|
||||
key={column.columnName}
|
||||
variant="outline"
|
||||
size="sm"
|
||||
onClick={() => addFilter(column.columnName)}
|
||||
className="flex items-center gap-1"
|
||||
>
|
||||
<Plus className="h-3 w-3" />
|
||||
{column.label || column.columnName}
|
||||
<Badge variant="secondary" className="text-xs">
|
||||
{column.dataType}
|
||||
</Badge>
|
||||
</Button>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* 설정된 필터 목록 */}
|
||||
{config.filter?.filters && config.filter.filters.length > 0 && (
|
||||
<div className="space-y-3">
|
||||
<h4 className="text-sm font-medium">설정된 필터</h4>
|
||||
{config.filter.filters.map((filter, index) => (
|
||||
<div key={filter.columnName} className="space-y-3 rounded-lg border p-4">
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex items-center gap-2">
|
||||
<Badge variant="outline">{filter.widgetType}</Badge>
|
||||
<span className="font-medium">{filter.label}</span>
|
||||
</div>
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
onClick={() => removeFilter(index)}
|
||||
className="text-red-600 hover:text-red-700"
|
||||
>
|
||||
<Trash2 className="h-3 w-3" />
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-2 gap-3">
|
||||
<div>
|
||||
<Label className="text-xs">표시명</Label>
|
||||
<Input
|
||||
value={filter.label}
|
||||
onChange={(e) => updateFilter(index, "label", e.target.value)}
|
||||
placeholder="필터 라벨"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<Label className="text-xs">그리드 컬럼</Label>
|
||||
<Select
|
||||
value={filter.gridColumns.toString()}
|
||||
onValueChange={(value) => updateFilter(index, "gridColumns", parseInt(value))}
|
||||
>
|
||||
<SelectTrigger>
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="2">2칸</SelectItem>
|
||||
<SelectItem value="3">3칸</SelectItem>
|
||||
<SelectItem value="4">4칸</SelectItem>
|
||||
<SelectItem value="6">6칸</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
|
||||
{/* 숫자 타입인 경우 검색 모드 선택 */}
|
||||
{(filter.widgetType === "number" || filter.widgetType === "decimal") && (
|
||||
<div>
|
||||
<Label className="text-xs">검색 모드</Label>
|
||||
<Select
|
||||
value={filter.numberFilterMode || "range"}
|
||||
onValueChange={(value) => updateFilter(index, "numberFilterMode", value)}
|
||||
>
|
||||
<SelectTrigger>
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="exact">정확한 값</SelectItem>
|
||||
<SelectItem value="range">범위 검색</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* 코드 타입인 경우 코드 카테고리 */}
|
||||
{filter.widgetType === "code" && (
|
||||
<div>
|
||||
<Label className="text-xs">코드 카테고리</Label>
|
||||
<Input
|
||||
value={filter.codeCategory || ""}
|
||||
onChange={(e) => updateFilter(index, "codeCategory", e.target.value)}
|
||||
placeholder="코드 카테고리"
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</CardContent>
|
||||
</Card>
|
||||
)}
|
||||
</ScrollArea>
|
||||
</TabsContent>
|
||||
|
||||
|
||||
@@ -59,10 +59,7 @@ export const TableListDefinition = createComponentDefinition({
|
||||
// 필터 설정
|
||||
filter: {
|
||||
enabled: true,
|
||||
quickSearch: true,
|
||||
showColumnSelector: true, // 검색컬럼 선택기 표시 기본값
|
||||
advancedFilter: false,
|
||||
filterableColumns: [],
|
||||
filters: [], // 사용자가 설정할 필터 목록
|
||||
},
|
||||
|
||||
// 액션 설정
|
||||
|
||||
@@ -71,14 +71,17 @@ export interface ColumnConfig {
|
||||
*/
|
||||
export interface FilterConfig {
|
||||
enabled: boolean;
|
||||
quickSearch: boolean;
|
||||
showColumnSelector?: boolean; // 검색 컬럼 선택기 표시 여부
|
||||
advancedFilter: boolean;
|
||||
filterableColumns: string[];
|
||||
defaultFilters?: Array<{
|
||||
column: string;
|
||||
operator: "=" | "!=" | ">" | "<" | ">=" | "<=" | "LIKE" | "IN";
|
||||
value: any;
|
||||
// 사용할 필터 목록 (DataTableFilter 타입 사용)
|
||||
filters: Array<{
|
||||
columnName: string;
|
||||
widgetType: string;
|
||||
label: string;
|
||||
gridColumns: number;
|
||||
numberFilterMode?: "exact" | "range"; // 숫자 필터 모드
|
||||
codeCategory?: string;
|
||||
referenceTable?: string;
|
||||
referenceColumn?: string;
|
||||
displayColumn?: string;
|
||||
}>;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user