diff --git a/frontend/components/admin/dashboard/types.ts b/frontend/components/admin/dashboard/types.ts
index 5b8b2f5d..0eece704 100644
--- a/frontend/components/admin/dashboard/types.ts
+++ b/frontend/components/admin/dashboard/types.ts
@@ -213,12 +213,14 @@ export interface ChartDataset {
// 리스트 위젯 설정
export interface ListWidgetConfig {
columnMode: "auto" | "manual"; // 컬럼 설정 방식 (자동 or 수동)
+ viewMode: "table" | "card"; // 뷰 모드 (테이블 or 카드) (기본: table)
columns: ListColumn[]; // 컬럼 정의
pageSize: number; // 페이지당 행 수 (기본: 10)
enablePagination: boolean; // 페이지네이션 활성화 (기본: true)
- showHeader: boolean; // 헤더 표시 (기본: true)
- stripedRows: boolean; // 줄무늬 행 (기본: true)
+ showHeader: boolean; // 헤더 표시 (기본: true, 테이블 모드에만 적용)
+ stripedRows: boolean; // 줄무늬 행 (기본: true, 테이블 모드에만 적용)
compactMode: boolean; // 압축 모드 (기본: false)
+ cardColumns: number; // 카드 뷰 컬럼 수 (기본: 3)
}
// 리스트 컬럼
diff --git a/frontend/components/admin/dashboard/widgets/ListWidget.tsx b/frontend/components/admin/dashboard/widgets/ListWidget.tsx
index e7b2781d..390767f4 100644
--- a/frontend/components/admin/dashboard/widgets/ListWidget.tsx
+++ b/frontend/components/admin/dashboard/widgets/ListWidget.tsx
@@ -4,6 +4,7 @@ import React, { useState, useEffect } from "react";
import { DashboardElement, QueryResult, ListWidgetConfig } from "../types";
import { Button } from "@/components/ui/button";
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table";
+import { Card } from "@/components/ui/card";
interface ListWidgetProps {
element: DashboardElement;
@@ -24,12 +25,14 @@ export function ListWidget({ element, onConfigUpdate }: ListWidgetProps) {
const config = element.listConfig || {
columnMode: "auto",
+ viewMode: "table",
columns: [],
pageSize: 10,
enablePagination: true,
showHeader: true,
stripedRows: true,
compactMode: false,
+ cardColumns: 3,
};
// 데이터 로드
@@ -209,55 +212,92 @@ export function ListWidget({ element, onConfigUpdate }: ListWidgetProps) {
{element.title}
- {/* 테이블 */}
-
-
- {config.showHeader && (
-
-
- {config.columns
- .filter((col) => col.visible)
- .map((col) => (
-
- {col.label}
-
- ))}
-
-
- )}
-
- {paginatedRows.length === 0 ? (
-
- col.visible).length}
- className="text-center text-gray-500"
- >
- 데이터가 없습니다
-
-
- ) : (
- paginatedRows.map((row, idx) => (
-
+ {/* 테이블 뷰 */}
+ {config.viewMode === "table" && (
+
+
+ {config.showHeader && (
+
+
{config.columns
.filter((col) => col.visible)
.map((col) => (
-
- {String(row[col.field] ?? "")}
-
+ {col.label}
+
))}
- ))
+
)}
-
-
-
+
+ {paginatedRows.length === 0 ? (
+
+ col.visible).length}
+ className="text-center text-gray-500"
+ >
+ 데이터가 없습니다
+
+
+ ) : (
+ paginatedRows.map((row, idx) => (
+
+ {config.columns
+ .filter((col) => col.visible)
+ .map((col) => (
+
+ {String(row[col.field] ?? "")}
+
+ ))}
+
+ ))
+ )}
+
+
+
+ )}
+
+ {/* 카드 뷰 */}
+ {config.viewMode === "card" && (
+
+ {paginatedRows.length === 0 ? (
+
데이터가 없습니다
+ ) : (
+
+ {paginatedRows.map((row, idx) => (
+
+
+ {config.columns
+ .filter((col) => col.visible)
+ .map((col) => (
+
+
{col.label}
+
+ {String(row[col.field] ?? "")}
+
+
+ ))}
+
+
+ ))}
+
+ )}
+
+ )}
{/* 페이지네이션 */}
{config.enablePagination && totalPages > 1 && (
diff --git a/frontend/components/admin/dashboard/widgets/ListWidgetConfigModal.tsx b/frontend/components/admin/dashboard/widgets/ListWidgetConfigModal.tsx
index d5b4e3d0..eb2a0e8a 100644
--- a/frontend/components/admin/dashboard/widgets/ListWidgetConfigModal.tsx
+++ b/frontend/components/admin/dashboard/widgets/ListWidgetConfigModal.tsx
@@ -36,12 +36,14 @@ export function ListWidgetConfigModal({ isOpen, element, onClose, onSave }: List
const [listConfig, setListConfig] = useState(
element.listConfig || {
columnMode: "auto",
+ viewMode: "table",
columns: [],
pageSize: 10,
enablePagination: true,
showHeader: true,
stripedRows: true,
compactMode: false,
+ cardColumns: 3,
},
);
@@ -64,6 +66,7 @@ export function ListWidgetConfigModal({ isOpen, element, onClose, onSave }: List
// 현재 스텝은 1로 초기화
setCurrentStep(1);
}
+ // eslint-disable-next-line react-hooks/exhaustive-deps
}, [isOpen, element.id]); // element.id가 변경될 때만 재실행
// 데이터 소스 타입 변경
diff --git a/frontend/components/admin/dashboard/widgets/list-widget/ListTableOptions.tsx b/frontend/components/admin/dashboard/widgets/list-widget/ListTableOptions.tsx
index 134f74ee..870d1abf 100644
--- a/frontend/components/admin/dashboard/widgets/list-widget/ListTableOptions.tsx
+++ b/frontend/components/admin/dashboard/widgets/list-widget/ListTableOptions.tsx
@@ -7,6 +7,7 @@ import { Label } from "@/components/ui/label";
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
import { Checkbox } from "@/components/ui/checkbox";
import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group";
+import { Input } from "@/components/ui/input";
interface ListTableOptionsProps {
config: ListWidgetConfig;
@@ -26,6 +27,28 @@ export function ListTableOptions({ config, onChange }: ListTableOptionsProps) {
+ {/* 뷰 모드 */}
+
+
+
onChange({ viewMode: value })}
+ >
+
+
+
+
+
+
+
+
+
+
+
{/* 컬럼 모드 */}
@@ -48,6 +71,22 @@ export function ListTableOptions({ config, onChange }: ListTableOptionsProps) {
+ {/* 카드 뷰 컬럼 수 */}
+ {config.viewMode === "card" && (
+
+
+
onChange({ cardColumns: parseInt(e.target.value) || 3 })}
+ className="w-full"
+ />
+
한 줄에 표시할 카드 개수 (1-6)
+
+ )}
+
{/* 페이지 크기 */}
@@ -86,26 +125,30 @@ export function ListTableOptions({ config, onChange }: ListTableOptionsProps) {
-
-
-
- onChange({ stripedRows: checked as boolean })}
- />
-
-
+ {config.viewMode === "table" && (
+ <>
+
+
+
+ onChange({ stripedRows: checked as boolean })}
+ />
+
+
+ >
+ )}