2026-02-12 11:07:58 +09:00
|
|
|
"use client";
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* pop-card-list 디자인 모드 미리보기 컴포넌트 (V2)
|
|
|
|
|
*
|
|
|
|
|
* 디자이너 캔버스에서 표시되는 미리보기
|
|
|
|
|
* 이미지 참조 기반 카드 구조 반영
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
import React from "react";
|
|
|
|
|
import { LayoutGrid, Package } from "lucide-react";
|
|
|
|
|
import type { PopCardListConfig } from "../types";
|
|
|
|
|
import {
|
2026-02-24 15:54:57 +09:00
|
|
|
CARD_SCROLL_DIRECTION_LABELS,
|
2026-02-12 11:07:58 +09:00
|
|
|
CARD_SIZE_LABELS,
|
|
|
|
|
DEFAULT_CARD_IMAGE,
|
|
|
|
|
} from "../types";
|
|
|
|
|
|
|
|
|
|
interface PopCardListPreviewProps {
|
|
|
|
|
config?: PopCardListConfig;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function PopCardListPreviewComponent({
|
|
|
|
|
config,
|
|
|
|
|
}: PopCardListPreviewProps) {
|
2026-02-24 15:54:57 +09:00
|
|
|
const scrollDirection = config?.scrollDirection || "vertical";
|
2026-02-12 11:07:58 +09:00
|
|
|
const cardSize = config?.cardSize || "medium";
|
|
|
|
|
const dataSource = config?.dataSource;
|
|
|
|
|
const template = config?.cardTemplate;
|
|
|
|
|
|
|
|
|
|
const hasTable = !!dataSource?.tableName;
|
|
|
|
|
const hasHeader =
|
|
|
|
|
!!template?.header?.codeField || !!template?.header?.titleField;
|
|
|
|
|
const hasImage = template?.image?.enabled ?? true;
|
|
|
|
|
const fieldCount = template?.body?.fields?.length || 0;
|
|
|
|
|
|
2026-02-24 15:54:57 +09:00
|
|
|
const sampleCardCount = 2;
|
2026-02-12 11:07:58 +09:00
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<div className="flex h-full w-full flex-col bg-muted/30 p-3">
|
|
|
|
|
{/* 헤더 */}
|
|
|
|
|
<div className="mb-2 flex items-center justify-between">
|
|
|
|
|
<div className="flex items-center gap-2 text-muted-foreground">
|
|
|
|
|
<LayoutGrid className="h-4 w-4" />
|
|
|
|
|
<span className="text-xs font-medium">카드 목록</span>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
{/* 설정 배지 */}
|
|
|
|
|
<div className="flex gap-1">
|
|
|
|
|
<span className="rounded bg-primary/10 px-1.5 py-0.5 text-[9px] text-primary">
|
2026-02-24 15:54:57 +09:00
|
|
|
{CARD_SCROLL_DIRECTION_LABELS[scrollDirection]}
|
2026-02-12 11:07:58 +09:00
|
|
|
</span>
|
|
|
|
|
<span className="rounded bg-secondary px-1.5 py-0.5 text-[9px] text-secondary-foreground">
|
|
|
|
|
{CARD_SIZE_LABELS[cardSize]}
|
|
|
|
|
</span>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
{/* 테이블 미선택 시 안내 */}
|
|
|
|
|
{!hasTable ? (
|
|
|
|
|
<div className="flex flex-1 items-center justify-center">
|
|
|
|
|
<div className="text-center">
|
|
|
|
|
<Package className="mx-auto mb-2 h-8 w-8 text-muted-foreground/50" />
|
|
|
|
|
<p className="text-xs text-muted-foreground">
|
|
|
|
|
데이터 소스를 설정하세요
|
|
|
|
|
</p>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
) : (
|
|
|
|
|
<>
|
|
|
|
|
{/* 테이블 정보 */}
|
|
|
|
|
<div className="mb-2 text-center">
|
|
|
|
|
<span className="rounded bg-muted px-2 py-0.5 text-[10px] text-muted-foreground">
|
|
|
|
|
{dataSource.tableName}
|
|
|
|
|
</span>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
{/* 카드 미리보기 */}
|
|
|
|
|
<div
|
|
|
|
|
className={`flex-1 flex gap-2 ${
|
2026-02-24 15:54:57 +09:00
|
|
|
scrollDirection === "vertical"
|
2026-02-12 11:07:58 +09:00
|
|
|
? "flex-col"
|
2026-02-24 15:54:57 +09:00
|
|
|
: "flex-row overflow-x-auto"
|
2026-02-12 11:07:58 +09:00
|
|
|
}`}
|
|
|
|
|
>
|
|
|
|
|
{Array.from({ length: sampleCardCount }).map((_, idx) => (
|
|
|
|
|
<PreviewCard
|
|
|
|
|
key={idx}
|
|
|
|
|
index={idx}
|
|
|
|
|
hasHeader={hasHeader}
|
|
|
|
|
hasImage={hasImage}
|
|
|
|
|
fieldCount={fieldCount}
|
|
|
|
|
cardSize={cardSize}
|
2026-02-24 15:54:57 +09:00
|
|
|
scrollDirection={scrollDirection}
|
2026-02-12 11:07:58 +09:00
|
|
|
/>
|
|
|
|
|
))}
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
{/* 필드 정보 */}
|
|
|
|
|
{fieldCount > 0 && (
|
|
|
|
|
<div className="mt-2 text-center">
|
|
|
|
|
<span className="text-[10px] text-muted-foreground">
|
|
|
|
|
{fieldCount}개 필드 설정됨
|
|
|
|
|
</span>
|
|
|
|
|
</div>
|
|
|
|
|
)}
|
|
|
|
|
</>
|
|
|
|
|
)}
|
|
|
|
|
</div>
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ===== 미리보기 카드 컴포넌트 =====
|
|
|
|
|
|
|
|
|
|
function PreviewCard({
|
|
|
|
|
index,
|
|
|
|
|
hasHeader,
|
|
|
|
|
hasImage,
|
|
|
|
|
fieldCount,
|
|
|
|
|
cardSize,
|
2026-02-24 15:54:57 +09:00
|
|
|
scrollDirection,
|
2026-02-12 11:07:58 +09:00
|
|
|
}: {
|
|
|
|
|
index: number;
|
|
|
|
|
hasHeader: boolean;
|
|
|
|
|
hasImage: boolean;
|
|
|
|
|
fieldCount: number;
|
|
|
|
|
cardSize: string;
|
2026-02-24 15:54:57 +09:00
|
|
|
scrollDirection: string;
|
2026-02-12 11:07:58 +09:00
|
|
|
}) {
|
|
|
|
|
const sizeClass =
|
|
|
|
|
cardSize === "small"
|
|
|
|
|
? "min-h-[60px]"
|
|
|
|
|
: cardSize === "large"
|
|
|
|
|
? "min-h-[100px]"
|
|
|
|
|
: "min-h-[80px]";
|
|
|
|
|
|
|
|
|
|
const widthClass =
|
2026-02-24 15:54:57 +09:00
|
|
|
scrollDirection === "vertical"
|
2026-02-12 11:07:58 +09:00
|
|
|
? "w-full"
|
2026-02-24 15:54:57 +09:00
|
|
|
: "min-w-[140px] flex-shrink-0";
|
2026-02-12 11:07:58 +09:00
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<div
|
|
|
|
|
className={`rounded-md border bg-card overflow-hidden ${sizeClass} ${widthClass}`}
|
|
|
|
|
>
|
|
|
|
|
{/* 헤더 */}
|
|
|
|
|
{hasHeader && (
|
|
|
|
|
<div className="border-b bg-muted/30 px-2 py-1">
|
|
|
|
|
<div className="flex items-center gap-1">
|
|
|
|
|
<span className="h-2 w-8 rounded bg-muted-foreground/20" />
|
|
|
|
|
<span className="h-2 w-12 rounded bg-muted-foreground/30" />
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
)}
|
|
|
|
|
|
|
|
|
|
{/* 본문 */}
|
|
|
|
|
<div className="flex p-2 gap-2">
|
|
|
|
|
{/* 이미지 */}
|
|
|
|
|
{hasImage && (
|
|
|
|
|
<div className="flex-shrink-0">
|
|
|
|
|
<div className="h-10 w-10 rounded border bg-muted/30 flex items-center justify-center">
|
|
|
|
|
<img
|
|
|
|
|
src={DEFAULT_CARD_IMAGE}
|
|
|
|
|
alt=""
|
|
|
|
|
className="h-6 w-6 opacity-50"
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
)}
|
|
|
|
|
|
|
|
|
|
{/* 필드 목록 */}
|
|
|
|
|
<div className="flex-1 space-y-1">
|
|
|
|
|
{fieldCount > 0 ? (
|
|
|
|
|
Array.from({ length: Math.min(fieldCount, 3) }).map((_, i) => (
|
|
|
|
|
<div key={i} className="flex items-center gap-1">
|
|
|
|
|
<span className="h-1.5 w-6 rounded bg-muted-foreground/20" />
|
|
|
|
|
<span className="h-1.5 w-10 rounded bg-muted-foreground/30" />
|
|
|
|
|
</div>
|
|
|
|
|
))
|
|
|
|
|
) : (
|
|
|
|
|
<div className="flex h-full items-center justify-center">
|
|
|
|
|
<span className="text-[8px] text-muted-foreground">
|
|
|
|
|
필드 추가
|
|
|
|
|
</span>
|
|
|
|
|
</div>
|
|
|
|
|
)}
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
);
|
|
|
|
|
}
|