'use client'; import { useEffect, useRef } from 'react'; import { useTranslations } from 'next-intl'; import { WIDGET_REGISTRY, type WidgetType, visibleWidgetTypes } from './widget-registry'; interface WidgetCatalogModalProps { isOpen: boolean; onClose: () => void; onAddWidget: (type: WidgetType) => void; /** * Slugs der Module, die der angemeldete Benutzer nutzen darf — geholt von * der Dashboard-Seite ueber `GET /modules/active` (quick-260922-m1h). * `null` heisst "noch unbekannt oder Abruf fehlgeschlagen": dann bleiben * Kacheln MIT `moduleSlug` ausgeblendet (fail-closed). * * Der Abruf steht bewusst NICHT in diesem Dialog, damit er ein reines * Anzeige-Bauteil bleibt und ohne Netzwerk-Attrappe testbar ist. */ accessibleModuleSlugs: readonly string[] | null; } /** * Modal dialog showing available widget types as selectable cards. * * quick-260922-m1h: Die Liste kommt aus WIDGET_REGISTRY (Reihenfolge der * Registry-Definition) statt aus einer zweiten, hier gepflegten Liste — eine * neue Kachel musste sonst an zwei Stellen eingetragen werden. * Click on a card adds the widget to the dashboard and closes the modal. * Escape to close, click outside to close, focus trap (D-01 flow). */ export function WidgetCatalogModal({ isOpen, onClose, onAddWidget, accessibleModuleSlugs, }: WidgetCatalogModalProps) { const t = useTranslations('widgets'); const tCommon = useTranslations('common'); const dialogRef = useRef(null); // Focus trap + Escape handler useEffect(() => { if (!isOpen) return; function handleKeyDown(e: KeyboardEvent) { if (e.key === 'Escape') { onClose(); } } document.addEventListener('keydown', handleKeyDown); // Focus the dialog on open dialogRef.current?.focus(); return () => document.removeEventListener('keydown', handleKeyDown); }, [isOpen, onClose]); if (!isOpen) return null; return (
{/* Hintergrund — jetzt eine echte, benannte Schaltflaeche statt einer rein optischen Flaeche: ein fokussierbares Element darf vor der Vorlesehilfe nicht verborgen sein (D-01). */}
{/* 2x2 grid of widget type cards */}
{visibleWidgetTypes(WIDGET_REGISTRY, accessibleModuleSlugs).map((type) => { const def = WIDGET_REGISTRY[type]; const Icon = def.icon; return ( ); })}
); }