import Layout from "@/components/Layout"; import SEO from "@/components/SEO"; import LoadingScreen from "@/components/LoadingScreen"; import { useAuth } from "@/contexts/AuthContext"; import { useCallback, useEffect, useMemo, useState } from "react"; import { useNavigate } from "react-router-dom"; import { aethexToast } from "@/lib/aethex-toast"; import { aethexUserService, type AethexUserProfile, } from "@/lib/aethex-database-adapter"; import { Card, CardContent, CardHeader, CardTitle, CardDescription, } from "@/components/ui/card"; import { Badge } from "@/components/ui/badge"; import { Button } from "@/components/ui/button"; import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; import AdminStatCard from "@/components/admin/AdminStatCard"; import AdminMemberManager from "@/components/admin/AdminMemberManager"; import AdminAchievementManager from "@/components/admin/AdminAchievementManager"; import AdminSpotlightManager from "@/components/admin/AdminSpotlightManager"; import AdminStatusOverview from "@/components/admin/AdminStatusOverview"; import AdminChangelogDigest from "@/components/admin/AdminChangelogDigest"; import AdminSystemMap from "@/components/admin/AdminSystemMap"; import AdminMentorshipManager from "@/components/admin/AdminMentorshipManager"; import AdminRoadmap from "@/components/admin/AdminRoadmap"; import BannerSettings from "@/components/admin/BannerSettings"; import { changelogEntries } from "@/pages/Changelog"; import { blogSeedPosts } from "@/data/blogSeed"; import { Shield, Users, Rocket, PenTool, Command, Activity, UserCog, Settings, ExternalLink, ClipboardList, Loader2, RefreshCw, CheckCircle, AlertTriangle, XCircle, Server, Database, Wifi, Zap, Heart, } from "lucide-react"; export default function Admin() { const { user, loading, roles } = useAuth(); const navigate = useNavigate(); const ownerEmail = "mrpiglr@gmail.com"; const normalizedEmail = user?.email?.toLowerCase() ?? ""; const isOwner = (Array.isArray(roles) && roles.includes("owner")) || normalizedEmail === ownerEmail; const [managedProfiles, setManagedProfiles] = useState( [], ); type Studio = { name: string; tagline?: string; metrics?: string; specialties?: string[]; }; type ProjectApplication = { id: string; status?: string | null; applicant_email?: string | null; applicant_name?: string | null; created_at?: string | null; notes?: string | null; projects?: { id?: string | null; title?: string | null; user_id?: string | null; } | null; }; const [studios, setStudios] = useState([ { name: "Lone Star Studio", tagline: "Indie craftsmanship with AAA polish", metrics: "Top-rated indie hits", specialties: ["Unity", "Unreal", "Pixel Art"], }, { name: "AeThex | GameForge", tagline: "High-performance cross-platform experiences", metrics: "Billions of player sessions", specialties: ["Roblox", "Backend", "LiveOps"], }, { name: "Gaming Control", tagline: "Strategy, simulation, and systems-first design", metrics: "Award-winning franchises", specialties: ["Simulation", "AI/ML", "Economy"], }, ]); const [projectApplications, setProjectApplications] = useState< ProjectApplication[] >([]); const [projectApplicationsLoading, setProjectApplicationsLoading] = useState(false); type OpportunityApplication = { id: string; type?: string | null; full_name?: string | null; email?: string | null; status?: string | null; availability?: string | null; role_interest?: string | null; primary_skill?: string | null; experience_level?: string | null; submitted_at?: string | null; message?: string | null; }; const [opportunityApplications, setOpportunityApplications] = useState< OpportunityApplication[] >([]); const [opportunityApplicationsLoading, setOpportunityApplicationsLoading] = useState(false); const [selectedMemberId, setSelectedMemberId] = useState(null); const loadProfiles = useCallback(async () => { try { const list = await aethexUserService.listProfiles(200); setManagedProfiles(list); } catch (error) { console.warn("Failed to load managed profiles:", error); setManagedProfiles([]); } }, []); useEffect(() => { loadProfiles().catch(() => undefined); }, [loadProfiles]); useEffect(() => { fetch("/api/featured-studios") .then((r) => (r.ok ? r.json() : [])) .then((data) => { if (Array.isArray(data) && data.length) setStudios(data); }) .catch(() => void 0); }, []); const loadProjectApplications = useCallback(async () => { if (!user?.id) return; setProjectApplicationsLoading(true); try { const response = await fetch( `/api/applications?owner=${encodeURIComponent(user.id)}`, ); if (response.ok) { const data = await response.json(); setProjectApplications(Array.isArray(data) ? data : []); } else { setProjectApplications([]); } } catch (error) { console.warn("Failed to load project applications:", error); setProjectApplications([]); } finally { setProjectApplicationsLoading(false); } }, [user?.id]); useEffect(() => { loadProjectApplications().catch(() => undefined); }, [loadProjectApplications]); const loadOpportunityApplications = useCallback(async () => { const email = user?.email?.toLowerCase(); if (!email) return; setOpportunityApplicationsLoading(true); try { const response = await fetch( `/api/opportunities/applications?email=${encodeURIComponent(email)}`, ); if (response.ok) { const data = await response.json(); setOpportunityApplications(Array.isArray(data) ? data : []); } else { const message = await response.text().catch(() => ""); if (response.status === 403) { aethexToast.error({ title: "Access denied", description: "You must be signed in as the owner to view opportunity applications.", }); } else { console.warn("Opportunity applications request failed:", message); } setOpportunityApplications([]); } } catch (error) { console.warn("Failed to load opportunity applications:", error); setOpportunityApplications([]); } finally { setOpportunityApplicationsLoading(false); } }, [user?.email]); useEffect(() => { loadOpportunityApplications().catch(() => undefined); }, [loadOpportunityApplications]); useEffect(() => { // Do not redirect unauthenticated users; show inline access UI instead }, [user, loading, navigate]); useEffect(() => { if (!selectedMemberId && managedProfiles.length) { setSelectedMemberId(managedProfiles[0].id); } }, [managedProfiles, selectedMemberId]); if (loading) { return ( ); } if (!user || !isOwner) { return ( <>
Access denied This panel is restricted to {ownerEmail}. If you need access, contact the site owner.
); } const [blogPosts, setBlogPosts] = useState([]); const [loadingPosts, setLoadingPosts] = useState(false); const [activeTab, setActiveTab] = useState("overview"); const resolvedBlogPosts = blogPosts.length ? blogPosts : blogSeedPosts; const blogHighlights = useMemo( () => resolvedBlogPosts.slice(0, 4).map((post) => ({ slug: post.slug || String(post.id || "post"), title: post.title || "Untitled", category: post.category || "General", date: post.date || post.published_at || null, })), [resolvedBlogPosts], ); type ChangelogEntry = (typeof changelogEntries)[number]; const latestChangelog = useMemo( () => changelogEntries.slice(0, 3), [], ); const statusSnapshot = useMemo( () => [ { name: "Core API", status: "operational" as const, uptime: "99.98%", responseTime: 145, icon: Server, }, { name: "Database", status: "operational" as const, uptime: "99.99%", responseTime: 89, icon: Database, }, { name: "Realtime", status: "operational" as const, uptime: "99.95%", responseTime: 112, icon: Wifi, }, { name: "Deploy & CDN", status: "operational" as const, uptime: "99.94%", responseTime: 76, icon: Zap, }, ], [], ); const overallStatus = useMemo(() => { const base = { label: "All systems operational", accentClass: "text-emerald-300", badgeClass: "border-emerald-500/40 bg-emerald-500/10 text-emerald-200", Icon: CheckCircle, } as const; if (!statusSnapshot.length) return base; if (statusSnapshot.some((service) => service.status === "outage")) { return { label: "Service disruption", accentClass: "text-red-300", badgeClass: "border-red-500/40 bg-red-500/10 text-red-200", Icon: XCircle, }; } if (statusSnapshot.some((service) => service.status === "degraded")) { return { label: "Partial degradation", accentClass: "text-yellow-300", badgeClass: "border-yellow-500/40 bg-yellow-500/10 text-yellow-200", Icon: AlertTriangle, }; } return base; }, [statusSnapshot]); const blogReach = useMemo( () => resolvedBlogPosts.reduce((total, post) => total + (post.likes ?? 0), 0), [resolvedBlogPosts], ); const selectedMember = useMemo( () => managedProfiles.find((profile) => profile.id === selectedMemberId) ?? null, [managedProfiles, selectedMemberId], ); const totalMembers = managedProfiles.length; const publishedPosts = resolvedBlogPosts.length; const featuredStudios = studios.length; const pendingProjectApplications = projectApplications.filter((app) => { const status = (app.status ?? "").toLowerCase(); return ( status !== "approved" && status !== "completed" && status !== "closed" ); }).length; const infrastructureMetrics = useMemo(() => { if (!statusSnapshot.length) { return { averageResponseTime: null as number | null, averageUptime: null as number | null, degradedServices: 0, healthyServices: 0, totalServices: 0, }; } const totalServices = statusSnapshot.length; const degradedServices = statusSnapshot.filter( (service) => service.status !== "operational", ).length; const averageResponseTime = Math.round( statusSnapshot.reduce((sum, service) => sum + service.responseTime, 0) / totalServices, ); const uptimeAccumulator = statusSnapshot.reduce( (acc, service) => { const numeric = Number.parseFloat(service.uptime); if (Number.isFinite(numeric)) { return { total: acc.total + numeric, count: acc.count + 1 }; } return acc; }, { total: 0, count: 0 }, ); const averageUptime = uptimeAccumulator.count ? uptimeAccumulator.total / uptimeAccumulator.count : null; return { averageResponseTime, averageUptime, degradedServices, healthyServices: totalServices - degradedServices, totalServices, }; }, [statusSnapshot]); const overviewStats = useMemo( () => [ { title: "Total members", value: totalMembers ? totalMembers.toString() : "—", description: "Profiles synced from AeThex identity service.", trend: totalMembers ? `${totalMembers} active profiles` : "Awaiting sync", icon: Users, tone: "blue" as const, }, { title: "Published posts", value: publishedPosts ? publishedPosts.toString() : "0", description: "Blog entries stored in Supabase content tables.", trend: loadingPosts ? "Refreshing content…" : blogHighlights.length ? `Latest: ${blogHighlights[0].title}` : "Curate new stories", icon: PenTool, tone: "purple" as const, }, { title: "Blog engagement", value: blogReach ? `${blogReach.toLocaleString()} applause` : "—", description: "Aggregate reactions across highlighted AeThex posts.", trend: blogHighlights.length > 1 ? `Next up: ${blogHighlights[1].title}` : "Share a new update", icon: Activity, tone: "red" as const, }, { title: "Average latency", value: infrastructureMetrics.averageResponseTime !== null ? `${infrastructureMetrics.averageResponseTime} ms` : "—", description: "Mean response time across monitored infrastructure services.", trend: infrastructureMetrics.degradedServices > 0 ? `${infrastructureMetrics.degradedServices} service${infrastructureMetrics.degradedServices === 1 ? "" : "s"} above SLA target` : "All services meeting SLA", icon: Zap, tone: "purple" as const, }, { title: "Reliability coverage", value: infrastructureMetrics.totalServices > 0 ? `${infrastructureMetrics.healthyServices}/${infrastructureMetrics.totalServices} healthy` : "—", description: "Operational services within the AeThex platform stack.", trend: infrastructureMetrics.averageUptime !== null ? `Avg uptime ${infrastructureMetrics.averageUptime.toFixed(2)}%` : "Awaiting uptime telemetry", icon: Shield, tone: "green" as const, }, { title: "Featured studios", value: featuredStudios ? featuredStudios.toString() : "0", description: "Studios highlighted on community landing pages.", trend: "Synced nightly from partner directory", icon: Rocket, tone: "green" as const, }, { title: "Pending project applications", value: projectApplicationsLoading ? "…" : pendingProjectApplications.toString(), description: "Project collaboration requests awaiting review.", trend: projectApplicationsLoading ? "Fetching submissions…" : `${projectApplications.length} total submissions`, icon: ClipboardList, tone: "orange" as const, }, { title: "Opportunity pipeline", value: opportunityApplicationsLoading ? "…" : opportunityApplications.length.toString(), description: "Contributor & career submissions captured via Opportunities.", trend: opportunityApplicationsLoading ? "Syncing applicant data…" : `${opportunityApplications.filter((app) => (app.status ?? "new").toLowerCase() === "new").length} awaiting review`, icon: Rocket, tone: "green" as const, }, ], [ projectApplications.length, projectApplicationsLoading, opportunityApplications.length, opportunityApplicationsLoading, featuredStudios, loadingPosts, pendingProjectApplications, publishedPosts, totalMembers, blogReach, blogHighlights, infrastructureMetrics, ], ); const quickActions = useMemo( () => [ { label: "Review dashboard", description: "Jump to the live product dashboard and KPIs.", icon: Activity, action: () => navigate("/dashboard"), }, { label: "Manage content", description: "Create, edit, and publish new blog updates.", icon: PenTool, action: () => setActiveTab("content"), }, { label: "Member directory", description: "Audit profiles, roles, and onboarding progress.", icon: Users, action: () => setActiveTab("community"), }, { label: "Operations runbook", description: "Review featured studios and partner programs.", icon: Settings, action: () => setActiveTab("operations"), }, { label: "Review applications", description: "Approve partnership or project requests.", icon: ClipboardList, action: () => setActiveTab("operations"), }, { label: "Opportunity applicants", description: "Review contributor and career applications from Opportunities.", icon: Users, action: () => { setActiveTab("operations"); if (typeof window !== "undefined") { setTimeout(() => { document .getElementById("opportunity-applications") ?.scrollIntoView({ behavior: "smooth", block: "start" }); }, 75); } }, }, { label: "System status", description: "Monitor uptime and live incidents for AeThex services.", icon: Server, action: () => navigate("/status"), }, { label: "Open Builder CMS", description: "Edit marketing pages and landing content in Builder.io.", icon: ExternalLink, action: () => { if (typeof window !== "undefined") { window.open("https://builder.io", "_blank", "noopener"); } }, }, { label: "Invite teammates", description: "Send access links and assign admin roles.", icon: UserCog, action: () => setActiveTab("community"), }, ], [navigate, setActiveTab], ); 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 ( <>

Admin Control Center

Unified oversight for AeThex operations, content, and community.

Owner Admin Founder

Signed in as{" "} {normalizedEmail || ownerEmail}

Overview System Map Roadmap Content Community Mentorship Arm Metrics Operations
{overviewStats.map((stat) => (
) : undefined } /> ))}
navigate("/status")} />
Quick actions
Launch frequent administrative workflows.
{quickActions.map( ({ label, description, icon: ActionIcon, action }) => ( ), )}
navigate("/changelog")} />
Access control
Owner-only access enforced via Supabase roles.
  • Owner email:{" "} {ownerEmail}
  • Roles are provisioned automatically on owner sign-in.
  • Grant additional admins by updating Supabase role assignments.
Content overview
{publishedPosts} published{" "} {publishedPosts === 1 ? "post" : "posts"} ·{" "} {loadingPosts ? "refreshing content…" : "latest Supabase sync"}

Drafts and announcements appear instantly on the public blog after saving. Use scheduled releases for major updates and keep thumbnails optimised for 1200×630.

Blog posts
Manage blog content stored in Supabase
{blogPosts.length === 0 && (

No posts loaded yet. Use “Refresh” or “Add post” to start managing content.

)} {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); }} />