'use client'; import { useState } from 'react'; import { useRouter } from 'next/navigation'; import { JsonForm, type JsonValue } from './JsonForm'; type Status = 'idle' | 'saving' | 'saved' | 'error'; type Tab = 'meta' | 'fa' | 'en'; /** * Edits a single blog article body. Top-level `date` / `accent` live in the * "Meta" tab; the long-form FA and EN articles each get their own tab so the * Persian body renders RTL. Saving stores the whole PostContent under the * article's slug via /api/admin/posts. */ export function PostEditor({ slug, title, initial, isOverridden, }: { slug: string; title: string; initial: { date: JsonValue; accent: JsonValue; fa: JsonValue; en: JsonValue }; isOverridden: boolean; }) { const router = useRouter(); const [data, setData] = useState(initial); const [tab, setTab] = useState('meta'); const [status, setStatus] = useState('idle'); const [overridden, setOverridden] = useState(isOverridden); async function save() { setStatus('saving'); try { const payload = { date: data.date, accent: data.accent, en: data.en, fa: data.fa, }; const res = await fetch('/api/admin/posts', { method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify({ slug, data: payload }), }); if (!res.ok) throw new Error(String(res.status)); setStatus('saved'); setOverridden(true); router.refresh(); setTimeout(() => setStatus('idle'), 2500); } catch { setStatus('error'); } } async function reset() { if (!confirm('Revert this article to its built-in default? Your edits will be removed.')) return; setStatus('saving'); try { const res = await fetch(`/api/admin/posts?slug=${encodeURIComponent(slug)}`, { method: 'DELETE', }); if (!res.ok) throw new Error(String(res.status)); router.refresh(); window.location.reload(); } catch { setStatus('error'); } } return (
{/* Toolbar */}
setTab('meta')}>Meta setTab('fa')}>FA · فارسی setTab('en')}>EN · English
{status === 'saved' && Saved ✓} {status === 'error' && Save failed} {overridden && ( )}
{tab === 'meta' && (

Editing {slug}. Accent must be one of: electric, violet, magenta, emerald, cyan. The card title/excerpt live under the Journal section.

{ const o = nv as { date: JsonValue; accent: JsonValue }; setData((d) => ({ ...d, date: o.date, accent: o.accent })); }} />
)} {tab === 'fa' && (
setData((d) => ({ ...d, fa: nv }))} />
)} {tab === 'en' && (
setData((d) => ({ ...d, en: nv }))} />
)}
); } function TabBtn({ active, onClick, children, }: { active: boolean; onClick: () => void; children: React.ReactNode; }) { return ( ); }