diff --git a/client/pages/Admin.tsx b/client/pages/Admin.tsx index 8cbe0272..d8acbc91 100644 --- a/client/pages/Admin.tsx +++ b/client/pages/Admin.tsx @@ -113,6 +113,48 @@ export default function Admin() { ); } + const [blogPosts, setBlogPosts] = useState([]); + const [loadingPosts, setLoadingPosts] = useState(false); + + useEffect(() => { + (async () => { + try { + setLoadingPosts(true); + const res = await fetch("/api/blog?limit=100"); + const data = res.ok ? await res.json() : []; + if (Array.isArray(data)) setBlogPosts(data); + } catch (e) { + console.warn("Failed to load blog posts:", e); + } finally { + setLoadingPosts(false); + } + })(); + }, []); + + const savePost = async (idx: number) => { + const p = blogPosts[idx]; + const payload = { ...p, slug: (p.slug || p.title || "").toLowerCase().trim().replace(/[^a-z0-9\s-]/g, "").replace(/\s+/g, "-") }; + const res = await fetch("/api/blog", { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify(payload), + }); + if (!res.ok) return aethexToast.error({ title: "Save failed", description: await res.text().catch(() => "") }); + const saved = await res.json(); + const next = blogPosts.slice(); + next[idx] = saved; + setBlogPosts(next); + aethexToast.success({ title: "Saved", description: saved.title }); + }; + + const deletePost = async (idx: number) => { + const p = blogPosts[idx]; + const res = await fetch(`/api/blog/${encodeURIComponent(p.slug)}`, { method: "DELETE" }); + if (!res.ok) return aethexToast.error({ title: "Delete failed", description: await res.text().catch(() => "") }); + setBlogPosts(blogPosts.filter((_, i) => i !== idx)); + aethexToast.info({ title: "Deleted", description: p.title }); + }; + return (
@@ -191,6 +233,72 @@ export default function Admin() { + {/* Blog Posts Management */} + + +
+ + Blog Posts +
+ Manage blog content stored in Supabase +
+ +
+ + +
+ + {blogPosts.map((p, i) => ( +
+
+ { + const next = blogPosts.slice(); next[i] = { ...next[i], title: e.target.value }; setBlogPosts(next); + }} /> + { + const next = blogPosts.slice(); next[i] = { ...next[i], slug: e.target.value }; setBlogPosts(next); + }} /> +
+
+ { const n = blogPosts.slice(); n[i] = { ...n[i], author: e.target.value }; setBlogPosts(n); }} /> + { const n = blogPosts.slice(); n[i] = { ...n[i], date: e.target.value }; setBlogPosts(n); }} /> +
+
+ { const n = blogPosts.slice(); n[i] = { ...n[i], read_time: e.target.value }; setBlogPosts(n); }} /> + { const n = blogPosts.slice(); n[i] = { ...n[i], category: e.target.value }; setBlogPosts(n); }} /> + { const n = blogPosts.slice(); n[i] = { ...n[i], image: e.target.value }; setBlogPosts(n); }} /> +
+