'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'; import { CLOCK_FONT_SIZE_MAX_PT, CLOCK_FONT_SIZE_MIN_PT, resolveClockTimeFontSizePt, } from '@/components/dashboard/widgets/clock-font-size'; /** * 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; // quick-260916-bwo: feste Schriftgroesse der Uhrzeit in Punkt (leer = automatisch). // Entwurf als Text im Feld, Uebernahme bei Blur oder Enter; Grenzen aus // clock-font-size.ts — dieselben, die das Widget beim Rendern anwendet (T-BWO-01). const [fontSizeDraft, setFontSizeDraft] = useState(() => { const pt = resolveClockTimeFontSizePt(config); return pt === null ? '' : String(pt); }); const [fontSizeError, setFontSizeError] = useState(false); const commitFontSize = () => { const raw = fontSizeDraft.trim(); if (raw === '') { setFontSizeError(false); if (resolveClockTimeFontSizePt(config) !== null) { onChange({ timeFontSizePt: null }); } return; } const n = Number(raw); if (!Number.isFinite(n) || n < CLOCK_FONT_SIZE_MIN_PT || n > CLOCK_FONT_SIZE_MAX_PT) { setFontSizeError(true); return; } setFontSizeError(false); if (n !== resolveClockTimeFontSizePt(config)) { onChange({ timeFontSizePt: n }); } }; return (
{/* Timezone select (D-12) */}
{/* Show date toggle (D-13) */}
onChange({ showDate: e.target.checked })} />
{/* Schriftgroesse der Uhrzeit in Punkt (quick-260916-bwo) */}
setFontSizeDraft(e.target.value)} onBlur={commitFontSize} onKeyDown={(e) => { if (e.key === 'Enter') { e.preventDefault(); commitFontSize(); } }} aria-invalid={fontSizeError || undefined} aria-describedby="clock-font-size-hint" className="h-9 w-full max-w-xs rounded border border-border bg-background px-3 text-sm text-foreground" />

{t('clock.fontSizeHint')}

{fontSizeError && (

{t('clock.fontSizeInvalid')}

)}
); } 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 })} />
); }