const { useState, useEffect, useCallback, useRef } = React; const api = { get: (p) => fetch(p).then((r) => r.json()), post: (p, body) => fetch(p, { method: "POST", headers: { "Content-Type": "application/json" }, body: body ? JSON.stringify(body) : null, }).then((r) => r.json()), patch: (p, body) => fetch(p, { method: "PATCH", headers: { "Content-Type": "application/json" }, body: JSON.stringify(body), }).then((r) => r.json()), delete: (p) => fetch(p, { method: "DELETE" }).then((r) => r.json()), }; function fmtDate(iso) { if (!iso) return ""; const d = new Date(iso); return d.toLocaleDateString("en-GB", { day: "numeric", month: "short" }); } function titleCase(s) { return s ? s[0].toUpperCase() + s.slice(1).replace(/_/g, " ") : ""; } const TABS = [ { id: "ideas", label: "Ideas" }, { id: "edit", label: "Edit" }, { id: "review", label: "Review" }, { id: "carousels", label: "Carousels" }, { id: "journey", label: "Journey" }, { id: "longvideo", label: "Long video"}, { id: "calendar", label: "Calendar" }, ]; function subtitle(tab) { switch (tab) { case "ideas": return "Type a topic and angle — AI writes the script and hooks"; case "edit": return "Drop raw footage to edit, or run the pipeline on a filmed idea"; case "review": return "Edited videos waiting for your approval"; case "carousels": return "Topic in → research, slides, caption, render, schedule"; case "journey": return "Reflective image+text posts about your business journey — X, Threads, LinkedIn"; case "longvideo": return "Weekly long-form for YouTube + auto-announcement on X / Threads / LinkedIn"; case "calendar": return "Everything in the queue, by day. Filter by channel or format."; default: return ""; } } // ───────────────────────────────────────────────────────────────────────────── // App shell // ───────────────────────────────────────────────────────────────────────────── function App() { const [tab, setTab] = useState(localStorage.getItem("ce-tab") || "ideas"); const [reviewCount, setReviewCount] = useState(0); useEffect(() => { localStorage.setItem("ce-tab", tab); }, [tab]); const refreshCounts = useCallback(async () => { try { const data = await api.get("/api/review"); setReviewCount(Array.isArray(data) ? data.length : 0); } catch {} }, []); useEffect(() => { refreshCounts(); const t = setInterval(refreshCounts, 20000); return () => clearInterval(t); }, [refreshCounts]); return (

{TABS.find((t) => t.id === tab)?.label}

{subtitle(tab)}
{tab === "ideas" && } {tab === "edit" && } {tab === "review" && } {tab === "carousels" && } {tab === "journey" && } {tab === "longvideo" && } {tab === "calendar" && }
); } // ───────────────────────────────────────────────────────────────────────────── // Ideas tab // ───────────────────────────────────────────────────────────────────────────── function Ideas({ onNavigate }) { const [ideas, setIdeas] = useState([]); const [topic, setTopic] = useState(""); const [description, setDescription] = useState(""); const [generating, setGenerating] = useState(false); const [expanded, setExpanded] = useState(null); const [teleprompter, setTeleprompter] = useState(null); const load = useCallback(async () => { try { const data = await api.get("/api/ideas"); setIdeas(Array.isArray(data) ? data : []); } catch {} }, []); useEffect(() => { load(); const t = setInterval(load, 8000); return () => clearInterval(t); }, [load]); const generate = async () => { if (!topic.trim() || !description.trim()) return; setGenerating(true); try { const idea = await api.post("/api/ideas", { topic: topic.trim(), description: description.trim() }); if (idea?.id) { setTopic(""); setDescription(""); setIdeas((prev) => [idea, ...prev]); setExpanded(idea.id); } } catch (e) { alert("Generation failed — check the server logs."); } finally { setGenerating(false); } }; const deleteIdea = async (id) => { if (!confirm("Delete this idea?")) return; await api.delete(`/api/ideas/${id}`); setIdeas((prev) => prev.filter((i) => i.id !== id)); if (expanded === id) setExpanded(null); }; return ( <> {/* Generation form */}
setTopic(e.target.value)} onKeyDown={(e) => e.key === "Enter" && !generating && description.trim() && generate()} disabled={generating} />