Spaces:
Sleeping
Sleeping
File size: 7,413 Bytes
e67ec6b | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 | "use client";
import { useState, useEffect } from 'react';
import { Users, FileAudio, Activity, ShieldCheck, Crown, Clock } from 'lucide-react';
import { useAuth } from '@clerk/nextjs';
import { useRouter } from 'next/navigation';
export default function AdminDashboard() {
const [stats, setStats] = useState<any>(null);
const [loading, setLoading] = useState(true);
const { isLoaded, isSignedIn } = useAuth();
const router = useRouter();
useEffect(() => {
if (isLoaded && !isSignedIn) {
router.push("/");
return;
}
// Fetch stats
const baseUrl = process.env.NEXT_PUBLIC_BACKEND_URL || "http://localhost:8000";
fetch(`${baseUrl}/api/admin/analytics`)
.then(res => res.json())
.then(data => {
setStats(data);
setLoading(false);
})
.catch(err => {
console.error("Failed to load admin stats", err);
setLoading(false);
});
}, [isLoaded, isSignedIn, router]);
if (loading) {
return (
<div className="min-h-screen flex items-center justify-center pt-20">
<div className="animate-spin rounded-full h-12 w-12 border-t-2 border-b-2 border-[#1877F2]"></div>
</div>
);
}
if (!stats) return <div className="p-20 text-center">Failed to load analytics</div>;
return (
<div className="min-h-screen pt-24 pb-20 px-4">
<div className="max-w-7xl mx-auto">
<div className="flex items-center justify-between mb-8">
<h1 className="text-3xl font-bold text-white flex items-center gap-3">
<ShieldCheck className="w-8 h-8 text-[#1877F2]" /> Platform Admin
</h1>
<div className="bg-[#1877F2]/20 text-[#1877F2] px-4 py-1.5 rounded-full text-sm font-bold border border-[#1877F2]/30">
Live Analytics
</div>
</div>
{/* Top Stats Row */}
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6 mb-8">
<div className="bg-[#111] border border-[#27272a] rounded-2xl p-6">
<div className="flex items-center gap-4 mb-4">
<div className="bg-blue-500/20 p-3 rounded-xl"><FileAudio className="w-6 h-6 text-blue-500" /></div>
<h3 className="text-gray-400 font-medium">Total Extractions</h3>
</div>
<div className="text-4xl font-black text-white">{stats.total_tasks}</div>
</div>
<div className="bg-[#111] border border-[#27272a] rounded-2xl p-6">
<div className="flex items-center gap-4 mb-4">
<div className="bg-emerald-500/20 p-3 rounded-xl"><Activity className="w-6 h-6 text-emerald-500" /></div>
<h3 className="text-gray-400 font-medium">Completed</h3>
</div>
<div className="text-4xl font-black text-white">{stats.status_counts?.completed || 0}</div>
</div>
<div className="bg-[#111] border border-[#27272a] rounded-2xl p-6">
<div className="flex items-center gap-4 mb-4">
<div className="bg-purple-500/20 p-3 rounded-xl"><Users className="w-6 h-6 text-purple-500" /></div>
<h3 className="text-gray-400 font-medium">Total Users</h3>
</div>
<div className="text-4xl font-black text-white">{stats.total_users}</div>
</div>
<div className="bg-[#111] border border-[#27272a] rounded-2xl p-6 relative overflow-hidden">
<div className="absolute top-0 right-0 w-32 h-32 bg-yellow-500/10 rounded-full blur-3xl" />
<div className="flex items-center gap-4 mb-4 relative z-10">
<div className="bg-yellow-500/20 p-3 rounded-xl"><Crown className="w-6 h-6 text-yellow-500" /></div>
<h3 className="text-gray-400 font-medium">Pro Subscribers</h3>
</div>
<div className="text-4xl font-black text-white relative z-10">{stats.premium_users}</div>
</div>
</div>
{/* Detailed Breakdown */}
<div className="grid grid-cols-1 lg:grid-cols-3 gap-8">
<div className="lg:col-span-1 bg-[#111] border border-[#27272a] rounded-2xl p-6">
<h2 className="text-xl font-bold text-white mb-6">Task Status Breakdown</h2>
<div className="space-y-4">
{Object.entries(stats.status_counts || {}).map(([status, count]: [string, any]) => (
<div key={status} className="flex items-center justify-between bg-black/40 p-4 rounded-xl border border-white/5">
<span className="text-gray-300 capitalize flex items-center gap-2">
<div className={`w-2 h-2 rounded-full ${
status === 'completed' ? 'bg-emerald-500' :
status === 'processing' ? 'bg-blue-500' :
status === 'failed' ? 'bg-rose-500' : 'bg-gray-500'
}`} />
{status}
</span>
<span className="font-bold text-white">{count}</span>
</div>
))}
</div>
</div>
<div className="lg:col-span-2 bg-[#111] border border-[#27272a] rounded-2xl p-6">
<h2 className="text-xl font-bold text-white mb-6">Recent Activity</h2>
<div className="overflow-x-auto">
<table className="w-full text-left border-collapse">
<thead>
<tr className="border-b border-[#27272a] text-gray-400 text-sm">
<th className="pb-3 font-medium">Task ID</th>
<th className="pb-3 font-medium">User Email</th>
<th className="pb-3 font-medium">Status</th>
<th className="pb-3 font-medium">Created At</th>
</tr>
</thead>
<tbody className="divide-y divide-[#27272a]">
{stats.recent_tasks?.map((task: any) => (
<tr key={task.task_id} className="text-sm">
<td className="py-4 font-mono text-gray-400 truncate max-w-[150px]">{task.task_id}</td>
<td className="py-4 text-gray-300">{task.user_email || "Anonymous"}</td>
<td className="py-4">
<span className={`px-2 py-1 rounded text-xs font-bold ${
task.status === 'completed' ? 'bg-emerald-500/20 text-emerald-500' :
task.status === 'processing' ? 'bg-blue-500/20 text-blue-500' :
task.status === 'failed' ? 'bg-rose-500/20 text-rose-500' : 'bg-gray-500/20 text-gray-300'
}`}>
{task.status.toUpperCase()}
</span>
</td>
<td className="py-4 text-gray-500 flex items-center gap-2">
<Clock className="w-3 h-3" />
{new Date(task.created_at * 1000).toLocaleString()}
</td>
</tr>
))}
{stats.recent_tasks?.length === 0 && (
<tr>
<td colSpan={4} className="py-8 text-center text-gray-500">No recent tasks found.</td>
</tr>
)}
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
);
}
|