import { useEffect, useLayoutEffect, useRef, useState } from 'react' import { api } from '../api.js' function newItem() { const id = typeof crypto !== 'undefined' && crypto.randomUUID ? crypto.randomUUID() : `s-${Date.now()}-${Math.random()}` return { id, text: '', done: false } } function rowsFrom(items) { if (items?.length) { return items.map((item) => ({ id: item.id || newItem().id, text: item.text || '', done: !!item.done })) } return [newItem()] } export default function DayScratch({ dayId, token, items, onError }) { const [local, setLocal] = useState(() => rowsFrom(items)) const timer = useRef(null) const seq = useRef(0) const pendingFocus = useRef(null) const inputRefs = useRef({}) useEffect(() => { window.clearTimeout(timer.current) seq.current += 1 setLocal(rowsFrom(items)) }, [dayId]) useLayoutEffect(() => { const id = pendingFocus.current if (!id) return const el = inputRefs.current[id] if (!el) return el.focus() pendingFocus.current = null }, [local]) const persist = (next, focusId) => { if (focusId) pendingFocus.current = focusId const mine = ++seq.current setLocal(next) window.clearTimeout(timer.current) timer.current = window.setTimeout(async () => { try { await api(`/api/journal/days/${dayId}/scratch`, { token, method: 'PATCH', body: { items: next.filter((item) => item.text.trim() || item.done) } }) } catch (err) { if (mine === seq.current) onError?.(err.message) } }, 400) } useEffect(() => () => window.clearTimeout(timer.current), []) const patch = (id, fields) => persist(local.map((row) => (row.id === id ? { ...row, ...fields } : row))) return ( ) }