'use client'; import { useCallback, useState } from 'react'; import { useTranslations } from 'next-intl'; import Link from 'next/link'; import { WIDGET_REGISTRY, type WidgetType } from '@/components/dashboard/widget-registry'; import { SearchProviderForm } from '@/components/settings/search-provider-form'; import { updateWidgetConfig } from '@/lib/dashboard-api'; /** * IANA timezones offered for clock configuration (D-12). * Covers the minimum set specified in the plan plus common ones. */ const TIMEZONE_OPTIONS = [ 'UTC', 'Europe/Berlin', 'Europe/London', 'Europe/Paris', 'Europe/Zurich', 'America/New_York', 'America/Chicago', 'America/Los_Angeles', 'Asia/Tokyo', 'Asia/Shanghai', 'Australia/Sydney', ]; interface WidgetSettingsPanelProps { widgets: Array<{ id: string; widgetType: string; config: Record; }>; onWidgetUpdate: (id: string, config: Record) => void; } /** * Widget Settings Panel (D-03, D-12, D-13, D-15, D-17). * Lists all placed widget instances grouped by type. * Each instance is expandable to its type-specific config form. */ export function WidgetSettingsPanel({ widgets, onWidgetUpdate, }: WidgetSettingsPanelProps) { const t = useTranslations('widgets'); const [expandedId, setExpandedId] = useState(null); const [saving, setSaving] = useState(null); const handleConfigChange = useCallback( async (id: string, partialConfig: Record) => { setSaving(id); try { await updateWidgetConfig(id, partialConfig); onWidgetUpdate(id, partialConfig); } catch { // Silent fail — config will revert on next load } finally { setSaving(null); } }, [onWidgetUpdate], ); // Group widgets by type for display const groupedByType = widgets.reduce( (acc, w) => { const type = w.widgetType as WidgetType; if (!acc[type]) acc[type] = []; acc[type].push(w); return acc; }, {} as Record, ); return (
{Object.entries(groupedByType).map(([type, instances]) => { const def = WIDGET_REGISTRY[type as WidgetType]; if (!def) return null; return (
{/* Type group heading */}

{t(def.nameKey)}

{instances.map((widget, idx) => { const isExpanded = expandedId === widget.id; const isSaving = saving === widget.id; return (
{/* Instance header (click to expand) */} {/* Expanded config form */} {isExpanded && (
{isSaving && (

Saving...

)} {/* Clock config (D-12, D-13) */} {widget.widgetType === 'clock' && ( handleConfigChange(widget.id, cfg) } /> )} {/* Search config (D-15) */} {widget.widgetType === 'search' && ( )} {/* Note config (D-17) */} {widget.widgetType === 'note' && ( handleConfigChange(widget.id, cfg) } /> )} {/* Calendar config — managed elsewhere */} {widget.widgetType === 'calendar' && (

Calendar sources are managed under{' '} Settings > Dashboard > Calendar .

)}
)}
); })}
); })}
); } // --- Type-specific config forms --- function ClockConfig({ config, onChange, }: { config: Record; onChange: (cfg: Record) => void; }) { const t = useTranslations('widgets'); const timezone = (config.timezone as string) || 'Europe/Berlin'; const showDate = config.showDate === true; return (
{/* Timezone select (D-12) */}
{/* Show date toggle (D-13) */}
onChange({ showDate: e.target.checked })} />
); } function NoteConfig({ config, onChange, }: { config: Record; onChange: (cfg: Record) => void; }) { const t = useTranslations('widgets'); const title = (config.title as string) || t('note.defaultTitle'); return (
onChange({ title: e.target.value })} />
); }