Update login redirection to home page and add automatic redirect after booking submission. Enhance user flow by ensuring users are directed to the home page upon successful actions.
This commit is contained in:
parent
75a89fc07c
commit
bff566eb9b
@ -65,7 +65,7 @@ export default function Login() {
|
||||
{/* Login Form */}
|
||||
<form className="space-y-6" onSubmit={(e) => {
|
||||
e.preventDefault();
|
||||
router.push("/dashboard");
|
||||
router.push("/");
|
||||
}}>
|
||||
{/* Email Field */}
|
||||
<div className="space-y-2">
|
||||
|
||||
@ -179,6 +179,11 @@ export default function BookNowPage() {
|
||||
|
||||
setBooking(bookingData);
|
||||
setLoading(false);
|
||||
|
||||
// Redirect to home after 2 seconds
|
||||
setTimeout(() => {
|
||||
router.push("/");
|
||||
}, 2000);
|
||||
} catch (err) {
|
||||
setError("Failed to submit booking. Please try again.");
|
||||
setLoading(false);
|
||||
|
||||
330
app/user/dashboard/page.tsx
Normal file
330
app/user/dashboard/page.tsx
Normal file
@ -0,0 +1,330 @@
|
||||
"use client";
|
||||
|
||||
import { useState, useEffect } from "react";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import {
|
||||
Calendar,
|
||||
Clock,
|
||||
User,
|
||||
Mail,
|
||||
Phone,
|
||||
Heart,
|
||||
CalendarPlus,
|
||||
Video,
|
||||
CheckCircle2,
|
||||
XCircle,
|
||||
CalendarCheck,
|
||||
ArrowUpRight,
|
||||
} from "lucide-react";
|
||||
import Link from "next/link";
|
||||
import { Navbar } from "@/components/Navbar";
|
||||
|
||||
interface Booking {
|
||||
ID: number;
|
||||
scheduled_at: string;
|
||||
duration: number;
|
||||
status: string;
|
||||
amount: number;
|
||||
notes: string;
|
||||
jitsi_room_url?: string;
|
||||
}
|
||||
|
||||
export default function UserDashboard() {
|
||||
const [bookings, setBookings] = useState<Booking[]>([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
|
||||
useEffect(() => {
|
||||
// Simulate API call to fetch user bookings
|
||||
const fetchBookings = async () => {
|
||||
setLoading(true);
|
||||
try {
|
||||
// Simulate network delay
|
||||
await new Promise((resolve) => setTimeout(resolve, 500));
|
||||
|
||||
// Mock data - in real app, this would fetch from API
|
||||
const mockBookings: Booking[] = [
|
||||
{
|
||||
ID: 1,
|
||||
scheduled_at: "2025-01-15T10:00:00Z",
|
||||
duration: 60,
|
||||
status: "scheduled",
|
||||
amount: 150,
|
||||
notes: "Initial consultation",
|
||||
jitsi_room_url: "https://meet.jit.si/sample-room",
|
||||
},
|
||||
];
|
||||
setBookings(mockBookings);
|
||||
} catch (error) {
|
||||
console.error("Failed to fetch bookings:", error);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
fetchBookings();
|
||||
}, []);
|
||||
|
||||
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 upcomingBookings = bookings.filter(
|
||||
(booking) => booking.status === "scheduled"
|
||||
);
|
||||
const completedBookings = bookings.filter(
|
||||
(booking) => booking.status === "completed"
|
||||
);
|
||||
const cancelledBookings = bookings.filter(
|
||||
(booking) => booking.status === "cancelled"
|
||||
);
|
||||
|
||||
const statCards = [
|
||||
{
|
||||
title: "Upcoming Appointments",
|
||||
value: upcomingBookings.length,
|
||||
icon: CalendarCheck,
|
||||
trend: "+2",
|
||||
trendUp: true,
|
||||
},
|
||||
{
|
||||
title: "Completed Sessions",
|
||||
value: completedBookings.length,
|
||||
icon: CheckCircle2,
|
||||
trend: "+5",
|
||||
trendUp: true,
|
||||
},
|
||||
{
|
||||
title: "Total Appointments",
|
||||
value: bookings.length,
|
||||
icon: Calendar,
|
||||
trend: "+12%",
|
||||
trendUp: true,
|
||||
},
|
||||
{
|
||||
title: "Total Spent",
|
||||
value: `$${bookings.reduce((sum, b) => sum + b.amount, 0)}`,
|
||||
icon: Heart,
|
||||
trend: "+18%",
|
||||
trendUp: true,
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-gray-50">
|
||||
<Navbar />
|
||||
|
||||
{/* Main Content */}
|
||||
<main className="container mx-auto px-4 sm:px-6 lg:px-8 space-y-6 pt-20 sm:pt-24 pb-8">
|
||||
{/* Welcome Section */}
|
||||
<div className="flex items-center justify-between mb-6">
|
||||
<div>
|
||||
<h1 className="text-2xl font-semibold text-gray-900 mb-1">
|
||||
Welcome Back!
|
||||
</h1>
|
||||
<p className="text-sm text-gray-600">
|
||||
Here's an overview of your appointments
|
||||
</p>
|
||||
</div>
|
||||
<Link href="/book-now">
|
||||
<Button
|
||||
className="bg-linear-to-r from-rose-500 to-pink-600 hover:from-rose-600 hover:to-pink-700 text-white"
|
||||
>
|
||||
<CalendarPlus className="w-4 h-4 mr-2" />
|
||||
Book Appointment
|
||||
</Button>
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
{loading ? (
|
||||
<div className="flex items-center justify-center py-12">
|
||||
<div className="animate-spin rounded-full h-8 w-8 border-b-2 border-gray-400"></div>
|
||||
</div>
|
||||
) : (
|
||||
<>
|
||||
{/* Stats Grid */}
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-3 sm:gap-4">
|
||||
{statCards.map((card, index) => {
|
||||
const Icon = card.icon;
|
||||
return (
|
||||
<div
|
||||
key={index}
|
||||
className="bg-white rounded-lg border border-gray-200 p-4 sm:p-5 md:p-6 hover:shadow-md transition-shadow"
|
||||
>
|
||||
<div className="flex items-start justify-between mb-3 sm:mb-4">
|
||||
<div className="p-2 sm:p-2.5 rounded-lg bg-gray-50">
|
||||
<Icon className="w-4 h-4 sm:w-5 sm:h-5 text-gray-600" />
|
||||
</div>
|
||||
<div
|
||||
className={`flex items-center gap-1 px-2 py-1 rounded-full text-xs font-medium ${
|
||||
card.trendUp
|
||||
? "bg-green-50 text-green-700"
|
||||
: "bg-red-50 text-red-700"
|
||||
}`}
|
||||
>
|
||||
{card.trendUp ? (
|
||||
<ArrowUpRight className="w-3 h-3" />
|
||||
) : (
|
||||
<ArrowUpRight className="w-3 h-3" />
|
||||
)}
|
||||
<span>{card.trend}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h3 className="text-xs font-medium text-rose-600 mb-1 sm:mb-2 uppercase tracking-wider">
|
||||
{card.title}
|
||||
</h3>
|
||||
<p className="text-xl sm:text-2xl font-bold text-gray-900 mb-1">
|
||||
{card.value}
|
||||
</p>
|
||||
<p className="text-xs text-gray-600 font-medium">vs last month</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
|
||||
{/* Upcoming Appointments Section */}
|
||||
{upcomingBookings.length > 0 && (
|
||||
<div className="bg-white rounded-lg border border-gray-200 p-4 sm:p-5 md:p-6">
|
||||
<h2 className="text-lg font-semibold text-gray-900 mb-4">
|
||||
Upcoming Appointments
|
||||
</h2>
|
||||
<div className="space-y-3">
|
||||
{upcomingBookings.map((booking) => (
|
||||
<div
|
||||
key={booking.ID}
|
||||
className="border border-gray-200 rounded-lg p-4 hover:shadow-md transition-shadow"
|
||||
>
|
||||
<div className="flex flex-col sm:flex-row sm:items-center sm:justify-between gap-4">
|
||||
<div className="flex-1">
|
||||
<div className="flex items-center gap-2 mb-2">
|
||||
<Calendar className="w-4 h-4 text-gray-600" />
|
||||
<span className="font-semibold text-gray-900">
|
||||
{formatDate(booking.scheduled_at)}
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-2 mb-2">
|
||||
<Clock className="w-4 h-4 text-gray-600" />
|
||||
<span className="text-gray-700">
|
||||
{formatTime(booking.scheduled_at)}
|
||||
</span>
|
||||
<span className="text-gray-600 text-sm font-medium">
|
||||
({booking.duration} minutes)
|
||||
</span>
|
||||
</div>
|
||||
{booking.notes && (
|
||||
<p className="text-gray-700 text-sm mt-2 font-medium">
|
||||
{booking.notes}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
<div className="flex flex-col sm:items-end gap-3">
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="px-3 py-1 rounded-full text-sm font-medium bg-green-50 text-green-700">
|
||||
{booking.status.charAt(0).toUpperCase() +
|
||||
booking.status.slice(1)}
|
||||
</span>
|
||||
<span className="text-lg font-bold text-gray-900">
|
||||
${booking.amount}
|
||||
</span>
|
||||
</div>
|
||||
{booking.jitsi_room_url && (
|
||||
<a
|
||||
href={booking.jitsi_room_url}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="inline-flex items-center gap-2 px-4 py-2 bg-blue-600 hover:bg-blue-700 text-white rounded-lg text-sm font-medium transition-colors"
|
||||
>
|
||||
<Video className="w-4 h-4" />
|
||||
Join Session
|
||||
</a>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Account Information */}
|
||||
<div className="bg-white rounded-lg border border-gray-200 p-4 sm:p-5 md:p-6">
|
||||
<h2 className="text-lg font-semibold text-gray-900 mb-4">
|
||||
Account Information
|
||||
</h2>
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="p-2 rounded-lg bg-gray-50">
|
||||
<User className="w-4 h-4 text-gray-600" />
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-sm font-medium text-gray-600 mb-1">
|
||||
Full Name
|
||||
</p>
|
||||
<p className="text-base font-semibold text-gray-900">
|
||||
John Doe
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="p-2 rounded-lg bg-gray-50">
|
||||
<Mail className="w-4 h-4 text-gray-600" />
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-sm font-medium text-gray-600 mb-1">
|
||||
Email
|
||||
</p>
|
||||
<p className="text-base font-semibold text-gray-900">
|
||||
john.doe@example.com
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="p-2 rounded-lg bg-gray-50">
|
||||
<Phone className="w-4 h-4 text-gray-600" />
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-sm font-medium text-gray-600 mb-1">
|
||||
Phone
|
||||
</p>
|
||||
<p className="text-base font-semibold text-gray-900">
|
||||
+1 (555) 123-4567
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="p-2 rounded-lg bg-gray-50">
|
||||
<Calendar className="w-4 h-4 text-gray-600" />
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-sm font-medium text-gray-600 mb-1">
|
||||
Member Since
|
||||
</p>
|
||||
<p className="text-base font-semibold text-gray-900">
|
||||
January 2025
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</main>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user