'use client' // IdeaMdEditor — bewerk grill_md of plan_md. // // - kind='grill': geen yaml-validatie (vrije markdown). // - kind='plan' : preflight via parsePlanMd (server-side action herhaalt // validation, dit is alleen UX om eerder te falen). // // Save → updateGrillMdAction / updatePlanMdAction. Cmd/Ctrl+S triggert save. // LocalStorage-backed draft per idea+kind, restore bij heropening. import { useEffect, useMemo, useState, useTransition } from 'react' import { useRouter } from 'next/navigation' import { Save, X } from 'lucide-react' import { toast } from 'sonner' import { Button } from '@/components/ui/button' import { Textarea } from '@/components/ui/textarea' import { debugProps } from '@/lib/debug' import { parsePlanMd, type PlanParseError } from '@/lib/idea-plan-parser' import { updateGrillMdAction, updatePlanMdAction } from '@/actions/ideas' type Kind = 'grill' | 'plan' interface Props { ideaId: string kind: Kind initialValue: string onCancel: () => void } // Lazily compute the seed: read draft from localStorage on first render, fall // back to initialValue. Avoids setState-in-useEffect for hydration. function readSeed(draftKey: string, initialValue: string): { value: string restored: boolean } { if (typeof window === 'undefined') return { value: initialValue, restored: false } const draft = window.localStorage.getItem(draftKey) if (draft && draft !== initialValue) return { value: draft, restored: true } return { value: initialValue, restored: false } } export function IdeaMdEditor({ ideaId, kind, initialValue, onCancel }: Props) { const router = useRouter() const draftKey = `idea-md-draft-${ideaId}-${kind}` const [seed] = useState(() => readSeed(draftKey, initialValue)) const [value, setValue] = useState(seed.value) const [submitErrors, setSubmitErrors] = useState([]) const [submitting, startSubmit] = useTransition() // Eenmalige toast voor restore — de seed is al toegepast bij mount. useEffect(() => { if (seed.restored) { toast.info('Niet-opgeslagen wijziging hersteld uit lokale draft.') } }, [seed.restored]) // Auto-save naar localStorage on change. useEffect(() => { if (typeof window === 'undefined') return if (value === initialValue) { window.localStorage.removeItem(draftKey) } else { window.localStorage.setItem(draftKey, value) } }, [value, initialValue, draftKey]) // Live yaml-validatie als afgeleide state — geen useEffect nodig. const validationErrors = useMemo(() => { if (kind !== 'plan') return [] if (value === '' || value === initialValue) return [] const r = parsePlanMd(value) return r.ok ? [] : r.errors }, [value, initialValue, kind]) // Combine: validation errors voor live feedback, submitErrors voor server-side details. const errors = submitErrors.length > 0 ? submitErrors : validationErrors function save() { if (errors.length > 0 && kind === 'plan') { toast.error('Frontmatter heeft fouten — fix die eerst.') return } setSubmitErrors([]) startSubmit(async () => { const r = kind === 'grill' ? await updateGrillMdAction(ideaId, value) : await updatePlanMdAction(ideaId, value) if ('error' in r) { toast.error(r.error) if ('details' in r && Array.isArray(r.details)) { setSubmitErrors(r.details as PlanParseError[]) } return } toast.success('Opgeslagen') window.localStorage.removeItem(draftKey) router.refresh() onCancel() }) } // Cmd/Ctrl+S → save function onKeyDown(e: React.KeyboardEvent) { if ((e.metaKey || e.ctrlKey) && e.key.toLowerCase() === 's') { e.preventDefault() save() } } const dirty = value !== initialValue return (
{errors.length > 0 && (

{kind === 'plan' ? 'YAML-frontmatter fouten' : 'Validatiefouten'}

    {errors.map((err, i) => (
  • {err.line ? `Regel ${err.line}: ` : ''} {err.message} {err.hint && (
    Tip: {err.hint}
    )}
  • ))}
)}