Prettier format pending files

This commit is contained in:
Builder.io 2025-11-11 04:35:21 +00:00
parent 89aa046bfe
commit 78d02ec135
8 changed files with 558 additions and 451 deletions

View file

@ -551,14 +551,70 @@ const App = () => (
/>
{/* Staff Pages Routes - Protected */}
<Route path="/staff/announcements" element={<RequireAccess><StaffAnnouncements /></RequireAccess>} />
<Route path="/staff/expense-reports" element={<RequireAccess><StaffExpenseReports /></RequireAccess>} />
<Route path="/staff/marketplace" element={<RequireAccess><StaffInternalMarketplace /></RequireAccess>} />
<Route path="/staff/knowledge-base" element={<RequireAccess><StaffKnowledgeBase /></RequireAccess>} />
<Route path="/staff/learning-portal" element={<RequireAccess><StaffLearningPortal /></RequireAccess>} />
<Route path="/staff/performance-reviews" element={<RequireAccess><StaffPerformanceReviews /></RequireAccess>} />
<Route path="/staff/project-tracking" element={<RequireAccess><StaffProjectTracking /></RequireAccess>} />
<Route path="/staff/team-handbook" element={<RequireAccess><StaffTeamHandbook /></RequireAccess>} />
<Route
path="/staff/announcements"
element={
<RequireAccess>
<StaffAnnouncements />
</RequireAccess>
}
/>
<Route
path="/staff/expense-reports"
element={
<RequireAccess>
<StaffExpenseReports />
</RequireAccess>
}
/>
<Route
path="/staff/marketplace"
element={
<RequireAccess>
<StaffInternalMarketplace />
</RequireAccess>
}
/>
<Route
path="/staff/knowledge-base"
element={
<RequireAccess>
<StaffKnowledgeBase />
</RequireAccess>
}
/>
<Route
path="/staff/learning-portal"
element={
<RequireAccess>
<StaffLearningPortal />
</RequireAccess>
}
/>
<Route
path="/staff/performance-reviews"
element={
<RequireAccess>
<StaffPerformanceReviews />
</RequireAccess>
}
/>
<Route
path="/staff/project-tracking"
element={
<RequireAccess>
<StaffProjectTracking />
</RequireAccess>
}
/>
<Route
path="/staff/team-handbook"
element={
<RequireAccess>
<StaffTeamHandbook />
</RequireAccess>
}
/>
{/* Explicit 404 route for static hosting fallbacks */}
<Route path="/404" element={<FourOhFourPage />} />

View file

@ -78,7 +78,8 @@ const ARMS: Arm[] = [
];
const LOGO_URLS: Record<string, string> = {
staff: "https://cdn.builder.io/api/v1/image/assets%2Ffc53d607e21d497595ac97e0637001a1%2Fc0414efd7af54ef4b821a05d469150d0?format=webp&width=800",
staff:
"https://cdn.builder.io/api/v1/image/assets%2Ffc53d607e21d497595ac97e0637001a1%2Fc0414efd7af54ef4b821a05d469150d0?format=webp&width=800",
labs: "https://cdn.builder.io/api/v1/image/assets%2Ffc53d607e21d497595ac97e0637001a1%2Fd93f7113d34347469e74421c3a3412e5?format=webp&width=800",
gameforge:
"https://cdn.builder.io/api/v1/image/assets%2Ffc53d607e21d497595ac97e0637001a1%2Fcd3534c1caa0497abfd44224040c6059?format=webp&width=800",

View file

@ -367,7 +367,10 @@ export default function CodeLayout({ children, hideFooter }: LayoutProps) {
</Link>
</DropdownMenuItem>
<DropdownMenuItem asChild>
<Link to="/internal-docs" className="cursor-pointer">
<Link
to="/internal-docs"
className="cursor-pointer"
>
<BookOpen className="mr-2 h-4 w-4" />
Internal Docs
</Link>

View file

@ -87,46 +87,46 @@ export default function AdminBlogManager() {
loadBlogPosts();
}, [loadBlogPosts]);
const handleDeleteBlogPost = useCallback(
async (slug: string) => {
setDeleting(slug);
try {
const res = await fetch(`/api/blog/${slug}`, { method: "DELETE" });
if (res.ok) {
setBlogPosts((posts) => posts.filter((p) => p.slug !== slug));
aethexToast.success({
title: "Blog post deleted",
description: `Post "${slug}" has been removed`,
});
} else {
aethexToast.error({
title: "Failed to delete blog post",
description: res.statusText || "Unknown error",
});
}
} catch (error) {
console.error("Error deleting blog post:", error);
aethexToast.error({
title: "Error deleting blog post",
description: String(error),
const handleDeleteBlogPost = useCallback(async (slug: string) => {
setDeleting(slug);
try {
const res = await fetch(`/api/blog/${slug}`, { method: "DELETE" });
if (res.ok) {
setBlogPosts((posts) => posts.filter((p) => p.slug !== slug));
aethexToast.success({
title: "Blog post deleted",
description: `Post "${slug}" has been removed`,
});
} else {
aethexToast.error({
title: "Failed to delete blog post",
description: res.statusText || "Unknown error",
});
} finally {
setDeleting(null);
}
},
[],
);
} catch (error) {
console.error("Error deleting blog post:", error);
aethexToast.error({
title: "Error deleting blog post",
description: String(error),
});
} finally {
setDeleting(null);
}
}, []);
const filteredPosts = blogPosts.filter((post) => {
const matchesSearch =
post.title.toLowerCase().includes(searchQuery.toLowerCase()) ||
post.slug.toLowerCase().includes(searchQuery.toLowerCase()) ||
(post.author && post.author.toLowerCase().includes(searchQuery.toLowerCase()));
(post.author &&
post.author.toLowerCase().includes(searchQuery.toLowerCase()));
const matchesCategory = !filterCategory || post.category === filterCategory;
return matchesSearch && matchesCategory;
});
const categories = Array.from(new Set(blogPosts.map((p) => p.category).filter(Boolean)));
const categories = Array.from(
new Set(blogPosts.map((p) => p.category).filter(Boolean)),
);
const formatDate = (dateStr?: string | null) => {
if (!dateStr) return "—";
@ -149,7 +149,8 @@ export default function AdminBlogManager() {
<div>
<CardTitle>Blog Management</CardTitle>
<CardDescription>
{blogPosts.length} published {blogPosts.length === 1 ? "post" : "posts"}
{blogPosts.length} published{" "}
{blogPosts.length === 1 ? "post" : "posts"}
</CardDescription>
</div>
<Button
@ -159,7 +160,11 @@ export default function AdminBlogManager() {
disabled={loading}
className="gap-2"
>
{loading ? <Loader2 className="h-4 w-4 animate-spin" /> : <RefreshCw className="h-4 w-4" />}
{loading ? (
<Loader2 className="h-4 w-4 animate-spin" />
) : (
<RefreshCw className="h-4 w-4" />
)}
{loading ? "Loading..." : "Refresh"}
</Button>
</div>
@ -201,7 +206,9 @@ export default function AdminBlogManager() {
{filteredPosts.length === 0 ? (
<div className="text-center py-8">
<p className="text-muted-foreground">
{blogPosts.length === 0 ? "No blog posts found" : "No matching blog posts"}
{blogPosts.length === 0
? "No blog posts found"
: "No matching blog posts"}
</p>
{blogPosts.length === 0 && (
<Button
@ -231,7 +238,9 @@ export default function AdminBlogManager() {
<TableCell className="font-medium">
<div className="max-w-xs">
<p className="truncate">{post.title}</p>
<p className="text-xs text-muted-foreground">{post.slug}</p>
<p className="text-xs text-muted-foreground">
{post.slug}
</p>
</div>
</TableCell>
<TableCell className="text-sm text-muted-foreground">
@ -243,7 +252,9 @@ export default function AdminBlogManager() {
{post.category}
</Badge>
) : (
<span className="text-xs text-muted-foreground"></span>
<span className="text-xs text-muted-foreground">
</span>
)}
</TableCell>
<TableCell className="text-sm text-muted-foreground">
@ -254,7 +265,9 @@ export default function AdminBlogManager() {
<Button
size="sm"
variant="ghost"
onClick={() => window.open(`/blog/${post.slug}`, "_blank")}
onClick={() =>
window.open(`/blog/${post.slug}`, "_blank")
}
title="View published post"
>
<ExternalLink className="h-4 w-4" />
@ -280,12 +293,16 @@ export default function AdminBlogManager() {
</Card>
{deleteConfirm && (
<AlertDialog open={!!deleteConfirm} onOpenChange={(open) => !open && setDeleteConfirm(null)}>
<AlertDialog
open={!!deleteConfirm}
onOpenChange={(open) => !open && setDeleteConfirm(null)}
>
<AlertDialogContent>
<AlertDialogHeader>
<AlertDialogTitle>Delete blog post?</AlertDialogTitle>
<AlertDialogDescription>
Are you sure you want to delete "{deleteConfirm.title}"? This action cannot be undone.
Are you sure you want to delete "{deleteConfirm.title}"? This
action cannot be undone.
</AlertDialogDescription>
</AlertDialogHeader>
<div className="flex gap-2 justify-end">

View file

@ -213,7 +213,7 @@ export default function AdminSidebar({
"w-full flex items-center gap-3 px-3 py-2 rounded-md text-sm font-medium transition-colors",
activeTab === item.id
? "bg-aethex-500/20 text-aethex-200 border border-aethex-400/30"
: "text-muted-foreground hover:text-foreground hover:bg-muted/50"
: "text-muted-foreground hover:text-foreground hover:bg-muted/50",
)}
>
{item.icon}

View file

@ -335,410 +335,412 @@ export default function Admin() {
<AdminSidebar activeTab={activeTab} onTabChange={setActiveTab} />
<div className="flex-1 overflow-y-auto py-8">
<div className="container mx-auto px-4 max-w-7xl">
<div className="mb-8">
<h1 className="text-4xl font-bold text-aethex-200 mb-2">
Control Center
</h1>
<p className="text-muted-foreground">
Manage platform, users, content, and integrations · Roles:{" "}
<span className="text-aethex-300 font-medium">
{roles.join(", ") || "none"}
</span>
</p>
</div>
<div className="mb-8">
<h1 className="text-4xl font-bold text-aethex-200 mb-2">
Control Center
</h1>
<p className="text-muted-foreground">
Manage platform, users, content, and integrations · Roles:{" "}
<span className="text-aethex-300 font-medium">
{roles.join(", ") || "none"}
</span>
</p>
</div>
<Tabs
value={activeTab}
onValueChange={setActiveTab}
className="space-y-6"
>
<TabsList className="w-full justify-start gap-2 overflow-x-auto border border-border/40 bg-background/40 px-1 py-1 backdrop-blur flex-wrap h-auto lg:hidden">
<TabsTrigger value="overview">Overview</TabsTrigger>
<TabsTrigger value="system-map">System Map</TabsTrigger>
<TabsTrigger value="roadmap">Roadmap</TabsTrigger>
<TabsTrigger value="staff">Staff</TabsTrigger>
<TabsTrigger value="blogs">Blogs</TabsTrigger>
<TabsTrigger value="community">Community</TabsTrigger>
<TabsTrigger value="mentorship">Mentorship</TabsTrigger>
<TabsTrigger value="arm-metrics">Arm Metrics</TabsTrigger>
<TabsTrigger value="discord">Discord</TabsTrigger>
<TabsTrigger value="operations">Operations</TabsTrigger>
</TabsList>
<Tabs
value={activeTab}
onValueChange={setActiveTab}
className="space-y-6"
>
<TabsList className="w-full justify-start gap-2 overflow-x-auto border border-border/40 bg-background/40 px-1 py-1 backdrop-blur flex-wrap h-auto lg:hidden">
<TabsTrigger value="overview">Overview</TabsTrigger>
<TabsTrigger value="system-map">System Map</TabsTrigger>
<TabsTrigger value="roadmap">Roadmap</TabsTrigger>
<TabsTrigger value="staff">Staff</TabsTrigger>
<TabsTrigger value="blogs">Blogs</TabsTrigger>
<TabsTrigger value="community">Community</TabsTrigger>
<TabsTrigger value="mentorship">Mentorship</TabsTrigger>
<TabsTrigger value="arm-metrics">Arm Metrics</TabsTrigger>
<TabsTrigger value="discord">Discord</TabsTrigger>
<TabsTrigger value="operations">Operations</TabsTrigger>
</TabsList>
<TabsContent value="overview" className="space-y-6">
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4">
<Card className="bg-card/60 border-border/40 backdrop-blur">
<CardHeader className="pb-3">
<CardTitle className="text-sm font-medium text-muted-foreground">
Total Members
</CardTitle>
</CardHeader>
<CardContent>
<div className="text-3xl font-bold text-aethex-200">
{totalMembers || "—"}
</div>
<p className="text-xs text-muted-foreground mt-1">
Active profiles synced
</p>
</CardContent>
</Card>
<Card className="bg-card/60 border-border/40 backdrop-blur">
<CardHeader className="pb-3">
<CardTitle className="text-sm font-medium text-muted-foreground">
Published Posts
</CardTitle>
</CardHeader>
<CardContent>
<div className="text-3xl font-bold text-aethex-200">
{publishedPosts || "0"}
</div>
<p className="text-xs text-muted-foreground mt-1">
Blog entries available
</p>
</CardContent>
</Card>
<Card className="bg-card/60 border-border/40 backdrop-blur">
<CardHeader className="pb-3">
<CardTitle className="text-sm font-medium text-muted-foreground">
Featured Studios
</CardTitle>
</CardHeader>
<CardContent>
<div className="text-3xl font-bold text-aethex-200">
{featuredStudios}
</div>
<p className="text-xs text-muted-foreground mt-1">
Highlighted partners
</p>
</CardContent>
</Card>
<Card className="bg-card/60 border-border/40 backdrop-blur">
<CardHeader className="pb-3">
<CardTitle className="text-sm font-medium text-muted-foreground">
Pending Applications
</CardTitle>
</CardHeader>
<CardContent>
<div className="text-3xl font-bold text-aethex-200">
{pendingProjectApplications}
</div>
<p className="text-xs text-muted-foreground mt-1">
Awaiting review
</p>
</CardContent>
</Card>
</div>
<TabsContent value="overview" className="space-y-6">
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4">
<Card className="bg-card/60 border-border/40 backdrop-blur">
<CardHeader className="pb-3">
<CardTitle className="text-sm font-medium text-muted-foreground">
Total Members
</CardTitle>
<CardHeader>
<div className="flex items-center gap-2">
<Command className="h-5 w-5 text-aethex-300" />
<CardTitle>Quick Actions</CardTitle>
</div>
</CardHeader>
<CardContent className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-3">
<Button
variant="outline"
onClick={() => navigate("/dashboard")}
className="justify-start h-auto py-3"
>
<BarChart3 className="h-4 w-4 mr-2" />
<div className="text-left">
<div className="font-medium">Dashboard</div>
<div className="text-xs text-muted-foreground">
View KPIs
</div>
</div>
</Button>
<Button
variant="outline"
onClick={() => setActiveTab("blogs")}
className="justify-start h-auto py-3"
>
<PenTool className="h-4 w-4 mr-2" />
<div className="text-left">
<div className="font-medium">Blogs</div>
<div className="text-xs text-muted-foreground">
Manage posts
</div>
</div>
</Button>
<Button
variant="outline"
onClick={() => setActiveTab("community")}
className="justify-start h-auto py-3"
>
<Users className="h-4 w-4 mr-2" />
<div className="text-left">
<div className="font-medium">Members</div>
<div className="text-xs text-muted-foreground">
Manage users
</div>
</div>
</Button>
<Button
variant="outline"
onClick={() => setActiveTab("operations")}
className="justify-start h-auto py-3"
>
<Settings className="h-4 w-4 mr-2" />
<div className="text-left">
<div className="font-medium">Operations</div>
<div className="text-xs text-muted-foreground">
Settings & config
</div>
</div>
</Button>
<Button
variant="outline"
onClick={() => setActiveTab("discord")}
className="justify-start h-auto py-3"
>
<MessageSquare className="h-4 w-4 mr-2" />
<div className="text-left">
<div className="font-medium">Discord</div>
<div className="text-xs text-muted-foreground">
Bot management
</div>
</div>
</Button>
<Button
variant="outline"
onClick={() => navigate("/status")}
className="justify-start h-auto py-3"
>
<Gauge className="h-4 w-4 mr-2" />
<div className="text-left">
<div className="font-medium">Status</div>
<div className="text-xs text-muted-foreground">
System health
</div>
</div>
</Button>
</CardContent>
</Card>
</TabsContent>
<TabsContent value="system-map" className="space-y-6">
<AdminSystemMap />
</TabsContent>
<TabsContent value="roadmap" className="space-y-6">
<AdminRoadmap />
</TabsContent>
<TabsContent value="staff" className="space-y-6">
<Tabs defaultValue="operations" className="space-y-4">
<TabsList className="w-full justify-start gap-2 border border-border/40 bg-background/40 px-1 py-1 backdrop-blur flex-wrap h-auto">
<TabsTrigger value="operations">Operations</TabsTrigger>
<TabsTrigger value="directory">Directory</TabsTrigger>
<TabsTrigger value="chat">Chat</TabsTrigger>
<TabsTrigger value="admin">Admin</TabsTrigger>
<TabsTrigger value="docs">Docs</TabsTrigger>
<TabsTrigger value="achievements">
Achievements
</TabsTrigger>
</TabsList>
<TabsContent value="operations" className="space-y-6">
<AdminStaffOperations />
</TabsContent>
<TabsContent value="directory" className="space-y-6">
<AdminStaffDirectory />
</TabsContent>
<TabsContent value="chat" className="space-y-6">
<AdminStaffChat />
</TabsContent>
<TabsContent value="admin" className="space-y-6">
<AdminStaffAdmin />
</TabsContent>
<TabsContent value="docs" className="space-y-6">
<AdminStaffDocs />
</TabsContent>
<TabsContent value="achievements" className="space-y-6">
<AdminStaffAchievements />
</TabsContent>
</Tabs>
</TabsContent>
<TabsContent value="blogs" className="space-y-6">
<AdminBlogManager />
</TabsContent>
<TabsContent value="community" className="space-y-6">
<AdminMemberManager
profiles={managedProfiles}
selectedId={selectedMemberId}
onSelectedIdChange={(id) => setSelectedMemberId(id)}
onRefresh={loadProfiles}
ownerEmail="admin@aethex.tech"
/>
<AdminAchievementManager targetUser={selectedMember} />
<AdminSpotlightManager profiles={managedProfiles} />
</TabsContent>
<TabsContent value="mentorship" className="space-y-6">
<AdminMentorshipManager />
</TabsContent>
<TabsContent value="arm-metrics" className="space-y-6">
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-5 gap-4">
<Card className="bg-card/60 border-border/40 backdrop-blur">
<CardHeader className="pb-3">
<div className="flex items-center gap-2">
<div className="h-2 w-2 rounded-full bg-yellow-400" />
<CardTitle className="text-sm">Labs</CardTitle>
</div>
</CardHeader>
<CardContent className="space-y-2 text-xs">
<div>
<p className="text-muted-foreground">Research</p>
<p className="text-lg font-bold">12 projects</p>
</div>
<div>
<p className="text-muted-foreground">Team</p>
<p className="text-lg font-bold">24 members</p>
</div>
</CardContent>
</Card>
<Card className="bg-card/60 border-border/40 backdrop-blur">
<CardHeader className="pb-3">
<div className="flex items-center gap-2">
<div className="h-2 w-2 rounded-full bg-green-400" />
<CardTitle className="text-sm">GameForge</CardTitle>
</div>
</CardHeader>
<CardContent className="space-y-2 text-xs">
<div>
<p className="text-muted-foreground">Games</p>
<p className="text-lg font-bold">45 shipped</p>
</div>
<div>
<p className="text-muted-foreground">Players</p>
<p className="text-lg font-bold">2.8M MAU</p>
</div>
</CardContent>
</Card>
<Card className="bg-card/60 border-border/40 backdrop-blur">
<CardHeader className="pb-3">
<div className="flex items-center gap-2">
<div className="h-2 w-2 rounded-full bg-blue-400" />
<CardTitle className="text-sm">Corp</CardTitle>
</div>
</CardHeader>
<CardContent className="space-y-2 text-xs">
<div>
<p className="text-muted-foreground">Clients</p>
<p className="text-lg font-bold">34 active</p>
</div>
<div>
<p className="text-muted-foreground">ARR</p>
<p className="text-lg font-bold">$4.2M</p>
</div>
</CardContent>
</Card>
<Card className="bg-card/60 border-border/40 backdrop-blur">
<CardHeader className="pb-3">
<div className="flex items-center gap-2">
<div className="h-2 w-2 rounded-full bg-red-400" />
<CardTitle className="text-sm">Foundation</CardTitle>
</div>
</CardHeader>
<CardContent className="space-y-2 text-xs">
<div>
<p className="text-muted-foreground">Learners</p>
<p className="text-lg font-bold">342 active</p>
</div>
<div>
<p className="text-muted-foreground">Completion</p>
<p className="text-lg font-bold">87.5%</p>
</div>
</CardContent>
</Card>
<Card className="bg-card/60 border-border/40 backdrop-blur">
<CardHeader className="pb-3">
<div className="flex items-center gap-2">
<div className="h-2 w-2 rounded-full bg-purple-400" />
<CardTitle className="text-sm">Nexus</CardTitle>
</div>
</CardHeader>
<CardContent className="space-y-2 text-xs">
<div>
<p className="text-muted-foreground">Creators</p>
<p className="text-lg font-bold">1,240 active</p>
</div>
<div>
<p className="text-muted-foreground">Success Rate</p>
<p className="text-lg font-bold">68%</p>
</div>
</CardContent>
</Card>
</div>
</TabsContent>
<TabsContent value="discord" className="space-y-6">
<AdminDiscordManagement />
<AdminDiscordDiagnostic />
</TabsContent>
<TabsContent value="operations" className="space-y-6">
<Card className="bg-card/60 border-border/40 backdrop-blur">
<CardHeader>
<div className="flex items-center gap-2">
<Settings className="h-5 w-5 text-aethex-300" />
<CardTitle>Home Banner</CardTitle>
</div>
<CardDescription>
Controls the notice shown at the top of the home page
</CardDescription>
</CardHeader>
<CardContent>
<div className="text-3xl font-bold text-aethex-200">
{totalMembers || "—"}
</div>
<p className="text-xs text-muted-foreground mt-1">
Active profiles synced
</p>
<BannerSettings />
</CardContent>
</Card>
<Card className="bg-card/60 border-border/40 backdrop-blur">
<CardHeader className="pb-3">
<CardTitle className="text-sm font-medium text-muted-foreground">
Published Posts
</CardTitle>
<CardHeader>
<div className="flex items-center gap-2">
<Grid3x3 className="h-5 w-5 text-yellow-300" />
<CardTitle>Featured Studios</CardTitle>
</div>
<CardDescription>
Control studios highlighted across AeThex
</CardDescription>
</CardHeader>
<CardContent>
<div className="text-3xl font-bold text-aethex-200">
{publishedPosts || "0"}
</div>
<p className="text-xs text-muted-foreground mt-1">
Blog entries available
</p>
</CardContent>
</Card>
<Card className="bg-card/60 border-border/40 backdrop-blur">
<CardHeader className="pb-3">
<CardTitle className="text-sm font-medium text-muted-foreground">
Featured Studios
</CardTitle>
</CardHeader>
<CardContent>
<div className="text-3xl font-bold text-aethex-200">
{featuredStudios}
</div>
<p className="text-xs text-muted-foreground mt-1">
Highlighted partners
</p>
</CardContent>
</Card>
<Card className="bg-card/60 border-border/40 backdrop-blur">
<CardHeader className="pb-3">
<CardTitle className="text-sm font-medium text-muted-foreground">
Pending Applications
</CardTitle>
</CardHeader>
<CardContent>
<div className="text-3xl font-bold text-aethex-200">
{pendingProjectApplications}
</div>
<p className="text-xs text-muted-foreground mt-1">
Awaiting review
</p>
</CardContent>
</Card>
</div>
<Card className="bg-card/60 border-border/40 backdrop-blur">
<CardHeader>
<div className="flex items-center gap-2">
<Command className="h-5 w-5 text-aethex-300" />
<CardTitle>Quick Actions</CardTitle>
</div>
</CardHeader>
<CardContent className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-3">
<Button
variant="outline"
onClick={() => navigate("/dashboard")}
className="justify-start h-auto py-3"
>
<BarChart3 className="h-4 w-4 mr-2" />
<div className="text-left">
<div className="font-medium">Dashboard</div>
<div className="text-xs text-muted-foreground">
View KPIs
</div>
</div>
</Button>
<Button
variant="outline"
onClick={() => setActiveTab("blogs")}
className="justify-start h-auto py-3"
>
<PenTool className="h-4 w-4 mr-2" />
<div className="text-left">
<div className="font-medium">Blogs</div>
<div className="text-xs text-muted-foreground">
Manage posts
</div>
</div>
</Button>
<Button
variant="outline"
onClick={() => setActiveTab("community")}
className="justify-start h-auto py-3"
>
<Users className="h-4 w-4 mr-2" />
<div className="text-left">
<div className="font-medium">Members</div>
<div className="text-xs text-muted-foreground">
Manage users
</div>
</div>
</Button>
<Button
variant="outline"
onClick={() => setActiveTab("operations")}
className="justify-start h-auto py-3"
>
<Settings className="h-4 w-4 mr-2" />
<div className="text-left">
<div className="font-medium">Operations</div>
<div className="text-xs text-muted-foreground">
Settings & config
</div>
</div>
</Button>
<Button
variant="outline"
onClick={() => setActiveTab("discord")}
className="justify-start h-auto py-3"
>
<MessageSquare className="h-4 w-4 mr-2" />
<div className="text-left">
<div className="font-medium">Discord</div>
<div className="text-xs text-muted-foreground">
Bot management
</div>
</div>
</Button>
<Button
variant="outline"
onClick={() => navigate("/status")}
className="justify-start h-auto py-3"
>
<Gauge className="h-4 w-4 mr-2" />
<div className="text-left">
<div className="font-medium">Status</div>
<div className="text-xs text-muted-foreground">
System health
</div>
</div>
</Button>
</CardContent>
</Card>
</TabsContent>
<TabsContent value="system-map" className="space-y-6">
<AdminSystemMap />
</TabsContent>
<TabsContent value="roadmap" className="space-y-6">
<AdminRoadmap />
</TabsContent>
<TabsContent value="staff" className="space-y-6">
<Tabs defaultValue="operations" className="space-y-4">
<TabsList className="w-full justify-start gap-2 border border-border/40 bg-background/40 px-1 py-1 backdrop-blur flex-wrap h-auto">
<TabsTrigger value="operations">Operations</TabsTrigger>
<TabsTrigger value="directory">Directory</TabsTrigger>
<TabsTrigger value="chat">Chat</TabsTrigger>
<TabsTrigger value="admin">Admin</TabsTrigger>
<TabsTrigger value="docs">Docs</TabsTrigger>
<TabsTrigger value="achievements">Achievements</TabsTrigger>
</TabsList>
<TabsContent value="operations" className="space-y-6">
<AdminStaffOperations />
</TabsContent>
<TabsContent value="directory" className="space-y-6">
<AdminStaffDirectory />
</TabsContent>
<TabsContent value="chat" className="space-y-6">
<AdminStaffChat />
</TabsContent>
<TabsContent value="admin" className="space-y-6">
<AdminStaffAdmin />
</TabsContent>
<TabsContent value="docs" className="space-y-6">
<AdminStaffDocs />
</TabsContent>
<TabsContent value="achievements" className="space-y-6">
<AdminStaffAchievements />
</TabsContent>
</Tabs>
</TabsContent>
<TabsContent value="blogs" className="space-y-6">
<AdminBlogManager />
</TabsContent>
<TabsContent value="community" className="space-y-6">
<AdminMemberManager
profiles={managedProfiles}
selectedId={selectedMemberId}
onSelectedIdChange={(id) => setSelectedMemberId(id)}
onRefresh={loadProfiles}
ownerEmail="admin@aethex.tech"
/>
<AdminAchievementManager targetUser={selectedMember} />
<AdminSpotlightManager profiles={managedProfiles} />
</TabsContent>
<TabsContent value="mentorship" className="space-y-6">
<AdminMentorshipManager />
</TabsContent>
<TabsContent value="arm-metrics" className="space-y-6">
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-5 gap-4">
<Card className="bg-card/60 border-border/40 backdrop-blur">
<CardHeader className="pb-3">
<div className="flex items-center gap-2">
<div className="h-2 w-2 rounded-full bg-yellow-400" />
<CardTitle className="text-sm">Labs</CardTitle>
</div>
</CardHeader>
<CardContent className="space-y-2 text-xs">
<div>
<p className="text-muted-foreground">Research</p>
<p className="text-lg font-bold">12 projects</p>
</div>
<div>
<p className="text-muted-foreground">Team</p>
<p className="text-lg font-bold">24 members</p>
<div className="space-y-3">
{studios.map((s, i) => (
<div
key={`${s.name}-${i}`}
className="p-3 border border-border/40 rounded-lg bg-background/40"
>
<p className="font-medium">{s.name}</p>
<p className="text-sm text-muted-foreground">
{s.tagline}
</p>
</div>
))}
</div>
</CardContent>
</Card>
<Card className="bg-card/60 border-border/40 backdrop-blur">
<CardHeader className="pb-3">
<div className="flex items-center gap-2">
<div className="h-2 w-2 rounded-full bg-green-400" />
<CardTitle className="text-sm">GameForge</CardTitle>
</div>
</CardHeader>
<CardContent className="space-y-2 text-xs">
<div>
<p className="text-muted-foreground">Games</p>
<p className="text-lg font-bold">45 shipped</p>
</div>
<div>
<p className="text-muted-foreground">Players</p>
<p className="text-lg font-bold">2.8M MAU</p>
</div>
</CardContent>
</Card>
<Card className="bg-card/60 border-border/40 backdrop-blur">
<CardHeader className="pb-3">
<div className="flex items-center gap-2">
<div className="h-2 w-2 rounded-full bg-blue-400" />
<CardTitle className="text-sm">Corp</CardTitle>
</div>
</CardHeader>
<CardContent className="space-y-2 text-xs">
<div>
<p className="text-muted-foreground">Clients</p>
<p className="text-lg font-bold">34 active</p>
</div>
<div>
<p className="text-muted-foreground">ARR</p>
<p className="text-lg font-bold">$4.2M</p>
</div>
</CardContent>
</Card>
<Card className="bg-card/60 border-border/40 backdrop-blur">
<CardHeader className="pb-3">
<div className="flex items-center gap-2">
<div className="h-2 w-2 rounded-full bg-red-400" />
<CardTitle className="text-sm">Foundation</CardTitle>
</div>
</CardHeader>
<CardContent className="space-y-2 text-xs">
<div>
<p className="text-muted-foreground">Learners</p>
<p className="text-lg font-bold">342 active</p>
</div>
<div>
<p className="text-muted-foreground">Completion</p>
<p className="text-lg font-bold">87.5%</p>
</div>
</CardContent>
</Card>
<Card className="bg-card/60 border-border/40 backdrop-blur">
<CardHeader className="pb-3">
<div className="flex items-center gap-2">
<div className="h-2 w-2 rounded-full bg-purple-400" />
<CardTitle className="text-sm">Nexus</CardTitle>
</div>
</CardHeader>
<CardContent className="space-y-2 text-xs">
<div>
<p className="text-muted-foreground">Creators</p>
<p className="text-lg font-bold">1,240 active</p>
</div>
<div>
<p className="text-muted-foreground">Success Rate</p>
<p className="text-lg font-bold">68%</p>
</div>
</CardContent>
</Card>
</div>
</TabsContent>
<TabsContent value="discord" className="space-y-6">
<AdminDiscordManagement />
<AdminDiscordDiagnostic />
</TabsContent>
<TabsContent value="operations" className="space-y-6">
<Card className="bg-card/60 border-border/40 backdrop-blur">
<CardHeader>
<div className="flex items-center gap-2">
<Settings className="h-5 w-5 text-aethex-300" />
<CardTitle>Home Banner</CardTitle>
</div>
<CardDescription>
Controls the notice shown at the top of the home page
</CardDescription>
</CardHeader>
<CardContent>
<BannerSettings />
</CardContent>
</Card>
<Card className="bg-card/60 border-border/40 backdrop-blur">
<CardHeader>
<div className="flex items-center gap-2">
<Grid3x3 className="h-5 w-5 text-yellow-300" />
<CardTitle>Featured Studios</CardTitle>
</div>
<CardDescription>
Control studios highlighted across AeThex
</CardDescription>
</CardHeader>
<CardContent>
<div className="space-y-3">
{studios.map((s, i) => (
<div
key={`${s.name}-${i}`}
className="p-3 border border-border/40 rounded-lg bg-background/40"
>
<p className="font-medium">{s.name}</p>
<p className="text-sm text-muted-foreground">
{s.tagline}
</p>
</div>
))}
</div>
</CardContent>
</Card>
</TabsContent>
</TabsContent>
</Tabs>
</div>
</div>

View file

@ -140,14 +140,14 @@ export default function Staff() {
<div className="absolute top-1/4 left-1/4 w-96 h-96 bg-purple-600 rounded-full mix-blend-multiply filter blur-3xl opacity-20 animate-blob" />
<div className="absolute top-1/3 right-1/4 w-96 h-96 bg-violet-600 rounded-full mix-blend-multiply filter blur-3xl opacity-20 animate-blob animation-delay-2000" />
<div className="absolute bottom-1/4 left-1/2 w-96 h-96 bg-purple-500 rounded-full mix-blend-multiply filter blur-3xl opacity-20 animate-blob animation-delay-4000" />
{/* Grid background */}
<div
className="absolute inset-0 opacity-5"
style={{
backgroundImage:
'linear-gradient(rgba(139, 92, 246, 0.1) 1px, transparent 1px), linear-gradient(90deg, rgba(139, 92, 246, 0.1) 1px, transparent 1px)',
backgroundSize: '50px 50px',
"linear-gradient(rgba(139, 92, 246, 0.1) 1px, transparent 1px), linear-gradient(90deg, rgba(139, 92, 246, 0.1) 1px, transparent 1px)",
backgroundSize: "50px 50px",
}}
/>
</div>
@ -181,7 +181,10 @@ export default function Staff() {
The Staff Command Center
</h1>
<p className="text-lg md:text-xl text-gray-300 max-w-3xl mx-auto leading-relaxed">
Unified workspace for internal communications, team collaboration, and operational excellence. Manage projects, track performance, and connect with your teamall in one secure platform.
Unified workspace for internal communications, team
collaboration, and operational excellence. Manage projects,
track performance, and connect with your teamall in one secure
platform.
</p>
</div>
@ -233,7 +236,8 @@ export default function Staff() {
Quick Access Resources
</h2>
<p className="text-gray-400 text-lg">
Everything you need to manage operations, collaborate with your team, and stay informed
Everything you need to manage operations, collaborate with
your team, and stay informed
</p>
</div>
@ -282,7 +286,9 @@ export default function Staff() {
<div className="p-2 rounded-lg bg-purple-500/20">
<MessageSquare className="w-6 h-6 text-purple-400" />
</div>
<h3 className="text-xl font-bold">Communication & Collaboration</h3>
<h3 className="text-xl font-bold">
Communication & Collaboration
</h3>
</div>
<ul className="space-y-3">
{[
@ -341,7 +347,10 @@ export default function Staff() {
Enterprise Security & Privacy
</h3>
<p className="text-gray-300 leading-relaxed">
All staff information is protected with enterprise-grade security. Access is restricted to authenticated team members only. Your data is encrypted, audited, and compliant with privacy regulations.
All staff information is protected with enterprise-grade
security. Access is restricted to authenticated team members
only. Your data is encrypted, audited, and compliant with
privacy regulations.
</p>
</div>
</div>

View file

@ -2355,14 +2355,23 @@ export function createServer() {
const { data, error } = await query;
if (error) {
// If table doesn't exist, return empty array (client will use seed data)
if (error.message?.includes("does not exist") || error.code === "42P01") {
console.log("[Blog] blog_posts table not found, returning empty array");
if (
error.message?.includes("does not exist") ||
error.code === "42P01"
) {
console.log(
"[Blog] blog_posts table not found, returning empty array",
);
return res.json([]);
}
console.error("[Blog] Error fetching blog posts:", error);
return res.status(500).json({ error: error.message });
}
console.log("[Blog] Successfully fetched", (data || []).length, "blog posts");
console.log(
"[Blog] Successfully fetched",
(data || []).length,
"blog posts",
);
res.json(data || []);
} catch (e: any) {
console.error("[Blog] Exception:", e);
@ -2386,7 +2395,10 @@ export function createServer() {
// No rows returned - 404
return res.status(404).json({ error: "Blog post not found" });
}
if (error.message?.includes("does not exist") || error.code === "42P01") {
if (
error.message?.includes("does not exist") ||
error.code === "42P01"
) {
// Table doesn't exist
return res.status(404).json({ error: "Blog not configured" });
}
@ -4851,7 +4863,10 @@ export function createServer() {
// Staff Members API
app.get("/api/staff/members", async (_req, res) => {
try {
console.log("[Staff] GET /api/staff/members - adminSupabase initialized:", !!adminSupabase);
console.log(
"[Staff] GET /api/staff/members - adminSupabase initialized:",
!!adminSupabase,
);
if (!adminSupabase) {
console.error("[Staff] adminSupabase is not initialized");
@ -4875,7 +4890,11 @@ export function createServer() {
return res.status(500).json({ error: error.message });
}
console.log("[Staff] Successfully fetched", (data || []).length, "staff members");
console.log(
"[Staff] Successfully fetched",
(data || []).length,
"staff members",
);
return res.json(data || []);
} catch (e: any) {
console.error("[Staff] Unexpected error:", e);