import React, { useState } from 'react'; import { cn } from '@/lib/utils'; import { Button } from '@/components/ui/button'; import { Plus, Minus, Grid3X3, Trash2, Settings, ArrowUp, ArrowDown } from 'lucide-react'; import { LayoutContainer as LayoutContainerType, WidgetInstance } from '@/lib/unifiedLayoutManager'; import { widgetRegistry } from '@/lib/widgetRegistry'; import { WidgetSettingsManager } from '@/components/widgets/WidgetSettingsManager'; import { WidgetMovementControls } from '@/components/widgets/WidgetMovementControls'; import { useLayout } from '@/contexts/LayoutContext'; import CollapsibleSection from '@/components/CollapsibleSection'; import { ContainerSettingsManager } from '@/components/containers/ContainerSettingsManager'; interface LayoutContainerProps { container: LayoutContainerType; isEditMode: boolean; pageId: string; selectedContainerId?: string | null; onSelect?: (containerId: string) => void; onAddWidget?: (containerId: string, targetColumn?: number) => void; onRemoveWidget?: (widgetInstanceId: string) => void; onMoveWidget?: (widgetInstanceId: string, direction: 'up' | 'down' | 'left' | 'right') => void; onUpdateColumns?: (containerId: string, columns: number) => void; onUpdateSettings?: (containerId: string, settings: Partial) => void; onAddContainer?: (parentContainerId: string) => void; onRemoveContainer?: (containerId: string) => void; onMoveContainer?: (containerId: string, direction: 'up' | 'down') => void; canMoveContainerUp?: boolean; canMoveContainerDown?: boolean; selectedWidgetId?: string | null; onSelectWidget?: (widgetId: string) => void; depth?: number; isCompactMode?: boolean; editingWidgetId?: string | null; onEditWidget?: (widgetId: string | null) => void; newlyAddedWidgetId?: string | null; } const LayoutContainerComponent: React.FC = ({ container, isEditMode, pageId, selectedContainerId, onSelect, onAddWidget, onRemoveWidget, onMoveWidget, onUpdateColumns, onUpdateSettings, onAddContainer, onRemoveContainer, onMoveContainer, canMoveContainerUp, canMoveContainerDown, selectedWidgetId, onSelectWidget, depth = 0, isCompactMode = false, editingWidgetId, onEditWidget, newlyAddedWidgetId, }) => { const maxDepth = 3; // Limit nesting depth const canNest = depth < maxDepth; const isSelected = selectedContainerId === container.id; const [showContainerSettings, setShowContainerSettings] = useState(false); // Generate responsive grid classes based on container.columns const getGridClasses = (columns: number) => { const baseClass = "grid gap-4"; // Always grid with gap // Mobile: always 1 column, Desktop: respect container.columns switch (columns) { case 1: return `${baseClass} grid-cols-1`; case 2: return `${baseClass} grid-cols-1 md:grid-cols-2`; case 3: return `${baseClass} grid-cols-1 md:grid-cols-2 lg:grid-cols-3`; case 4: return `${baseClass} grid-cols-1 md:grid-cols-2 lg:grid-cols-4`; case 5: return `${baseClass} grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-5`; case 6: return `${baseClass} grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-6`; default: // For 7+ columns, use a more conservative approach return `${baseClass} grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 2xl:grid-cols-${Math.min(columns, 12)}`; } }; const gridClasses = getGridClasses(container.columns); // Extract container content rendering logic const renderContainerContent = () => ( <> {/* Grid Column Indicators (only in edit mode when selected) */} {isEditMode && isSelected && container.widgets.length === 0 && container.children.length === 0 && ( <> {Array.from({ length: container.columns }, (_, i) => (
{ e.stopPropagation(); onAddWidget?.(container.id, i); }} title={`Double-click to add widget to column ${i + 1}`} > Col {i + 1}
))} )} {/* Render Widgets */} {container.widgets .sort((a, b) => (a.order || 0) - (b.order || 0)) .map((widget, index) => ( onSelectWidget?.(widget.id)} canMoveUp={index > 0} canMoveDown={index < container.widgets.length - 1} onRemove={onRemoveWidget} onMove={onMoveWidget} isEditing={editingWidgetId === widget.id} onEditWidget={onEditWidget} /> ))} {/* Add Widget Buttons - one per column for non-empty containers (in edit mode) */} {isEditMode && container.widgets.length > 0 && container.children.length === 0 && ( <> {Array.from({ length: container.columns }, (_, colIndex) => (
{ e.stopPropagation(); onAddWidget?.(container.id, colIndex); }} onDoubleClick={(e) => { e.stopPropagation(); onAddWidget?.(container.id, colIndex); }} title={`Click to add widget to column ${colIndex + 1}`} >

Add Widget

{container.columns > 1 &&

Col {colIndex + 1}

}
))} )} {/* Render Nested Containers */} {container.children .sort((a, b) => (a.order || 0) - (b.order || 0)) .map((childContainer) => (
))} {/* Empty State - only show when not showing column indicators */} {container.widgets.length === 0 && container.children.length === 0 && !(isEditMode && isSelected) && (
{ e.stopPropagation(); onSelect?.(container.id); setTimeout(() => onAddWidget?.(container.id), 100); // Small delay to ensure selection happens first, no column = append } : undefined} title={isEditMode ? "Double-click to add widget" : undefined} > {isEditMode ? (

Double-click to add widgets

) : (

)}
)} ); return (
{/* Edit Mode Controls */} {isEditMode && (
{/* Responsive layout: title and buttons wrap on small screens */}
{container.settings?.showTitle && container.settings?.title ? container.settings.title : `Container (${container.columns} col${container.columns !== 1 ? 's' : ''})`} {(container.settings?.collapsible || container.settings?.showTitle) && ( ⚙️ )}
{/* Minimalist button row - wraps and justifies to the end */}
{/* Move controls for root containers */} {depth === 0 && (
)} {/* Column controls */} {container.columns} {/* Add widget button */} {/* Add nested container button */} {canNest && ( )} {/* Container settings button */} {/* Remove container button */}
)} {/* Container Content */}
{ e.stopPropagation(); if (isEditMode) { onSelect?.(container.id); } }} > {container.settings?.collapsible ? (
{renderContainerContent()}
) : (
{/* Title for non-collapsible containers */} {container.settings?.showTitle && (

{container.settings?.title || `Container (${container.columns} col${container.columns !== 1 ? 's' : ''})`}

)}
{renderContainerContent()}
)}
{/* Container Settings Dialog */} {showContainerSettings && ( setShowContainerSettings(false)} onSave={(settings) => { onUpdateSettings?.(container.id, settings); setShowContainerSettings(false); }} currentSettings={container.settings} containerInfo={{ id: container.id, columns: container.columns, }} /> )}
); }; interface WidgetItemProps { widget: WidgetInstance; isEditMode: boolean; pageId: string; canMoveUp: boolean; canMoveDown: boolean; onRemove?: (widgetInstanceId: string) => void; onMove?: (widgetInstanceId: string, direction: 'up' | 'down' | 'left' | 'right') => void; isSelected?: boolean; onSelect?: () => void; isEditing?: boolean; onEditWidget?: (widgetId: string | null) => void; isNew?: boolean; } const WidgetItem: React.FC = ({ widget, isEditMode, pageId, canMoveUp, canMoveDown, onRemove, onMove, isSelected, onSelect, isEditing, onEditWidget, isNew }) => { const widgetDefinition = widgetRegistry.get(widget.widgetId); const { updateWidgetProps, renameWidget } = useLayout(); // Internal state removed in favor of controlled state // const [showSettingsModal, setShowSettingsModal] = useState(false); // pageId is now passed as a prop from the parent component if (!widgetDefinition) { return (

Widget "{widget.widgetId}" not found in registry

{isEditMode && ( )}
); } const WidgetComponent = widgetDefinition.component; const handleSettingsSave = async (settings: Record) => { try { await updateWidgetProps(pageId, widget.id, settings); } catch (error) { console.error('Failed to save widget settings:', error); } }; const handleSettingsCancel = () => { if (isNew) { // If it's a new widget and the user cancels settings, remove it onRemove?.(widget.id); } onEditWidget?.(null); // Close the settings modal }; // Handle Enabled State const isEnabled = widget.props?.enabled !== false; // Default to true if (!isEnabled && !isEditMode) { return null; } return (
{/* Edit Mode Controls */} {isEditMode && ( <> {/* Widget Info Overlay */}
{ e.stopPropagation(); onSelect?.(); }} >
{widgetDefinition.metadata.name}
{/* Settings Gear Icon - For widgets with configSchema */} {widgetDefinition.metadata.configSchema && ( )} {/* Remove Button */}
{/* Move Controls - Cross Pattern (Only show on hover or selection) */}
onMove?.(widget.id, direction)} canMoveUp={canMoveUp} canMoveDown={canMoveDown} />
)} {/* Widget Content - With selection wrapper */}
{ if (isEditMode) { e.preventDefault(); // Prevent focus stealing if clicking background e.stopPropagation(); onSelect?.(); } }} > ) => { try { await updateWidgetProps(pageId, widget.id, newProps); } catch (error) { console.error('Failed to update widget props:', error); } }} />
{/* Generic Settings Modal */} { widgetDefinition.metadata.configSchema && isEditing && ( onEditWidget?.(null)} widgetDefinition={widgetDefinition} currentProps={widget.props || {}} onSave={handleSettingsSave} /> ) }
); }; // Export without memoization to ensure reliable updates (Selection highlighting fix) export const LayoutContainer = LayoutContainerComponent;