"use client"; import React from "react"; import { LayoutRendererProps } from "../BaseLayoutRenderer"; /** * 카드 레이아웃 컴포넌트 * 3x2 격자로 구성된 카드 대시보드 레이아웃 */ interface CardLayoutProps extends LayoutRendererProps { tableData?: any[]; // 테이블 데이터 tableColumns?: any[]; // 테이블 컬럼 정보 (라벨 포함) } export const CardLayoutLayout: React.FC = ({ layout, children, onUpdateLayout, onSelectComponent, isDesignMode = false, className = "", onClick, allComponents, tableData = [], tableColumns = [], }) => { // 카드 설정 가져오기 (기본값 보장) const cardConfig = { cardsPerRow: layout.layoutConfig?.card?.cardsPerRow ?? 3, cardSpacing: layout.layoutConfig?.card?.cardSpacing ?? 16, columnMapping: layout.layoutConfig?.card?.columnMapping || {}, cardStyle: { showTitle: layout.layoutConfig?.card?.cardStyle?.showTitle ?? true, showSubtitle: layout.layoutConfig?.card?.cardStyle?.showSubtitle ?? true, showDescription: layout.layoutConfig?.card?.cardStyle?.showDescription ?? true, showImage: layout.layoutConfig?.card?.cardStyle?.showImage ?? false, maxDescriptionLength: layout.layoutConfig?.card?.cardStyle?.maxDescriptionLength ?? 100, }, }; // 실제 테이블 데이터 사용 (없으면 샘플 데이터) const displayData = tableData.length > 0 ? tableData : [ { id: 1, name: "김철수", email: "kim@example.com", phone: "010-1234-5678", department: "개발팀", position: "시니어 개발자", description: "풀스택 개발자로 React, Node.js 전문가입니다. 5년 이상의 경험을 보유하고 있습니다.", avatar: "/images/avatar1.jpg", }, { id: 2, name: "이영희", email: "lee@example.com", phone: "010-2345-6789", department: "디자인팀", position: "UI/UX 디자이너", description: "사용자 경험을 중시하는 디자이너로 Figma, Adobe XD를 능숙하게 다룹니다.", avatar: "/images/avatar2.jpg", }, { id: 3, name: "박민수", email: "park@example.com", phone: "010-3456-7890", department: "기획팀", position: "프로덕트 매니저", description: "데이터 기반 의사결정을 통해 제품을 성장시키는 PM입니다.", avatar: "/images/avatar3.jpg", }, ]; // 컨테이너 스타일 const containerStyle: React.CSSProperties = { display: "grid", gridTemplateColumns: `repeat(${cardConfig.cardsPerRow || 3}, 1fr)`, gap: `${cardConfig.cardSpacing || 16}px`, padding: "16px", width: "100%", height: "100%", background: "transparent", overflow: "auto", }; // 카드 스타일 const cardStyle: React.CSSProperties = { backgroundColor: "white", border: "1px solid #e5e7eb", borderRadius: "8px", padding: "16px", boxShadow: "0 1px 3px 0 rgba(0, 0, 0, 0.1)", transition: "all 0.2s ease-in-out", overflow: "hidden", display: "flex", flexDirection: "column", position: "relative", minHeight: "200px", cursor: isDesignMode ? "pointer" : "default", }; // 텍스트 자르기 함수 const truncateText = (text: string, maxLength: number) => { if (text.length <= maxLength) return text; return text.substring(0, maxLength) + "..."; }; // 컬럼 매핑에서 값 가져오기 const getColumnValue = (data: any, columnName?: string) => { if (!columnName) return ""; return data[columnName] || ""; }; // 컬럼명을 라벨로 변환하는 헬퍼 함수 const getColumnLabel = (columnName: string) => { if (!tableColumns || tableColumns.length === 0) return columnName; const column = tableColumns.find((col) => col.columnName === columnName); return column?.columnLabel || columnName; }; // 자동 폴백 로직 - 컬럼이 설정되지 않은 경우 적절한 기본값 찾기 const getAutoFallbackValue = (data: any, type: "title" | "subtitle" | "description") => { const keys = Object.keys(data); switch (type) { case "title": // 이름 관련 필드 우선 검색 return data.name || data.title || data.label || data[keys[0]] || "제목 없음"; case "subtitle": // 직책, 부서, 카테고리 관련 필드 검색 return data.position || data.role || data.department || data.category || data.type || ""; case "description": // 설명, 내용 관련 필드 검색 return data.description || data.content || data.summary || data.memo || ""; default: return ""; } }; return ( <>
{isDesignMode ? // 디자인 모드: 존 기반 렌더링 layout.zones?.map((zone, index) => { const zoneChildren = children?.filter((child) => child.props.parentId === zone.id) || []; return (
onSelectComponent?.(zone.id)} className="hover:border-blue-500 hover:shadow-md" > {/* 존 라벨 */}
{zone.name}
{/* 존 내용 */}
{zoneChildren.length > 0 ? (
{zoneChildren}
) : (
카드 존
컴포넌트를 드래그하여 추가하세요
)}
); }) : // 실행 모드: 데이터 기반 카드 렌더링 displayData.map((item, index) => (
{/* 카드 이미지 */} {cardConfig.cardStyle?.showImage && cardConfig.columnMapping?.imageColumn && (
👤
)} {/* 카드 타이틀 */} {cardConfig.cardStyle?.showTitle && (

{getColumnValue(item, cardConfig.columnMapping?.titleColumn) || getAutoFallbackValue(item, "title")}

)} {/* 카드 서브타이틀 */} {cardConfig.cardStyle?.showSubtitle && (

{getColumnValue(item, cardConfig.columnMapping?.subtitleColumn) || getAutoFallbackValue(item, "subtitle")}

)} {/* 카드 설명 */} {cardConfig.cardStyle?.showDescription && (

{truncateText( getColumnValue(item, cardConfig.columnMapping?.descriptionColumn) || getAutoFallbackValue(item, "description"), cardConfig.cardStyle?.maxDescriptionLength || 100, )}

)} {/* 추가 표시 컬럼들 */} {cardConfig.columnMapping?.displayColumns && cardConfig.columnMapping.displayColumns.length > 0 && (
{cardConfig.columnMapping.displayColumns.map((columnName, idx) => { const value = getColumnValue(item, columnName); if (!value) return null; return (
{getColumnLabel(columnName)}: {value}
); })}
)} {/* 카드 액션 (선택사항) */}
))} {/* 빈 상태 표시 */} {!isDesignMode && displayData.length === 0 && (
📋
표시할 데이터가 없습니다
)}
); };