Enhance UI components across the application by integrating theme support for dark mode. Update styles in Login, Book Now, User Dashboard, and Settings pages to improve visual consistency. Refactor various components to utilize the ThemeProvider for dynamic styling based on the selected theme.
This commit is contained in:
parent
1cf94cca0a
commit
c0ff0386a0
@ -7,8 +7,11 @@ import { Heart, Eye, EyeOff, X } from "lucide-react";
|
|||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
import { useRouter } from "next/navigation";
|
import { useRouter } from "next/navigation";
|
||||||
|
import { useAppTheme } from "@/components/ThemeProvider";
|
||||||
|
|
||||||
export default function Login() {
|
export default function Login() {
|
||||||
|
const { theme } = useAppTheme();
|
||||||
|
const isDark = theme === "dark";
|
||||||
const [showPassword, setShowPassword] = useState(false);
|
const [showPassword, setShowPassword] = useState(false);
|
||||||
const [rememberMe, setRememberMe] = useState(false);
|
const [rememberMe, setRememberMe] = useState(false);
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
@ -38,29 +41,33 @@ export default function Login() {
|
|||||||
|
|
||||||
|
|
||||||
{/* Centered White Card - Login Form */}
|
{/* Centered White Card - Login Form */}
|
||||||
<div className="relative z-20 w-full max-w-md bg-white rounded-2xl shadow-2xl p-8">
|
<div className={`relative z-20 w-full max-w-md rounded-2xl shadow-2xl p-8 ${isDark ? 'bg-gray-800 border border-gray-700' : 'bg-white'}`}>
|
||||||
{/* Close Button */}
|
{/* Header with Close Button */}
|
||||||
<Button
|
<div className="flex items-start justify-between mb-2">
|
||||||
onClick={() => router.back()}
|
<div className="flex-1">
|
||||||
variant="ghost"
|
{/* Heading */}
|
||||||
size="icon"
|
<h1 className="text-3xl font-bold bg-gradient-to-r from-rose-600 via-pink-600 to-rose-600 bg-clip-text text-transparent mb-2">
|
||||||
className="ml-auto mb-6 w-8 h-8 rounded-full"
|
Welcome back
|
||||||
aria-label="Close"
|
</h1>
|
||||||
>
|
{/* Sign Up Prompt */}
|
||||||
</Button>
|
<p className={`mb-6 ${isDark ? 'text-gray-400' : 'text-gray-600'}`}>
|
||||||
|
New to Attune Heart Therapy?{" "}
|
||||||
{/* Heading */}
|
<Link href="/signup" className={`underline font-medium ${isDark ? 'text-blue-400 hover:text-blue-300' : 'text-blue-600 hover:text-blue-700'}`}>
|
||||||
<h1 className="text-3xl font-bold bg-linear-to-r from-rose-600 via-pink-600 to-rose-600 bg-clip-text text-transparent mb-2">
|
Sign up
|
||||||
Welcome back
|
</Link>
|
||||||
</h1>
|
</p>
|
||||||
|
</div>
|
||||||
{/* Sign Up Prompt */}
|
{/* Close Button */}
|
||||||
<p className="text-gray-600 mb-8">
|
<Button
|
||||||
New to Attune Heart Therapy?{" "}
|
onClick={() => router.back()}
|
||||||
<Link href="/signup" className="text-blue-600 underline font-medium">
|
variant="ghost"
|
||||||
Sign up
|
size="icon"
|
||||||
</Link>
|
className={`flex-shrink-0 w-8 h-8 rounded-full ${isDark ? 'text-gray-400 hover:text-gray-300 hover:bg-gray-700' : 'text-gray-500 hover:text-gray-700 hover:bg-gray-100'}`}
|
||||||
</p>
|
aria-label="Close"
|
||||||
|
>
|
||||||
|
<X className="w-5 h-5" />
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
|
||||||
{/* Login Form */}
|
{/* Login Form */}
|
||||||
<form className="space-y-6" onSubmit={(e) => {
|
<form className="space-y-6" onSubmit={(e) => {
|
||||||
@ -69,21 +76,21 @@ export default function Login() {
|
|||||||
}}>
|
}}>
|
||||||
{/* Email Field */}
|
{/* Email Field */}
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<label htmlFor="email" className="text-sm font-medium text-black">
|
<label htmlFor="email" className={`text-sm font-medium ${isDark ? 'text-gray-300' : 'text-black'}`}>
|
||||||
Email address
|
Email address
|
||||||
</label>
|
</label>
|
||||||
<Input
|
<Input
|
||||||
id="email"
|
id="email"
|
||||||
type="email"
|
type="email"
|
||||||
placeholder="Email address"
|
placeholder="Email address"
|
||||||
className="h-12 bg-white border-gray-300"
|
className={`h-12 ${isDark ? 'bg-gray-700 border-gray-600 text-white placeholder:text-gray-400' : 'bg-white border-gray-300 text-gray-900'}`}
|
||||||
required
|
required
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Password Field */}
|
{/* Password Field */}
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<label htmlFor="password" className="text-sm font-medium text-black">
|
<label htmlFor="password" className={`text-sm font-medium ${isDark ? 'text-gray-300' : 'text-black'}`}>
|
||||||
Your password
|
Your password
|
||||||
</label>
|
</label>
|
||||||
<div className="relative">
|
<div className="relative">
|
||||||
@ -91,7 +98,7 @@ export default function Login() {
|
|||||||
id="password"
|
id="password"
|
||||||
type={showPassword ? "text" : "password"}
|
type={showPassword ? "text" : "password"}
|
||||||
placeholder="Your password"
|
placeholder="Your password"
|
||||||
className="h-12 bg-white border-gray-300 pr-12"
|
className={`h-12 pr-12 ${isDark ? 'bg-gray-700 border-gray-600 text-white placeholder:text-gray-400' : 'bg-white border-gray-300 text-gray-900'}`}
|
||||||
required
|
required
|
||||||
/>
|
/>
|
||||||
<Button
|
<Button
|
||||||
@ -99,7 +106,7 @@ export default function Login() {
|
|||||||
variant="ghost"
|
variant="ghost"
|
||||||
size="icon"
|
size="icon"
|
||||||
onClick={() => setShowPassword(!showPassword)}
|
onClick={() => setShowPassword(!showPassword)}
|
||||||
className="absolute right-4 top-1/2 -translate-y-1/2 h-auto w-auto p-0 text-gray-500 hover:text-gray-700"
|
className={`absolute right-4 top-1/2 -translate-y-1/2 h-auto w-auto p-0 ${isDark ? 'text-gray-400 hover:text-gray-300' : 'text-gray-500 hover:text-gray-700'}`}
|
||||||
aria-label={showPassword ? "Hide password" : "Show password"}
|
aria-label={showPassword ? "Hide password" : "Show password"}
|
||||||
>
|
>
|
||||||
{showPassword ? (
|
{showPassword ? (
|
||||||
@ -114,7 +121,7 @@ export default function Login() {
|
|||||||
{/* Submit Button */}
|
{/* Submit Button */}
|
||||||
<Button
|
<Button
|
||||||
type="submit"
|
type="submit"
|
||||||
className="w-full h-12 text-base font-semibold bg-linear-to-r from-rose-500 to-pink-600 hover:from-rose-600 hover:to-pink-700 text-white shadow-lg hover:shadow-xl transition-all"
|
className="w-full h-12 text-base font-semibold bg-gradient-to-r from-rose-500 to-pink-600 hover:from-rose-600 hover:to-pink-700 text-white shadow-lg hover:shadow-xl transition-all"
|
||||||
>
|
>
|
||||||
Log in
|
Log in
|
||||||
</Button>
|
</Button>
|
||||||
@ -126,13 +133,13 @@ export default function Login() {
|
|||||||
type="checkbox"
|
type="checkbox"
|
||||||
checked={rememberMe}
|
checked={rememberMe}
|
||||||
onChange={(e) => setRememberMe(e.target.checked)}
|
onChange={(e) => setRememberMe(e.target.checked)}
|
||||||
className="w-4 h-4 rounded border-gray-300 text-rose-600 focus:ring-2 focus:ring-rose-500 cursor-pointer"
|
className={`w-4 h-4 rounded text-rose-600 focus:ring-2 focus:ring-rose-500 cursor-pointer ${isDark ? 'border-gray-600 bg-gray-700' : 'border-gray-300'}`}
|
||||||
/>
|
/>
|
||||||
<span className="text-black">Remember me</span>
|
<span className={isDark ? 'text-gray-300' : 'text-black'}>Remember me</span>
|
||||||
</label>
|
</label>
|
||||||
<Link
|
<Link
|
||||||
href="/forgot-password"
|
href="/forgot-password"
|
||||||
className="text-blue-600 hover:text-blue-700 font-medium"
|
className={`font-medium ${isDark ? 'text-blue-400 hover:text-blue-300' : 'text-blue-600 hover:text-blue-700'}`}
|
||||||
>
|
>
|
||||||
Forgot password?
|
Forgot password?
|
||||||
</Link>
|
</Link>
|
||||||
|
|||||||
@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
|
import { useAppTheme } from "@/components/ThemeProvider";
|
||||||
import { Input } from "@/components/ui/input";
|
import { Input } from "@/components/ui/input";
|
||||||
import {
|
import {
|
||||||
Select,
|
Select,
|
||||||
@ -70,6 +71,8 @@ interface BookingsResponse {
|
|||||||
|
|
||||||
export default function BookNowPage() {
|
export default function BookNowPage() {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
const { theme } = useAppTheme();
|
||||||
|
const isDark = theme === "dark";
|
||||||
const [formData, setFormData] = useState({
|
const [formData, setFormData] = useState({
|
||||||
firstName: "",
|
firstName: "",
|
||||||
lastName: "",
|
lastName: "",
|
||||||
@ -208,11 +211,11 @@ export default function BookNowPage() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="min-h-screen bg-white">
|
<div className={`min-h-screen ${isDark ? 'bg-gray-900' : 'bg-white'}`}>
|
||||||
{/* Main Content */}
|
{/* Main Content */}
|
||||||
<main className="min-h-screen flex">
|
<main className="min-h-screen flex">
|
||||||
{/* Left Side - Image (Fixed) */}
|
{/* Left Side - Image (Fixed) */}
|
||||||
<div className="hidden lg:block fixed top-0 left-0 h-screen w-1/2 overflow-hidden z-10 bg-gradient-to-br from-rose-100 via-pink-50 to-orange-50">
|
<div className={`hidden lg:block fixed top-0 left-0 h-screen w-1/2 overflow-hidden z-10 bg-gradient-to-br ${isDark ? 'from-gray-900 via-gray-800 to-gray-900' : 'from-rose-100 via-pink-50 to-orange-50'}`}>
|
||||||
<div className="absolute inset-0">
|
<div className="absolute inset-0">
|
||||||
<Image
|
<Image
|
||||||
src="/doctors.png"
|
src="/doctors.png"
|
||||||
@ -231,7 +234,7 @@ export default function BookNowPage() {
|
|||||||
<div className="bg-gradient-to-r from-rose-500 to-pink-600 p-2 rounded-xl">
|
<div className="bg-gradient-to-r from-rose-500 to-pink-600 p-2 rounded-xl">
|
||||||
<Heart className="h-5 w-5 text-white fill-white" />
|
<Heart className="h-5 w-5 text-white fill-white" />
|
||||||
</div>
|
</div>
|
||||||
<span className="font-bold text-lg text-rose-500 drop-shadow-lg">
|
<span className={`font-bold text-lg drop-shadow-lg ${isDark ? 'text-rose-400' : 'text-rose-500'}`}>
|
||||||
Attune Heart Therapy
|
Attune Heart Therapy
|
||||||
</span>
|
</span>
|
||||||
</Link>
|
</Link>
|
||||||
@ -279,78 +282,78 @@ export default function BookNowPage() {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Right Side - Form (Scrollable) */}
|
{/* Right Side - Form (Scrollable) */}
|
||||||
<div className="w-full lg:w-1/2 lg:ml-auto fixed top-0 right-0 h-screen overflow-y-auto bg-white custom-scrollbar">
|
<div className={`w-full lg:w-1/2 lg:ml-auto fixed top-0 right-0 h-screen overflow-y-auto custom-scrollbar ${isDark ? 'bg-gray-900' : 'bg-white'}`}>
|
||||||
<div className="flex items-start justify-center min-h-full">
|
<div className="flex items-start justify-center min-h-full">
|
||||||
<div className="w-full max-w-2xl">
|
<div className="w-full max-w-2xl">
|
||||||
{/* Page Header */}
|
{/* Page Header */}
|
||||||
<div className="pt-4 sm:pt-6 lg:pt-8 px-6 sm:px-8 lg:px-12 pb-6">
|
<div className="pt-4 sm:pt-6 lg:pt-8 px-4 sm:px-6 lg:px-12 pb-4 sm:pb-6">
|
||||||
<Button
|
<Button
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
onClick={() => router.back()}
|
onClick={() => router.back()}
|
||||||
className="flex items-center gap-2 text-black hover:bg-gray-100 mb-4"
|
className={`flex items-center gap-2 mb-3 sm:mb-4 ${isDark ? 'text-white hover:bg-gray-800' : 'text-black hover:bg-gray-100'}`}
|
||||||
>
|
>
|
||||||
<ArrowLeft className="w-5 h-5" />
|
<ArrowLeft className="w-4 h-4 sm:w-5 sm:h-5" />
|
||||||
<span className="hidden sm:inline">Back</span>
|
<span className="hidden sm:inline text-sm sm:text-base">Back</span>
|
||||||
</Button>
|
</Button>
|
||||||
<div>
|
<div>
|
||||||
<h1 className="text-2xl font-semibold text-gray-900 mb-1">
|
<h1 className={`text-xl sm:text-2xl font-semibold mb-1 ${isDark ? 'text-white' : 'text-gray-900'}`}>
|
||||||
Book Your Appointment
|
Book Your Appointment
|
||||||
</h1>
|
</h1>
|
||||||
<p className="text-sm text-gray-500">
|
<p className={`text-xs sm:text-sm ${isDark ? 'text-gray-400' : 'text-gray-500'}`}>
|
||||||
Fill out the form below and we'll get back to you to confirm your appointment
|
Fill out the form below and we'll get back to you to confirm your appointment
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Booking Form or Success Message */}
|
{/* Booking Form or Success Message */}
|
||||||
<div className="px-6 sm:px-8 lg:px-12 pb-6 sm:pb-8 lg:pb-12">
|
<div className="px-4 sm:px-6 lg:px-12 pb-6 sm:pb-8 lg:pb-12">
|
||||||
{booking ? (
|
{booking ? (
|
||||||
<div className="bg-white rounded-2xl shadow-lg p-6 sm:p-8 border border-gray-200">
|
<div className={`rounded-xl sm:rounded-2xl shadow-lg p-4 sm:p-6 lg:p-8 border ${isDark ? 'bg-gray-800 border-gray-700' : 'bg-white border-gray-200'}`}>
|
||||||
<div className="text-center space-y-4">
|
<div className="text-center space-y-4">
|
||||||
<div className="mx-auto w-16 h-16 bg-green-100 rounded-full flex items-center justify-center">
|
<div className={`mx-auto w-16 h-16 rounded-full flex items-center justify-center ${isDark ? 'bg-green-900/30' : 'bg-green-100'}`}>
|
||||||
<CheckCircle className="w-8 h-8 text-green-600" />
|
<CheckCircle className={`w-8 h-8 ${isDark ? 'text-green-400' : 'text-green-600'}`} />
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<h2 className="text-2xl font-semibold text-gray-900 mb-2">
|
<h2 className={`text-2xl font-semibold mb-2 ${isDark ? 'text-white' : 'text-gray-900'}`}>
|
||||||
Booking Confirmed!
|
Booking Confirmed!
|
||||||
</h2>
|
</h2>
|
||||||
<p className="text-gray-600">
|
<p className={isDark ? 'text-gray-300' : 'text-gray-600'}>
|
||||||
Your appointment has been successfully booked.
|
Your appointment has been successfully booked.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="bg-gray-50 rounded-lg p-6 space-y-4 text-left">
|
<div className={`rounded-lg p-6 space-y-4 text-left ${isDark ? 'bg-gray-700/50' : 'bg-gray-50'}`}>
|
||||||
<div>
|
<div>
|
||||||
<p className="text-sm font-medium text-gray-500 mb-1">Booking ID</p>
|
<p className={`text-sm font-medium mb-1 ${isDark ? 'text-gray-400' : 'text-gray-500'}`}>Booking ID</p>
|
||||||
<p className="text-base font-semibold text-gray-900">#{booking.ID}</p>
|
<p className={`text-base font-semibold ${isDark ? 'text-white' : 'text-gray-900'}`}>#{booking.ID}</p>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<p className="text-sm font-medium text-gray-500 mb-1">Patient</p>
|
<p className={`text-sm font-medium mb-1 ${isDark ? 'text-gray-400' : 'text-gray-500'}`}>Patient</p>
|
||||||
<p className="text-base text-gray-900">
|
<p className={`text-base ${isDark ? 'text-white' : 'text-gray-900'}`}>
|
||||||
{booking.user.first_name} {booking.user.last_name}
|
{booking.user.first_name} {booking.user.last_name}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<p className="text-sm font-medium text-gray-500 mb-1">Scheduled Time</p>
|
<p className={`text-sm font-medium mb-1 ${isDark ? 'text-gray-400' : 'text-gray-500'}`}>Scheduled Time</p>
|
||||||
<p className="text-base text-gray-900">{formatDateTime(booking.scheduled_at)}</p>
|
<p className={`text-base ${isDark ? 'text-white' : 'text-gray-900'}`}>{formatDateTime(booking.scheduled_at)}</p>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<p className="text-sm font-medium text-gray-500 mb-1">Duration</p>
|
<p className={`text-sm font-medium mb-1 ${isDark ? 'text-gray-400' : 'text-gray-500'}`}>Duration</p>
|
||||||
<p className="text-base text-gray-900">{booking.duration} minutes</p>
|
<p className={`text-base ${isDark ? 'text-white' : 'text-gray-900'}`}>{booking.duration} minutes</p>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<p className="text-sm font-medium text-gray-500 mb-1">Status</p>
|
<p className={`text-sm font-medium mb-1 ${isDark ? 'text-gray-400' : 'text-gray-500'}`}>Status</p>
|
||||||
<span className="inline-flex items-center px-3 py-1 rounded-full text-sm font-medium bg-blue-100 text-blue-800">
|
<span className={`inline-flex items-center px-3 py-1 rounded-full text-sm font-medium ${isDark ? 'bg-blue-900/50 text-blue-200' : 'bg-blue-100 text-blue-800'}`}>
|
||||||
{booking.status}
|
{booking.status}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<p className="text-sm font-medium text-gray-500 mb-1">Amount</p>
|
<p className={`text-sm font-medium mb-1 ${isDark ? 'text-gray-400' : 'text-gray-500'}`}>Amount</p>
|
||||||
<p className="text-base font-semibold text-gray-900">${booking.amount}</p>
|
<p className={`text-base font-semibold ${isDark ? 'text-white' : 'text-gray-900'}`}>${booking.amount}</p>
|
||||||
</div>
|
</div>
|
||||||
{booking.notes && (
|
{booking.notes && (
|
||||||
<div>
|
<div>
|
||||||
<p className="text-sm font-medium text-gray-500 mb-1">Notes</p>
|
<p className={`text-sm font-medium mb-1 ${isDark ? 'text-gray-400' : 'text-gray-500'}`}>Notes</p>
|
||||||
<p className="text-base text-gray-900">{booking.notes}</p>
|
<p className={`text-base ${isDark ? 'text-white' : 'text-gray-900'}`}>{booking.notes}</p>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
@ -384,17 +387,17 @@ export default function BookNowPage() {
|
|||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
<div className="bg-white rounded-2xl shadow-lg p-6 sm:p-8 border border-gray-200">
|
<div className={`rounded-xl sm:rounded-2xl shadow-lg p-4 sm:p-6 lg:p-8 border ${isDark ? 'bg-gray-800 border-gray-700' : 'bg-white border-gray-200'}`}>
|
||||||
{error && (
|
{error && (
|
||||||
<div className="mb-6 p-4 bg-red-50 border border-red-200 rounded-lg">
|
<div className={`mb-6 p-4 rounded-lg border ${isDark ? 'bg-red-900/20 border-red-800' : 'bg-red-50 border-red-200'}`}>
|
||||||
<p className="text-sm text-red-800">{error}</p>
|
<p className={`text-sm ${isDark ? 'text-red-200' : 'text-red-800'}`}>{error}</p>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<form onSubmit={handleSubmit} className="space-y-6">
|
<form onSubmit={handleSubmit} className="space-y-6">
|
||||||
{/* Personal Information Section */}
|
{/* Personal Information Section */}
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
<h2 className="text-lg font-semibold text-gray-900 flex items-center gap-2">
|
<h2 className={`text-lg font-semibold flex items-center gap-2 ${isDark ? 'text-white' : 'text-gray-900'}`}>
|
||||||
<User className="w-5 h-5 text-rose-600" />
|
<User className={`w-5 h-5 ${isDark ? 'text-rose-400' : 'text-rose-600'}`} />
|
||||||
Personal Information
|
Personal Information
|
||||||
</h2>
|
</h2>
|
||||||
|
|
||||||
@ -402,7 +405,7 @@ export default function BookNowPage() {
|
|||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<label
|
<label
|
||||||
htmlFor="firstName"
|
htmlFor="firstName"
|
||||||
className="text-sm font-medium text-gray-700"
|
className={`text-sm font-medium ${isDark ? 'text-gray-300' : 'text-gray-700'}`}
|
||||||
>
|
>
|
||||||
First Name *
|
First Name *
|
||||||
</label>
|
</label>
|
||||||
@ -415,14 +418,14 @@ export default function BookNowPage() {
|
|||||||
handleChange("firstName", e.target.value)
|
handleChange("firstName", e.target.value)
|
||||||
}
|
}
|
||||||
required
|
required
|
||||||
className="h-11"
|
className={`h-11 ${isDark ? 'bg-gray-700 border-gray-600 text-white placeholder:text-gray-400' : 'bg-white border-gray-300 text-gray-900 placeholder:text-gray-500'}`}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<label
|
<label
|
||||||
htmlFor="lastName"
|
htmlFor="lastName"
|
||||||
className="text-sm font-medium text-gray-700"
|
className={`text-sm font-medium ${isDark ? 'text-gray-300' : 'text-gray-700'}`}
|
||||||
>
|
>
|
||||||
Last Name *
|
Last Name *
|
||||||
</label>
|
</label>
|
||||||
@ -435,7 +438,7 @@ export default function BookNowPage() {
|
|||||||
handleChange("lastName", e.target.value)
|
handleChange("lastName", e.target.value)
|
||||||
}
|
}
|
||||||
required
|
required
|
||||||
className="h-11"
|
className={`h-11 ${isDark ? 'bg-gray-700 border-gray-600 text-white placeholder:text-gray-400' : 'bg-white border-gray-300 text-gray-900 placeholder:text-gray-500'}`}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -443,9 +446,9 @@ export default function BookNowPage() {
|
|||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<label
|
<label
|
||||||
htmlFor="email"
|
htmlFor="email"
|
||||||
className="text-sm font-medium text-gray-700 flex items-center gap-2"
|
className={`text-sm font-medium flex items-center gap-2 ${isDark ? 'text-gray-300' : 'text-gray-700'}`}
|
||||||
>
|
>
|
||||||
<Mail className="w-4 h-4 text-gray-500" />
|
<Mail className={`w-4 h-4 ${isDark ? 'text-gray-400' : 'text-gray-500'}`} />
|
||||||
Email Address *
|
Email Address *
|
||||||
</label>
|
</label>
|
||||||
<Input
|
<Input
|
||||||
@ -455,16 +458,16 @@ export default function BookNowPage() {
|
|||||||
value={formData.email}
|
value={formData.email}
|
||||||
onChange={(e) => handleChange("email", e.target.value)}
|
onChange={(e) => handleChange("email", e.target.value)}
|
||||||
required
|
required
|
||||||
className="h-11"
|
className={`h-11 ${isDark ? 'bg-gray-700 border-gray-600 text-white placeholder:text-gray-400' : 'bg-white border-gray-300 text-gray-900 placeholder:text-gray-500'}`}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<label
|
<label
|
||||||
htmlFor="phone"
|
htmlFor="phone"
|
||||||
className="text-sm font-medium text-gray-700 flex items-center gap-2"
|
className={`text-sm font-medium flex items-center gap-2 ${isDark ? 'text-gray-300' : 'text-gray-700'}`}
|
||||||
>
|
>
|
||||||
<Phone className="w-4 h-4 text-gray-500" />
|
<Phone className={`w-4 h-4 ${isDark ? 'text-gray-400' : 'text-gray-500'}`} />
|
||||||
Phone Number *
|
Phone Number *
|
||||||
</label>
|
</label>
|
||||||
<Input
|
<Input
|
||||||
@ -474,22 +477,22 @@ export default function BookNowPage() {
|
|||||||
value={formData.phone}
|
value={formData.phone}
|
||||||
onChange={(e) => handleChange("phone", e.target.value)}
|
onChange={(e) => handleChange("phone", e.target.value)}
|
||||||
required
|
required
|
||||||
className="h-11"
|
className={`h-11 ${isDark ? 'bg-gray-700 border-gray-600 text-white placeholder:text-gray-400' : 'bg-white border-gray-300 text-gray-900 placeholder:text-gray-500'}`}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Appointment Details Section */}
|
{/* Appointment Details Section */}
|
||||||
<div className="space-y-4 pt-6 border-t border-gray-200">
|
<div className={`space-y-4 pt-6 border-t ${isDark ? 'border-gray-700' : 'border-gray-200'}`}>
|
||||||
<h2 className="text-lg font-semibold text-gray-900 flex items-center gap-2">
|
<h2 className={`text-lg font-semibold flex items-center gap-2 ${isDark ? 'text-white' : 'text-gray-900'}`}>
|
||||||
<Calendar className="w-5 h-5 text-rose-600" />
|
<Calendar className={`w-5 h-5 ${isDark ? 'text-rose-400' : 'text-rose-600'}`} />
|
||||||
Appointment Details
|
Appointment Details
|
||||||
</h2>
|
</h2>
|
||||||
|
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<label
|
<label
|
||||||
htmlFor="appointmentType"
|
htmlFor="appointmentType"
|
||||||
className="text-sm font-medium text-gray-700"
|
className={`text-sm font-medium ${isDark ? 'text-gray-300' : 'text-gray-700'}`}
|
||||||
>
|
>
|
||||||
Appointment Type *
|
Appointment Type *
|
||||||
</label>
|
</label>
|
||||||
@ -500,10 +503,10 @@ export default function BookNowPage() {
|
|||||||
}
|
}
|
||||||
required
|
required
|
||||||
>
|
>
|
||||||
<SelectTrigger id="appointmentType" className="h-11 bg-white">
|
<SelectTrigger id="appointmentType" className={`h-11 ${isDark ? 'bg-gray-700 border-gray-600 text-white' : 'bg-white border-gray-300 text-gray-900'}`}>
|
||||||
<SelectValue placeholder="Select appointment type" />
|
<SelectValue placeholder="Select appointment type" />
|
||||||
</SelectTrigger>
|
</SelectTrigger>
|
||||||
<SelectContent className="bg-white">
|
<SelectContent className={isDark ? 'bg-gray-700 border-gray-600' : 'bg-white border-gray-300'}>
|
||||||
<SelectItem value="initial-consultation">
|
<SelectItem value="initial-consultation">
|
||||||
Initial Consultation
|
Initial Consultation
|
||||||
</SelectItem>
|
</SelectItem>
|
||||||
@ -524,9 +527,9 @@ export default function BookNowPage() {
|
|||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<label
|
<label
|
||||||
htmlFor="preferredDate"
|
htmlFor="preferredDate"
|
||||||
className="text-sm font-medium text-gray-700 flex items-center gap-2"
|
className={`text-sm font-medium flex items-center gap-2 ${isDark ? 'text-gray-300' : 'text-gray-700'}`}
|
||||||
>
|
>
|
||||||
<Calendar className="w-4 h-4 text-gray-500" />
|
<Calendar className={`w-4 h-4 ${isDark ? 'text-gray-400' : 'text-gray-500'}`} />
|
||||||
Preferred Date *
|
Preferred Date *
|
||||||
</label>
|
</label>
|
||||||
<Input
|
<Input
|
||||||
@ -538,16 +541,16 @@ export default function BookNowPage() {
|
|||||||
}
|
}
|
||||||
required
|
required
|
||||||
min={new Date().toISOString().split("T")[0]}
|
min={new Date().toISOString().split("T")[0]}
|
||||||
className="h-11"
|
className={`h-11 ${isDark ? 'bg-gray-700 border-gray-600 text-white' : 'bg-white border-gray-300 text-gray-900'}`}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<label
|
<label
|
||||||
htmlFor="preferredTime"
|
htmlFor="preferredTime"
|
||||||
className="text-sm font-medium text-gray-700 flex items-center gap-2"
|
className={`text-sm font-medium flex items-center gap-2 ${isDark ? 'text-gray-300' : 'text-gray-700'}`}
|
||||||
>
|
>
|
||||||
<Clock className="w-4 h-4 text-gray-500" />
|
<Clock className={`w-4 h-4 ${isDark ? 'text-gray-400' : 'text-gray-500'}`} />
|
||||||
Preferred Time *
|
Preferred Time *
|
||||||
</label>
|
</label>
|
||||||
<Select
|
<Select
|
||||||
@ -557,10 +560,10 @@ export default function BookNowPage() {
|
|||||||
}
|
}
|
||||||
required
|
required
|
||||||
>
|
>
|
||||||
<SelectTrigger id="preferredTime" className="h-11">
|
<SelectTrigger id="preferredTime" className={`h-11 ${isDark ? 'bg-gray-700 border-gray-600 text-white' : 'bg-white border-gray-300 text-gray-900'}`}>
|
||||||
<SelectValue placeholder="Select time" />
|
<SelectValue placeholder="Select time" />
|
||||||
</SelectTrigger>
|
</SelectTrigger>
|
||||||
<SelectContent className="bg-white">
|
<SelectContent className={isDark ? 'bg-gray-700 border-gray-600' : 'bg-white border-gray-300'}>
|
||||||
<SelectItem value="9:00 AM">9:00 AM</SelectItem>
|
<SelectItem value="9:00 AM">9:00 AM</SelectItem>
|
||||||
<SelectItem value="10:00 AM">10:00 AM</SelectItem>
|
<SelectItem value="10:00 AM">10:00 AM</SelectItem>
|
||||||
<SelectItem value="11:00 AM">11:00 AM</SelectItem>
|
<SelectItem value="11:00 AM">11:00 AM</SelectItem>
|
||||||
@ -577,12 +580,12 @@ export default function BookNowPage() {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Additional Message Section */}
|
{/* Additional Message Section */}
|
||||||
<div className="space-y-4 pt-6 border-t border-gray-200">
|
<div className={`space-y-4 pt-6 border-t ${isDark ? 'border-gray-700' : 'border-gray-200'}`}>
|
||||||
<label
|
<label
|
||||||
htmlFor="message"
|
htmlFor="message"
|
||||||
className="text-sm font-medium text-gray-700 flex items-center gap-2"
|
className={`text-sm font-medium flex items-center gap-2 ${isDark ? 'text-gray-300' : 'text-gray-700'}`}
|
||||||
>
|
>
|
||||||
<MessageSquare className="w-4 h-4 text-gray-500" />
|
<MessageSquare className={`w-4 h-4 ${isDark ? 'text-gray-400' : 'text-gray-500'}`} />
|
||||||
Additional Message (Optional)
|
Additional Message (Optional)
|
||||||
</label>
|
</label>
|
||||||
<textarea
|
<textarea
|
||||||
@ -591,7 +594,7 @@ export default function BookNowPage() {
|
|||||||
placeholder="Tell us about any specific concerns or preferences..."
|
placeholder="Tell us about any specific concerns or preferences..."
|
||||||
value={formData.message}
|
value={formData.message}
|
||||||
onChange={(e) => handleChange("message", e.target.value)}
|
onChange={(e) => handleChange("message", e.target.value)}
|
||||||
className="w-full rounded-md border border-gray-300 bg-transparent px-3 py-2 text-sm shadow-sm transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-rose-500 focus-visible:border-rose-500 disabled:cursor-not-allowed disabled:opacity-50"
|
className={`w-full rounded-md border px-3 py-2 text-sm shadow-sm transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-rose-500 focus-visible:border-rose-500 disabled:cursor-not-allowed disabled:opacity-50 ${isDark ? 'border-gray-600 bg-gray-700 text-white placeholder:text-gray-400 focus-visible:ring-rose-400 focus-visible:border-rose-400' : 'border-gray-300 bg-white text-gray-900 placeholder:text-gray-500'}`}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -612,7 +615,7 @@ export default function BookNowPage() {
|
|||||||
"Submit Booking Request"
|
"Submit Booking Request"
|
||||||
)}
|
)}
|
||||||
</Button>
|
</Button>
|
||||||
<p className="text-xs text-gray-500 text-center mt-4">
|
<p className={`text-xs text-center mt-4 ${isDark ? 'text-gray-400' : 'text-gray-500'}`}>
|
||||||
We'll review your request and get back to you within 24 hours
|
We'll review your request and get back to you within 24 hours
|
||||||
to confirm your appointment.
|
to confirm your appointment.
|
||||||
</p>
|
</p>
|
||||||
@ -622,11 +625,11 @@ export default function BookNowPage() {
|
|||||||
|
|
||||||
{/* Contact Information */}
|
{/* Contact Information */}
|
||||||
<div className="mt-6 text-center">
|
<div className="mt-6 text-center">
|
||||||
<p className="text-gray-600">
|
<p className={isDark ? 'text-gray-300' : 'text-gray-600'}>
|
||||||
Prefer to book by phone?{" "}
|
Prefer to book by phone?{" "}
|
||||||
<a
|
<a
|
||||||
href="tel:+19548073027"
|
href="tel:+19548073027"
|
||||||
className="text-rose-600 hover:text-rose-700 font-medium underline"
|
className={`font-medium underline ${isDark ? 'text-rose-400 hover:text-rose-300' : 'text-rose-600 hover:text-rose-700'}`}
|
||||||
>
|
>
|
||||||
Call us at (954) 807-3027
|
Call us at (954) 807-3027
|
||||||
</a>
|
</a>
|
||||||
|
|||||||
@ -19,6 +19,7 @@ import {
|
|||||||
} from "lucide-react";
|
} from "lucide-react";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import { Navbar } from "@/components/Navbar";
|
import { Navbar } from "@/components/Navbar";
|
||||||
|
import { useAppTheme } from "@/components/ThemeProvider";
|
||||||
|
|
||||||
interface Booking {
|
interface Booking {
|
||||||
ID: number;
|
ID: number;
|
||||||
@ -31,6 +32,8 @@ interface Booking {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default function UserDashboard() {
|
export default function UserDashboard() {
|
||||||
|
const { theme } = useAppTheme();
|
||||||
|
const isDark = theme === "dark";
|
||||||
const [bookings, setBookings] = useState<Booking[]>([]);
|
const [bookings, setBookings] = useState<Booking[]>([]);
|
||||||
const [loading, setLoading] = useState(true);
|
const [loading, setLoading] = useState(true);
|
||||||
|
|
||||||
@ -125,7 +128,7 @@ export default function UserDashboard() {
|
|||||||
];
|
];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="min-h-screen bg-gray-50">
|
<div className={`min-h-screen ${isDark ? 'bg-gray-900' : 'bg-gray-50'}`}>
|
||||||
<Navbar />
|
<Navbar />
|
||||||
|
|
||||||
{/* Main Content */}
|
{/* Main Content */}
|
||||||
@ -133,10 +136,10 @@ export default function UserDashboard() {
|
|||||||
{/* Welcome Section */}
|
{/* Welcome Section */}
|
||||||
<div className="flex items-center justify-between mb-6">
|
<div className="flex items-center justify-between mb-6">
|
||||||
<div>
|
<div>
|
||||||
<h1 className="text-2xl font-semibold text-gray-900 mb-1">
|
<h1 className={`text-2xl font-semibold mb-1 ${isDark ? 'text-white' : 'text-gray-900'}`}>
|
||||||
Welcome Back!
|
Welcome Back!
|
||||||
</h1>
|
</h1>
|
||||||
<p className="text-sm text-gray-600">
|
<p className={`text-sm ${isDark ? 'text-gray-400' : 'text-gray-600'}`}>
|
||||||
Here's an overview of your appointments
|
Here's an overview of your appointments
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
@ -144,7 +147,7 @@ export default function UserDashboard() {
|
|||||||
<Link href="/user/settings">
|
<Link href="/user/settings">
|
||||||
<Button
|
<Button
|
||||||
variant="outline"
|
variant="outline"
|
||||||
className="hover:bg-gray-100"
|
className={isDark ? 'hover:bg-gray-800 border-gray-700 text-gray-300' : 'hover:bg-gray-100'}
|
||||||
>
|
>
|
||||||
<Settings className="w-4 h-4 mr-2" />
|
<Settings className="w-4 h-4 mr-2" />
|
||||||
Settings
|
Settings
|
||||||
@ -152,7 +155,7 @@ export default function UserDashboard() {
|
|||||||
</Link>
|
</Link>
|
||||||
<Link href="/book-now">
|
<Link href="/book-now">
|
||||||
<Button
|
<Button
|
||||||
className="bg-linear-to-r from-rose-500 to-pink-600 hover:from-rose-600 hover:to-pink-700 text-white"
|
className="bg-gradient-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" />
|
<CalendarPlus className="w-4 h-4 mr-2" />
|
||||||
Book Appointment
|
Book Appointment
|
||||||
@ -163,7 +166,7 @@ export default function UserDashboard() {
|
|||||||
|
|
||||||
{loading ? (
|
{loading ? (
|
||||||
<div className="flex items-center justify-center py-12">
|
<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 className={`animate-spin rounded-full h-8 w-8 border-b-2 ${isDark ? 'border-gray-600' : 'border-gray-400'}`}></div>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
@ -174,17 +177,17 @@ export default function UserDashboard() {
|
|||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
key={index}
|
key={index}
|
||||||
className="bg-white rounded-lg border border-gray-200 p-4 sm:p-5 md:p-6 hover:shadow-md transition-shadow"
|
className={`rounded-lg border p-4 sm:p-5 md:p-6 hover:shadow-md transition-shadow ${isDark ? 'bg-gray-800 border-gray-700' : 'bg-white border-gray-200'}`}
|
||||||
>
|
>
|
||||||
<div className="flex items-start justify-between mb-3 sm:mb-4">
|
<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">
|
<div className={`p-2 sm:p-2.5 rounded-lg ${isDark ? 'bg-gray-700' : 'bg-gray-50'}`}>
|
||||||
<Icon className="w-4 h-4 sm:w-5 sm:h-5 text-gray-600" />
|
<Icon className={`w-4 h-4 sm:w-5 sm:h-5 ${isDark ? 'text-gray-400' : 'text-gray-600'}`} />
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
className={`flex items-center gap-1 px-2 py-1 rounded-full text-xs font-medium ${
|
className={`flex items-center gap-1 px-2 py-1 rounded-full text-xs font-medium ${
|
||||||
card.trendUp
|
card.trendUp
|
||||||
? "bg-green-50 text-green-700"
|
? isDark ? "bg-green-900/30 text-green-400" : "bg-green-50 text-green-700"
|
||||||
: "bg-red-50 text-red-700"
|
: isDark ? "bg-red-900/30 text-red-400" : "bg-red-50 text-red-700"
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
{card.trendUp ? (
|
{card.trendUp ? (
|
||||||
@ -197,13 +200,13 @@ export default function UserDashboard() {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<h3 className="text-xs font-medium text-rose-600 mb-1 sm:mb-2 uppercase tracking-wider">
|
<h3 className={`text-xs font-medium mb-1 sm:mb-2 uppercase tracking-wider ${isDark ? 'text-rose-400' : 'text-rose-600'}`}>
|
||||||
{card.title}
|
{card.title}
|
||||||
</h3>
|
</h3>
|
||||||
<p className="text-xl sm:text-2xl font-bold text-gray-900 mb-1">
|
<p className={`text-xl sm:text-2xl font-bold mb-1 ${isDark ? 'text-white' : 'text-gray-900'}`}>
|
||||||
{card.value}
|
{card.value}
|
||||||
</p>
|
</p>
|
||||||
<p className="text-xs text-gray-600 font-medium">vs last month</p>
|
<p className={`text-xs font-medium ${isDark ? 'text-gray-400' : 'text-gray-600'}`}>vs last month</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
@ -212,46 +215,46 @@ export default function UserDashboard() {
|
|||||||
|
|
||||||
{/* Upcoming Appointments Section */}
|
{/* Upcoming Appointments Section */}
|
||||||
{upcomingBookings.length > 0 && (
|
{upcomingBookings.length > 0 && (
|
||||||
<div className="bg-white rounded-lg border border-gray-200 p-4 sm:p-5 md:p-6">
|
<div className={`rounded-lg border p-4 sm:p-5 md:p-6 ${isDark ? 'bg-gray-800 border-gray-700' : 'bg-white border-gray-200'}`}>
|
||||||
<h2 className="text-lg font-semibold text-gray-900 mb-4">
|
<h2 className={`text-lg font-semibold mb-4 ${isDark ? 'text-white' : 'text-gray-900'}`}>
|
||||||
Upcoming Appointments
|
Upcoming Appointments
|
||||||
</h2>
|
</h2>
|
||||||
<div className="space-y-3">
|
<div className="space-y-3">
|
||||||
{upcomingBookings.map((booking) => (
|
{upcomingBookings.map((booking) => (
|
||||||
<div
|
<div
|
||||||
key={booking.ID}
|
key={booking.ID}
|
||||||
className="border border-gray-200 rounded-lg p-4 hover:shadow-md transition-shadow"
|
className={`border rounded-lg p-4 hover:shadow-md transition-shadow ${isDark ? 'border-gray-700 bg-gray-700/50' : 'border-gray-200'}`}
|
||||||
>
|
>
|
||||||
<div className="flex flex-col sm:flex-row sm:items-center sm:justify-between gap-4">
|
<div className="flex flex-col sm:flex-row sm:items-center sm:justify-between gap-4">
|
||||||
<div className="flex-1">
|
<div className="flex-1">
|
||||||
<div className="flex items-center gap-2 mb-2">
|
<div className="flex items-center gap-2 mb-2">
|
||||||
<Calendar className="w-4 h-4 text-gray-600" />
|
<Calendar className={`w-4 h-4 ${isDark ? 'text-gray-400' : 'text-gray-600'}`} />
|
||||||
<span className="font-semibold text-gray-900">
|
<span className={`font-semibold ${isDark ? 'text-white' : 'text-gray-900'}`}>
|
||||||
{formatDate(booking.scheduled_at)}
|
{formatDate(booking.scheduled_at)}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center gap-2 mb-2">
|
<div className="flex items-center gap-2 mb-2">
|
||||||
<Clock className="w-4 h-4 text-gray-600" />
|
<Clock className={`w-4 h-4 ${isDark ? 'text-gray-400' : 'text-gray-600'}`} />
|
||||||
<span className="text-gray-700">
|
<span className={isDark ? 'text-gray-300' : 'text-gray-700'}>
|
||||||
{formatTime(booking.scheduled_at)}
|
{formatTime(booking.scheduled_at)}
|
||||||
</span>
|
</span>
|
||||||
<span className="text-gray-600 text-sm font-medium">
|
<span className={`text-sm font-medium ${isDark ? 'text-gray-400' : 'text-gray-600'}`}>
|
||||||
({booking.duration} minutes)
|
({booking.duration} minutes)
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
{booking.notes && (
|
{booking.notes && (
|
||||||
<p className="text-gray-700 text-sm mt-2 font-medium">
|
<p className={`text-sm mt-2 font-medium ${isDark ? 'text-gray-300' : 'text-gray-700'}`}>
|
||||||
{booking.notes}
|
{booking.notes}
|
||||||
</p>
|
</p>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-col sm:items-end gap-3">
|
<div className="flex flex-col sm:items-end gap-3">
|
||||||
<div className="flex items-center gap-2">
|
<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">
|
<span className={`px-3 py-1 rounded-full text-sm font-medium ${isDark ? 'bg-green-900/30 text-green-400' : 'bg-green-50 text-green-700'}`}>
|
||||||
{booking.status.charAt(0).toUpperCase() +
|
{booking.status.charAt(0).toUpperCase() +
|
||||||
booking.status.slice(1)}
|
booking.status.slice(1)}
|
||||||
</span>
|
</span>
|
||||||
<span className="text-lg font-bold text-gray-900">
|
<span className={`text-lg font-bold ${isDark ? 'text-white' : 'text-gray-900'}`}>
|
||||||
${booking.amount}
|
${booking.amount}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
@ -275,59 +278,59 @@ export default function UserDashboard() {
|
|||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Account Information */}
|
{/* Account Information */}
|
||||||
<div className="bg-white rounded-lg border border-gray-200 p-4 sm:p-5 md:p-6">
|
<div className={`rounded-lg border p-4 sm:p-5 md:p-6 ${isDark ? 'bg-gray-800 border-gray-700' : 'bg-white border-gray-200'}`}>
|
||||||
<h2 className="text-lg font-semibold text-gray-900 mb-4">
|
<h2 className={`text-lg font-semibold mb-4 ${isDark ? 'text-white' : 'text-gray-900'}`}>
|
||||||
Account Information
|
Account Information
|
||||||
</h2>
|
</h2>
|
||||||
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
|
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
|
||||||
<div className="flex items-center gap-3">
|
<div className="flex items-center gap-3">
|
||||||
<div className="p-2 rounded-lg bg-gray-50">
|
<div className={`p-2 rounded-lg ${isDark ? 'bg-gray-700' : 'bg-gray-50'}`}>
|
||||||
<User className="w-4 h-4 text-gray-600" />
|
<User className={`w-4 h-4 ${isDark ? 'text-gray-400' : 'text-gray-600'}`} />
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<p className="text-sm font-medium text-gray-600 mb-1">
|
<p className={`text-sm font-medium mb-1 ${isDark ? 'text-gray-400' : 'text-gray-600'}`}>
|
||||||
Full Name
|
Full Name
|
||||||
</p>
|
</p>
|
||||||
<p className="text-base font-semibold text-gray-900">
|
<p className={`text-base font-semibold ${isDark ? 'text-white' : 'text-gray-900'}`}>
|
||||||
John Doe
|
John Doe
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center gap-3">
|
<div className="flex items-center gap-3">
|
||||||
<div className="p-2 rounded-lg bg-gray-50">
|
<div className={`p-2 rounded-lg ${isDark ? 'bg-gray-700' : 'bg-gray-50'}`}>
|
||||||
<Mail className="w-4 h-4 text-gray-600" />
|
<Mail className={`w-4 h-4 ${isDark ? 'text-gray-400' : 'text-gray-600'}`} />
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<p className="text-sm font-medium text-gray-600 mb-1">
|
<p className={`text-sm font-medium mb-1 ${isDark ? 'text-gray-400' : 'text-gray-600'}`}>
|
||||||
Email
|
Email
|
||||||
</p>
|
</p>
|
||||||
<p className="text-base font-semibold text-gray-900">
|
<p className={`text-base font-semibold ${isDark ? 'text-white' : 'text-gray-900'}`}>
|
||||||
john.doe@example.com
|
john.doe@example.com
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center gap-3">
|
<div className="flex items-center gap-3">
|
||||||
<div className="p-2 rounded-lg bg-gray-50">
|
<div className={`p-2 rounded-lg ${isDark ? 'bg-gray-700' : 'bg-gray-50'}`}>
|
||||||
<Phone className="w-4 h-4 text-gray-600" />
|
<Phone className={`w-4 h-4 ${isDark ? 'text-gray-400' : 'text-gray-600'}`} />
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<p className="text-sm font-medium text-gray-600 mb-1">
|
<p className={`text-sm font-medium mb-1 ${isDark ? 'text-gray-400' : 'text-gray-600'}`}>
|
||||||
Phone
|
Phone
|
||||||
</p>
|
</p>
|
||||||
<p className="text-base font-semibold text-gray-900">
|
<p className={`text-base font-semibold ${isDark ? 'text-white' : 'text-gray-900'}`}>
|
||||||
+1 (555) 123-4567
|
+1 (555) 123-4567
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center gap-3">
|
<div className="flex items-center gap-3">
|
||||||
<div className="p-2 rounded-lg bg-gray-50">
|
<div className={`p-2 rounded-lg ${isDark ? 'bg-gray-700' : 'bg-gray-50'}`}>
|
||||||
<Calendar className="w-4 h-4 text-gray-600" />
|
<Calendar className={`w-4 h-4 ${isDark ? 'text-gray-400' : 'text-gray-600'}`} />
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<p className="text-sm font-medium text-gray-600 mb-1">
|
<p className={`text-sm font-medium mb-1 ${isDark ? 'text-gray-400' : 'text-gray-600'}`}>
|
||||||
Member Since
|
Member Since
|
||||||
</p>
|
</p>
|
||||||
<p className="text-base font-semibold text-gray-900">
|
<p className={`text-base font-semibold ${isDark ? 'text-white' : 'text-gray-900'}`}>
|
||||||
January 2025
|
January 2025
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -16,8 +16,11 @@ import {
|
|||||||
} from "lucide-react";
|
} from "lucide-react";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import { Navbar } from "@/components/Navbar";
|
import { Navbar } from "@/components/Navbar";
|
||||||
|
import { useAppTheme } from "@/components/ThemeProvider";
|
||||||
|
|
||||||
export default function SettingsPage() {
|
export default function SettingsPage() {
|
||||||
|
const { theme } = useAppTheme();
|
||||||
|
const isDark = theme === "dark";
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
const [formData, setFormData] = useState({
|
const [formData, setFormData] = useState({
|
||||||
fullName: "John Doe",
|
fullName: "John Doe",
|
||||||
@ -89,7 +92,7 @@ export default function SettingsPage() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="min-h-screen bg-gray-50">
|
<div className={`min-h-screen ${isDark ? 'bg-gray-900' : 'bg-gray-50'}`}>
|
||||||
<Navbar />
|
<Navbar />
|
||||||
|
|
||||||
{/* Main Content */}
|
{/* Main Content */}
|
||||||
@ -98,15 +101,15 @@ export default function SettingsPage() {
|
|||||||
<div className="flex items-center justify-between mb-6">
|
<div className="flex items-center justify-between mb-6">
|
||||||
<div className="flex items-center gap-4">
|
<div className="flex items-center gap-4">
|
||||||
<Link href="/user/dashboard">
|
<Link href="/user/dashboard">
|
||||||
<Button variant="ghost" size="icon" className="hover:bg-gray-100">
|
<Button variant="ghost" size="icon" className={isDark ? 'hover:bg-gray-800 text-gray-300' : 'hover:bg-gray-100'}>
|
||||||
<ArrowLeft className="w-4 h-4" />
|
<ArrowLeft className="w-4 h-4" />
|
||||||
</Button>
|
</Button>
|
||||||
</Link>
|
</Link>
|
||||||
<div>
|
<div>
|
||||||
<h1 className="text-2xl font-semibold text-gray-900 mb-1">
|
<h1 className={`text-2xl font-semibold mb-1 ${isDark ? 'text-white' : 'text-gray-900'}`}>
|
||||||
Settings
|
Settings
|
||||||
</h1>
|
</h1>
|
||||||
<p className="text-sm text-gray-600">
|
<p className={`text-sm ${isDark ? 'text-gray-400' : 'text-gray-600'}`}>
|
||||||
Manage your account settings and preferences
|
Manage your account settings and preferences
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
@ -114,7 +117,7 @@ export default function SettingsPage() {
|
|||||||
<Button
|
<Button
|
||||||
onClick={handleSave}
|
onClick={handleSave}
|
||||||
disabled={loading}
|
disabled={loading}
|
||||||
className="bg-linear-to-r from-rose-500 to-pink-600 hover:from-rose-600 hover:to-pink-700 text-white"
|
className="bg-gradient-to-r from-rose-500 to-pink-600 hover:from-rose-600 hover:to-pink-700 text-white"
|
||||||
>
|
>
|
||||||
{loading ? (
|
{loading ? (
|
||||||
<div className="animate-spin rounded-full h-4 w-4 border-b-2 border-white mr-2"></div>
|
<div className="animate-spin rounded-full h-4 w-4 border-b-2 border-white mr-2"></div>
|
||||||
@ -128,60 +131,60 @@ export default function SettingsPage() {
|
|||||||
<div className="max-w-4xl mx-auto">
|
<div className="max-w-4xl mx-auto">
|
||||||
<div className="space-y-6">
|
<div className="space-y-6">
|
||||||
{/* Profile Information */}
|
{/* Profile Information */}
|
||||||
<Card className="bg-white border-gray-200">
|
<Card className={isDark ? 'bg-gray-800 border-gray-700' : 'bg-white border-gray-200'}>
|
||||||
<CardHeader>
|
<CardHeader>
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<User className="w-5 h-5 text-gray-600" />
|
<User className={`w-5 h-5 ${isDark ? 'text-gray-400' : 'text-gray-600'}`} />
|
||||||
<CardTitle className="text-gray-900">Profile Information</CardTitle>
|
<CardTitle className={isDark ? 'text-white' : 'text-gray-900'}>Profile Information</CardTitle>
|
||||||
</div>
|
</div>
|
||||||
<CardDescription className="text-gray-600">
|
<CardDescription className={isDark ? 'text-gray-400' : 'text-gray-600'}>
|
||||||
Update your personal information and contact details
|
Update your personal information and contact details
|
||||||
</CardDescription>
|
</CardDescription>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardContent className="space-y-4">
|
<CardContent className="space-y-4">
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<label className="text-sm font-medium text-gray-700">
|
<label className={`text-sm font-medium ${isDark ? 'text-gray-300' : 'text-gray-700'}`}>
|
||||||
Full Name
|
Full Name
|
||||||
</label>
|
</label>
|
||||||
<div className="relative">
|
<div className="relative">
|
||||||
<User className="absolute left-3 top-1/2 transform -translate-y-1/2 w-4 h-4 text-gray-400" />
|
<User className={`absolute left-3 top-1/2 transform -translate-y-1/2 w-4 h-4 ${isDark ? 'text-gray-500' : 'text-gray-400'}`} />
|
||||||
<Input
|
<Input
|
||||||
type="text"
|
type="text"
|
||||||
value={formData.fullName}
|
value={formData.fullName}
|
||||||
onChange={(e) => handleInputChange("fullName", e.target.value)}
|
onChange={(e) => handleInputChange("fullName", e.target.value)}
|
||||||
className="pl-10"
|
className={`pl-10 ${isDark ? 'bg-gray-700 border-gray-600 text-white placeholder:text-gray-400' : 'bg-white border-gray-300 text-gray-900'}`}
|
||||||
placeholder="Enter your full name"
|
placeholder="Enter your full name"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<label className="text-sm font-medium text-gray-700">
|
<label className={`text-sm font-medium ${isDark ? 'text-gray-300' : 'text-gray-700'}`}>
|
||||||
Email Address
|
Email Address
|
||||||
</label>
|
</label>
|
||||||
<div className="relative">
|
<div className="relative">
|
||||||
<Mail className="absolute left-3 top-1/2 transform -translate-y-1/2 w-4 h-4 text-gray-400" />
|
<Mail className={`absolute left-3 top-1/2 transform -translate-y-1/2 w-4 h-4 ${isDark ? 'text-gray-500' : 'text-gray-400'}`} />
|
||||||
<Input
|
<Input
|
||||||
type="email"
|
type="email"
|
||||||
value={formData.email}
|
value={formData.email}
|
||||||
onChange={(e) => handleInputChange("email", e.target.value)}
|
onChange={(e) => handleInputChange("email", e.target.value)}
|
||||||
className="pl-10"
|
className={`pl-10 ${isDark ? 'bg-gray-700 border-gray-600 text-white placeholder:text-gray-400' : 'bg-white border-gray-300 text-gray-900'}`}
|
||||||
placeholder="Enter your email"
|
placeholder="Enter your email"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<label className="text-sm font-medium text-gray-700">
|
<label className={`text-sm font-medium ${isDark ? 'text-gray-300' : 'text-gray-700'}`}>
|
||||||
Phone Number
|
Phone Number
|
||||||
</label>
|
</label>
|
||||||
<div className="relative">
|
<div className="relative">
|
||||||
<Phone className="absolute left-3 top-1/2 transform -translate-y-1/2 w-4 h-4 text-gray-400" />
|
<Phone className={`absolute left-3 top-1/2 transform -translate-y-1/2 w-4 h-4 ${isDark ? 'text-gray-500' : 'text-gray-400'}`} />
|
||||||
<Input
|
<Input
|
||||||
type="tel"
|
type="tel"
|
||||||
value={formData.phone}
|
value={formData.phone}
|
||||||
onChange={(e) => handleInputChange("phone", e.target.value)}
|
onChange={(e) => handleInputChange("phone", e.target.value)}
|
||||||
className="pl-10"
|
className={`pl-10 ${isDark ? 'bg-gray-700 border-gray-600 text-white placeholder:text-gray-400' : 'bg-white border-gray-300 text-gray-900'}`}
|
||||||
placeholder="Enter your phone number"
|
placeholder="Enter your phone number"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@ -190,34 +193,34 @@ export default function SettingsPage() {
|
|||||||
</Card>
|
</Card>
|
||||||
|
|
||||||
{/* Change Password */}
|
{/* Change Password */}
|
||||||
<Card className="bg-white border-gray-200">
|
<Card className={isDark ? 'bg-gray-800 border-gray-700' : 'bg-white border-gray-200'}>
|
||||||
<CardHeader>
|
<CardHeader>
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<Lock className="w-5 h-5 text-gray-600" />
|
<Lock className={`w-5 h-5 ${isDark ? 'text-gray-400' : 'text-gray-600'}`} />
|
||||||
<CardTitle className="text-gray-900">Change Password</CardTitle>
|
<CardTitle className={isDark ? 'text-white' : 'text-gray-900'}>Change Password</CardTitle>
|
||||||
</div>
|
</div>
|
||||||
<CardDescription className="text-gray-600">
|
<CardDescription className={isDark ? 'text-gray-400' : 'text-gray-600'}>
|
||||||
Update your password to keep your account secure
|
Update your password to keep your account secure
|
||||||
</CardDescription>
|
</CardDescription>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardContent className="space-y-4">
|
<CardContent className="space-y-4">
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<label className="text-sm font-medium text-gray-700">
|
<label className={`text-sm font-medium ${isDark ? 'text-gray-300' : 'text-gray-700'}`}>
|
||||||
Current Password
|
Current Password
|
||||||
</label>
|
</label>
|
||||||
<div className="relative">
|
<div className="relative">
|
||||||
<Lock className="absolute left-3 top-1/2 transform -translate-y-1/2 w-4 h-4 text-gray-400" />
|
<Lock className={`absolute left-3 top-1/2 transform -translate-y-1/2 w-4 h-4 ${isDark ? 'text-gray-500' : 'text-gray-400'}`} />
|
||||||
<Input
|
<Input
|
||||||
type={showPasswords.current ? "text" : "password"}
|
type={showPasswords.current ? "text" : "password"}
|
||||||
value={passwordData.currentPassword}
|
value={passwordData.currentPassword}
|
||||||
onChange={(e) => handlePasswordChange("currentPassword", e.target.value)}
|
onChange={(e) => handlePasswordChange("currentPassword", e.target.value)}
|
||||||
className="pl-10 pr-10"
|
className={`pl-10 pr-10 ${isDark ? 'bg-gray-700 border-gray-600 text-white placeholder:text-gray-400' : 'bg-white border-gray-300 text-gray-900'}`}
|
||||||
placeholder="Enter your current password"
|
placeholder="Enter your current password"
|
||||||
/>
|
/>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={() => togglePasswordVisibility("current")}
|
onClick={() => togglePasswordVisibility("current")}
|
||||||
className="absolute right-3 top-1/2 transform -translate-y-1/2 text-gray-400 hover:text-gray-600"
|
className={`absolute right-3 top-1/2 transform -translate-y-1/2 ${isDark ? 'text-gray-400 hover:text-gray-300' : 'text-gray-400 hover:text-gray-600'}`}
|
||||||
>
|
>
|
||||||
{showPasswords.current ? (
|
{showPasswords.current ? (
|
||||||
<EyeOff className="w-4 h-4" />
|
<EyeOff className="w-4 h-4" />
|
||||||
@ -229,22 +232,22 @@ export default function SettingsPage() {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<label className="text-sm font-medium text-gray-700">
|
<label className={`text-sm font-medium ${isDark ? 'text-gray-300' : 'text-gray-700'}`}>
|
||||||
New Password
|
New Password
|
||||||
</label>
|
</label>
|
||||||
<div className="relative">
|
<div className="relative">
|
||||||
<Lock className="absolute left-3 top-1/2 transform -translate-y-1/2 w-4 h-4 text-gray-400" />
|
<Lock className={`absolute left-3 top-1/2 transform -translate-y-1/2 w-4 h-4 ${isDark ? 'text-gray-500' : 'text-gray-400'}`} />
|
||||||
<Input
|
<Input
|
||||||
type={showPasswords.new ? "text" : "password"}
|
type={showPasswords.new ? "text" : "password"}
|
||||||
value={passwordData.newPassword}
|
value={passwordData.newPassword}
|
||||||
onChange={(e) => handlePasswordChange("newPassword", e.target.value)}
|
onChange={(e) => handlePasswordChange("newPassword", e.target.value)}
|
||||||
className="pl-10 pr-10"
|
className={`pl-10 pr-10 ${isDark ? 'bg-gray-700 border-gray-600 text-white placeholder:text-gray-400' : 'bg-white border-gray-300 text-gray-900'}`}
|
||||||
placeholder="Enter your new password"
|
placeholder="Enter your new password"
|
||||||
/>
|
/>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={() => togglePasswordVisibility("new")}
|
onClick={() => togglePasswordVisibility("new")}
|
||||||
className="absolute right-3 top-1/2 transform -translate-y-1/2 text-gray-400 hover:text-gray-600"
|
className={`absolute right-3 top-1/2 transform -translate-y-1/2 ${isDark ? 'text-gray-400 hover:text-gray-300' : 'text-gray-400 hover:text-gray-600'}`}
|
||||||
>
|
>
|
||||||
{showPasswords.new ? (
|
{showPasswords.new ? (
|
||||||
<EyeOff className="w-4 h-4" />
|
<EyeOff className="w-4 h-4" />
|
||||||
@ -253,28 +256,28 @@ export default function SettingsPage() {
|
|||||||
)}
|
)}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<p className="text-xs text-gray-500">
|
<p className={`text-xs ${isDark ? 'text-gray-400' : 'text-gray-500'}`}>
|
||||||
Password must be at least 8 characters long
|
Password must be at least 8 characters long
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<label className="text-sm font-medium text-gray-700">
|
<label className={`text-sm font-medium ${isDark ? 'text-gray-300' : 'text-gray-700'}`}>
|
||||||
Confirm New Password
|
Confirm New Password
|
||||||
</label>
|
</label>
|
||||||
<div className="relative">
|
<div className="relative">
|
||||||
<Lock className="absolute left-3 top-1/2 transform -translate-y-1/2 w-4 h-4 text-gray-400" />
|
<Lock className={`absolute left-3 top-1/2 transform -translate-y-1/2 w-4 h-4 ${isDark ? 'text-gray-500' : 'text-gray-400'}`} />
|
||||||
<Input
|
<Input
|
||||||
type={showPasswords.confirm ? "text" : "password"}
|
type={showPasswords.confirm ? "text" : "password"}
|
||||||
value={passwordData.confirmPassword}
|
value={passwordData.confirmPassword}
|
||||||
onChange={(e) => handlePasswordChange("confirmPassword", e.target.value)}
|
onChange={(e) => handlePasswordChange("confirmPassword", e.target.value)}
|
||||||
className="pl-10 pr-10"
|
className={`pl-10 pr-10 ${isDark ? 'bg-gray-700 border-gray-600 text-white placeholder:text-gray-400' : 'bg-white border-gray-300 text-gray-900'}`}
|
||||||
placeholder="Confirm your new password"
|
placeholder="Confirm your new password"
|
||||||
/>
|
/>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={() => togglePasswordVisibility("confirm")}
|
onClick={() => togglePasswordVisibility("confirm")}
|
||||||
className="absolute right-3 top-1/2 transform -translate-y-1/2 text-gray-400 hover:text-gray-600"
|
className={`absolute right-3 top-1/2 transform -translate-y-1/2 ${isDark ? 'text-gray-400 hover:text-gray-300' : 'text-gray-400 hover:text-gray-600'}`}
|
||||||
>
|
>
|
||||||
{showPasswords.confirm ? (
|
{showPasswords.confirm ? (
|
||||||
<EyeOff className="w-4 h-4" />
|
<EyeOff className="w-4 h-4" />
|
||||||
@ -289,7 +292,7 @@ export default function SettingsPage() {
|
|||||||
<Button
|
<Button
|
||||||
onClick={handlePasswordSave}
|
onClick={handlePasswordSave}
|
||||||
disabled={loading || !passwordData.currentPassword || !passwordData.newPassword || !passwordData.confirmPassword}
|
disabled={loading || !passwordData.currentPassword || !passwordData.newPassword || !passwordData.confirmPassword}
|
||||||
className="bg-linear-to-r from-rose-500 to-pink-600 hover:from-rose-600 hover:to-pink-700 text-white"
|
className="bg-gradient-to-r from-rose-500 to-pink-600 hover:from-rose-600 hover:to-pink-700 text-white"
|
||||||
>
|
>
|
||||||
{loading ? (
|
{loading ? (
|
||||||
<div className="animate-spin rounded-full h-4 w-4 border-b-2 border-white mr-2"></div>
|
<div className="animate-spin rounded-full h-4 w-4 border-b-2 border-white mr-2"></div>
|
||||||
|
|||||||
@ -2,28 +2,15 @@
|
|||||||
|
|
||||||
import { motion } from "framer-motion";
|
import { motion } from "framer-motion";
|
||||||
import { useInView } from "framer-motion";
|
import { useInView } from "framer-motion";
|
||||||
import { useRef, useEffect, useState } from "react";
|
import { useRef } from "react";
|
||||||
import { Award, Heart, Users } from "lucide-react";
|
import { Award, Heart, Users } from "lucide-react";
|
||||||
|
import { useAppTheme } from "@/components/ThemeProvider";
|
||||||
|
|
||||||
export function About() {
|
export function About() {
|
||||||
const ref = useRef(null);
|
const ref = useRef(null);
|
||||||
const isInView = useInView(ref, { once: true, margin: "-100px" });
|
const isInView = useInView(ref, { once: true, margin: "-100px" });
|
||||||
const [isDark, setIsDark] = useState(false);
|
const { theme } = useAppTheme();
|
||||||
|
const isDark = theme === "dark";
|
||||||
useEffect(() => {
|
|
||||||
const checkTheme = () => {
|
|
||||||
setIsDark(document.documentElement.classList.contains('dark'));
|
|
||||||
};
|
|
||||||
|
|
||||||
checkTheme();
|
|
||||||
const observer = new MutationObserver(checkTheme);
|
|
||||||
observer.observe(document.documentElement, {
|
|
||||||
attributes: true,
|
|
||||||
attributeFilter: ['class']
|
|
||||||
});
|
|
||||||
|
|
||||||
return () => observer.disconnect();
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const credentials = [
|
const credentials = [
|
||||||
{
|
{
|
||||||
@ -114,7 +101,7 @@ export function About() {
|
|||||||
className="text-center mb-16"
|
className="text-center mb-16"
|
||||||
>
|
>
|
||||||
<motion.h2
|
<motion.h2
|
||||||
className="text-4xl md:text-5xl font-bold mb-6 bg-gradient-to-r from-rose-600 via-pink-600 to-orange-600 bg-clip-text text-transparent"
|
className="text-3xl sm:text-4xl md:text-5xl font-bold mb-4 sm:mb-6 px-4 bg-gradient-to-r from-rose-600 via-pink-600 to-orange-600 bg-clip-text text-transparent"
|
||||||
initial={{ opacity: 0, y: 20 }}
|
initial={{ opacity: 0, y: 20 }}
|
||||||
animate={isInView ? { opacity: 1, y: 0 } : {}}
|
animate={isInView ? { opacity: 1, y: 0 } : {}}
|
||||||
transition={{ duration: 0.8, delay: 0.2 }}
|
transition={{ duration: 0.8, delay: 0.2 }}
|
||||||
@ -122,7 +109,7 @@ export function About() {
|
|||||||
Meet Nathalie Mac-Guffie
|
Meet Nathalie Mac-Guffie
|
||||||
</motion.h2>
|
</motion.h2>
|
||||||
<motion.p
|
<motion.p
|
||||||
className="text-xl text-muted-foreground max-w-3xl mx-auto"
|
className="text-base sm:text-lg md:text-xl text-muted-foreground max-w-3xl mx-auto px-4"
|
||||||
initial={{ opacity: 0, y: 20 }}
|
initial={{ opacity: 0, y: 20 }}
|
||||||
animate={isInView ? { opacity: 1, y: 0 } : {}}
|
animate={isInView ? { opacity: 1, y: 0 } : {}}
|
||||||
transition={{ duration: 0.8, delay: 0.4 }}
|
transition={{ duration: 0.8, delay: 0.4 }}
|
||||||
@ -133,15 +120,15 @@ export function About() {
|
|||||||
</motion.p>
|
</motion.p>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
|
|
||||||
<div className="grid md:grid-cols-2 gap-12 items-center mb-16">
|
<div className="grid md:grid-cols-2 gap-8 md:gap-12 items-center mb-12 md:mb-16 px-4">
|
||||||
<motion.div
|
<motion.div
|
||||||
initial={{ opacity: 0, x: -50 }}
|
initial={{ opacity: 0, x: -50 }}
|
||||||
animate={isInView ? { opacity: 1, x: 0 } : {}}
|
animate={isInView ? { opacity: 1, x: 0 } : {}}
|
||||||
transition={{ duration: 0.8, delay: 0.2 }}
|
transition={{ duration: 0.8, delay: 0.2 }}
|
||||||
>
|
>
|
||||||
<div className="bg-gradient-to-br from-rose-100/30 via-pink-100/30 to-orange-100/30 dark:from-rose-900/20 dark:via-pink-900/20 dark:to-orange-900/20 rounded-3xl p-8 border border-border/50 backdrop-blur-sm">
|
<div className="bg-gradient-to-br from-rose-100/30 via-pink-100/30 to-orange-100/30 dark:from-rose-900/20 dark:via-pink-900/20 dark:to-orange-900/20 rounded-2xl md:rounded-3xl p-6 md:p-8 border border-border/50 backdrop-blur-sm">
|
||||||
<motion.h3
|
<motion.h3
|
||||||
className="text-2xl font-semibold mb-4 bg-gradient-to-r from-rose-600 via-pink-600 to-orange-600 bg-clip-text text-transparent"
|
className="text-xl sm:text-2xl font-semibold mb-3 md:mb-4 bg-gradient-to-r from-rose-600 via-pink-600 to-orange-600 bg-clip-text text-transparent"
|
||||||
initial={{ opacity: 0 }}
|
initial={{ opacity: 0 }}
|
||||||
animate={isInView ? { opacity: 1 } : {}}
|
animate={isInView ? { opacity: 1 } : {}}
|
||||||
transition={{ duration: 0.8, delay: 0.3 }}
|
transition={{ duration: 0.8, delay: 0.3 }}
|
||||||
|
|||||||
@ -2,28 +2,15 @@
|
|||||||
|
|
||||||
import { motion } from "framer-motion";
|
import { motion } from "framer-motion";
|
||||||
import { useInView } from "framer-motion";
|
import { useInView } from "framer-motion";
|
||||||
import { useRef, useEffect, useState } from "react";
|
import { useRef } from "react";
|
||||||
import { Users, UserCheck, Globe } from "lucide-react";
|
import { Users, UserCheck, Globe } from "lucide-react";
|
||||||
|
import { useAppTheme } from "@/components/ThemeProvider";
|
||||||
|
|
||||||
export function ClientFocus() {
|
export function ClientFocus() {
|
||||||
const ref = useRef(null);
|
const ref = useRef(null);
|
||||||
const isInView = useInView(ref, { once: true, margin: "-100px" });
|
const isInView = useInView(ref, { once: true, margin: "-100px" });
|
||||||
const [isDark, setIsDark] = useState(false);
|
const { theme } = useAppTheme();
|
||||||
|
const isDark = theme === "dark";
|
||||||
useEffect(() => {
|
|
||||||
const checkTheme = () => {
|
|
||||||
setIsDark(document.documentElement.classList.contains('dark'));
|
|
||||||
};
|
|
||||||
|
|
||||||
checkTheme();
|
|
||||||
const observer = new MutationObserver(checkTheme);
|
|
||||||
observer.observe(document.documentElement, {
|
|
||||||
attributes: true,
|
|
||||||
attributeFilter: ['class']
|
|
||||||
});
|
|
||||||
|
|
||||||
return () => observer.disconnect();
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const ages = [
|
const ages = [
|
||||||
"Children (6 to 10)",
|
"Children (6 to 10)",
|
||||||
|
|||||||
@ -1,18 +1,20 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { motion, useInView } from "framer-motion";
|
import { motion, useInView } from "framer-motion";
|
||||||
import { useEffect, useRef, useState } from "react";
|
import { useRef, useState } from "react";
|
||||||
import { Send } from "lucide-react";
|
import { Send } from "lucide-react";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import { Input } from "@/components/ui/input";
|
import { Input } from "@/components/ui/input";
|
||||||
import { Textarea } from "@/components/ui/textarea";
|
import { Textarea } from "@/components/ui/textarea";
|
||||||
import { Card, CardContent } from "@/components/ui/card";
|
import { Card, CardContent } from "@/components/ui/card";
|
||||||
import { toast } from "sonner";
|
import { toast } from "sonner";
|
||||||
|
import { useAppTheme } from "@/components/ThemeProvider";
|
||||||
|
|
||||||
export function ContactSection() {
|
export function ContactSection() {
|
||||||
const ref = useRef(null);
|
const ref = useRef(null);
|
||||||
const isInView = useInView(ref, { once: true, margin: "-100px" });
|
const isInView = useInView(ref, { once: true, margin: "-100px" });
|
||||||
const [isDark, setIsDark] = useState(false);
|
const { theme } = useAppTheme();
|
||||||
|
const isDark = theme === "dark";
|
||||||
const [formData, setFormData] = useState({
|
const [formData, setFormData] = useState({
|
||||||
name: "",
|
name: "",
|
||||||
email: "",
|
email: "",
|
||||||
@ -20,15 +22,6 @@ export function ContactSection() {
|
|||||||
message: "",
|
message: "",
|
||||||
});
|
});
|
||||||
|
|
||||||
// Sync with global theme class like Navbar/Hero/About
|
|
||||||
useEffect(() => {
|
|
||||||
const sync = () => setIsDark(document.documentElement.classList.contains("dark"));
|
|
||||||
sync();
|
|
||||||
const observer = new MutationObserver(sync);
|
|
||||||
observer.observe(document.documentElement, { attributes: true, attributeFilter: ["class"] });
|
|
||||||
return () => observer.disconnect();
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const handleSubmit = (e: React.FormEvent) => {
|
const handleSubmit = (e: React.FormEvent) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
toast("Message Received", {
|
toast("Message Received", {
|
||||||
@ -87,16 +80,16 @@ export function ContactSection() {
|
|||||||
transition={{ duration: 0.6 }}
|
transition={{ duration: 0.6 }}
|
||||||
className="mb-16 text-center"
|
className="mb-16 text-center"
|
||||||
>
|
>
|
||||||
<h2 className="text-4xl md:text-5xl font-bold mb-4 bg-gradient-to-r from-rose-600 via-pink-600 to-orange-600 bg-clip-text text-transparent">
|
<h2 className="text-3xl sm:text-4xl md:text-5xl font-bold mb-4 bg-gradient-to-r from-rose-600 via-pink-600 to-orange-600 bg-clip-text text-transparent px-4">
|
||||||
Get in Touch
|
Get in Touch
|
||||||
</h2>
|
</h2>
|
||||||
<div className="mx-auto mb-6 h-1 w-24 rounded-full bg-gradient-to-r from-rose-500 to-pink-600" />
|
<div className="mx-auto mb-4 sm:mb-6 h-1 w-20 sm:w-24 rounded-full bg-gradient-to-r from-rose-500 to-pink-600" />
|
||||||
<p className="mx-auto max-w-2xl text-lg text-muted-foreground">
|
<p className="mx-auto max-w-2xl text-base sm:text-lg text-muted-foreground px-4">
|
||||||
Ready to start your journey? Reach out to schedule a consultation.
|
Ready to start your journey? Reach out to schedule a consultation.
|
||||||
</p>
|
</p>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
|
|
||||||
<div className="grid gap-12 lg:grid-cols-2 lg:items-stretch">
|
<div className="grid gap-8 sm:gap-10 lg:gap-12 lg:grid-cols-2 lg:items-stretch">
|
||||||
{/* Left: Illustration replacing cards */}
|
{/* Left: Illustration replacing cards */}
|
||||||
<motion.div
|
<motion.div
|
||||||
initial={{ opacity: 0, x: -50 }}
|
initial={{ opacity: 0, x: -50 }}
|
||||||
@ -139,8 +132,8 @@ export function ContactSection() {
|
|||||||
</div>
|
</div>
|
||||||
{/* Text content */}
|
{/* Text content */}
|
||||||
<div className="flex-1 text-center sm:text-left flex flex-col justify-center">
|
<div className="flex-1 text-center sm:text-left flex flex-col justify-center">
|
||||||
<h3 className="text-2xl font-bold mb-4 text-foreground">Let's Begin Your Healing Journey</h3>
|
<h3 className="text-xl sm:text-2xl font-bold mb-3 sm:mb-4 text-foreground">Let's Begin Your Healing Journey</h3>
|
||||||
<div className="space-y-3 text-muted-foreground leading-relaxed">
|
<div className="space-y-2 sm:space-y-3 text-sm sm:text-base text-muted-foreground leading-relaxed">
|
||||||
<p>
|
<p>
|
||||||
Taking the first step toward therapy can feel daunting, but you're not alone. I'm here to support
|
Taking the first step toward therapy can feel daunting, but you're not alone. I'm here to support
|
||||||
you through every stage of your journey toward wellness and growth.
|
you through every stage of your journey toward wellness and growth.
|
||||||
@ -168,8 +161,8 @@ export function ContactSection() {
|
|||||||
transition={{ duration: 0.6, delay: 0.4 }}
|
transition={{ duration: 0.6, delay: 0.4 }}
|
||||||
>
|
>
|
||||||
<Card className="border border-border/50 bg-card/70 backdrop-blur-sm">
|
<Card className="border border-border/50 bg-card/70 backdrop-blur-sm">
|
||||||
<CardContent className="p-6 md:p-8">
|
<CardContent className="p-4 sm:p-6 md:p-8">
|
||||||
<h3 className="mb-6 text-2xl font-bold text-foreground">Send a Message</h3>
|
<h3 className="mb-4 sm:mb-6 text-xl sm:text-2xl font-bold text-foreground">Send a Message</h3>
|
||||||
<form onSubmit={handleSubmit} className="space-y-4">
|
<form onSubmit={handleSubmit} className="space-y-4">
|
||||||
<div>
|
<div>
|
||||||
<Input
|
<Input
|
||||||
|
|||||||
@ -2,28 +2,15 @@
|
|||||||
|
|
||||||
import { motion } from "framer-motion";
|
import { motion } from "framer-motion";
|
||||||
import { useInView } from "framer-motion";
|
import { useInView } from "framer-motion";
|
||||||
import { useRef, useEffect, useState } from "react";
|
import { useRef } from "react";
|
||||||
import { CreditCard, DollarSign, Shield } from "lucide-react";
|
import { CreditCard, DollarSign, Shield } from "lucide-react";
|
||||||
|
import { useAppTheme } from "@/components/ThemeProvider";
|
||||||
|
|
||||||
export function Finances() {
|
export function Finances() {
|
||||||
const ref = useRef(null);
|
const ref = useRef(null);
|
||||||
const isInView = useInView(ref, { once: true, margin: "-100px" });
|
const isInView = useInView(ref, { once: true, margin: "-100px" });
|
||||||
const [isDark, setIsDark] = useState(false);
|
const { theme } = useAppTheme();
|
||||||
|
const isDark = theme === "dark";
|
||||||
useEffect(() => {
|
|
||||||
const checkTheme = () => {
|
|
||||||
setIsDark(document.documentElement.classList.contains('dark'));
|
|
||||||
};
|
|
||||||
|
|
||||||
checkTheme();
|
|
||||||
const observer = new MutationObserver(checkTheme);
|
|
||||||
observer.observe(document.documentElement, {
|
|
||||||
attributes: true,
|
|
||||||
attributeFilter: ['class']
|
|
||||||
});
|
|
||||||
|
|
||||||
return () => observer.disconnect();
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const paymentMethods = [
|
const paymentMethods = [
|
||||||
"American Express",
|
"American Express",
|
||||||
|
|||||||
@ -2,25 +2,11 @@
|
|||||||
|
|
||||||
import { motion } from "framer-motion";
|
import { motion } from "framer-motion";
|
||||||
import { Heart, Mail, Phone, MapPin } from "lucide-react";
|
import { Heart, Mail, Phone, MapPin } from "lucide-react";
|
||||||
import { useEffect, useState } from "react";
|
import { useAppTheme } from "@/components/ThemeProvider";
|
||||||
|
|
||||||
export function Footer() {
|
export function Footer() {
|
||||||
const [isDark, setIsDark] = useState(false);
|
const { theme } = useAppTheme();
|
||||||
|
const isDark = theme === "dark";
|
||||||
useEffect(() => {
|
|
||||||
const checkTheme = () => {
|
|
||||||
setIsDark(document.documentElement.classList.contains('dark'));
|
|
||||||
};
|
|
||||||
|
|
||||||
checkTheme();
|
|
||||||
const observer = new MutationObserver(checkTheme);
|
|
||||||
observer.observe(document.documentElement, {
|
|
||||||
attributes: true,
|
|
||||||
attributeFilter: ['class']
|
|
||||||
});
|
|
||||||
|
|
||||||
return () => observer.disconnect();
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const scrollToSection = (id: string) => {
|
const scrollToSection = (id: string) => {
|
||||||
const element = document.getElementById(id);
|
const element = document.getElementById(id);
|
||||||
@ -50,8 +36,8 @@ export function Footer() {
|
|||||||
<div className="absolute inset-0 bg-gradient-to-br from-gray-900/40 via-gray-800/40 to-gray-900/40" />
|
<div className="absolute inset-0 bg-gradient-to-br from-gray-900/40 via-gray-800/40 to-gray-900/40" />
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<div className="container mx-auto px-4 relative z-10">
|
<div className="container mx-auto px-4 sm:px-6 relative z-10">
|
||||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-8 mb-8">
|
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-6 sm:gap-8 mb-6 sm:mb-8">
|
||||||
{/* Brand Section */}
|
{/* Brand Section */}
|
||||||
<motion.div
|
<motion.div
|
||||||
initial={{ opacity: 0, y: 20 }}
|
initial={{ opacity: 0, y: 20 }}
|
||||||
@ -163,7 +149,7 @@ export function Footer() {
|
|||||||
transition={{ duration: 0.6, delay: 0.4 }}
|
transition={{ duration: 0.6, delay: 0.4 }}
|
||||||
className="mt-8 pt-8 border-t border-border/50"
|
className="mt-8 pt-8 border-t border-border/50"
|
||||||
>
|
>
|
||||||
<div className="grid grid-cols-1 items-center gap-3 text-center md:grid-cols-3 md:text-left">
|
<div className="grid grid-cols-1 items-center gap-3 text-center sm:grid-cols-3 sm:text-left">
|
||||||
<p className="text-sm text-muted-foreground md:justify-self-start">
|
<p className="text-sm text-muted-foreground md:justify-self-start">
|
||||||
© {new Date().getFullYear()} Attune Heart Therapy. All rights reserved.
|
© {new Date().getFullYear()} Attune Heart Therapy. All rights reserved.
|
||||||
</p>
|
</p>
|
||||||
|
|||||||
@ -3,25 +3,11 @@
|
|||||||
import { motion } from 'framer-motion';
|
import { motion } from 'framer-motion';
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
import { ArrowRight, Calendar } from 'lucide-react';
|
import { ArrowRight, Calendar } from 'lucide-react';
|
||||||
import { useEffect, useState } from 'react';
|
import { useAppTheme } from '@/components/ThemeProvider';
|
||||||
|
|
||||||
export function HeroSection() {
|
export function HeroSection() {
|
||||||
const [isDark, setIsDark] = useState(false);
|
const { theme } = useAppTheme();
|
||||||
|
const isDark = theme === "dark";
|
||||||
useEffect(() => {
|
|
||||||
const checkTheme = () => {
|
|
||||||
setIsDark(document.documentElement.classList.contains('dark'));
|
|
||||||
};
|
|
||||||
|
|
||||||
checkTheme();
|
|
||||||
const observer = new MutationObserver(checkTheme);
|
|
||||||
observer.observe(document.documentElement, {
|
|
||||||
attributes: true,
|
|
||||||
attributeFilter: ['class']
|
|
||||||
});
|
|
||||||
|
|
||||||
return () => observer.disconnect();
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section
|
<section
|
||||||
@ -106,7 +92,7 @@ export function HeroSection() {
|
|||||||
transition={{ duration: 0.8 }}
|
transition={{ duration: 0.8 }}
|
||||||
>
|
>
|
||||||
<motion.h1
|
<motion.h1
|
||||||
className="text-5xl md:text-7xl font-bold mb-6 text-white drop-shadow-lg"
|
className="text-3xl sm:text-4xl md:text-5xl lg:text-7xl font-bold mb-4 sm:mb-6 text-white drop-shadow-lg px-4"
|
||||||
initial={{ opacity: 0, y: 20 }}
|
initial={{ opacity: 0, y: 20 }}
|
||||||
animate={{ opacity: 1, y: 0 }}
|
animate={{ opacity: 1, y: 0 }}
|
||||||
transition={{ duration: 0.8, delay: 0.2 }}
|
transition={{ duration: 0.8, delay: 0.2 }}
|
||||||
@ -115,7 +101,7 @@ export function HeroSection() {
|
|||||||
</motion.h1>
|
</motion.h1>
|
||||||
|
|
||||||
<motion.p
|
<motion.p
|
||||||
className="text-xl md:text-2xl text-white/95 mb-4 drop-shadow-md"
|
className="text-lg sm:text-xl md:text-2xl text-white/95 mb-3 sm:mb-4 drop-shadow-md px-4"
|
||||||
initial={{ opacity: 0, y: 20 }}
|
initial={{ opacity: 0, y: 20 }}
|
||||||
animate={{ opacity: 1, y: 0 }}
|
animate={{ opacity: 1, y: 0 }}
|
||||||
transition={{ duration: 0.8, delay: 0.4 }}
|
transition={{ duration: 0.8, delay: 0.4 }}
|
||||||
@ -124,7 +110,7 @@ export function HeroSection() {
|
|||||||
</motion.p>
|
</motion.p>
|
||||||
|
|
||||||
<motion.p
|
<motion.p
|
||||||
className="text-lg md:text-xl text-white/90 mb-8 max-w-2xl mx-auto drop-shadow-md"
|
className="text-base sm:text-lg md:text-xl text-white/90 mb-6 sm:mb-8 max-w-2xl mx-auto drop-shadow-md px-4"
|
||||||
initial={{ opacity: 0, y: 20 }}
|
initial={{ opacity: 0, y: 20 }}
|
||||||
animate={{ opacity: 1, y: 0 }}
|
animate={{ opacity: 1, y: 0 }}
|
||||||
transition={{ duration: 0.8, delay: 0.6 }}
|
transition={{ duration: 0.8, delay: 0.6 }}
|
||||||
|
|||||||
@ -2,28 +2,15 @@
|
|||||||
|
|
||||||
import { motion } from "framer-motion";
|
import { motion } from "framer-motion";
|
||||||
import { useInView } from "framer-motion";
|
import { useInView } from "framer-motion";
|
||||||
import { useRef, useEffect, useState } from "react";
|
import { useRef } from "react";
|
||||||
import { MapPin, Phone, Building2 } from "lucide-react";
|
import { MapPin, Phone, Building2 } from "lucide-react";
|
||||||
|
import { useAppTheme } from "@/components/ThemeProvider";
|
||||||
|
|
||||||
export function Location() {
|
export function Location() {
|
||||||
const ref = useRef(null);
|
const ref = useRef(null);
|
||||||
const isInView = useInView(ref, { once: true, margin: "-100px" });
|
const isInView = useInView(ref, { once: true, margin: "-100px" });
|
||||||
const [isDark, setIsDark] = useState(false);
|
const { theme } = useAppTheme();
|
||||||
|
const isDark = theme === "dark";
|
||||||
useEffect(() => {
|
|
||||||
const checkTheme = () => {
|
|
||||||
setIsDark(document.documentElement.classList.contains('dark'));
|
|
||||||
};
|
|
||||||
|
|
||||||
checkTheme();
|
|
||||||
const observer = new MutationObserver(checkTheme);
|
|
||||||
observer.observe(document.documentElement, {
|
|
||||||
attributes: true,
|
|
||||||
attributeFilter: ['class']
|
|
||||||
});
|
|
||||||
|
|
||||||
return () => observer.disconnect();
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const cities = [
|
const cities = [
|
||||||
"Hollywood, FL",
|
"Hollywood, FL",
|
||||||
|
|||||||
@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
|
import { useAppTheme } from "@/components/ThemeProvider";
|
||||||
import { Input } from "@/components/ui/input";
|
import { Input } from "@/components/ui/input";
|
||||||
import {
|
import {
|
||||||
Dialog,
|
Dialog,
|
||||||
@ -10,7 +11,7 @@ import {
|
|||||||
DialogHeader,
|
DialogHeader,
|
||||||
DialogTitle,
|
DialogTitle,
|
||||||
} from "@/components/ui/dialog";
|
} from "@/components/ui/dialog";
|
||||||
import { Eye, EyeOff, Loader2 } from "lucide-react";
|
import { Eye, EyeOff, Loader2, X } from "lucide-react";
|
||||||
|
|
||||||
interface LoginDialogProps {
|
interface LoginDialogProps {
|
||||||
open: boolean;
|
open: boolean;
|
||||||
@ -20,6 +21,8 @@ interface LoginDialogProps {
|
|||||||
|
|
||||||
// Login Dialog component
|
// Login Dialog component
|
||||||
export function LoginDialog({ open, onOpenChange, onLoginSuccess }: LoginDialogProps) {
|
export function LoginDialog({ open, onOpenChange, onLoginSuccess }: LoginDialogProps) {
|
||||||
|
const { theme } = useAppTheme();
|
||||||
|
const isDark = theme === "dark";
|
||||||
const [isSignup, setIsSignup] = useState(false);
|
const [isSignup, setIsSignup] = useState(false);
|
||||||
const [loginData, setLoginData] = useState({
|
const [loginData, setLoginData] = useState({
|
||||||
email: "",
|
email: "",
|
||||||
@ -89,30 +92,44 @@ export function LoginDialog({ open, onOpenChange, onLoginSuccess }: LoginDialogP
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Dialog open={open} onOpenChange={onOpenChange}>
|
<Dialog open={open} onOpenChange={onOpenChange}>
|
||||||
<DialogContent className="sm:max-w-md bg-white">
|
<DialogContent
|
||||||
<DialogHeader>
|
showCloseButton={false}
|
||||||
<DialogTitle className="text-3xl font-bold bg-linear-to-r from-rose-600 via-pink-600 to-rose-600 bg-clip-text text-transparent">
|
className={`sm:max-w-md ${isDark ? 'bg-gray-800 border-gray-700' : 'bg-white border-gray-200'}`}
|
||||||
{isSignup ? "Create an account" : "Welcome back"}
|
>
|
||||||
</DialogTitle>
|
{/* Header with Close Button */}
|
||||||
<DialogDescription className="text-gray-600">
|
<div className="flex items-start justify-between mb-2">
|
||||||
{isSignup
|
<DialogHeader className="flex-1">
|
||||||
? "Sign up to complete your booking"
|
<DialogTitle className="text-3xl font-bold bg-gradient-to-r from-rose-600 via-pink-600 to-rose-600 bg-clip-text text-transparent">
|
||||||
: "Please log in to complete your booking"}
|
{isSignup ? "Create an account" : "Welcome back"}
|
||||||
</DialogDescription>
|
</DialogTitle>
|
||||||
</DialogHeader>
|
<DialogDescription className={isDark ? 'text-gray-400' : 'text-gray-600'}>
|
||||||
|
{isSignup
|
||||||
|
? "Sign up to complete your booking"
|
||||||
|
: "Please log in to complete your booking"}
|
||||||
|
</DialogDescription>
|
||||||
|
</DialogHeader>
|
||||||
|
{/* Close Button */}
|
||||||
|
<button
|
||||||
|
onClick={() => onOpenChange(false)}
|
||||||
|
className={`flex-shrink-0 w-8 h-8 rounded-full flex items-center justify-center transition-colors ${isDark ? 'text-gray-400 hover:text-gray-300 hover:bg-gray-700' : 'text-gray-500 hover:text-gray-700 hover:bg-gray-100'}`}
|
||||||
|
aria-label="Close"
|
||||||
|
>
|
||||||
|
<X className="w-5 h-5" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
{/* Signup Form */}
|
{/* Signup Form */}
|
||||||
{isSignup ? (
|
{isSignup ? (
|
||||||
<form className="space-y-6 mt-4" onSubmit={handleSignup}>
|
<form className="space-y-6 mt-4" onSubmit={handleSignup}>
|
||||||
{error && (
|
{error && (
|
||||||
<div className="p-3 bg-red-50 border border-red-200 rounded-lg">
|
<div className={`p-3 rounded-lg border ${isDark ? 'bg-red-900/20 border-red-800' : 'bg-red-50 border-red-200'}`}>
|
||||||
<p className="text-sm text-red-800">{error}</p>
|
<p className={`text-sm ${isDark ? 'text-red-200' : 'text-red-800'}`}>{error}</p>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Full Name Field */}
|
{/* Full Name Field */}
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<label htmlFor="signup-fullName" className="text-sm font-medium text-black">
|
<label htmlFor="signup-fullName" className={`text-sm font-medium ${isDark ? 'text-gray-300' : 'text-black'}`}>
|
||||||
Full Name *
|
Full Name *
|
||||||
</label>
|
</label>
|
||||||
<Input
|
<Input
|
||||||
@ -121,14 +138,14 @@ export function LoginDialog({ open, onOpenChange, onLoginSuccess }: LoginDialogP
|
|||||||
placeholder="John Doe"
|
placeholder="John Doe"
|
||||||
value={signupData.fullName}
|
value={signupData.fullName}
|
||||||
onChange={(e) => setSignupData({ ...signupData, fullName: e.target.value })}
|
onChange={(e) => setSignupData({ ...signupData, fullName: e.target.value })}
|
||||||
className="h-12 bg-white border-gray-300"
|
className={`h-12 ${isDark ? 'bg-gray-700 border-gray-600 text-white placeholder:text-gray-400' : 'bg-white border-gray-300 text-gray-900'}`}
|
||||||
required
|
required
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Email Field */}
|
{/* Email Field */}
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<label htmlFor="signup-email" className="text-sm font-medium text-black">
|
<label htmlFor="signup-email" className={`text-sm font-medium ${isDark ? 'text-gray-300' : 'text-black'}`}>
|
||||||
Email address *
|
Email address *
|
||||||
</label>
|
</label>
|
||||||
<Input
|
<Input
|
||||||
@ -137,14 +154,14 @@ export function LoginDialog({ open, onOpenChange, onLoginSuccess }: LoginDialogP
|
|||||||
placeholder="Email address"
|
placeholder="Email address"
|
||||||
value={signupData.email}
|
value={signupData.email}
|
||||||
onChange={(e) => setSignupData({ ...signupData, email: e.target.value })}
|
onChange={(e) => setSignupData({ ...signupData, email: e.target.value })}
|
||||||
className="h-12 bg-white border-gray-300"
|
className={`h-12 ${isDark ? 'bg-gray-700 border-gray-600 text-white placeholder:text-gray-400' : 'bg-white border-gray-300 text-gray-900'}`}
|
||||||
required
|
required
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Phone Field */}
|
{/* Phone Field */}
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<label htmlFor="signup-phone" className="text-sm font-medium text-black">
|
<label htmlFor="signup-phone" className={`text-sm font-medium ${isDark ? 'text-gray-300' : 'text-black'}`}>
|
||||||
Phone Number *
|
Phone Number *
|
||||||
</label>
|
</label>
|
||||||
<Input
|
<Input
|
||||||
@ -153,7 +170,7 @@ export function LoginDialog({ open, onOpenChange, onLoginSuccess }: LoginDialogP
|
|||||||
placeholder="+1 (555) 123-4567"
|
placeholder="+1 (555) 123-4567"
|
||||||
value={signupData.phone}
|
value={signupData.phone}
|
||||||
onChange={(e) => setSignupData({ ...signupData, phone: e.target.value })}
|
onChange={(e) => setSignupData({ ...signupData, phone: e.target.value })}
|
||||||
className="h-12 bg-white border-gray-300"
|
className={`h-12 ${isDark ? 'bg-gray-700 border-gray-600 text-white placeholder:text-gray-400' : 'bg-white border-gray-300 text-gray-900'}`}
|
||||||
required
|
required
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@ -162,7 +179,7 @@ export function LoginDialog({ open, onOpenChange, onLoginSuccess }: LoginDialogP
|
|||||||
<Button
|
<Button
|
||||||
type="submit"
|
type="submit"
|
||||||
disabled={signupLoading}
|
disabled={signupLoading}
|
||||||
className="w-full h-12 text-base font-semibold bg-linear-to-r from-rose-500 to-pink-600 hover:from-rose-600 hover:to-pink-700 text-white shadow-lg hover:shadow-xl transition-all disabled:opacity-50 disabled:cursor-not-allowed"
|
className="w-full h-12 text-base font-semibold bg-gradient-to-r from-rose-500 to-pink-600 hover:from-rose-600 hover:to-pink-700 text-white shadow-lg hover:shadow-xl transition-all disabled:opacity-50 disabled:cursor-not-allowed"
|
||||||
>
|
>
|
||||||
{signupLoading ? (
|
{signupLoading ? (
|
||||||
<>
|
<>
|
||||||
@ -175,12 +192,12 @@ export function LoginDialog({ open, onOpenChange, onLoginSuccess }: LoginDialogP
|
|||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
{/* Switch to Login */}
|
{/* Switch to Login */}
|
||||||
<p className="text-sm text-gray-600 text-center">
|
<p className={`text-sm text-center ${isDark ? 'text-gray-400' : 'text-gray-600'}`}>
|
||||||
Already have an account?{" "}
|
Already have an account?{" "}
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={handleSwitchToLogin}
|
onClick={handleSwitchToLogin}
|
||||||
className="text-blue-600 underline font-medium hover:text-blue-700"
|
className={`underline font-medium ${isDark ? 'text-blue-400 hover:text-blue-300' : 'text-blue-600 hover:text-blue-700'}`}
|
||||||
>
|
>
|
||||||
Log in
|
Log in
|
||||||
</button>
|
</button>
|
||||||
@ -190,14 +207,14 @@ export function LoginDialog({ open, onOpenChange, onLoginSuccess }: LoginDialogP
|
|||||||
/* Login Form */
|
/* Login Form */
|
||||||
<form className="space-y-6 mt-4" onSubmit={handleLogin}>
|
<form className="space-y-6 mt-4" onSubmit={handleLogin}>
|
||||||
{error && (
|
{error && (
|
||||||
<div className="p-3 bg-red-50 border border-red-200 rounded-lg">
|
<div className={`p-3 rounded-lg border ${isDark ? 'bg-red-900/20 border-red-800' : 'bg-red-50 border-red-200'}`}>
|
||||||
<p className="text-sm text-red-800">{error}</p>
|
<p className={`text-sm ${isDark ? 'text-red-200' : 'text-red-800'}`}>{error}</p>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Email Field */}
|
{/* Email Field */}
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<label htmlFor="login-email" className="text-sm font-medium text-black">
|
<label htmlFor="login-email" className={`text-sm font-medium ${isDark ? 'text-gray-300' : 'text-black'}`}>
|
||||||
Email address
|
Email address
|
||||||
</label>
|
</label>
|
||||||
<Input
|
<Input
|
||||||
@ -206,14 +223,14 @@ export function LoginDialog({ open, onOpenChange, onLoginSuccess }: LoginDialogP
|
|||||||
placeholder="Email address"
|
placeholder="Email address"
|
||||||
value={loginData.email}
|
value={loginData.email}
|
||||||
onChange={(e) => setLoginData({ ...loginData, email: e.target.value })}
|
onChange={(e) => setLoginData({ ...loginData, email: e.target.value })}
|
||||||
className="h-12 bg-white border-gray-300"
|
className={`h-12 ${isDark ? 'bg-gray-700 border-gray-600 text-white placeholder:text-gray-400' : 'bg-white border-gray-300 text-gray-900'}`}
|
||||||
required
|
required
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Password Field */}
|
{/* Password Field */}
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<label htmlFor="login-password" className="text-sm font-medium text-black">
|
<label htmlFor="login-password" className={`text-sm font-medium ${isDark ? 'text-gray-300' : 'text-black'}`}>
|
||||||
Your password
|
Your password
|
||||||
</label>
|
</label>
|
||||||
<div className="relative">
|
<div className="relative">
|
||||||
@ -223,7 +240,7 @@ export function LoginDialog({ open, onOpenChange, onLoginSuccess }: LoginDialogP
|
|||||||
placeholder="Your password"
|
placeholder="Your password"
|
||||||
value={loginData.password}
|
value={loginData.password}
|
||||||
onChange={(e) => setLoginData({ ...loginData, password: e.target.value })}
|
onChange={(e) => setLoginData({ ...loginData, password: e.target.value })}
|
||||||
className="h-12 bg-white border-gray-300 pr-12"
|
className={`h-12 pr-12 ${isDark ? 'bg-gray-700 border-gray-600 text-white placeholder:text-gray-400' : 'bg-white border-gray-300 text-gray-900'}`}
|
||||||
required
|
required
|
||||||
/>
|
/>
|
||||||
<Button
|
<Button
|
||||||
@ -231,7 +248,7 @@ export function LoginDialog({ open, onOpenChange, onLoginSuccess }: LoginDialogP
|
|||||||
variant="ghost"
|
variant="ghost"
|
||||||
size="icon"
|
size="icon"
|
||||||
onClick={() => setShowPassword(!showPassword)}
|
onClick={() => setShowPassword(!showPassword)}
|
||||||
className="absolute right-4 top-1/2 -translate-y-1/2 h-auto w-auto p-0 text-gray-500 hover:text-gray-700"
|
className={`absolute right-4 top-1/2 -translate-y-1/2 h-auto w-auto p-0 ${isDark ? 'text-gray-400 hover:text-gray-300' : 'text-gray-500 hover:text-gray-700'}`}
|
||||||
aria-label={showPassword ? "Hide password" : "Show password"}
|
aria-label={showPassword ? "Hide password" : "Show password"}
|
||||||
>
|
>
|
||||||
{showPassword ? (
|
{showPassword ? (
|
||||||
@ -247,7 +264,7 @@ export function LoginDialog({ open, onOpenChange, onLoginSuccess }: LoginDialogP
|
|||||||
<Button
|
<Button
|
||||||
type="submit"
|
type="submit"
|
||||||
disabled={loginLoading}
|
disabled={loginLoading}
|
||||||
className="w-full h-12 text-base font-semibold bg-linear-to-r from-rose-500 to-pink-600 hover:from-rose-600 hover:to-pink-700 text-white shadow-lg hover:shadow-xl transition-all disabled:opacity-50 disabled:cursor-not-allowed"
|
className="w-full h-12 text-base font-semibold bg-gradient-to-r from-rose-500 to-pink-600 hover:from-rose-600 hover:to-pink-700 text-white shadow-lg hover:shadow-xl transition-all disabled:opacity-50 disabled:cursor-not-allowed"
|
||||||
>
|
>
|
||||||
{loginLoading ? (
|
{loginLoading ? (
|
||||||
<>
|
<>
|
||||||
@ -266,13 +283,13 @@ export function LoginDialog({ open, onOpenChange, onLoginSuccess }: LoginDialogP
|
|||||||
type="checkbox"
|
type="checkbox"
|
||||||
checked={rememberMe}
|
checked={rememberMe}
|
||||||
onChange={(e) => setRememberMe(e.target.checked)}
|
onChange={(e) => setRememberMe(e.target.checked)}
|
||||||
className="w-4 h-4 rounded border-gray-300 text-rose-600 focus:ring-2 focus:ring-rose-500 cursor-pointer"
|
className={`w-4 h-4 rounded text-rose-600 focus:ring-2 focus:ring-rose-500 cursor-pointer ${isDark ? 'border-gray-600 bg-gray-700' : 'border-gray-300'}`}
|
||||||
/>
|
/>
|
||||||
<span className="text-black">Remember me</span>
|
<span className={isDark ? 'text-gray-300' : 'text-black'}>Remember me</span>
|
||||||
</label>
|
</label>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
className="text-blue-600 hover:text-blue-700 font-medium"
|
className={`font-medium ${isDark ? 'text-blue-400 hover:text-blue-300' : 'text-blue-600 hover:text-blue-700'}`}
|
||||||
onClick={() => onOpenChange(false)}
|
onClick={() => onOpenChange(false)}
|
||||||
>
|
>
|
||||||
Forgot password?
|
Forgot password?
|
||||||
@ -280,12 +297,12 @@ export function LoginDialog({ open, onOpenChange, onLoginSuccess }: LoginDialogP
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Sign Up Prompt */}
|
{/* Sign Up Prompt */}
|
||||||
<p className="text-sm text-gray-600 text-center">
|
<p className={`text-sm text-center ${isDark ? 'text-gray-400' : 'text-gray-600'}`}>
|
||||||
New to Attune Heart Therapy?{" "}
|
New to Attune Heart Therapy?{" "}
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={handleSwitchToSignup}
|
onClick={handleSwitchToSignup}
|
||||||
className="text-blue-600 underline font-medium hover:text-blue-700"
|
className={`underline font-medium ${isDark ? 'text-blue-400 hover:text-blue-300' : 'text-blue-600 hover:text-blue-700'}`}
|
||||||
>
|
>
|
||||||
Sign up
|
Sign up
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@ -1,46 +1,48 @@
|
|||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import { motion } from "framer-motion";
|
import { motion, AnimatePresence } from "framer-motion";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import { Heart } from "lucide-react";
|
import { Heart, Menu, X } from "lucide-react";
|
||||||
import { ThemeToggle } from "@/components/ThemeToggle";
|
import { ThemeToggle } from "@/components/ThemeToggle";
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import { LoginDialog } from "@/components/LoginDialog";
|
import { LoginDialog } from "@/components/LoginDialog";
|
||||||
import { useRouter } from "next/navigation";
|
import { useRouter } from "next/navigation";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
|
import { useAppTheme } from "@/components/ThemeProvider";
|
||||||
|
|
||||||
export function Navbar() {
|
export function Navbar() {
|
||||||
const [isDark, setIsDark] = useState(false);
|
const { theme } = useAppTheme();
|
||||||
|
const isDark = theme === "dark";
|
||||||
const [loginDialogOpen, setLoginDialogOpen] = useState(false);
|
const [loginDialogOpen, setLoginDialogOpen] = useState(false);
|
||||||
|
const [mobileMenuOpen, setMobileMenuOpen] = useState(false);
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
const checkTheme = () => {
|
|
||||||
setIsDark(document.documentElement.classList.contains('dark'));
|
|
||||||
};
|
|
||||||
|
|
||||||
checkTheme();
|
|
||||||
const observer = new MutationObserver(checkTheme);
|
|
||||||
observer.observe(document.documentElement, {
|
|
||||||
attributes: true,
|
|
||||||
attributeFilter: ['class']
|
|
||||||
});
|
|
||||||
|
|
||||||
return () => observer.disconnect();
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const scrollToSection = (id: string) => {
|
const scrollToSection = (id: string) => {
|
||||||
const element = document.getElementById(id);
|
const element = document.getElementById(id);
|
||||||
if (element) {
|
if (element) {
|
||||||
element.scrollIntoView({ behavior: "smooth" });
|
element.scrollIntoView({ behavior: "smooth" });
|
||||||
|
setMobileMenuOpen(false); // Close mobile menu after navigation
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleLoginSuccess = () => {
|
const handleLoginSuccess = () => {
|
||||||
// Redirect to user dashboard after successful login
|
// Redirect to user dashboard after successful login
|
||||||
router.push("/user/dashboard");
|
router.push("/user/dashboard");
|
||||||
|
setMobileMenuOpen(false);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Close mobile menu when clicking outside
|
||||||
|
useEffect(() => {
|
||||||
|
if (mobileMenuOpen) {
|
||||||
|
document.body.style.overflow = 'hidden';
|
||||||
|
} else {
|
||||||
|
document.body.style.overflow = 'unset';
|
||||||
|
}
|
||||||
|
return () => {
|
||||||
|
document.body.style.overflow = 'unset';
|
||||||
|
};
|
||||||
|
}, [mobileMenuOpen]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<motion.nav
|
<motion.nav
|
||||||
initial={{ y: -100 }}
|
initial={{ y: -100 }}
|
||||||
@ -51,60 +53,154 @@ export function Navbar() {
|
|||||||
backgroundColor: isDark ? '#1a1e26' : '#ffffff'
|
backgroundColor: isDark ? '#1a1e26' : '#ffffff'
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div className="container mx-auto px-4">
|
<div className="container mx-auto px-3 sm:px-4">
|
||||||
<div className="flex items-center justify-between h-16">
|
<div className="flex items-center justify-between h-14 sm:h-16">
|
||||||
<motion.div
|
<motion.div
|
||||||
className="flex items-center gap-2"
|
className="flex items-center gap-1.5 sm:gap-2"
|
||||||
whileHover={{ scale: 1.05 }}
|
whileHover={{ scale: 1.05 }}
|
||||||
whileTap={{ scale: 0.95 }}
|
whileTap={{ scale: 0.95 }}
|
||||||
>
|
>
|
||||||
<Link href="/" className="flex items-center gap-2">
|
<Link href="/" className="flex items-center gap-1.5 sm:gap-2">
|
||||||
<div className="bg-linear-to-r from-rose-500 to-pink-600 p-2 rounded-xl">
|
<div className="bg-gradient-to-r from-rose-500 to-pink-600 p-1.5 sm:p-2 rounded-lg sm:rounded-xl">
|
||||||
<Heart className="h-5 w-5 text-white fill-white" />
|
<Heart className="h-4 w-4 sm:h-5 sm:w-5 text-white fill-white" />
|
||||||
</div>
|
</div>
|
||||||
<span className="font-bold text-lg bg-linear-to-r from-rose-600 via-pink-600 to-orange-600 bg-clip-text text-transparent">
|
<span className="font-bold text-sm sm:text-base md:text-lg bg-gradient-to-r from-rose-600 via-pink-600 to-orange-600 bg-clip-text text-transparent">
|
||||||
Attune Heart Therapy
|
<span className="hidden xs:inline sm:hidden">Attune Heart</span>
|
||||||
|
<span className="hidden sm:inline">Attune Heart Therapy</span>
|
||||||
|
<span className="xs:hidden">AHT</span>
|
||||||
</span>
|
</span>
|
||||||
</Link>
|
</Link>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
|
|
||||||
<div className="hidden md:flex items-center gap-6">
|
{/* Desktop Navigation */}
|
||||||
|
<div className="hidden lg:flex items-center gap-4 xl:gap-6">
|
||||||
<button
|
<button
|
||||||
onClick={() => scrollToSection("about")}
|
onClick={() => scrollToSection("about")}
|
||||||
className="text-sm font-medium hover:text-primary transition-colors cursor-pointer px-3 py-2 rounded-lg hover:bg-gray-100 dark:hover:bg-cyan-900/30"
|
className={`text-sm font-medium transition-colors cursor-pointer px-3 py-2 rounded-lg ${isDark ? 'text-gray-300 hover:text-white hover:bg-gray-800' : 'text-gray-700 hover:text-primary hover:bg-gray-100'}`}
|
||||||
>
|
>
|
||||||
About
|
About
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
onClick={() => scrollToSection("services")}
|
onClick={() => scrollToSection("services")}
|
||||||
className="text-sm font-medium hover:text-primary transition-colors cursor-pointer px-3 py-2 rounded-lg hover:bg-gray-100 dark:hover:bg-cyan-900/30"
|
className={`text-sm font-medium transition-colors cursor-pointer px-3 py-2 rounded-lg ${isDark ? 'text-gray-300 hover:text-white hover:bg-gray-800' : 'text-gray-700 hover:text-primary hover:bg-gray-100'}`}
|
||||||
>
|
>
|
||||||
Services
|
Services
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
onClick={() => scrollToSection("contact")}
|
onClick={() => scrollToSection("contact")}
|
||||||
className="text-sm font-medium hover:text-primary transition-colors cursor-pointer px-3 py-2 rounded-lg hover:bg-gray-100 dark:hover:bg-cyan-900/30"
|
className={`text-sm font-medium transition-colors cursor-pointer px-3 py-2 rounded-lg ${isDark ? 'text-gray-300 hover:text-white hover:bg-gray-800' : 'text-gray-700 hover:text-primary hover:bg-gray-100'}`}
|
||||||
>
|
>
|
||||||
Contact
|
Contact
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex items-center gap-2">
|
{/* Desktop Actions */}
|
||||||
|
<div className="hidden lg:flex items-center gap-2">
|
||||||
<Button
|
<Button
|
||||||
size="sm"
|
size="sm"
|
||||||
variant="outline"
|
variant="outline"
|
||||||
className="hidden sm:inline-flex hover:opacity-90 hover:scale-105 transition-all dark:hover:bg-cyan-900/30"
|
className={`hover:opacity-90 hover:scale-105 transition-all text-xs sm:text-sm ${isDark ? 'border-gray-700 text-gray-300 hover:bg-gray-800' : ''}`}
|
||||||
onClick={() => setLoginDialogOpen(true)}
|
onClick={() => setLoginDialogOpen(true)}
|
||||||
>
|
>
|
||||||
Sign In
|
Sign In
|
||||||
</Button>
|
</Button>
|
||||||
<ThemeToggle />
|
<ThemeToggle />
|
||||||
<Button size="sm" className="hidden sm:inline-flex hover:opacity-90 hover:scale-105 transition-all dark:hover:bg-emerald-600" asChild>
|
<Button size="sm" className="hover:opacity-90 hover:scale-105 transition-all text-xs sm:text-sm" asChild>
|
||||||
<a href="/book-now">Book Now</a>
|
<a href="/book-now">Book Now</a>
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* Mobile Actions */}
|
||||||
|
<div className="flex lg:hidden items-center gap-1.5 sm:gap-2">
|
||||||
|
<ThemeToggle />
|
||||||
|
<Button
|
||||||
|
variant="ghost"
|
||||||
|
size="icon"
|
||||||
|
onClick={() => setMobileMenuOpen(!mobileMenuOpen)}
|
||||||
|
className="hover:bg-gray-100 dark:hover:bg-gray-800 h-9 w-9 sm:h-10 sm:w-10"
|
||||||
|
aria-label="Toggle menu"
|
||||||
|
>
|
||||||
|
{mobileMenuOpen ? (
|
||||||
|
<X className="h-5 w-5 sm:h-6 sm:w-6" />
|
||||||
|
) : (
|
||||||
|
<Menu className="h-5 w-5 sm:h-6 sm:w-6" />
|
||||||
|
)}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* Mobile Menu */}
|
||||||
|
<AnimatePresence>
|
||||||
|
{mobileMenuOpen && (
|
||||||
|
<>
|
||||||
|
{/* Backdrop */}
|
||||||
|
<motion.div
|
||||||
|
initial={{ opacity: 0 }}
|
||||||
|
animate={{ opacity: 1 }}
|
||||||
|
exit={{ opacity: 0 }}
|
||||||
|
transition={{ duration: 0.2 }}
|
||||||
|
className="fixed inset-0 bg-black/50 z-40 lg:hidden"
|
||||||
|
onClick={() => setMobileMenuOpen(false)}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{/* Mobile Menu Panel */}
|
||||||
|
<motion.div
|
||||||
|
initial={{ x: '100%' }}
|
||||||
|
animate={{ x: 0 }}
|
||||||
|
exit={{ x: '100%' }}
|
||||||
|
transition={{ type: 'spring', damping: 25, stiffness: 200 }}
|
||||||
|
className="fixed top-14 sm:top-16 right-0 bottom-0 w-[280px] sm:w-80 max-w-[85vw] z-50 lg:hidden overflow-y-auto"
|
||||||
|
style={{
|
||||||
|
backgroundColor: isDark ? '#1a1e26' : '#ffffff'
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div className="flex flex-col p-4 sm:p-6 space-y-3 sm:space-y-4">
|
||||||
|
{/* Mobile Navigation Links */}
|
||||||
|
<button
|
||||||
|
onClick={() => scrollToSection("about")}
|
||||||
|
className={`text-left text-sm sm:text-base font-medium py-2.5 sm:py-3 px-3 sm:px-4 rounded-lg transition-colors ${isDark ? 'text-gray-300 hover:bg-gray-800' : 'text-gray-700 hover:bg-gray-100'}`}
|
||||||
|
>
|
||||||
|
About
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
onClick={() => scrollToSection("services")}
|
||||||
|
className={`text-left text-sm sm:text-base font-medium py-2.5 sm:py-3 px-3 sm:px-4 rounded-lg transition-colors ${isDark ? 'text-gray-300 hover:bg-gray-800' : 'text-gray-700 hover:bg-gray-100'}`}
|
||||||
|
>
|
||||||
|
Services
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
onClick={() => scrollToSection("contact")}
|
||||||
|
className={`text-left text-sm sm:text-base font-medium py-2.5 sm:py-3 px-3 sm:px-4 rounded-lg transition-colors ${isDark ? 'text-gray-300 hover:bg-gray-800' : 'text-gray-700 hover:bg-gray-100'}`}
|
||||||
|
>
|
||||||
|
Contact
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<div className={`border-t pt-3 sm:pt-4 mt-3 sm:mt-4 space-y-2 sm:space-y-3 ${isDark ? 'border-gray-700' : 'border-gray-200'}`}>
|
||||||
|
<Button
|
||||||
|
variant="outline"
|
||||||
|
className={`w-full justify-start text-sm sm:text-base ${isDark ? 'border-gray-700 text-gray-300 hover:bg-gray-800' : ''}`}
|
||||||
|
onClick={() => {
|
||||||
|
setLoginDialogOpen(true);
|
||||||
|
setMobileMenuOpen(false);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Sign In
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
className="w-full justify-start bg-gradient-to-r from-rose-500 to-pink-600 hover:from-rose-600 hover:to-pink-700 text-white text-sm sm:text-base"
|
||||||
|
asChild
|
||||||
|
>
|
||||||
|
<Link href="/book-now" onClick={() => setMobileMenuOpen(false)}>
|
||||||
|
Book Now
|
||||||
|
</Link>
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</motion.div>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</AnimatePresence>
|
||||||
|
|
||||||
{/* Login Dialog */}
|
{/* Login Dialog */}
|
||||||
<LoginDialog
|
<LoginDialog
|
||||||
|
|||||||
@ -2,28 +2,15 @@
|
|||||||
|
|
||||||
import { motion } from "framer-motion";
|
import { motion } from "framer-motion";
|
||||||
import { useInView } from "framer-motion";
|
import { useInView } from "framer-motion";
|
||||||
import { useRef, useEffect, useState } from "react";
|
import { useRef } from "react";
|
||||||
import { Baby, Brain, HeartHandshake, Sparkles, Users2, Shield } from "lucide-react";
|
import { Baby, Brain, HeartHandshake, Sparkles, Users2, Shield } from "lucide-react";
|
||||||
|
import { useAppTheme } from "@/components/ThemeProvider";
|
||||||
|
|
||||||
export function Services() {
|
export function Services() {
|
||||||
const ref = useRef(null);
|
const ref = useRef(null);
|
||||||
const isInView = useInView(ref, { once: true, margin: "-100px" });
|
const isInView = useInView(ref, { once: true, margin: "-100px" });
|
||||||
const [isDark, setIsDark] = useState(false);
|
const { theme } = useAppTheme();
|
||||||
|
const isDark = theme === "dark";
|
||||||
useEffect(() => {
|
|
||||||
const checkTheme = () => {
|
|
||||||
setIsDark(document.documentElement.classList.contains('dark'));
|
|
||||||
};
|
|
||||||
|
|
||||||
checkTheme();
|
|
||||||
const observer = new MutationObserver(checkTheme);
|
|
||||||
observer.observe(document.documentElement, {
|
|
||||||
attributes: true,
|
|
||||||
attributeFilter: ['class']
|
|
||||||
});
|
|
||||||
|
|
||||||
return () => observer.disconnect();
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const services = [
|
const services = [
|
||||||
{
|
{
|
||||||
@ -135,7 +122,7 @@ export function Services() {
|
|||||||
className="text-center mb-16"
|
className="text-center mb-16"
|
||||||
>
|
>
|
||||||
<motion.h2
|
<motion.h2
|
||||||
className="text-4xl md:text-5xl font-bold mb-6 bg-gradient-to-r from-rose-600 via-pink-600 to-orange-600 bg-clip-text text-transparent"
|
className="text-3xl sm:text-4xl md:text-5xl font-bold mb-4 sm:mb-6 bg-gradient-to-r from-rose-600 via-pink-600 to-orange-600 bg-clip-text text-transparent px-4"
|
||||||
initial={{ opacity: 0, y: 20 }}
|
initial={{ opacity: 0, y: 20 }}
|
||||||
animate={isInView ? { opacity: 1, y: 0 } : {}}
|
animate={isInView ? { opacity: 1, y: 0 } : {}}
|
||||||
transition={{ duration: 0.8, delay: 0.2 }}
|
transition={{ duration: 0.8, delay: 0.2 }}
|
||||||
@ -143,7 +130,7 @@ export function Services() {
|
|||||||
Specialized Services
|
Specialized Services
|
||||||
</motion.h2>
|
</motion.h2>
|
||||||
<motion.p
|
<motion.p
|
||||||
className="text-xl text-muted-foreground max-w-3xl mx-auto"
|
className="text-base sm:text-lg md:text-xl text-muted-foreground max-w-3xl mx-auto px-4"
|
||||||
initial={{ opacity: 0, y: 20 }}
|
initial={{ opacity: 0, y: 20 }}
|
||||||
animate={isInView ? { opacity: 1, y: 0 } : {}}
|
animate={isInView ? { opacity: 1, y: 0 } : {}}
|
||||||
transition={{ duration: 0.8, delay: 0.4 }}
|
transition={{ duration: 0.8, delay: 0.4 }}
|
||||||
@ -152,7 +139,7 @@ export function Services() {
|
|||||||
</motion.p>
|
</motion.p>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
|
|
||||||
<div className="grid md:grid-cols-2 lg:grid-cols-3 gap-6">
|
<div className="grid sm:grid-cols-2 lg:grid-cols-3 gap-4 sm:gap-6 px-4">
|
||||||
{services.map((service, index) => {
|
{services.map((service, index) => {
|
||||||
const Icon = service.icon;
|
const Icon = service.icon;
|
||||||
return (
|
return (
|
||||||
@ -161,7 +148,7 @@ export function Services() {
|
|||||||
initial={{ opacity: 0, y: 20 }}
|
initial={{ opacity: 0, y: 20 }}
|
||||||
animate={isInView ? { opacity: 1, y: 0 } : {}}
|
animate={isInView ? { opacity: 1, y: 0 } : {}}
|
||||||
transition={{ duration: 0.5, delay: index * 0.1 }}
|
transition={{ duration: 0.5, delay: index * 0.1 }}
|
||||||
className="group bg-card/50 backdrop-blur-sm rounded-2xl p-6 border border-border/50 hover:border-rose-500/50 hover:shadow-lg hover:shadow-rose-500/10 hover:scale-105 transition-all duration-300 cursor-pointer"
|
className="group bg-card/50 backdrop-blur-sm rounded-xl sm:rounded-2xl p-4 sm:p-6 border border-border/50 hover:border-rose-500/50 hover:shadow-lg hover:shadow-rose-500/10 hover:scale-105 transition-all duration-300 cursor-pointer"
|
||||||
>
|
>
|
||||||
{/* Content */}
|
{/* Content */}
|
||||||
<div className="relative z-10">
|
<div className="relative z-10">
|
||||||
|
|||||||
@ -2,28 +2,15 @@
|
|||||||
|
|
||||||
import { motion } from "framer-motion";
|
import { motion } from "framer-motion";
|
||||||
import { useInView } from "framer-motion";
|
import { useInView } from "framer-motion";
|
||||||
import { useRef, useEffect, useState } from "react";
|
import { useRef } from "react";
|
||||||
import { Star, Award } from "lucide-react";
|
import { Star, Award } from "lucide-react";
|
||||||
|
import { useAppTheme } from "@/components/ThemeProvider";
|
||||||
|
|
||||||
export function Specialties() {
|
export function Specialties() {
|
||||||
const ref = useRef(null);
|
const ref = useRef(null);
|
||||||
const isInView = useInView(ref, { once: true, margin: "-100px" });
|
const isInView = useInView(ref, { once: true, margin: "-100px" });
|
||||||
const [isDark, setIsDark] = useState(false);
|
const { theme } = useAppTheme();
|
||||||
|
const isDark = theme === "dark";
|
||||||
useEffect(() => {
|
|
||||||
const checkTheme = () => {
|
|
||||||
setIsDark(document.documentElement.classList.contains('dark'));
|
|
||||||
};
|
|
||||||
|
|
||||||
checkTheme();
|
|
||||||
const observer = new MutationObserver(checkTheme);
|
|
||||||
observer.observe(document.documentElement, {
|
|
||||||
attributes: true,
|
|
||||||
attributeFilter: ['class']
|
|
||||||
});
|
|
||||||
|
|
||||||
return () => observer.disconnect();
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const topSpecialties = [
|
const topSpecialties = [
|
||||||
"Child or Adolescent",
|
"Child or Adolescent",
|
||||||
|
|||||||
@ -1,32 +1,18 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
import { Moon, Sun } from "lucide-react";
|
import { Moon, Sun } from "lucide-react";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import { useEffect, useState } from "react";
|
import { useAppTheme } from "@/components/ThemeProvider";
|
||||||
|
|
||||||
export function ThemeToggle() {
|
export function ThemeToggle() {
|
||||||
const [theme, setTheme] = useState<"light" | "dark">("light");
|
const { theme, toggleTheme } = useAppTheme();
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
const savedTheme = localStorage.getItem("theme") as "light" | "dark" | null;
|
|
||||||
const prefersDark = window.matchMedia("(prefers-color-scheme: dark)").matches;
|
|
||||||
const initialTheme = savedTheme || (prefersDark ? "dark" : "light");
|
|
||||||
|
|
||||||
setTheme(initialTheme);
|
|
||||||
document.documentElement.classList.toggle("dark", initialTheme === "dark");
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const toggleTheme = () => {
|
|
||||||
const newTheme = theme === "light" ? "dark" : "light";
|
|
||||||
setTheme(newTheme);
|
|
||||||
localStorage.setItem("theme", newTheme);
|
|
||||||
document.documentElement.classList.toggle("dark", newTheme === "dark");
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Button
|
<Button
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
size="icon"
|
size="icon"
|
||||||
onClick={toggleTheme}
|
onClick={toggleTheme}
|
||||||
className="relative rounded-full cursor-pointer hover:bg-gray-100 dark:hover:bg-cyan-900/30 transition-colors"
|
className="relative rounded-full cursor-pointer hover:bg-transparent transition-colors"
|
||||||
aria-label="Toggle theme"
|
aria-label="Toggle theme"
|
||||||
>
|
>
|
||||||
<Sun className={`h-5 w-5 transition-all absolute ${theme === "light" ? "rotate-0 scale-100" : "rotate-90 scale-0"}`} />
|
<Sun className={`h-5 w-5 transition-all absolute ${theme === "light" ? "rotate-0 scale-100" : "rotate-90 scale-0"}`} />
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user