"use client"; import { useMemo, useEffect, useState } from "react"; import { useRouter } from "next/navigation"; import { Button } from "@/components/ui/button"; import { Calendar, Clock, User, Mail, Phone, Heart, CalendarPlus, Video, CheckCircle2, XCircle, CalendarCheck, ArrowUpRight, Settings, Loader2, } from "lucide-react"; import Link from "next/link"; import { Navbar } from "@/components/Navbar"; import { useAppTheme } from "@/components/ThemeProvider"; import { useAuth } from "@/hooks/useAuth"; import { getUserAppointments, getUserAppointmentStats } from "@/lib/actions/appointments"; import type { Appointment, UserAppointmentStats } from "@/lib/models/appointments"; import { toast } from "sonner"; export default function UserDashboard() { const router = useRouter(); const { theme } = useAppTheme(); const isDark = theme === "dark"; const { user } = useAuth(); const [appointments, setAppointments] = useState([]); const [loading, setLoading] = useState(true); const [stats, setStats] = useState(null); const [loadingStats, setLoadingStats] = useState(true); // Fetch user appointments from user-specific endpoint useEffect(() => { const fetchAppointments = async () => { setLoading(true); try { const data = await getUserAppointments(); setAppointments(data || []); } catch (error) { toast.error("Failed to load appointments. Please try again."); setAppointments([]); } finally { setLoading(false); } }; fetchAppointments(); }, []); // Fetch stats from API for authenticated user useEffect(() => { const fetchStats = async () => { setLoadingStats(true); try { const statsData = await getUserAppointmentStats(); setStats(statsData); } catch (error) { toast.error("Failed to load appointment statistics."); setStats({ total_requests: 0, pending_review: 0, scheduled: 0, rejected: 0, completed: 0, completion_rate: 0, }); } finally { setLoadingStats(false); } }; fetchStats(); }, []); const formatDate = (dateString: string) => { const date = new Date(dateString); return date.toLocaleDateString("en-US", { weekday: "long", year: "numeric", month: "long", day: "numeric", }); }; const formatTime = (dateString: string) => { const date = new Date(dateString); return date.toLocaleTimeString("en-US", { hour: "numeric", minute: "2-digit", }); }; const formatMemberSince = (dateString?: string) => { if (!dateString) return "N/A"; const date = new Date(dateString); return date.toLocaleDateString("en-US", { month: "long", year: "numeric", }); }; // Filter appointments by status const upcomingAppointments = useMemo(() => { return appointments.filter( (appointment) => appointment.status === "scheduled" ); }, [appointments]); const pendingAppointments = useMemo(() => { return appointments.filter( (appointment) => appointment.status === "pending_review" ); }, [appointments]); const completedAppointments = useMemo(() => { return appointments.filter( (appointment) => appointment.status === "completed" ); }, [appointments]); const rejectedAppointments = useMemo(() => { return appointments.filter( (appointment) => appointment.status === "rejected" ); }, [appointments]); // Sort appointments by created_at (newest first) const allAppointments = useMemo(() => { return [...appointments].sort((a, b) => { const dateA = new Date(a.created_at).getTime(); const dateB = new Date(b.created_at).getTime(); return dateB - dateA; }); }, [appointments]); // Use stats from API, fallback to calculated stats if API stats not available const displayStats = useMemo(() => { if (stats) { return { scheduled: stats.scheduled || 0, scheduled_pct: stats.scheduled_pct, completed: stats.completed || 0, completed_pct: stats.completed_pct, pending_review: stats.pending_review || 0, pending_review_pct: stats.pending_review_pct, rejected: stats.rejected || 0, rejected_pct: stats.rejected_pct, total_requests: stats.total_requests || 0, completion_rate: stats.completion_rate || 0, }; } // Fallback: calculate from appointments if stats not loaded yet const scheduled = appointments.filter(a => a.status === "scheduled").length; const completed = appointments.filter(a => a.status === "completed").length; const pending_review = appointments.filter(a => a.status === "pending_review").length; const rejected = appointments.filter(a => a.status === "rejected").length; const total_requests = appointments.length; const completion_rate = total_requests > 0 ? (scheduled / total_requests) * 100 : 0; return { scheduled, completed, pending_review, rejected, total_requests, completion_rate, }; }, [stats, appointments]); return (
{/* Main Content */}
{/* Welcome Section */}

Welcome Back!

Here's an overview of your appointments

{loading ? (
) : ( <> {/* Stats Grid */}
{displayStats.scheduled_pct !== undefined && (
{`${Math.round(displayStats.scheduled_pct)}%`}
)}

Upcoming Appointments

{displayStats.scheduled}

{displayStats.completed_pct !== undefined && (
{`${Math.round(displayStats.completed_pct)}%`}
)}

Completed Sessions

{displayStats.completed}

{/* No percentage badge for total appointments */}

Total Appointments

{displayStats.total_requests}

{displayStats.pending_review_pct !== undefined && (
{`${Math.round(displayStats.pending_review_pct)}%`}
)}

Pending Review

{displayStats.pending_review}

{/* All Appointments Section */} {allAppointments.length > 0 ? (

All Appointments

{allAppointments.map((appointment) => { const getStatusColor = (status: string) => { switch (status) { case "scheduled": return isDark ? 'bg-green-900/30 text-green-400' : 'bg-green-50 text-green-700'; case "pending_review": return isDark ? 'bg-yellow-900/30 text-yellow-400' : 'bg-yellow-50 text-yellow-700'; case "completed": return isDark ? 'bg-blue-900/30 text-blue-400' : 'bg-blue-50 text-blue-700'; case "rejected": return isDark ? 'bg-red-900/30 text-red-400' : 'bg-red-50 text-red-700'; default: return isDark ? 'bg-gray-700 text-gray-300' : 'bg-gray-100 text-gray-700'; } }; const formatStatus = (status: string) => { return status.replace("_", " ").replace(/\b\w/g, (l) => l.toUpperCase()); }; const dayNames = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday']; const timeSlotLabels: Record = { morning: 'Morning', afternoon: 'Lunchtime', evening: 'Evening', }; return ( router.push(`/user/appointments/${appointment.id}`)} > ); })}
Appointment Status Actions
{appointment.first_name} {appointment.last_name}
{appointment.reason && (
{appointment.reason}
)} {appointment.scheduled_datetime && (
{formatDate(appointment.scheduled_datetime)}
)}
{appointment.scheduled_datetime ? ( <>
{formatDate(appointment.scheduled_datetime)}
{formatTime(appointment.scheduled_datetime)}
) : (
Not scheduled
)}
{formatStatus(appointment.status)}
) : !loading && (

No Appointments

You don't have any appointments yet. Book an appointment to get started.

)} {/* Account Information */} {user && (

Account Information

Full Name

{user.first_name} {user.last_name}

Email

{user.email}

{user.phone_number && (

Phone

{user.phone_number}

)} {user.date_joined && (

Member Since

{formatMemberSince(user.date_joined)}

)}
)} )}
); }