import { useEffect, useState } from 'react'; import { ApiError, deletePost, getPost, savePost } from '../../../../lib/api'; import { confirmDialog, notify } from '../../../../lib/confirm'; import { clientSlugify } from './codemirror'; interface Opts { editSlug?: string; getContent: () => string; setContent: (s: string) => void; } /** Post metadata form + slug derivation + load/save/delete. */ export function usePostMeta({ editSlug, getContent, setContent }: Opts) { const today = new Date().toISOString().slice(0, 10); const [title, setTitle] = useState(''); const [slug, setSlug] = useState(editSlug || ''); const [slugTouched, setSlugTouched] = useState(!!editSlug); const [date, setDate] = useState(today); const [summary, setSummary] = useState(''); const [tagsInput, setTagsInput] = useState(''); const [draft, setDraft] = useState(false); const [originalSlug, setOriginalSlug] = useState(editSlug || ''); // Load existing post for editing. useEffect(() => { if (!editSlug) return; getPost(editSlug) .then(post => { if (post.title) setTitle(post.title); if (post.summary) setSummary(post.summary); if (post.date) setDate(post.date); if (post.tags?.length) setTagsInput(post.tags.join(', ')); setDraft(!!post.draft); if (post.content) setContent(post.content); }) .catch(() => notify('Failed to load post.', 'error')); }, [editSlug, setContent]); // Auto-derive slug from title until the user edits the slug field. useEffect(() => { if (slugTouched) return; setSlug(clientSlugify(title)); }, [title, slugTouched]); async function handleSave() { const content = getContent(); if (!title.trim() || !slug || !content) { notify('Title, slug, and body are required.', 'error'); return; } if (!/!\[[^\]]*\]\([^)]+\)/.test(content)) { notify( 'Add at least one image before saving — drag, paste, or use the Add image button.', 'error', ); return; } const tags = tagsInput .split(',') .map(t => t.trim()) .filter(Boolean); try { const saved = await savePost({ slug, old_slug: originalSlug || null, title: title.trim(), date, summary: summary || null, tags, draft, content, }); notify('Post saved!', 'success'); if (saved?.slug && saved.slug !== slug) { setSlug(saved.slug); setSlugTouched(true); } setOriginalSlug(saved?.slug ?? slug); } catch (e) { notify( e instanceof ApiError ? `Error: ${e.message}` : 'Failed to connect to server.', 'error', ); } } async function handleDelete() { const target = originalSlug || slug; const ok = await confirmDialog({ title: 'Remove from catalogue?', message: `“${target}” will be permanently removed. This cannot be undone.`, confirmLabel: 'Remove', }); if (!ok) return; try { await deletePost(target); window.location.href = '/admin'; } catch { notify('Error deleting post.', 'error'); } } return { title, setTitle, slug, setSlug, setSlugTouched, date, setDate, summary, setSummary, tagsInput, setTagsInput, draft, setDraft, originalSlug, handleSave, handleDelete, }; }