diff --git a/License.md b/License.md index eb0d38b..91b2fd8 100644 --- a/License.md +++ b/License.md @@ -5,7 +5,7 @@ This project is licensed under the MIT License: ``` MIT License -Copyright (c) 2024 Table Tennis Booking System +Copyright (c) 2025 Vilim Mikic Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/app/api/admin/users/route.ts b/app/api/admin/users/route.ts index 18d1540..c1b2b67 100644 --- a/app/api/admin/users/route.ts +++ b/app/api/admin/users/route.ts @@ -1,7 +1,7 @@ import { NextRequest, NextResponse } from 'next/server'; import { db } from '@/lib/db'; -import { users } from '@/lib/db/schema'; -import { eq } from 'drizzle-orm'; +import { users, bookings } from '@/lib/db/schema'; +import { eq, desc, max, sql, and } from 'drizzle-orm'; import { getSession } from '@/lib/session'; import bcrypt from 'bcryptjs'; @@ -12,6 +12,7 @@ export async function GET(request: NextRequest) { return NextResponse.json({ error: 'Unauthorized' }, { status: 401 }); } + // Get all users with their last booking date using LEFT JOIN and GROUP BY const allUsers = await db .select({ id: users.id, @@ -20,8 +21,11 @@ export async function GET(request: NextRequest) { email: users.email, role: users.role, createdAt: users.createdAt, + lastBookingDate: max(bookings.date), }) - .from(users); + .from(users) + .leftJoin(bookings, and(eq(bookings.userId, users.id), eq(bookings.status, 'active'))) + .groupBy(users.id, users.name, users.surname, users.email, users.role, users.createdAt); return NextResponse.json({ users: allUsers }); } catch (error) { diff --git a/components/admin/AdminUserManagement.tsx b/components/admin/AdminUserManagement.tsx index 35fd833..30d6d02 100644 --- a/components/admin/AdminUserManagement.tsx +++ b/components/admin/AdminUserManagement.tsx @@ -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() { All Users ({filteredUsers.length}) - - - - Name - Email - Role - Created - Actions - - - - {filteredUsers.map((user) => ( - - - {user.name} {user.surname} - - -
- - {user.email} + {/* Desktop Table */} +
+
+ + + Name + Email + Role + Created + Last Played + Actions + + + + {filteredUsers.map((user) => ( + + + {user.name} {user.surname} + + +
+ + {user.email} +
+
+ + + {user.role} + + + +
+ + {new Date(user.createdAt).toLocaleDateString('en-IE')} +
+
+ + {user.lastBookingDate ? ( +
+ + {new Date(user.lastBookingDate).toLocaleDateString('en-IE')} +
+ ) : ( +
+ + Never played +
+ )} +
+ +
+ + +
+
+
+ ))} +
+
+ + + {/* Mobile Card Layout */} +
+ {filteredUsers.map((user) => ( + +
+
+
+

+ {user.name} {user.surname} +

+

+ + {user.email} +

- - - + {user.role} - - -
- - {new Date(user.createdAt).toLocaleDateString('en-IE')} +
+ +
+
+ Created + + + {new Date(user.createdAt).toLocaleDateString('en-IE')} +
- - -
- - +
+ Last Played + {user.lastBookingDate ? ( + + + {new Date(user.lastBookingDate).toLocaleDateString('en-IE')} + + ) : ( + + + Never played + + )}
- - - ))} - - +
+ +
+ + +
+
+ + ))} +
{filteredUsers.length === 0 && (
No users found matching your search criteria diff --git a/components/admin/admin-dashboard.tsx b/components/admin/admin-dashboard.tsx index 661d974..97cd4e4 100644 --- a/components/admin/admin-dashboard.tsx +++ b/components/admin/admin-dashboard.tsx @@ -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({ totalUsers: 0, activeCourts: 0, @@ -206,8 +214,9 @@ export function AdminDashboard() {
{/* Admin Tabs */} - - + + {/* Desktop Tabs */} + Bookings Users Courts @@ -215,6 +224,48 @@ export function AdminDashboard() { Announcements Logs + {/* Mobile Dropdown */} +
+ + + + + + setActiveTab('bookings')}> + + Bookings + + setActiveTab('users')}> + + Users + + setActiveTab('courts')}> + + Courts + + setActiveTab('settings')}> + + Settings + + setActiveTab('announcements')}> + + Announcements + + setActiveTab('logs')}> + + Logs + + + +