'use client'; import { useCallback, useEffect, useRef, useState } from 'react'; import { useTranslations } from 'next-intl'; import MDEditor, { commands } from '@uiw/react-md-editor'; import rehypeSanitize from 'rehype-sanitize'; import type { WidgetProps } from '../widget-registry'; import { updateWidgetConfig } from '@/lib/dashboard-api'; const DEBOUNCE_MS = 1500; const NOTE_COMMANDS = [ commands.bold, commands.italic, commands.strikethrough, commands.divider, commands.unorderedListCommand, commands.checkedListCommand, commands.divider, commands.link, commands.code, ]; export function NoteWidget({ instanceId, config }: WidgetProps) { const t = useTranslations('widgets'); const [title, setTitle] = useState( (config.title as string) || t('note.defaultTitle'), ); const [content, setContent] = useState( (config.content as string) || '', ); const [isEditing, setIsEditing] = useState(false); const [saveError, setSaveError] = useState(false); const timerRef = useRef | undefined>(undefined); const abortRef = useRef(undefined); useEffect(() => { return () => { clearTimeout(timerRef.current); abortRef.current?.abort(); }; }, []); const save = useCallback( async (newContent: string, newTitle: string) => { abortRef.current?.abort(); abortRef.current = new AbortController(); try { await updateWidgetConfig(instanceId, { content: newContent, title: newTitle }, abortRef.current.signal); setSaveError(false); } catch (err: unknown) { if (err instanceof DOMException && err.name === 'AbortError') return; setSaveError(true); } }, [instanceId], ); const scheduleSave = useCallback( (newContent: string, newTitle: string) => { clearTimeout(timerRef.current); timerRef.current = setTimeout(() => save(newContent, newTitle), DEBOUNCE_MS); }, [save], ); const handleContentChange = useCallback( (val?: string) => { const newContent = val ?? ''; setContent(newContent); scheduleSave(newContent, title); }, [scheduleSave, title], ); const handleTitleChange = useCallback( (e: React.ChangeEvent) => { const newTitle = e.target.value; setTitle(newTitle); scheduleSave(content, newTitle); }, [scheduleSave, content], ); return (
{/* Header: title + pencil toggle */}
{saveError && ( )}
{/* Content: preview or editor */}
); }