"use client"; import { useState, useEffect } from "react"; import Link from "next/link"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from "@/components/ui/select"; import { Button } from "@/components/ui/button"; import { Users, UserCheck, Calendar, CalendarCheck, CalendarX, DollarSign, TrendingUp, ArrowUpRight, ArrowDownRight, FileText, } from "lucide-react"; import { useAppTheme } from "@/components/ThemeProvider"; import { getAllUsers } from "@/lib/actions/auth"; import { getAppointmentStats, listAppointments } from "@/lib/actions/appointments"; import { useAuth } from "@/hooks/useAuth"; import { toast } from "sonner"; import type { User } from "@/lib/models/auth"; import type { Appointment } from "@/lib/models/appointments"; interface DashboardStats { total_users: number; active_users: number; total_bookings: number; upcoming_bookings: number; completed_bookings: number; cancelled_bookings: number; active_upcoming_meetings: number; total_revenue: number; monthly_revenue: number; // Percentage fields from API users_pct?: number; scheduled_pct?: number; completed_pct?: number; pending_review_pct?: number; rejected_pct?: number; trends: { total_users: string; active_users: string; total_bookings: string; upcoming_bookings: string; completed_bookings: string; cancelled_bookings: string; total_revenue: string; monthly_revenue: string; }; } export default function Dashboard() { const [stats, setStats] = useState(null); const [loading, setLoading] = useState(true); const [timePeriod, setTimePeriod] = useState("last_month"); const { theme } = useAppTheme(); const { user } = useAuth(); const isDark = theme === "dark"; useEffect(() => { const fetchStats = async () => { setLoading(true); try { // Fetch all data in parallel const [users, appointmentStats, appointments] = await Promise.all([ getAllUsers().catch(() => [] as User[]), getAppointmentStats().catch(() => null), listAppointments().catch(() => [] as Appointment[]), ]); // Calculate statistics // Use users count from appointment stats if available, otherwise use getAllUsers result const totalUsers = appointmentStats?.users ?? users.length; const activeUsers = users.filter( (user) => user.is_active === true || user.isActive === true ).length; const totalBookings = appointmentStats?.total_requests || appointments.length; const upcomingBookings = appointmentStats?.scheduled || appointments.filter((apt) => apt.status === "scheduled").length; // Completed bookings from API stats const completedBookings = appointmentStats?.completed || appointments.filter((apt) => apt.status === "completed").length; const cancelledBookings = appointmentStats?.rejected || appointments.filter((apt) => apt.status === "rejected").length; const activeUpcomingMeetings = appointmentStats?.active_upcoming_meetings || 0; // Calculate revenue (assuming appointments have amount field, defaulting to 0) const now = new Date(); const currentMonth = now.getMonth(); const currentYear = now.getFullYear(); const totalRevenue = appointments.reduce((sum, apt) => { // If appointment has amount field, use it, otherwise default to 0 const amount = (apt as any).amount || 0; return sum + amount; }, 0); const monthlyRevenue = appointments .filter((apt) => { if (!apt.scheduled_datetime) return false; const aptDate = new Date(apt.scheduled_datetime); return ( aptDate.getMonth() === currentMonth && aptDate.getFullYear() === currentYear ); }) .reduce((sum, apt) => { const amount = (apt as any).amount || 0; return sum + amount; }, 0); // Trends object kept for compatibility but not used for percentage badges // All percentage badges now use API-provided _pct values const trends = { total_users: "0%", active_users: "0%", total_bookings: "0%", upcoming_bookings: "0", completed_bookings: "0%", cancelled_bookings: "0%", total_revenue: "0%", monthly_revenue: "0%", }; setStats({ total_users: totalUsers, active_users: activeUsers, total_bookings: totalBookings, upcoming_bookings: upcomingBookings, completed_bookings: completedBookings, cancelled_bookings: cancelledBookings, active_upcoming_meetings: activeUpcomingMeetings, total_revenue: totalRevenue, monthly_revenue: monthlyRevenue, // Include percentage fields from API users_pct: appointmentStats?.users_pct, scheduled_pct: appointmentStats?.scheduled_pct, completed_pct: appointmentStats?.completed_pct, pending_review_pct: appointmentStats?.pending_review_pct, rejected_pct: appointmentStats?.rejected_pct, trends, }); } catch (error) { toast.error("Failed to load dashboard statistics"); // Set default values on error setStats({ total_users: 0, active_users: 0, total_bookings: 0, upcoming_bookings: 0, completed_bookings: 0, cancelled_bookings: 0, active_upcoming_meetings: 0, total_revenue: 0, monthly_revenue: 0, users_pct: undefined, scheduled_pct: undefined, completed_pct: undefined, pending_review_pct: undefined, rejected_pct: undefined, trends: { total_users: "0%", active_users: "0%", total_bookings: "0%", upcoming_bookings: "0", completed_bookings: "0%", cancelled_bookings: "0%", total_revenue: "0%", monthly_revenue: "0%", }, }); } finally { setLoading(false); } }; fetchStats(); }, [timePeriod]); const statCards = [ { title: "Total Users", value: stats?.total_users ?? 0, icon: Users, trend: stats?.users_pct !== undefined ? `${Math.round(stats.users_pct)}%` : undefined, trendUp: true, }, { title: "Active Users", value: stats?.active_users ?? 0, icon: UserCheck, trend: undefined, // No _pct field from API for active users trendUp: true, }, { title: "Total Bookings", value: stats?.total_bookings ?? 0, icon: Calendar, trend: undefined, // No _pct field from API for total bookings trendUp: true, }, { title: "Upcoming Bookings", value: stats?.upcoming_bookings ?? 0, icon: CalendarCheck, trend: stats?.scheduled_pct !== undefined ? `${Math.round(stats.scheduled_pct)}%` : undefined, trendUp: true, }, { title: "Completed Bookings", value: stats?.completed_bookings ?? 0, icon: CalendarCheck, trend: stats?.completed_pct !== undefined ? `${Math.round(stats.completed_pct)}%` : undefined, trendUp: true, }, { title: "Cancelled Bookings", value: stats?.cancelled_bookings ?? 0, icon: CalendarX, trend: stats?.rejected_pct !== undefined ? `${Math.round(stats.rejected_pct)}%` : undefined, trendUp: false, }, { title: "Active Upcoming Meetings", value: stats?.active_upcoming_meetings ?? 0, icon: CalendarCheck, trend: undefined, trendUp: true, }, ]; const getTrendClasses = (trendUp: boolean) => { if (isDark) { return trendUp ? "bg-green-500/20 text-green-200" : "bg-red-500/20 text-red-200"; } return trendUp ? "bg-green-50 text-green-700" : "bg-red-50 text-red-700"; }; return (
{/* Main Content */}
{/* Welcome Section */}

Welcome Back! {user?.first_name || ""}

Here's an overview of your practice today

{loading ? (
) : ( <> {/* Stats Grid */}
{statCards.map((card, index) => { const Icon = card.icon; return (
{card.trend && (
{card.trendUp ? ( ) : ( )} {card.trend}
)}

{card.title}

{card.value}

{/* "vs last month" text commented out */} {/*

vs last month

*/}
); })}
)}
); }