"use client"; import { useState } from "react"; import { useDrag } from "react-dnd"; import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from "@/components/ui/select"; import { Collapsible, CollapsibleContent, CollapsibleTrigger, } from "@/components/ui/collapsible"; import { Plus, Settings, LayoutGrid, Type, MousePointer, List, Activity, ScanLine, Calculator, Trash2, ChevronDown, GripVertical, } from "lucide-react"; import { cn } from "@/lib/utils"; import { PopLayoutDataV2, PopLayoutModeKey, PopSectionDefinition, PopComponentType, MODE_RESOLUTIONS, } from "../types/pop-layout"; // ======================================== // 드래그 아이템 타입 // ======================================== export const DND_ITEM_TYPES = { SECTION: "section", COMPONENT: "component", } as const; export interface DragItemSection { type: typeof DND_ITEM_TYPES.SECTION; } export interface DragItemComponent { type: typeof DND_ITEM_TYPES.COMPONENT; componentType: PopComponentType; } // ======================================== // 컴포넌트 팔레트 정의 // ======================================== const COMPONENT_PALETTE: { type: PopComponentType; label: string; icon: React.ElementType; description: string; }[] = [ { type: "pop-field", label: "필드", icon: Type, description: "텍스트, 숫자 등 데이터 입력", }, { type: "pop-button", label: "버튼", icon: MousePointer, description: "저장, 삭제 등 액션 실행", }, { type: "pop-list", label: "리스트", icon: List, description: "데이터 목록 표시", }, { type: "pop-indicator", label: "인디케이터", icon: Activity, description: "KPI, 상태 표시", }, { type: "pop-scanner", label: "스캐너", icon: ScanLine, description: "바코드/QR 스캔", }, { type: "pop-numpad", label: "숫자패드", icon: Calculator, description: "숫자 입력 전용", }, ]; // ======================================== // Props // ======================================== interface PopPanelProps { layout: PopLayoutDataV2; activeModeKey: PopLayoutModeKey; selectedSectionId: string | null; selectedSection: PopSectionDefinition | null; onUpdateSectionDefinition: (id: string, updates: Partial) => void; onDeleteSection: (id: string) => void; activeDevice: "mobile" | "tablet"; } // ======================================== // 메인 컴포넌트 // ======================================== export function PopPanel({ layout, activeModeKey, selectedSectionId, selectedSection, onUpdateSectionDefinition, onDeleteSection, activeDevice, }: PopPanelProps) { const [activeTab, setActiveTab] = useState("components"); // 현재 모드의 섹션 위치 const currentModeLayout = layout.layouts[activeModeKey]; const selectedSectionPosition = selectedSectionId ? currentModeLayout.sectionPositions[selectedSectionId] : null; return (
컴포넌트 편집 {/* 컴포넌트 탭 */}
{/* 현재 모드 표시 */}

편집 중: {getModeLabel(activeModeKey)}

{MODE_RESOLUTIONS[activeModeKey].width} x {MODE_RESOLUTIONS[activeModeKey].height}

{/* 섹션 드래그 아이템 */}

레이아웃

캔버스에 드래그하여 섹션 추가

{/* 컴포넌트 팔레트 */}

컴포넌트

{COMPONENT_PALETTE.map((item) => ( ))}

섹션 안으로 드래그하여 배치

{/* 편집 탭 */} {selectedSection && selectedSectionPosition ? ( onUpdateSectionDefinition(selectedSection.id, updates) } onDelete={() => onDeleteSection(selectedSection.id)} /> ) : (
섹션을 선택하세요
)}
); } // ======================================== // 모드 라벨 헬퍼 // ======================================== function getModeLabel(modeKey: PopLayoutModeKey): string { const labels: Record = { tablet_landscape: "태블릿 가로", tablet_portrait: "태블릿 세로", mobile_landscape: "모바일 가로", mobile_portrait: "모바일 세로", }; return labels[modeKey]; } // ======================================== // 드래그 가능한 섹션 아이템 // ======================================== function DraggableSectionItem() { const [{ isDragging }, drag] = useDrag(() => ({ type: DND_ITEM_TYPES.SECTION, item: { type: DND_ITEM_TYPES.SECTION } as DragItemSection, collect: (monitor) => ({ isDragging: monitor.isDragging(), }), })); return (

섹션

컴포넌트를 그룹화하는 컨테이너

); } // ======================================== // 드래그 가능한 컴포넌트 아이템 // ======================================== interface DraggableComponentItemProps { type: PopComponentType; label: string; icon: React.ElementType; description: string; } function DraggableComponentItem({ type, label, icon: Icon, description, }: DraggableComponentItemProps) { const [{ isDragging }, drag] = useDrag(() => ({ type: DND_ITEM_TYPES.COMPONENT, item: { type: DND_ITEM_TYPES.COMPONENT, componentType: type } as DragItemComponent, collect: (monitor) => ({ isDragging: monitor.isDragging(), }), })); return (

{label}

{description}

); } // ======================================== // v2 섹션 편집기 // ======================================== interface SectionEditorV2Props { section: PopSectionDefinition; position: { col: number; row: number; colSpan: number; rowSpan: number }; activeModeKey: PopLayoutModeKey; onUpdateDefinition: (updates: Partial) => void; onDelete: () => void; } function SectionEditorV2({ section, position, activeModeKey, onUpdateDefinition, onDelete, }: SectionEditorV2Props) { const [isGridOpen, setIsGridOpen] = useState(true); return (
{/* 섹션 기본 정보 */}
섹션 설정
{/* 라벨 */}
onUpdateDefinition({ label: e.target.value })} placeholder="섹션 이름" className="h-8 text-xs" />

라벨은 4개 모드에서 공유됩니다

{/* 현재 모드 위치 (읽기 전용 - 드래그로 조정) */} 현재 모드 위치

{getModeLabel(activeModeKey)}

시작 열: {position.col}
시작 행: {position.row}
열 크기: {position.colSpan}
행 크기: {position.rowSpan}

위치/크기는 캔버스에서 드래그하여 조정하세요. 각 모드(가로/세로)별로 별도 저장됩니다.

{/* 내부 그리드 설정 */}

내부 그리드 (공유)

내부 그리드 설정은 4개 모드에서 공유됩니다

{/* 컴포넌트 목록 */}

포함된 컴포넌트 ({section.componentIds.length}개)

{section.componentIds.length > 0 ? (
{section.componentIds.map((compId) => (
{compId}
))}
) : (

아직 컴포넌트가 없습니다

)}
); }