'use client'; import { useCallback, useEffect, useState } from 'react'; import { Badge } from '@/components/ui'; import { Markdown } from '@/components/content/Markdown'; import { t } from '@/lib/i18n'; import { formatDate, slugify } from '@/lib/utils'; type Doc = { id: string; slug: string; title: string; section: string; order: number; body: string; status: string; updatedAt: string; }; type FormState = { id: string | null; title: string; slug: string; slugEdited: boolean; section: string; order: number; body: string; status: 'draft' | 'published'; }; const EMPTY = (section: string): FormState => ({ id: null, title: '', slug: '', slugEdited: false, section, order: 1, body: '', status: 'published', }); export function DocsManager({ sectionOptions }: { sectionOptions: string[] }) { const [items, setItems] = useState([]); const [status, setStatus] = useState(''); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); const [form, setForm] = useState(null); const [showPreview, setShowPreview] = useState(false); const [formError, setFormError] = useState(null); const [busy, setBusy] = useState(false); const load = useCallback(async () => { setLoading(true); setError(null); try { const res = await fetch(`/api/admin/docs${status ? `?status=${status}` : ''}`); if (!res.ok) throw new Error('request failed'); const data = (await res.json()) as { docs: Doc[] }; setItems(data.docs); } catch { setError(t('common.error')); } finally { setLoading(false); } }, [status]); useEffect(() => { void load(); }, [load]); function openCreate() { setFormError(null); setShowPreview(false); setForm(EMPTY(sectionOptions[0] ?? 'Getting started')); } function openEdit(doc: Doc) { setFormError(null); setShowPreview(false); setForm({ id: doc.id, title: doc.title, slug: doc.slug, slugEdited: true, section: doc.section, order: doc.order, body: doc.body, status: doc.status === 'draft' ? 'draft' : 'published', }); } function setField(key: K, value: FormState[K]) { setForm((prev) => (prev ? { ...prev, [key]: value } : prev)); } async function save() { if (!form) return; setBusy(true); setFormError(null); const res = await fetch(form.id ? `/api/admin/docs/${form.id}` : '/api/admin/docs', { method: form.id ? 'PATCH' : 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ title: form.title, slug: form.slug, section: form.section, order: Number(form.order) || 1, body: form.body, status: form.status, }), }); setBusy(false); if (!res.ok) { setFormError(((await res.json().catch(() => ({}))) as { error?: string }).error || t('common.error')); return; } setForm(null); await load(); } async function toggleStatus(doc: Doc) { setBusy(true); await fetch(`/api/admin/docs/${doc.id}`, { method: 'PATCH', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ title: doc.title, slug: doc.slug, section: doc.section, order: doc.order, body: doc.body, status: doc.status === 'published' ? 'draft' : 'published', }), }); setBusy(false); await load(); } async function remove(doc: Doc) { if (!window.confirm(`${t('admin.delete')} “${doc.title}”? This cannot be undone.`)) return; setBusy(true); await fetch(`/api/admin/docs/${doc.id}`, { method: 'DELETE' }); setBusy(false); await load(); } const suggestedSlug = form ? (form.slugEdited ? form.slug : slugify(form.title)) : ''; return (

{t('admin.docs')}

{items.length} pages

{form && (

{form.id ? t('admin.editDoc') : t('admin.newDoc')}

{showPreview ? (

{form.title || t('admin.untitled')}

{t('admin.previewHint')}

) : (