From d69feef9da2a6c18ba7933c1a06446d83f9acb6e Mon Sep 17 00:00:00 2001 From: leeheejin Date: Thu, 25 Sep 2025 18:54:12 +0900 Subject: [PATCH 1/2] =?UTF-8?q?=ED=99=94=EB=A9=B4=EA=B4=80=EB=A6=AC?= =?UTF-8?q?=EC=9D=98=20=ED=85=8C=EC=9D=B4=EB=B8=94=20=EB=A6=AC=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8,=20=EC=B9=B4=EB=93=9C=20=EB=94=94=EC=8A=A4=ED=94=8C?= =?UTF-8?q?=EB=A0=88=EC=9D=B4=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../table-list/CardModeRenderer.tsx | 224 ++++++++++++++++++ 1 file changed, 224 insertions(+) create mode 100644 frontend/lib/registry/components/table-list/CardModeRenderer.tsx diff --git a/frontend/lib/registry/components/table-list/CardModeRenderer.tsx b/frontend/lib/registry/components/table-list/CardModeRenderer.tsx new file mode 100644 index 00000000..2988f864 --- /dev/null +++ b/frontend/lib/registry/components/table-list/CardModeRenderer.tsx @@ -0,0 +1,224 @@ +"use client"; + +import React from "react"; +import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; +import { Badge } from "@/components/ui/badge"; +import { Button } from "@/components/ui/button"; +import { Eye, Edit, Trash2, MoreHorizontal } from "lucide-react"; +import { CardDisplayConfig, ColumnConfig } from "./types"; + +interface CardModeRendererProps { + data: Record[]; + cardConfig: CardDisplayConfig; + visibleColumns: ColumnConfig[]; + onRowClick?: (row: Record) => void; + onRowSelect?: (row: Record, selected: boolean) => void; + selectedRows?: string[]; + showActions?: boolean; +} + +/** + * 카드 모드 렌더러 + * 테이블 데이터를 카드 형태로 표시 + */ +export const CardModeRenderer: React.FC = ({ + data, + cardConfig, + visibleColumns, + onRowClick, + onRowSelect, + selectedRows = [], + showActions = true, +}) => { + // 기본값 설정 + const config = { + cardsPerRow: 3, + cardSpacing: 16, + showActions: true, + cardHeight: "auto", + ...cardConfig, + }; + + // 카드 그리드 스타일 계산 + const gridStyle: React.CSSProperties = { + display: "grid", + gridTemplateColumns: `repeat(${config.cardsPerRow}, 1fr)`, + gap: `${config.cardSpacing}px`, + padding: `${config.cardSpacing}px`, + }; + + // 카드 높이 스타일 + const cardStyle: React.CSSProperties = { + height: config.cardHeight === "auto" ? "auto" : `${config.cardHeight}px`, + cursor: onRowClick ? "pointer" : "default", + }; + + // 컬럼 값 가져오기 함수 + const getColumnValue = (row: Record, columnName?: string): string => { + if (!columnName || !row) return ""; + return String(row[columnName] || ""); + }; + + // 액션 버튼 렌더링 + const renderActions = (row: Record) => { + if (!showActions || !config.showActions) return null; + + return ( +
+ + + + +
+ ); + }; + + // 데이터가 없는 경우 + if (!data || data.length === 0) { + return ( +
+
+
+
+
표시할 데이터가 없습니다
+
조건을 변경하거나 새로운 데이터를 추가해보세요
+
+ ); + } + + return ( +
+ {data.map((row, index) => { + const idValue = getColumnValue(row, config.idColumn); + const titleValue = getColumnValue(row, config.titleColumn); + const subtitleValue = getColumnValue(row, config.subtitleColumn); + const descriptionValue = getColumnValue(row, config.descriptionColumn); + const imageValue = getColumnValue(row, config.imageColumn); + + const isSelected = selectedRows.includes(idValue); + + return ( + onRowClick?.(row)} + > + +
+
+ + {titleValue || "제목 없음"} + + {subtitleValue && ( +
+ {subtitleValue} +
+ )} +
+ + {/* ID 뱃지 */} + {idValue && ( + + {idValue} + + )} +
+
+ + + {/* 이미지 표시 */} + {imageValue && ( +
+ {titleValue} { + const target = e.target as HTMLImageElement; + target.style.display = "none"; + }} + /> +
+ )} + + {/* 설명 표시 */} + {descriptionValue && ( +
+ {descriptionValue} +
+ )} + + {/* 추가 필드들 표시 (선택적) */} +
+ {visibleColumns + .filter(col => + col.columnName !== config.idColumn && + col.columnName !== config.titleColumn && + col.columnName !== config.subtitleColumn && + col.columnName !== config.descriptionColumn && + col.columnName !== config.imageColumn && + col.columnName !== "__checkbox__" && + col.visible + ) + .slice(0, 3) // 최대 3개 추가 필드만 표시 + .map((col) => { + const value = getColumnValue(row, col.columnName); + if (!value) return null; + + return ( +
+ {col.displayName}: + {value} +
+ ); + })} +
+ + {/* 액션 버튼들 */} + {renderActions(row)} +
+
+ ); + })} +
+ ); +}; -- 2.43.0 From c28e27f3e83422c04f1df1b84a1ba95fef8b68c6 Mon Sep 17 00:00:00 2001 From: leeheejin Date: Thu, 25 Sep 2025 18:54:25 +0900 Subject: [PATCH 2/2] =?UTF-8?q?=ED=99=94=EB=A9=B4=EA=B4=80=EB=A6=AC?= =?UTF-8?q?=EC=9D=98=20=ED=85=8C=EC=9D=B4=EB=B8=94=20=EB=A6=AC=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8,=20=EC=B9=B4=EB=93=9C=20=EB=94=94=EC=8A=A4=ED=94=8C?= =?UTF-8?q?=EB=A0=88=EC=9D=B4=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../app/(main)/screens/[screenId]/page.tsx | 20 +- frontend/components/screen/ScreenDesigner.tsx | 19 ++ .../screen/panels/ComponentsPanel.tsx | 25 +- .../card-display/CardDisplayComponent.tsx | 245 +++++++++++++++--- .../table-list/TableListComponent.tsx | 24 ++ .../table-list/TableListConfigPanel.tsx | 183 +++++++++++++ .../table-list/TableListRenderer.tsx | 13 + .../registry/components/table-list/index.ts | 14 +- .../registry/components/table-list/types.ts | 25 +- 9 files changed, 512 insertions(+), 56 deletions(-) diff --git a/frontend/app/(main)/screens/[screenId]/page.tsx b/frontend/app/(main)/screens/[screenId]/page.tsx index 86dd77df..dd48479d 100644 --- a/frontend/app/(main)/screens/[screenId]/page.tsx +++ b/frontend/app/(main)/screens/[screenId]/page.tsx @@ -275,16 +275,16 @@ export default function ScreenViewPage() { zIndex: component.position.z || 1, }} onMouseEnter={() => { - console.log("🎯 할당된 화면 컴포넌트:", { - id: component.id, - type: component.type, - position: component.position, - size: component.size, - styleWidth: component.style?.width, - styleHeight: component.style?.height, - finalWidth: `${component.size.width}px`, - finalHeight: `${component.size.height}px`, - }); + // console.log("🎯 할당된 화면 컴포넌트:", { + // id: component.id, + // type: component.type, + // position: component.position, + // size: component.size, + // styleWidth: component.style?.width, + // styleHeight: component.style?.height, + // finalWidth: `${component.size.width}px`, + // finalHeight: `${component.size.height}px`, + // }); }} > {/* 위젯 컴포넌트가 아닌 경우 DynamicComponentRenderer 사용 */} diff --git a/frontend/components/screen/ScreenDesigner.tsx b/frontend/components/screen/ScreenDesigner.tsx index 39c8e34e..a0f6ad09 100644 --- a/frontend/components/screen/ScreenDesigner.tsx +++ b/frontend/components/screen/ScreenDesigner.tsx @@ -1809,6 +1809,25 @@ export default function ScreenDesigner({ selectedScreen, onBackToList }: ScreenD filters: [], displayFormat: "simple" as const, }; + case "table": + return { + tableName: "", + displayMode: "table" as const, + showHeader: true, + showFooter: true, + pagination: { + enabled: true, + pageSize: 10, + showPageSizeSelector: true, + showPageInfo: true, + showFirstLast: true, + }, + columns: [], + searchable: true, + sortable: true, + filterable: true, + exportable: true, + }; default: return undefined; } diff --git a/frontend/components/screen/panels/ComponentsPanel.tsx b/frontend/components/screen/panels/ComponentsPanel.tsx index d6daef4e..a779d484 100644 --- a/frontend/components/screen/panels/ComponentsPanel.tsx +++ b/frontend/components/screen/panels/ComponentsPanel.tsx @@ -20,7 +20,30 @@ export function ComponentsPanel({ className }: ComponentsPanelProps) { // 레지스트리에서 모든 컴포넌트 조회 const allComponents = useMemo(() => { - return ComponentRegistry.getAllComponents(); + const components = ComponentRegistry.getAllComponents(); + console.log("🔍 ComponentsPanel - 로드된 컴포넌트:", components.map(c => ({ id: c.id, name: c.name, category: c.category }))); + + // 수동으로 table-list 컴포넌트 추가 (임시) + const hasTableList = components.some(c => c.id === 'table-list'); + if (!hasTableList) { + console.log("⚠️ table-list 컴포넌트가 없어서 수동 추가"); + components.push({ + id: "table-list", + name: "테이블 리스트", + nameEng: "TableList Component", + description: "데이터베이스 테이블의 데이터를 목록으로 표시하는 컴포넌트", + category: "display", + webType: "text", + defaultConfig: {}, + defaultSize: { width: 800, height: 400 }, + icon: "Table", + tags: ["테이블", "데이터", "목록", "그리드"], + version: "1.0.0", + author: "개발팀", + }); + } + + return components; }, []); // 카테고리별 분류 (input 카테고리 제외) diff --git a/frontend/lib/registry/components/card-display/CardDisplayComponent.tsx b/frontend/lib/registry/components/card-display/CardDisplayComponent.tsx index 2cd69c54..318ca587 100644 --- a/frontend/lib/registry/components/card-display/CardDisplayComponent.tsx +++ b/frontend/lib/registry/components/card-display/CardDisplayComponent.tsx @@ -4,6 +4,10 @@ import React, { useEffect, useState, useMemo } from "react"; import { ComponentRendererProps } from "@/types/component"; import { CardDisplayConfig } from "./types"; import { tableTypeApi } from "@/lib/api/screen"; +import { filterDOMProps } from "@/lib/utils/domPropsFilter"; +import { Dialog, DialogContent, DialogHeader, DialogTitle } from "@/components/ui/dialog"; +import { Input } from "@/components/ui/input"; +import { Button } from "@/components/ui/button"; export interface CardDisplayComponentProps extends ComponentRendererProps { config?: CardDisplayConfig; @@ -39,6 +43,59 @@ export const CardDisplayComponent: React.FC = ({ const [loadedTableColumns, setLoadedTableColumns] = useState([]); const [loading, setLoading] = useState(false); + // 상세보기 모달 상태 + const [viewModalOpen, setViewModalOpen] = useState(false); + const [selectedData, setSelectedData] = useState(null); + + // 편집 모달 상태 + const [editModalOpen, setEditModalOpen] = useState(false); + const [editData, setEditData] = useState(null); + + // 카드 액션 핸들러 + const handleCardView = (data: any) => { + // console.log("👀 상세보기 클릭:", data); + setSelectedData(data); + setViewModalOpen(true); + }; + + const handleCardEdit = (data: any) => { + // console.log("✏️ 편집 클릭:", data); + setEditData({ ...data }); // 복사본 생성 + setEditModalOpen(true); + }; + + // 편집 폼 데이터 변경 핸들러 + const handleEditFormChange = (key: string, value: string) => { + setEditData((prev: any) => ({ + ...prev, + [key]: value + })); + }; + + // 편집 저장 핸들러 + const handleEditSave = async () => { + // console.log("💾 편집 저장:", editData); + + try { + // TODO: 실제 API 호출로 데이터 업데이트 + // await tableTypeApi.updateTableData(tableName, editData); + + // console.log("✅ 편집 저장 완료"); + alert("✅ 저장되었습니다!"); + + // 모달 닫기 + setEditModalOpen(false); + setEditData(null); + + // 데이터 새로고침 (필요시) + // loadTableData(); + + } catch (error) { + console.error("❌ 편집 저장 실패:", error); + alert("❌ 저장에 실패했습니다."); + } + }; + // 테이블 데이터 로딩 useEffect(() => { const loadTableData = async () => { @@ -48,19 +105,25 @@ export const CardDisplayComponent: React.FC = ({ } // tableName 확인 (props에서 전달받은 tableName 사용) - const tableNameToUse = tableName || component.componentConfig?.tableName; + const tableNameToUse = tableName || component.componentConfig?.tableName || 'user_info'; // 기본 테이블명 설정 if (!tableNameToUse) { - console.log("📋 CardDisplay: 테이블명이 설정되지 않음", { - tableName, - componentTableName: component.componentConfig?.tableName, - }); + // console.log("📋 CardDisplay: 테이블명이 설정되지 않음", { + // tableName, + // componentTableName: component.componentConfig?.tableName, + // }); return; } + // console.log("📋 CardDisplay: 사용할 테이블명", { + // tableName, + // componentTableName: component.componentConfig?.tableName, + // finalTableName: tableNameToUse, + // }); + try { setLoading(true); - console.log(`📋 CardDisplay: ${tableNameToUse} 테이블 데이터 로딩 시작`); + // console.log(`📋 CardDisplay: ${tableNameToUse} 테이블 데이터 로딩 시작`); // 테이블 데이터와 컬럼 정보를 병렬로 로드 const [dataResponse, columnsResponse] = await Promise.all([ @@ -71,13 +134,13 @@ export const CardDisplayComponent: React.FC = ({ tableTypeApi.getColumns(tableNameToUse), ]); - console.log(`📋 CardDisplay: ${tableNameToUse} 데이터 로딩 완료`, { - total: dataResponse.total, - dataLength: dataResponse.data.length, - columnsLength: columnsResponse.length, - sampleData: dataResponse.data.slice(0, 2), - sampleColumns: columnsResponse.slice(0, 3), - }); + // console.log(`📋 CardDisplay: ${tableNameToUse} 데이터 로딩 완료`, { + // total: dataResponse.total, + // dataLength: dataResponse.data.length, + // columnsLength: columnsResponse.length, + // sampleData: dataResponse.data.slice(0, 2), + // sampleColumns: columnsResponse.slice(0, 3), + // }); setLoadedTableData(dataResponse.data); setLoadedTableColumns(columnsResponse); @@ -130,32 +193,32 @@ export const CardDisplayComponent: React.FC = ({ // 표시할 데이터 결정 (로드된 테이블 데이터 우선 사용) const displayData = useMemo(() => { - console.log("📋 CardDisplay: displayData 결정 중", { - dataSource: componentConfig.dataSource, - loadedTableDataLength: loadedTableData.length, - tableDataLength: tableData.length, - staticDataLength: componentConfig.staticData?.length || 0, - }); + // console.log("📋 CardDisplay: displayData 결정 중", { + // dataSource: componentConfig.dataSource, + // loadedTableDataLength: loadedTableData.length, + // tableDataLength: tableData.length, + // staticDataLength: componentConfig.staticData?.length || 0, + // }); // 로드된 테이블 데이터가 있으면 항상 우선 사용 (dataSource 설정 무시) if (loadedTableData.length > 0) { - console.log("📋 CardDisplay: 로드된 테이블 데이터 사용", loadedTableData.slice(0, 2)); + // console.log("📋 CardDisplay: 로드된 테이블 데이터 사용", loadedTableData.slice(0, 2)); return loadedTableData; } // props로 전달받은 테이블 데이터가 있으면 사용 if (tableData.length > 0) { - console.log("📋 CardDisplay: props 테이블 데이터 사용", tableData.slice(0, 2)); + // console.log("📋 CardDisplay: props 테이블 데이터 사용", tableData.slice(0, 2)); return tableData; } if (componentConfig.staticData && componentConfig.staticData.length > 0) { - console.log("📋 CardDisplay: 정적 데이터 사용", componentConfig.staticData.slice(0, 2)); + // console.log("📋 CardDisplay: 정적 데이터 사용", componentConfig.staticData.slice(0, 2)); return componentConfig.staticData; } // 데이터가 없으면 빈 배열 반환 - console.log("📋 CardDisplay: 표시할 데이터가 없음"); + // console.log("📋 CardDisplay: 표시할 데이터가 없음"); return []; }, [componentConfig.dataSource, loadedTableData, tableData, componentConfig.staticData]); @@ -260,23 +323,8 @@ export const CardDisplayComponent: React.FC = ({ } }; - // DOM에 전달하면 안 되는 React-specific props 필터링 - const { - selectedScreen, - onZoneComponentDrop, - onZoneClick, - componentConfig: _componentConfig, - component: _component, - isSelected: _isSelected, - onClick: _onClick, - onDragStart: _onDragStart, - onDragEnd: _onDragEnd, - size: _size, - position: _position, - style: _style, - onRefresh: _onRefresh, // React DOM 속성이 아니므로 필터링 - ...domProps - } = props; + // DOM 안전한 props만 필터링 (filterDOMProps 유틸리티 사용) + const safeDomProps = filterDOMProps(props); return ( <> @@ -301,7 +349,7 @@ export const CardDisplayComponent: React.FC = ({ onClick={handleClick} onDragStart={onDragStart} onDragEnd={onDragEnd} - {...domProps} + {...safeDomProps} >
{displayData.length === 0 ? ( @@ -393,8 +441,24 @@ export const CardDisplayComponent: React.FC = ({ {/* 카드 액션 (선택사항) */}
- - + +
); @@ -402,6 +466,101 @@ export const CardDisplayComponent: React.FC = ({ )} + + {/* 상세보기 모달 */} + + + + + 📋 + 상세 정보 + + + + {selectedData && ( +
+
+ {Object.entries(selectedData) + .filter(([key, value]) => value !== null && value !== undefined && value !== '') + .map(([key, value]) => ( +
+
+ {key.replace(/_/g, ' ')} +
+
+ {String(value)} +
+
+ )) + } +
+ +
+ +
+
+ )} +
+
+ + {/* 편집 모달 */} + + + + + ✏️ + 데이터 편집 + + + + {editData && ( +
+
+ {Object.entries(editData) + .filter(([key, value]) => value !== null && value !== undefined) + .map(([key, value]) => ( +
+ + handleEditFormChange(key, e.target.value)} + className="w-full" + placeholder={`${key} 입력`} + /> +
+ )) + } +
+ +
+ + +
+
+ )} +
+
); }; diff --git a/frontend/lib/registry/components/table-list/TableListComponent.tsx b/frontend/lib/registry/components/table-list/TableListComponent.tsx index e78d5c21..03754e7f 100644 --- a/frontend/lib/registry/components/table-list/TableListComponent.tsx +++ b/frontend/lib/registry/components/table-list/TableListComponent.tsx @@ -24,6 +24,7 @@ import { Checkbox } from "@/components/ui/checkbox"; import { cn } from "@/lib/utils"; import { AdvancedSearchFilters } from "@/components/screen/filters/AdvancedSearchFilters"; import { SingleTableWithSticky } from "./SingleTableWithSticky"; +import { CardModeRenderer } from "./CardModeRenderer"; export interface TableListComponentProps { component: any; @@ -1290,6 +1291,29 @@ export const TableListComponent: React.FC = ({
{error}
+ ) : tableConfig.displayMode === "card" ? ( + // 카드 모드 렌더링 +
+ { + const rowIndex = data.findIndex(d => d === row); + const rowKey = getRowKey(row, rowIndex); + handleRowSelection(rowKey, selected); + }} + selectedRows={Array.from(selectedRows)} + showActions={tableConfig.actions?.showActions} + /> +
) : needsHorizontalScroll ? ( // 가로 스크롤이 필요한 경우 - 단일 테이블에서 sticky 컬럼 사용
diff --git a/frontend/lib/registry/components/table-list/TableListConfigPanel.tsx b/frontend/lib/registry/components/table-list/TableListConfigPanel.tsx index e78dcce5..34bf0e2f 100644 --- a/frontend/lib/registry/components/table-list/TableListConfigPanel.tsx +++ b/frontend/lib/registry/components/table-list/TableListConfigPanel.tsx @@ -10,6 +10,7 @@ import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"; import { Badge } from "@/components/ui/badge"; import { ScrollArea } from "@/components/ui/scroll-area"; +import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group"; import { TableListConfig, ColumnConfig } from "./types"; import { entityJoinApi } from "@/lib/api/entityJoin"; import { tableTypeApi } from "@/lib/api/screen"; @@ -755,6 +756,188 @@ export const TableListConfigPanel: React.FC = ({ {/* 기본 설정 탭 */} + {/* 표시 모드 설정 */} + + + 표시 모드 + 데이터를 어떤 형태로 표시할지 선택하세요 + + +
+ + handleChange("displayMode", value)} + > +
+ + +
+
+ + +
+
+
+ + {/* 카드 모드 설정 */} + {config.displayMode === "card" && ( +
+
+ + +
+
+ + +
+ +
+ + + handleNestedChange("cardConfig", "cardSpacing", parseInt(e.target.value)) + } + min="0" + max="50" + /> +
+
+ +
+ + +
+
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+
+
+ +
+ + handleNestedChange("cardConfig", "showActions", checked as boolean) + } + /> + +
+
+
+ )} +
+
+ 연결된 테이블 diff --git a/frontend/lib/registry/components/table-list/TableListRenderer.tsx b/frontend/lib/registry/components/table-list/TableListRenderer.tsx index 84569200..05f75319 100644 --- a/frontend/lib/registry/components/table-list/TableListRenderer.tsx +++ b/frontend/lib/registry/components/table-list/TableListRenderer.tsx @@ -67,3 +67,16 @@ export class TableListRenderer extends AutoRegisteringComponentRenderer { // 자동 등록 실행 TableListRenderer.registerSelf(); + +// 강제 등록 (디버깅용) +if (typeof window !== "undefined") { + setTimeout(() => { + try { + console.log("🔄 TableList 강제 등록 시도..."); + TableListRenderer.registerSelf(); + console.log("✅ TableList 강제 등록 완료"); + } catch (error) { + console.error("❌ TableList 강제 등록 실패:", error); + } + }, 1000); +} diff --git a/frontend/lib/registry/components/table-list/index.ts b/frontend/lib/registry/components/table-list/index.ts index 5236b80f..b846e9f6 100644 --- a/frontend/lib/registry/components/table-list/index.ts +++ b/frontend/lib/registry/components/table-list/index.ts @@ -18,9 +18,21 @@ export const TableListDefinition = createComponentDefinition({ nameEng: "TableList Component", description: "데이터베이스 테이블의 데이터를 목록으로 표시하는 컴포넌트", category: ComponentCategory.DISPLAY, - webType: "table", + webType: "text", component: TableListWrapper, defaultConfig: { + // 표시 모드 설정 + displayMode: "table" as const, + + // 카드 모드 기본 설정 + cardConfig: { + idColumn: "id", + titleColumn: "name", + cardsPerRow: 3, + cardSpacing: 16, + showActions: true, + }, + // 테이블 기본 설정 showHeader: true, showFooter: true, diff --git a/frontend/lib/registry/components/table-list/types.ts b/frontend/lib/registry/components/table-list/types.ts index 1067bade..fc120b7c 100644 --- a/frontend/lib/registry/components/table-list/types.ts +++ b/frontend/lib/registry/components/table-list/types.ts @@ -74,6 +74,21 @@ export interface ColumnConfig { autoGeneration?: AutoGenerationConfig; // 자동생성 설정 } +/** + * 카드 디스플레이 설정 + */ +export interface CardDisplayConfig { + idColumn: string; // ID 컬럼 (사번 등) + titleColumn: string; // 제목 컬럼 (이름 등) + subtitleColumn?: string; // 부제목 컬럼 (부서 등) + descriptionColumn?: string; // 설명 컬럼 + imageColumn?: string; // 이미지 컬럼 + cardsPerRow: number; // 한 행당 카드 수 (기본: 3) + cardSpacing: number; // 카드 간격 (기본: 16px) + showActions: boolean; // 액션 버튼 표시 여부 + cardHeight?: number; // 카드 높이 (기본: auto) +} + /** * 필터 설정 */ @@ -147,6 +162,12 @@ export interface CheckboxConfig { * TableList 컴포넌트 설정 타입 */ export interface TableListConfig extends ComponentConfig { + // 표시 모드 설정 + displayMode?: "table" | "card"; // 기본: "table" + + // 카드 디스플레이 설정 (displayMode가 "card"일 때 사용) + cardConfig?: CardDisplayConfig; + // 테이블 기본 설정 selectedTable?: string; tableName?: string; @@ -175,7 +196,9 @@ export interface TableListConfig extends ComponentConfig { }; // 페이지네이션 - pagination: PaginationConfig; + pagination: PaginationConfig & { + currentPage?: number; // 현재 페이지 (추가) + }; // 필터 설정 filter: FilterConfig; -- 2.43.0