diff --git a/app/(admin)/booking/page.tsx b/app/(admin)/booking/page.tsx index d3dd3db..97bcdb5 100644 --- a/app/(admin)/booking/page.tsx +++ b/app/(admin)/booking/page.tsx @@ -1,15 +1,351 @@ "use client"; +import { useState, useEffect } from "react"; import SideNav from "@/app/(admin)/_components/side-nav"; +import { + Search, + Bell, + Calendar, + Clock, + User, + DollarSign, + Video, + FileText, + MoreVertical, +} from "lucide-react"; + +interface User { + ID: number; + CreatedAt?: string; + UpdatedAt?: string; + DeletedAt?: string | null; + first_name: string; + last_name: string; + email: string; + phone: string; + location: string; + date_of_birth?: string; + is_admin?: boolean; + bookings?: null; +} + +interface Booking { + ID: number; + CreatedAt: string; + UpdatedAt: string; + DeletedAt: string | null; + user_id: number; + user: User; + scheduled_at: string; + duration: number; + status: string; + jitsi_room_id: string; + jitsi_room_url: string; + payment_id: string; + payment_status: string; + amount: number; + notes: string; +} + +interface BookingsResponse { + bookings: Booking[]; + limit: number; + offset: number; + total: number; +} export default function Booking() { + const [bookings, setBookings] = useState([]); + const [loading, setLoading] = useState(true); + const [searchTerm, setSearchTerm] = useState(""); + + useEffect(() => { + // Simulate API call + const fetchBookings = async () => { + setLoading(true); + await new Promise((resolve) => setTimeout(resolve, 500)); + + // Mock API response + const mockData: BookingsResponse = { + bookings: [ + { + ID: 1, + CreatedAt: "2025-11-06T11:33:45.704633Z", + UpdatedAt: "2025-11-06T11:33:45.707543Z", + DeletedAt: null, + user_id: 3, + user: { + ID: 3, + CreatedAt: "2025-11-06T10:43:01.299311Z", + UpdatedAt: "2025-11-06T10:43:48.427284Z", + DeletedAt: null, + first_name: "John", + last_name: "Smith", + email: "john.doe@example.com", + phone: "+1234567891", + location: "Los Angeles, CA", + date_of_birth: "0001-01-01T00:00:00Z", + is_admin: true, + bookings: null, + }, + scheduled_at: "2025-11-07T10:00:00Z", + duration: 60, + status: "scheduled", + jitsi_room_id: "booking-1-1762428825-22c92ced2870c17c", + jitsi_room_url: + "https://meet.jit.si/booking-1-1762428825-22c92ced2870c17c", + payment_id: "", + payment_status: "pending", + amount: 52, + notes: "Initial consultation session", + }, + ], + limit: 50, + offset: 0, + total: 1, + }; + + setBookings(mockData.bookings); + setLoading(false); + }; + + fetchBookings(); + }, []); + + const formatDate = (dateString: string) => { + const date = new Date(dateString); + return date.toLocaleDateString("en-US", { + month: "short", + day: "numeric", + year: "numeric", + }); + }; + + const formatTime = (dateString: string) => { + const date = new Date(dateString); + return date.toLocaleTimeString("en-US", { + hour: "numeric", + minute: "2-digit", + hour12: true, + }); + }; + + const getStatusColor = (status: string) => { + switch (status.toLowerCase()) { + case "scheduled": + return "bg-blue-100 text-blue-700"; + case "completed": + return "bg-green-100 text-green-700"; + case "cancelled": + return "bg-red-100 text-red-700"; + case "pending": + return "bg-yellow-100 text-yellow-700"; + default: + return "bg-gray-100 text-gray-700"; + } + }; + + const getPaymentStatusColor = (status: string) => { + switch (status.toLowerCase()) { + case "paid": + return "bg-green-100 text-green-700"; + case "pending": + return "bg-yellow-100 text-yellow-700"; + case "failed": + return "bg-red-100 text-red-700"; + default: + return "bg-gray-100 text-gray-700"; + } + }; + + const filteredBookings = bookings.filter( + (booking) => + booking.user.first_name + .toLowerCase() + .includes(searchTerm.toLowerCase()) || + booking.user.last_name + .toLowerCase() + .includes(searchTerm.toLowerCase()) || + booking.user.email.toLowerCase().includes(searchTerm.toLowerCase()) + ); + return (
{/* Side Navigation */}
+ {/* Top Header Bar */} +
+
+
+
+ + setSearchTerm(e.target.value)} + className="w-full pl-10 pr-4 py-2 border border-gray-200 rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-gray-200 focus:border-transparent" + /> +
+
+
+ +
+
+
+ + {/* Main Content */}
+ {/* Page Header */} +
+
+

+ Bookings +

+

+ Manage and view all appointment bookings +

+
+ +
+ + {loading ? ( +
+
+
+ ) : filteredBookings.length === 0 ? ( +
+ +

No bookings found

+

+ {searchTerm + ? "Try adjusting your search terms" + : "Create a new booking to get started"} +

+
+ ) : ( +
+
+ + + + + + + + + + + + + + {filteredBookings.map((booking) => ( + + + + + + + + + + ))} + +
+ Patient + + Date & Time + + Duration + + Status + + Payment + + Amount + + Actions +
+
+
+ +
+
+
+ {booking.user.first_name} {booking.user.last_name} +
+
+ {booking.user.email} +
+
+
+
+
+ {formatDate(booking.scheduled_at)} +
+
+ + {formatTime(booking.scheduled_at)} +
+
+ {booking.duration} min + + + {booking.status} + + + + {booking.payment_status} + + + ${booking.amount} + +
+ {booking.jitsi_room_url && ( + + + )} + {booking.notes && ( + + )} + +
+
+
+
+ )}
diff --git a/app/(admin)/dashboard/page.tsx b/app/(admin)/dashboard/page.tsx index 00938e6..50ec090 100644 --- a/app/(admin)/dashboard/page.tsx +++ b/app/(admin)/dashboard/page.tsx @@ -1,17 +1,184 @@ "use client"; +import { useState, useEffect } from "react"; import SideNav from "@/app/(admin)/_components/side-nav"; +import { + Users, + UserCheck, + Calendar, + CalendarCheck, + CalendarX, + DollarSign, + TrendingUp, + Search, + Bell, +} from "lucide-react"; + +interface DashboardStats { + total_users: number; + active_users: number; + total_bookings: number; + upcoming_bookings: number; + completed_bookings: number; + cancelled_bookings: number; + total_revenue: number; + monthly_revenue: number; +} export default function Dashboard() { + const [stats, setStats] = useState(null); + const [loading, setLoading] = useState(true); + + useEffect(() => { + // Simulate API call + const fetchStats = async () => { + setLoading(true); + // Simulate network delay + await new Promise((resolve) => setTimeout(resolve, 500)); + + // Mock API response + const mockData: DashboardStats = { + total_users: 3, + active_users: 3, + total_bookings: 6, + upcoming_bookings: 6, + completed_bookings: 0, + cancelled_bookings: 0, + total_revenue: 0, + monthly_revenue: 0, + }; + + setStats(mockData); + setLoading(false); + }; + + fetchStats(); + }, []); + + const statCards = [ + { + title: "Total Users", + value: stats?.total_users ?? 0, + icon: Users, + bgColor: "bg-gray-50", + iconColor: "text-gray-600", + }, + { + title: "Active Users", + value: stats?.active_users ?? 0, + icon: UserCheck, + bgColor: "bg-gray-50", + iconColor: "text-gray-600", + }, + { + title: "Total Bookings", + value: stats?.total_bookings ?? 0, + icon: Calendar, + bgColor: "bg-gray-50", + iconColor: "text-gray-600", + }, + { + title: "Upcoming Bookings", + value: stats?.upcoming_bookings ?? 0, + icon: CalendarCheck, + bgColor: "bg-gray-50", + iconColor: "text-gray-600", + }, + { + title: "Completed Bookings", + value: stats?.completed_bookings ?? 0, + icon: CalendarCheck, + bgColor: "bg-gray-50", + iconColor: "text-green-600", + }, + { + title: "Cancelled Bookings", + value: stats?.cancelled_bookings ?? 0, + icon: CalendarX, + bgColor: "bg-gray-50", + iconColor: "text-red-600", + }, + { + title: "Total Revenue", + value: `$${stats?.total_revenue.toLocaleString() ?? 0}`, + icon: DollarSign, + bgColor: "bg-gray-50", + iconColor: "text-gray-600", + }, + { + title: "Monthly Revenue", + value: `$${stats?.monthly_revenue.toLocaleString() ?? 0}`, + icon: TrendingUp, + bgColor: "bg-gray-50", + iconColor: "text-gray-600", + }, + ]; + return (
{/* Side Navigation */}
+ {/* Top Header Bar */} +
+
+
+
+ + +
+
+
+ +
+
+
+ {/* Main Content */}
-

Dashboard

+ {/* Page Header */} +
+

Dashboard

+

Overview of your practice statistics

+
+ + {loading ? ( +
+
+
+ ) : ( +
+ {statCards.map((card, index) => { + const Icon = card.icon; + return ( +
+
+
+ +
+
+

+ {card.title} +

+

+ {card.value} +

+
+ ); + })} +
+ )}