'use client'; import { useState, useEffect, useCallback } from 'react'; import { ChevronDown, ChevronRight } from 'lucide-react'; import type { DashboardSection as DashboardSectionType, WidgetData } from '@/lib/pipeline-types'; import { getWidgetData } from '@/lib/pipeline-api'; import { renderWidget } from './WidgetRegistry'; interface DashboardSectionProps { section: DashboardSectionType; pipelineId: string; businessId?: string; timeRange?: string; } /** * Renders a dashboard section with its widgets. */ export function DashboardSection({ section, pipelineId, businessId, timeRange = '30d', }: DashboardSectionProps) { const [collapsed, setCollapsed] = useState(section.collapsed ?? false); const [widgetData, setWidgetData] = useState>({}); const [widgetLoading, setWidgetLoading] = useState>({}); const [widgetErrors, setWidgetErrors] = useState>({}); const [tablePagination, setTablePagination] = useState>({}); // Fetch data for a single widget const fetchWidgetData = useCallback( async (widgetId: string, page?: number) => { setWidgetLoading((prev) => ({ ...prev, [widgetId]: true })); setWidgetErrors((prev) => ({ ...prev, [widgetId]: undefined })); try { const data = await getWidgetData(pipelineId, widgetId, { business_id: businessId, time_range: timeRange, page: page || tablePagination[widgetId] || 1, }); setWidgetData((prev) => ({ ...prev, [widgetId]: data })); } catch (error) { setWidgetErrors((prev) => ({ ...prev, [widgetId]: error instanceof Error ? error.message : 'Failed to load', })); } finally { setWidgetLoading((prev) => ({ ...prev, [widgetId]: false })); } }, [pipelineId, businessId, timeRange, tablePagination] ); // Fetch all widget data on mount and when params change useEffect(() => { if (!collapsed) { section.widgets.forEach((widget) => { fetchWidgetData(widget.id); }); } }, [section.widgets, collapsed, pipelineId, businessId, timeRange]); // Handle page change for tables const handlePageChange = (widgetId: string, page: number) => { setTablePagination((prev) => ({ ...prev, [widgetId]: page })); fetchWidgetData(widgetId, page); }; // Calculate grid layout // Using a 12-column grid const getGridClass = (widget: typeof section.widgets[0]) => { const { grid } = widget; // Map grid units to Tailwind classes const colSpanClasses: Record = { 1: 'col-span-1', 2: 'col-span-2', 3: 'col-span-3', 4: 'col-span-4', 5: 'col-span-5', 6: 'col-span-6', 7: 'col-span-7', 8: 'col-span-8', 9: 'col-span-9', 10: 'col-span-10', 11: 'col-span-11', 12: 'col-span-12', }; const rowSpanClasses: Record = { 1: 'row-span-1', 2: 'row-span-2', 3: 'row-span-3', 4: 'row-span-4', }; return `${colSpanClasses[grid.w] || 'col-span-4'} ${rowSpanClasses[grid.h] || 'row-span-1'}`; }; return (
{/* Section Header */} {/* Widgets Grid */} {!collapsed && (
{section.widgets.map((widget) => (
{renderWidget( widget, widgetData[widget.id] || null, widgetLoading[widget.id] ?? true, widgetErrors[widget.id], () => fetchWidgetData(widget.id), widget.type === 'table' ? (page) => handlePageChange(widget.id, page) : undefined, tablePagination[widget.id] || 1 )}
))}
)}
); }