244 lines
7.6 KiB
TypeScript
244 lines
7.6 KiB
TypeScript
'use client';
|
|
|
|
import { useState, useEffect } from 'react';
|
|
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
|
import { Button } from '@/components/ui/button';
|
|
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs';
|
|
import { Badge } from '@/components/ui/badge';
|
|
import {
|
|
Users,
|
|
Calendar,
|
|
Settings,
|
|
BarChart3,
|
|
Bell,
|
|
Shield,
|
|
Clock,
|
|
MapPin,
|
|
Activity,
|
|
LogOut,
|
|
ArrowLeft,
|
|
} from 'lucide-react';
|
|
import { useRouter } from 'next/navigation';
|
|
import { AdminUserManagement } from './AdminUserManagement';
|
|
import { AdminAnnouncementManagement } from './AdminAnnouncementManagement';
|
|
import { AdminLogs } from './AdminLogs';
|
|
import { AdminRecentBookings } from './AdminRecentBookings';
|
|
import { AdminCourtManagement } from './AdminCourtManagement';
|
|
import { AdminSettingsManagement } from './AdminSettingsManagement';
|
|
import { AdminTimeSlotManagement } from './AdminTimeSlotManagement';
|
|
import { ModeToggle } from '@/components/ui/mode-toggle';
|
|
|
|
interface AdminStats {
|
|
totalUsers: number;
|
|
activeCourts: number;
|
|
todaysBookings: number;
|
|
monthlyBookings: number;
|
|
}
|
|
|
|
interface RecentBooking {
|
|
id: string;
|
|
date: string;
|
|
startTime: string;
|
|
endTime: string;
|
|
courtName: string;
|
|
userName: string;
|
|
status: string;
|
|
}
|
|
|
|
export function AdminDashboard() {
|
|
const router = useRouter();
|
|
const [stats, setStats] = useState<AdminStats>({
|
|
totalUsers: 0,
|
|
activeCourts: 0,
|
|
todaysBookings: 0,
|
|
monthlyBookings: 0,
|
|
});
|
|
const [recentBookings, setRecentBookings] = useState<RecentBooking[]>([]);
|
|
const [loading, setLoading] = useState(true);
|
|
|
|
useEffect(() => {
|
|
fetchStats();
|
|
}, []);
|
|
|
|
const fetchStats = async () => {
|
|
try {
|
|
const response = await fetch('/api/admin/stats');
|
|
if (response.ok) {
|
|
const data = await response.json();
|
|
setStats(data.stats);
|
|
setRecentBookings(data.recentBookings);
|
|
}
|
|
} catch (error) {
|
|
console.error('Error fetching admin stats:', error);
|
|
} finally {
|
|
setLoading(false);
|
|
}
|
|
};
|
|
|
|
const handleLogout = async () => {
|
|
try {
|
|
await fetch('/api/auth/logout', {
|
|
method: 'POST',
|
|
});
|
|
router.push('/');
|
|
} catch (error) {
|
|
console.error('Logout error:', error);
|
|
}
|
|
};
|
|
|
|
return (
|
|
<div className='min-h-screen bg-background'>
|
|
{/* Header */}
|
|
<header className='bg-card border-b border-border'>
|
|
<div className='container mx-auto px-4'>
|
|
{/* Desktop Layout */}
|
|
<div className='hidden md:flex items-center justify-between h-16'>
|
|
<div className='flex items-center space-x-4'>
|
|
<Shield className='h-6 w-6 text-blue-600 dark:text-blue-400' />
|
|
<h1 className='text-xl font-semibold text-foreground'>Admin Dashboard</h1>
|
|
</div>
|
|
|
|
<div className='flex items-center space-x-4'>
|
|
<Button
|
|
variant='ghost'
|
|
size='sm'
|
|
onClick={() => router.push('/dashboard')}
|
|
className='flex items-center gap-2'
|
|
>
|
|
<ArrowLeft className='h-4 w-4' />
|
|
Back to Booking
|
|
</Button>
|
|
<Badge variant='secondary'>Administrator</Badge>
|
|
<ModeToggle />
|
|
<Button variant='ghost' size='sm' onClick={handleLogout}>
|
|
<LogOut className='h-4 w-4' />
|
|
Logout
|
|
</Button>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Mobile Layout */}
|
|
<div className='md:hidden py-3'>
|
|
{/* Top row */}
|
|
<div className='flex items-center justify-between mb-3'>
|
|
<div className='flex items-center space-x-2 min-w-0 flex-1'>
|
|
<Shield className='h-5 w-5 text-blue-600 dark:text-blue-400 flex-shrink-0' />
|
|
<h1 className='text-lg font-semibold text-foreground truncate'>Admin Dashboard</h1>
|
|
<Badge variant='secondary' className='flex-shrink-0'>
|
|
Admin
|
|
</Badge>
|
|
</div>
|
|
<ModeToggle />
|
|
</div>
|
|
|
|
{/* Bottom row */}
|
|
<div className='flex items-center justify-between gap-2'>
|
|
<Button
|
|
variant='ghost'
|
|
size='sm'
|
|
onClick={() => router.push('/dashboard')}
|
|
className='flex items-center gap-1 px-3 py-2 min-h-[36px] touch-manipulation'
|
|
>
|
|
<ArrowLeft className='h-4 w-4' />
|
|
<span className='text-xs font-medium'>Booking</span>
|
|
</Button>
|
|
|
|
<Button
|
|
variant='ghost'
|
|
size='sm'
|
|
onClick={handleLogout}
|
|
className='flex items-center gap-1 px-3 py-2 min-h-[36px] touch-manipulation'
|
|
>
|
|
<LogOut className='h-4 w-4' />
|
|
<span className='text-xs font-medium'>Logout</span>
|
|
</Button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</header>
|
|
|
|
<main className='container mx-auto px-4 py-8'>
|
|
{/* Stats Cards */}
|
|
<div className='grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6 mb-8'>
|
|
<Card>
|
|
<CardHeader className='flex flex-row items-center justify-between space-y-0 pb-2'>
|
|
<CardTitle className='text-sm font-medium'>Total Users</CardTitle>
|
|
<Users className='h-4 w-4 text-muted-foreground' />
|
|
</CardHeader>
|
|
<CardContent>
|
|
<div className='text-2xl font-bold'>{loading ? '...' : stats.totalUsers}</div>
|
|
<p className='text-xs text-muted-foreground'>Registered users</p>
|
|
</CardContent>
|
|
</Card>
|
|
|
|
<Card>
|
|
<CardHeader className='flex flex-row items-center justify-between space-y-0 pb-2'>
|
|
<CardTitle className='text-sm font-medium'>Active Courts</CardTitle>
|
|
<MapPin className='h-4 w-4 text-muted-foreground' />
|
|
</CardHeader>
|
|
<CardContent>
|
|
<div className='text-2xl font-bold'>{loading ? '...' : stats.activeCourts}</div>
|
|
<p className='text-xs text-muted-foreground'>Available for booking</p>
|
|
</CardContent>
|
|
</Card>
|
|
|
|
<Card>
|
|
<CardHeader className='flex flex-row items-center justify-between space-y-0 pb-2'>
|
|
<CardTitle className='text-sm font-medium'>Today's Bookings</CardTitle>
|
|
<Calendar className='h-4 w-4 text-muted-foreground' />
|
|
</CardHeader>
|
|
<CardContent>
|
|
<div className='text-2xl font-bold'>{loading ? '...' : stats.todaysBookings}</div>
|
|
<p className='text-xs text-muted-foreground'>Bookings for today</p>
|
|
</CardContent>
|
|
</Card>
|
|
|
|
<Card>
|
|
<CardHeader className='flex flex-row items-center justify-between space-y-0 pb-2'>
|
|
<CardTitle className='text-sm font-medium'>Monthly Bookings</CardTitle>
|
|
<BarChart3 className='h-4 w-4 text-muted-foreground' />
|
|
</CardHeader>
|
|
<CardContent>
|
|
<div className='text-2xl font-bold'>{loading ? '...' : stats.monthlyBookings}</div>
|
|
<p className='text-xs text-muted-foreground'>This month's total</p>
|
|
</CardContent>
|
|
</Card>
|
|
</div>
|
|
|
|
{/* Admin Tabs */}
|
|
<Tabs defaultValue='bookings' className='space-y-6'>
|
|
<TabsList className='grid w-full grid-cols-6'>
|
|
<TabsTrigger value='bookings'>Bookings</TabsTrigger>
|
|
<TabsTrigger value='users'>Users</TabsTrigger>
|
|
<TabsTrigger value='courts'>Courts</TabsTrigger>
|
|
<TabsTrigger value='settings'>Settings</TabsTrigger>
|
|
<TabsTrigger value='announcements'>Announcements</TabsTrigger>
|
|
<TabsTrigger value='logs'>Logs</TabsTrigger>
|
|
</TabsList>
|
|
<TabsContent value='bookings'>
|
|
<AdminRecentBookings />
|
|
</TabsContent>
|
|
<TabsContent value='users'>
|
|
<AdminUserManagement />
|
|
</TabsContent>
|
|
<TabsContent value='courts'>
|
|
<AdminCourtManagement />
|
|
</TabsContent>{' '}
|
|
<TabsContent value='settings'>
|
|
<div className='space-y-6'>
|
|
<AdminSettingsManagement />
|
|
<AdminTimeSlotManagement />
|
|
</div>
|
|
</TabsContent>
|
|
<TabsContent value='announcements'>
|
|
<AdminAnnouncementManagement />
|
|
</TabsContent>{' '}
|
|
<TabsContent value='logs'>
|
|
<AdminLogs />
|
|
</TabsContent>
|
|
</Tabs>
|
|
</main>
|
|
</div>
|
|
);
|
|
}
|