import React from 'react'; import { X, Type, Image as ImageIcon, Plus, Trash2, Zap, Clock, Layers, Sparkles, Globe, Instagram, AtSign } from 'lucide-react'; import { ExpressScene, ExpressField, SceneLayout, BrandContentPiece, DesignMD, TimelineElement } from '../../../types'; import { useEditor } from '../../../context/EditorContext'; import { CollapsibleSection } from '../../ui/CollapsibleSection'; interface BuilderScenePanelProps { onClose: () => void; scene: ExpressScene; onUpdateScene: (updated: ExpressScene) => void; brandContent: BrandContentPiece[]; designMD: DesignMD; isVideo: boolean; } /** Layout options */ const LAYOUTS: { value: SceneLayout; label: string; icon: string }[] = [ { value: 'fullscreen-media', label: 'Pantalla completa', icon: '📸' }, { value: 'overlay', label: 'Overlay', icon: '🔲' }, { value: 'split', label: 'Dividido', icon: '◫' }, { value: 'media-left', label: 'Media izq.', icon: '◧' }, { value: 'media-right', label: 'Media der.', icon: '◨' }, { value: 'text-only', label: 'Solo texto', icon: '📝' }, ]; /** Brand variables available for insertion */ const BRAND_VARIABLES: { source: ExpressField['brandSource']; label: string; icon: React.ReactNode; type: ExpressField['type'] }[] = [ { source: 'brand-name', label: 'Nombre de Marca', icon: , type: 'text' }, { source: 'tagline', label: 'Tagline / Eslogan', icon: , type: 'text' }, { source: 'logo', label: 'Logo', icon: , type: 'logo' }, { source: 'instagram', label: 'Instagram', icon: , type: 'text' }, { source: 'tiktok', label: 'TikTok', icon: , type: 'text' }, { source: 'twitter', label: 'X / Twitter', icon: , type: 'text' }, { source: 'youtube', label: 'YouTube', icon: , type: 'text' }, { source: 'website', label: 'Website', icon: , type: 'text' }, ]; /** * BuilderScenePanel — Sliding panel for scene-specific configuration. * * This is the template-builder counterpart of TextPanel/ShapesPanel. * It manages scene metadata (name, type, duration, background, layout) * and lets users add brand variables and brand assets as TimelineElements. */ export const BuilderScenePanel: React.FC = ({ onClose, scene, onUpdateScene, brandContent, designMD, isVideo, }) => { const { setTimelineElements, setSelectedElementId, layers, activeLayerId, durationInFrames, } = useEditor(); // ── Add a brand-variable element to the canvas via EditorContext ── const addBrandField = ( type: ExpressField['type'], label: string, brandSource?: ExpressField['brandSource'], ) => { const newId = 'el-' + Date.now(); const elType: TimelineElement['type'] = type === 'text' ? 'text' : 'image'; // Determine target layer let targetLayerId = activeLayerId; const activeLayer = layers.find(l => l.id === activeLayerId); if (!activeLayer || activeLayer.type === 'brand' || activeLayer.type === 'audio') { const visual = layers.find(l => l.type === 'visual' || l.type == null); if (visual) targetLayerId = visual.id; } const newElement: TimelineElement = { id: newId, layerId: targetLayerId, type: elType, content: brandSource ? `{${brandSource}}` : label, startFrame: 0, endFrame: durationInFrames, x: 50, y: 50, width: type === 'text' ? 80 : 40, height: type === 'text' ? 10 : 20, fontSize: type === 'text' ? 24 : undefined, fontWeight: type === 'text' ? 400 : undefined, elementName: label, notes: JSON.stringify({ __expressField: true, brandSource, required: false, fieldType: type, }), }; setTimelineElements(prev => [...prev, newElement]); setSelectedElementId(newId); }; // ── Add a brand content asset ── const addBrandAsset = (asset: BrandContentPiece) => { const newId = 'el-asset-' + Date.now(); const elType: TimelineElement['type'] = asset.type === 'custom-image' ? 'image' : 'text'; let targetLayerId = activeLayerId; const activeLayer = layers.find(l => l.id === activeLayerId); if (!activeLayer || activeLayer.type === 'brand' || activeLayer.type === 'audio') { const visual = layers.find(l => l.type === 'visual' || l.type == null); if (visual) targetLayerId = visual.id; } const newElement: TimelineElement = { id: newId, layerId: targetLayerId, type: elType, content: asset.content.text || asset.name, startFrame: 0, endFrame: durationInFrames, x: 50, y: 50, width: 40, height: 20, fontSize: asset.style.fontSize || 20, fontWeight: 600, elementName: asset.name, notes: JSON.stringify({ __expressField: true, brandAssetId: asset.id, required: false, fieldType: asset.type === 'custom-image' ? 'media' : 'text', }), }; setTimelineElements(prev => [...prev, newElement]); setSelectedElementId(newId); }; return (

Escena

{/* Scene name */}
onUpdateScene({ ...scene, name: e.target.value })} className="w-full bg-neutral-800 border border-neutral-700 rounded-lg px-3 py-1.5 text-sm text-white focus:border-violet-500/50 focus:outline-none" />
{/* Type + Duration (video only) */} {isVideo && (
onUpdateScene({ ...scene, durationSeconds: Math.max(1, parseInt(e.target.value) || 1) })} className="w-full bg-neutral-800 border border-neutral-700 rounded-lg px-2 py-1.5 text-xs text-white text-center focus:border-violet-500/50 focus:outline-none" /> s
)} {/* Quick-add field buttons */}

{/* Brand Variables */}
{BRAND_VARIABLES.map(v => ( ))}

{/* ── Diseño y Fondo (collapsible) ── */} {/* Layout */}
{LAYOUTS.map(l => ( ))}
{/* Background */}
{(['brand', 'solid', 'gradient', 'media'] as const).map(bg => ( ))}
{/* Brand Content Assets */} {brandContent.length > 0 && (
{brandContent.map(asset => ( ))}
)}
); };