237 lines
7.1 KiB
TypeScript
237 lines
7.1 KiB
TypeScript
"use client";
|
|
|
|
import React from "react";
|
|
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
|
|
|
interface ContentRow {
|
|
label?: string;
|
|
field?: string;
|
|
type?: string;
|
|
}
|
|
|
|
interface DataSource {
|
|
filterField?: string;
|
|
sourceTable?: string;
|
|
}
|
|
|
|
interface Grouping {
|
|
enabled?: boolean;
|
|
aggregations?: any[];
|
|
groupField?: string;
|
|
}
|
|
|
|
interface TableLayout {
|
|
headerRows?: any[];
|
|
tableColumns?: any[];
|
|
}
|
|
|
|
interface CardLayoutItem {
|
|
field?: string;
|
|
label?: string;
|
|
width?: string;
|
|
}
|
|
|
|
export interface RepeatScreenModalProps {
|
|
// 기본 props
|
|
id?: string;
|
|
label?: string;
|
|
style?: React.CSSProperties;
|
|
|
|
// 컴포넌트 설정
|
|
cardMode?: "simple" | "detailed";
|
|
cardSpacing?: string;
|
|
cardTitle?: string;
|
|
contentRows?: ContentRow[];
|
|
dataSource?: DataSource;
|
|
grouping?: Grouping;
|
|
saveMode?: "all" | "single";
|
|
showCardBorder?: boolean;
|
|
showCardTitle?: boolean;
|
|
tableLayout?: TableLayout;
|
|
cardLayout?: CardLayoutItem[];
|
|
|
|
// 컴포넌트 config (componentConfig에서 전달됨)
|
|
componentConfig?: {
|
|
type?: string;
|
|
webType?: string;
|
|
cardMode?: string;
|
|
cardSpacing?: string;
|
|
cardTitle?: string;
|
|
contentRows?: ContentRow[];
|
|
dataSource?: DataSource;
|
|
grouping?: Grouping;
|
|
saveMode?: string;
|
|
showCardBorder?: boolean;
|
|
showCardTitle?: boolean;
|
|
tableLayout?: TableLayout;
|
|
cardLayout?: CardLayoutItem[];
|
|
};
|
|
}
|
|
|
|
/**
|
|
* RepeatScreenModal 컴포넌트
|
|
* 카드/테이블 형태로 데이터를 반복 표시하고 편집할 수 있는 모달
|
|
*/
|
|
export function RepeatScreenModalComponent(props: RepeatScreenModalProps) {
|
|
const {
|
|
id,
|
|
label = "반복 화면 모달",
|
|
style,
|
|
componentConfig,
|
|
} = props;
|
|
|
|
// componentConfig에서 설정 가져오기
|
|
const config = componentConfig || {};
|
|
const {
|
|
cardMode = "simple",
|
|
cardSpacing = "24px",
|
|
cardTitle = "",
|
|
contentRows = [],
|
|
dataSource,
|
|
grouping,
|
|
saveMode = "all",
|
|
showCardBorder = true,
|
|
showCardTitle = true,
|
|
tableLayout,
|
|
cardLayout = [],
|
|
} = config;
|
|
|
|
// 스타일에서 width, height 추출
|
|
const { width, height, ...restStyle } = style || {};
|
|
|
|
return (
|
|
<div
|
|
id={id}
|
|
className="flex h-full w-full flex-col overflow-auto"
|
|
style={restStyle}
|
|
>
|
|
<Card className={`h-full w-full ${showCardBorder ? "" : "border-0 shadow-none"}`}>
|
|
{showCardTitle && (
|
|
<CardHeader className="pb-2">
|
|
<CardTitle className="text-lg">{label}</CardTitle>
|
|
{cardTitle && (
|
|
<p className="text-sm text-muted-foreground">{cardTitle}</p>
|
|
)}
|
|
</CardHeader>
|
|
)}
|
|
<CardContent className="flex-1 overflow-auto">
|
|
{/* 데이터 소스 정보 표시 */}
|
|
{dataSource?.sourceTable && (
|
|
<div className="mb-4 rounded-md bg-muted p-3">
|
|
<p className="text-sm">
|
|
<span className="font-medium">데이터 소스:</span>{" "}
|
|
{dataSource.sourceTable}
|
|
</p>
|
|
{dataSource.filterField && (
|
|
<p className="text-sm">
|
|
<span className="font-medium">필터 필드:</span>{" "}
|
|
{dataSource.filterField}
|
|
</p>
|
|
)}
|
|
</div>
|
|
)}
|
|
|
|
{/* 카드 레이아웃 표시 */}
|
|
{cardLayout && cardLayout.length > 0 && (
|
|
<div
|
|
className="grid gap-4"
|
|
style={{
|
|
gridTemplateColumns: `repeat(auto-fill, minmax(250px, 1fr))`,
|
|
gap: cardSpacing,
|
|
}}
|
|
>
|
|
{cardLayout.map((item, index) => (
|
|
<div
|
|
key={index}
|
|
className="rounded-md border bg-card p-3"
|
|
>
|
|
<p className="text-xs text-muted-foreground">{item.label || item.field}</p>
|
|
<p className="text-sm font-medium">{item.field || "-"}</p>
|
|
</div>
|
|
))}
|
|
</div>
|
|
)}
|
|
|
|
{/* 컨텐츠 행 표시 */}
|
|
{contentRows && contentRows.length > 0 && (
|
|
<div className="space-y-2">
|
|
{contentRows.map((row, index) => (
|
|
<div
|
|
key={index}
|
|
className="flex items-center justify-between rounded-md border p-2"
|
|
>
|
|
<span className="text-sm text-muted-foreground">
|
|
{row.label || row.field || `Row ${index + 1}`}
|
|
</span>
|
|
<span className="text-sm font-medium">
|
|
{row.field || "-"}
|
|
</span>
|
|
</div>
|
|
))}
|
|
</div>
|
|
)}
|
|
|
|
{/* 테이블 레이아웃이 있으면 테이블 형태로 표시 */}
|
|
{tableLayout?.tableColumns && tableLayout.tableColumns.length > 0 && (
|
|
<div className="mt-4 overflow-auto">
|
|
<table className="w-full border-collapse">
|
|
<thead>
|
|
<tr className="bg-muted">
|
|
{tableLayout.tableColumns.map((col: any, index: number) => (
|
|
<th
|
|
key={index}
|
|
className="border px-3 py-2 text-left text-sm font-medium"
|
|
>
|
|
{col.label || col.field || `Column ${index + 1}`}
|
|
</th>
|
|
))}
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<tr>
|
|
<td
|
|
colSpan={tableLayout.tableColumns.length}
|
|
className="border px-3 py-8 text-center text-sm text-muted-foreground"
|
|
>
|
|
데이터가 없습니다
|
|
</td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
)}
|
|
|
|
{/* 빈 상태 표시 */}
|
|
{(!contentRows || contentRows.length === 0) &&
|
|
(!cardLayout || cardLayout.length === 0) &&
|
|
(!tableLayout?.tableColumns || tableLayout.tableColumns.length === 0) && (
|
|
<div className="flex h-48 items-center justify-center rounded-md border-2 border-dashed">
|
|
<div className="text-center">
|
|
<p className="text-sm font-medium text-muted-foreground">
|
|
반복 화면 모달
|
|
</p>
|
|
<p className="mt-1 text-xs text-muted-foreground">
|
|
컴포넌트 설정을 구성해주세요
|
|
</p>
|
|
</div>
|
|
</div>
|
|
)}
|
|
|
|
{/* 그룹핑 정보 */}
|
|
{grouping?.enabled && (
|
|
<div className="mt-4 rounded-md bg-blue-50 p-2 text-xs text-blue-700">
|
|
그룹핑 활성화됨 {grouping.groupField && `(${grouping.groupField})`}
|
|
</div>
|
|
)}
|
|
|
|
{/* 저장 모드 표시 */}
|
|
<div className="mt-4 text-xs text-muted-foreground">
|
|
저장 모드: {saveMode === "all" ? "전체 저장" : "개별 저장"}
|
|
</div>
|
|
</CardContent>
|
|
</Card>
|
|
</div>
|
|
);
|
|
}
|
|
|