Update license year, enhance user management with last booking date, and improve admin dashboard navigation
This commit is contained in:
@@ -30,6 +30,7 @@ interface User {
|
||||
email: string;
|
||||
role: 'user' | 'admin';
|
||||
createdAt: string;
|
||||
lastBookingDate: string | null;
|
||||
}
|
||||
|
||||
interface UserFormData {
|
||||
@@ -387,58 +388,151 @@ export function AdminUserManagement() {
|
||||
<CardTitle>All Users ({filteredUsers.length})</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<Table>
|
||||
<TableHeader>
|
||||
<TableRow>
|
||||
<TableHead>Name</TableHead>
|
||||
<TableHead>Email</TableHead>
|
||||
<TableHead>Role</TableHead>
|
||||
<TableHead>Created</TableHead>
|
||||
<TableHead>Actions</TableHead>
|
||||
</TableRow>
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
{filteredUsers.map((user) => (
|
||||
<TableRow key={user.id}>
|
||||
<TableCell className='font-medium'>
|
||||
{user.name} {user.surname}
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<div className='flex items-center gap-2'>
|
||||
<Mail className='h-4 w-4 text-gray-500' />
|
||||
{user.email}
|
||||
{/* Desktop Table */}
|
||||
<div className='hidden md:block'>
|
||||
<Table>
|
||||
<TableHeader>
|
||||
<TableRow>
|
||||
<TableHead>Name</TableHead>
|
||||
<TableHead>Email</TableHead>
|
||||
<TableHead>Role</TableHead>
|
||||
<TableHead>Created</TableHead>
|
||||
<TableHead>Last Played</TableHead>
|
||||
<TableHead>Actions</TableHead>
|
||||
</TableRow>
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
{filteredUsers.map((user) => (
|
||||
<TableRow key={user.id}>
|
||||
<TableCell className='font-medium'>
|
||||
{user.name} {user.surname}
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<div className='flex items-center gap-2'>
|
||||
<Mail className='h-4 w-4 text-gray-500' />
|
||||
{user.email}
|
||||
</div>
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<Badge variant={user.role === 'admin' ? 'default' : 'secondary'}>
|
||||
{user.role}
|
||||
</Badge>
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<div className='flex items-center gap-2'>
|
||||
<Calendar className='h-4 w-4 text-gray-500' />
|
||||
{new Date(user.createdAt).toLocaleDateString('en-IE')}
|
||||
</div>
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
{user.lastBookingDate ? (
|
||||
<div className='flex items-center gap-2'>
|
||||
<Calendar className='h-4 w-4 text-green-600' />
|
||||
{new Date(user.lastBookingDate).toLocaleDateString('en-IE')}
|
||||
</div>
|
||||
) : (
|
||||
<div className='flex items-center gap-2 text-gray-500'>
|
||||
<Calendar className='h-4 w-4' />
|
||||
Never played
|
||||
</div>
|
||||
)}
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<div className='flex gap-2'>
|
||||
<Button
|
||||
variant='outline'
|
||||
size='sm'
|
||||
onClick={() => openEditDialog(user)}
|
||||
>
|
||||
<Edit className='h-4 w-4' />
|
||||
</Button>
|
||||
<Button
|
||||
variant='outline'
|
||||
size='sm'
|
||||
onClick={() => openDeleteDialog(user)}
|
||||
className='text-red-600 hover:text-red-700'
|
||||
>
|
||||
<Trash2 className='h-4 w-4' />
|
||||
</Button>
|
||||
</div>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
))}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</div>
|
||||
|
||||
{/* Mobile Card Layout */}
|
||||
<div className='md:hidden space-y-4'>
|
||||
{filteredUsers.map((user) => (
|
||||
<Card key={user.id} className='p-4'>
|
||||
<div className='space-y-3'>
|
||||
<div className='flex items-start justify-between'>
|
||||
<div>
|
||||
<h3 className='font-medium text-sm'>
|
||||
{user.name} {user.surname}
|
||||
</h3>
|
||||
<p className='text-xs text-muted-foreground flex items-center gap-1 mt-1'>
|
||||
<Mail className='h-3 w-3' />
|
||||
{user.email}
|
||||
</p>
|
||||
</div>
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<Badge variant={user.role === 'admin' ? 'default' : 'secondary'}>
|
||||
<Badge
|
||||
variant={user.role === 'admin' ? 'default' : 'secondary'}
|
||||
className='text-xs'
|
||||
>
|
||||
{user.role}
|
||||
</Badge>
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<div className='flex items-center gap-2'>
|
||||
<Calendar className='h-4 w-4 text-gray-500' />
|
||||
{new Date(user.createdAt).toLocaleDateString('en-IE')}
|
||||
</div>
|
||||
|
||||
<div className='grid grid-cols-2 gap-3 text-xs'>
|
||||
<div>
|
||||
<span className='text-muted-foreground block'>Created</span>
|
||||
<span className='flex items-center gap-1 mt-1'>
|
||||
<Calendar className='h-3 w-3 text-gray-500' />
|
||||
{new Date(user.createdAt).toLocaleDateString('en-IE')}
|
||||
</span>
|
||||
</div>
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<div className='flex gap-2'>
|
||||
<Button variant='outline' size='sm' onClick={() => openEditDialog(user)}>
|
||||
<Edit className='h-4 w-4' />
|
||||
</Button>
|
||||
<Button
|
||||
variant='outline'
|
||||
size='sm'
|
||||
onClick={() => openDeleteDialog(user)}
|
||||
className='text-red-600 hover:text-red-700'
|
||||
>
|
||||
<Trash2 className='h-4 w-4' />
|
||||
</Button>
|
||||
<div>
|
||||
<span className='text-muted-foreground block'>Last Played</span>
|
||||
{user.lastBookingDate ? (
|
||||
<span className='flex items-center gap-1 mt-1'>
|
||||
<Calendar className='h-3 w-3 text-green-600' />
|
||||
{new Date(user.lastBookingDate).toLocaleDateString('en-IE')}
|
||||
</span>
|
||||
) : (
|
||||
<span className='flex items-center gap-1 mt-1 text-gray-500'>
|
||||
<Calendar className='h-3 w-3' />
|
||||
Never played
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
))}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</div>
|
||||
|
||||
<div className='flex gap-2 pt-2'>
|
||||
<Button
|
||||
variant='outline'
|
||||
size='sm'
|
||||
onClick={() => openEditDialog(user)}
|
||||
className='flex-1'
|
||||
>
|
||||
<Edit className='h-3 w-3 mr-1' />
|
||||
Edit
|
||||
</Button>
|
||||
<Button
|
||||
variant='outline'
|
||||
size='sm'
|
||||
onClick={() => openDeleteDialog(user)}
|
||||
className='flex-1 text-red-600 hover:text-red-700'
|
||||
>
|
||||
<Trash2 className='h-3 w-3 mr-1' />
|
||||
Delete
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
))}
|
||||
</div>
|
||||
{filteredUsers.length === 0 && (
|
||||
<div className='text-center py-8 text-gray-500'>
|
||||
No users found matching your search criteria
|
||||
|
||||
@@ -5,6 +5,12 @@ 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 {
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuItem,
|
||||
DropdownMenuTrigger,
|
||||
} from '@/components/ui/dropdown-menu';
|
||||
import {
|
||||
Users,
|
||||
Calendar,
|
||||
@@ -17,6 +23,7 @@ import {
|
||||
Activity,
|
||||
LogOut,
|
||||
ArrowLeft,
|
||||
ChevronDown,
|
||||
} from 'lucide-react';
|
||||
import { useRouter } from 'next/navigation';
|
||||
import { AdminUserManagement } from './AdminUserManagement';
|
||||
@@ -47,6 +54,7 @@ interface RecentBooking {
|
||||
|
||||
export function AdminDashboard() {
|
||||
const router = useRouter();
|
||||
const [activeTab, setActiveTab] = useState('bookings');
|
||||
const [stats, setStats] = useState<AdminStats>({
|
||||
totalUsers: 0,
|
||||
activeCourts: 0,
|
||||
@@ -206,8 +214,9 @@ export function AdminDashboard() {
|
||||
</div>
|
||||
|
||||
{/* Admin Tabs */}
|
||||
<Tabs defaultValue='bookings' className='space-y-6'>
|
||||
<TabsList className='grid w-full grid-cols-6'>
|
||||
<Tabs value={activeTab} onValueChange={setActiveTab} className='space-y-6'>
|
||||
{/* Desktop Tabs */}
|
||||
<TabsList className='hidden md:grid w-full grid-cols-6'>
|
||||
<TabsTrigger value='bookings'>Bookings</TabsTrigger>
|
||||
<TabsTrigger value='users'>Users</TabsTrigger>
|
||||
<TabsTrigger value='courts'>Courts</TabsTrigger>
|
||||
@@ -215,6 +224,48 @@ export function AdminDashboard() {
|
||||
<TabsTrigger value='announcements'>Announcements</TabsTrigger>
|
||||
<TabsTrigger value='logs'>Logs</TabsTrigger>
|
||||
</TabsList>
|
||||
{/* Mobile Dropdown */}
|
||||
<div className='md:hidden'>
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button variant='outline' className='w-full justify-between'>
|
||||
{activeTab === 'bookings' && 'Bookings'}
|
||||
{activeTab === 'users' && 'Users'}
|
||||
{activeTab === 'courts' && 'Courts'}
|
||||
{activeTab === 'settings' && 'Settings'}
|
||||
{activeTab === 'announcements' && 'Announcements'}
|
||||
{activeTab === 'logs' && 'Logs'}
|
||||
<ChevronDown className='h-4 w-4' />
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent className='w-full'>
|
||||
<DropdownMenuItem onClick={() => setActiveTab('bookings')}>
|
||||
<Calendar className='h-4 w-4 mr-2' />
|
||||
Bookings
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem onClick={() => setActiveTab('users')}>
|
||||
<Users className='h-4 w-4 mr-2' />
|
||||
Users
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem onClick={() => setActiveTab('courts')}>
|
||||
<MapPin className='h-4 w-4 mr-2' />
|
||||
Courts
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem onClick={() => setActiveTab('settings')}>
|
||||
<Settings className='h-4 w-4 mr-2' />
|
||||
Settings
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem onClick={() => setActiveTab('announcements')}>
|
||||
<Bell className='h-4 w-4 mr-2' />
|
||||
Announcements
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem onClick={() => setActiveTab('logs')}>
|
||||
<Activity className='h-4 w-4 mr-2' />
|
||||
Logs
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
</div>
|
||||
<TabsContent value='bookings'>
|
||||
<AdminRecentBookings />
|
||||
</TabsContent>
|
||||
|
||||
Reference in New Issue
Block a user