From 132f1810af0950bf49be66dc899dc5705310beae Mon Sep 17 00:00:00 2001 From: "Builder.io" Date: Sat, 18 Oct 2025 22:57:09 +0000 Subject: [PATCH] Add AdminSpotlightManager to manage community spotlights cgen-b90eba3198e34bfd91668440537fa77a --- .../admin/AdminSpotlightManager.tsx | 151 ++++++++++++++++++ 1 file changed, 151 insertions(+) create mode 100644 client/components/admin/AdminSpotlightManager.tsx diff --git a/client/components/admin/AdminSpotlightManager.tsx b/client/components/admin/AdminSpotlightManager.tsx new file mode 100644 index 00000000..06327f87 --- /dev/null +++ b/client/components/admin/AdminSpotlightManager.tsx @@ -0,0 +1,151 @@ +import { useEffect, useMemo, useState } from "react"; +import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"; +import { Button } from "@/components/ui/button"; +import { Input } from "@/components/ui/input"; +import { Badge } from "@/components/ui/badge"; +import { ScrollArea } from "@/components/ui/scroll-area"; +import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"; +import type { AethexUserProfile } from "@/lib/aethex-database-adapter"; +import { ArrowDown, ArrowUp, Plus, Save, Trash2, Users } from "lucide-react"; + +interface SpotlightEntry { + id: string; + type: "developer" | "group"; + label: string; + url?: string; + realms?: ("game_developer" | "client" | "community_member" | "customer" | "staff")[]; +} + +const STORAGE_KEY = "aethex_spotlights"; + +function loadSpotlights(): SpotlightEntry[] { + try { + const raw = localStorage.getItem(STORAGE_KEY); + if (!raw) return []; + const parsed = JSON.parse(raw); + return Array.isArray(parsed) ? parsed : []; + } catch { + return []; + } +} + +function saveSpotlights(entries: SpotlightEntry[]) { + localStorage.setItem(STORAGE_KEY, JSON.stringify(entries)); +} + +export default function AdminSpotlightManager({ profiles }: { profiles: AethexUserProfile[] }) { + const [entries, setEntries] = useState([]); + const [newGroupName, setNewGroupName] = useState(""); + const [newGroupUrl, setNewGroupUrl] = useState(""); + const [selectedProfileId, setSelectedProfileId] = useState(""); + + useEffect(() => { + setEntries(loadSpotlights()); + }, []); + + const devOptions = useMemo(() => profiles.map(p => ({ id: p.id, label: p.full_name || p.username || p.email || "Unknown" })), [profiles]); + + const addDeveloper = () => { + if (!selectedProfileId) return; + const profile = profiles.find(p => p.id === selectedProfileId); + if (!profile) return; + const label = profile.full_name || profile.username || profile.email || selectedProfileId; + setEntries(prev => [...prev, { id: selectedProfileId, type: "developer", label }]); + setSelectedProfileId(""); + }; + + const addGroup = () => { + if (!newGroupName.trim()) return; + setEntries(prev => [...prev, { id: crypto.randomUUID(), type: "group", label: newGroupName.trim(), url: newGroupUrl.trim() || undefined }]); + setNewGroupName(""); + setNewGroupUrl(""); + }; + + const move = (index: number, dir: -1 | 1) => { + setEntries(prev => { + const next = prev.slice(); + const j = index + dir; + if (j < 0 || j >= next.length) return prev; + const tmp = next[index]; + next[index] = next[j]; + next[j] = tmp; + return next; + }); + }; + + const remove = (index: number) => { + setEntries(prev => prev.filter((_, i) => i !== index)); + }; + + const saveAll = () => saveSpotlights(entries); + + return ( + + +
+ + Community spotlight +
+ Feature developers and groups on the Community page. Persists locally for now. +
+ +
+
+
Add developer
+
+ + +
+
+
+
Add group
+
+ setNewGroupName(e.target.value)} /> + setNewGroupUrl(e.target.value)} /> +
+ +
+
+ +
+
+
Spotlight queue
+ +
+ +
    + {entries.map((e, i) => ( +
  • +
    + {e.type} + {e.label} + {e.url ? open : null} +
    +
    + + + +
    +
  • + ))} + {!entries.length && ( +
  • No spotlight entries yet.
  • + )} +
+
+
+ +

Tip: After saving, visit /community#featured-developers or /community#featured-studios. A backend table can replace local persistence later.

+
+
+ ); +}