'use client'; import { useEffect, useRef, useState } from 'react'; import { Responsive, noCompactor } from 'react-grid-layout'; import type { ResponsiveLayouts } from 'react-grid-layout'; import 'react-grid-layout/css/styles.css'; import 'react-resizable/css/styles.css'; import { useTranslations } from 'next-intl'; import { WIDGET_CONSTRAINTS, type WidgetType } from './widget-registry'; import { WidgetWrapper } from './widgets/widget-wrapper'; import { TesseraLogo } from '@/components/brand/tessera-logo'; // quick-260916-bwo: Raster doppelt so fein wie zuvor (24 statt 12 Spalten, // 20 statt 40 px Zeilenhoehe, 8 statt 16 px Abstand). Gespeicherte Anordnungen // in alten Einheiten werden in `@/lib/grid-layout-migration` einmalig // umgerechnet und mit `__gridVersion: 2` markiert. BREAKPOINTS unveraendert. const BREAKPOINTS = { lg: 1200, md: 996, sm: 768, xs: 480, xxs: 0 }; const COLS = { lg: 24, md: 20, sm: 12, xs: 8, xxs: 2 }; interface DashboardGridProps { layouts: Record>; widgets: Array<{ id: string; widgetType: string; config: Record }>; isEditMode: boolean; onLayoutChange: (allLayouts: ResponsiveLayouts) => void; onRemoveWidget: (id: string) => void; } /** * Dashboard grid wrapper using react-grid-layout v2 Responsive component. * Renders widgets in a draggable/resizable grid when in edit mode. * Shows empty state when no widgets exist. * * v2 API: uses dragConfig/resizeConfig instead of isDraggable/isResizable. */ export function DashboardGrid({ layouts, widgets, isEditMode, onLayoutChange, onRemoveWidget, }: DashboardGridProps) { const t = useTranslations('widgets'); const containerRef = useRef(null); const [width, setWidth] = useState(1200); // Measure container width (v2 requires explicit width — Pitfall 1) useEffect(() => { if (!containerRef.current) return; const observer = new ResizeObserver((entries) => { setWidth(entries[0].contentRect.width); }); observer.observe(containerRef.current); return () => observer.disconnect(); }, []); // Empty state (D-02) if (widgets.length === 0) { return (
{/* Bildmarke statt des frueheren handgezeichneten Vier-Quadrate-Symbols. Ohne Rahmen und ohne Kontur: die Marke steht hier frei auf dem Seitenhintergrund, nicht auf einer eigenen Flaeche. */}

{t('emptyHeading')}

{t('emptyBody')}

); } return (
onLayoutChange(allLayouts)} > {widgets.map((widget) => { const constraints = WIDGET_CONSTRAINTS[widget.widgetType as WidgetType]; return (
l.i === widget.id) ?? { x: 0, y: 0, w: constraints?.defaultW ?? 4, h: constraints?.defaultH ?? 4, }), minW: constraints?.minW ?? 4, minH: constraints?.minH ?? 4, }} >
); })}
); }