ai-wpa/app/admin/page.tsx

257 lines
7.7 KiB
TypeScript

'use client'
import { useEffect, useState } from 'react'
import AdminLayout from '@/components/admin/AdminLayout'
import DashboardStats from '@/components/admin/DashboardStats'
import RecentActivity from '@/components/admin/RecentActivity'
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'
import { Skeleton } from '@/components/ui/skeleton'
import { toast } from '@/hooks/use-toast'
interface DashboardData {
users: {
total: number
active: number
newThisMonth: number
verified: number
verificationRate: string
}
revenue: {
total: number
monthly: number
weekly: number
}
transactions: {
total: number
monthly: number
}
services: {
total: number
active: number
breakdown: Array<{
_id: string
count: number
revenue: number
}>
}
developerRequests: {
total: number
pending: number
}
recentActivity: {
users: Array<{
_id: string
name: string
email: string
siliconId: string
createdAt: string
}>
transactions: Array<{
_id: string
type: string
amount: number
description: string
status: string
createdAt: string
userId: {
name: string
email: string
siliconId: string
}
}>
}
}
export default function AdminDashboard() {
const [data, setData] = useState<DashboardData | null>(null)
const [loading, setLoading] = useState(true)
useEffect(() => {
fetchDashboardData()
}, [])
const fetchDashboardData = async () => {
try {
const token =
localStorage.getItem('token') ||
document.cookie
.split('; ')
.find((row) => row.startsWith('token='))
?.split('=')[1]
const response = await fetch('/api/admin/dashboard', {
headers: {
Authorization: `Bearer ${token}`,
},
})
if (!response.ok) {
throw new Error('Failed to fetch dashboard data')
}
const dashboardData = await response.json()
setData(dashboardData)
} catch (error) {
console.error('Dashboard fetch error:', error)
toast({
title: 'Error',
description: 'Failed to load dashboard data',
variant: 'destructive',
})
} finally {
setLoading(false)
}
}
if (loading) {
return (
<AdminLayout>
<div className="space-y-6">
<div>
<h1 className="text-3xl font-bold text-gray-900">Dashboard</h1>
<p className="text-gray-600 mt-2">Overview of your SiliconPin platform</p>
</div>
{/* Loading skeletons */}
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
{Array.from({ length: 6 }).map((_, i) => (
<Card key={i}>
<CardHeader>
<Skeleton className="h-4 w-24" />
</CardHeader>
<CardContent>
<Skeleton className="h-8 w-16 mb-2" />
<Skeleton className="h-3 w-32" />
</CardContent>
</Card>
))}
</div>
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
<Card>
<CardHeader>
<Skeleton className="h-6 w-32" />
</CardHeader>
<CardContent>
<div className="space-y-4">
{Array.from({ length: 3 }).map((_, i) => (
<div key={i} className="flex justify-between">
<div>
<Skeleton className="h-4 w-24 mb-1" />
<Skeleton className="h-3 w-32" />
</div>
<Skeleton className="h-3 w-16" />
</div>
))}
</div>
</CardContent>
</Card>
<Card>
<CardHeader>
<Skeleton className="h-6 w-32" />
</CardHeader>
<CardContent>
<div className="space-y-4">
{Array.from({ length: 3 }).map((_, i) => (
<div key={i} className="flex justify-between">
<div>
<Skeleton className="h-4 w-24 mb-1" />
<Skeleton className="h-3 w-32" />
</div>
<Skeleton className="h-3 w-16" />
</div>
))}
</div>
</CardContent>
</Card>
</div>
</div>
</AdminLayout>
)
}
if (!data) {
return (
<AdminLayout>
<div className="flex items-center justify-center h-64">
<p className="text-gray-500">Failed to load dashboard data</p>
</div>
</AdminLayout>
)
}
return (
<AdminLayout>
<div className="space-y-6">
{/* Header */}
<div className="mb-8">
<div className="flex items-center space-x-3">
<div className="w-10 h-10 bg-gradient-to-r from-blue-500 to-indigo-600 rounded-xl flex items-center justify-center">
<span className="text-white font-bold">📊</span>
</div>
<div>
<h1 className="text-3xl font-bold text-gray-900 dark:text-white">Dashboard</h1>
<p className="text-gray-600 dark:text-gray-400 mt-1">
Overview of your SiliconPin platform
</p>
</div>
</div>
</div>
{/* Stats Cards */}
<DashboardStats stats={data} />
{/* Service Breakdown */}
<Card className="border border-gray-200 dark:border-gray-700 shadow-lg bg-white dark:bg-gray-800">
<CardHeader className="pb-4">
<CardTitle className="text-xl font-semibold text-gray-800 dark:text-white flex items-center space-x-2">
<span>🔧</span>
<span>Service Breakdown</span>
</CardTitle>
</CardHeader>
<CardContent>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
{data.services.breakdown.map((service, index) => {
const colors = [
'from-blue-500 to-blue-600',
'from-indigo-500 to-indigo-600',
'from-purple-500 to-purple-600',
'from-pink-500 to-pink-600',
'from-red-500 to-red-600',
'from-orange-500 to-orange-600',
'from-yellow-500 to-yellow-600',
'from-green-500 to-green-600',
'from-teal-500 to-teal-600',
'from-cyan-500 to-cyan-600',
]
const colorClass = colors[index % colors.length]
return (
<div
key={service._id}
className={`bg-gradient-to-br ${colorClass} p-5 rounded-xl text-white shadow-lg hover:shadow-xl transition-all duration-200 hover:scale-105`}
>
<h3 className="font-semibold text-white capitalize text-sm">
{service._id.replace('_', ' ')}
</h3>
<p className="text-2xl font-bold text-white mt-2">{service.count}</p>
<p className="text-sm text-white/90 mt-1">
{service.revenue.toLocaleString()} revenue
</p>
</div>
)
})}
</div>
</CardContent>
</Card>
{/* Recent Activity */}
<RecentActivity
recentUsers={data.recentActivity.users}
recentTransactions={data.recentActivity.transactions}
/>
</div>
</AdminLayout>
)
}