Compare commits

...

5 Commits

23 changed files with 717 additions and 611 deletions

View File

@ -4,11 +4,7 @@ import { useState } from "react";
import Link from "next/link"; import Link from "next/link";
import { usePathname, useRouter } from "next/navigation"; import { usePathname, useRouter } from "next/navigation";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover";
Popover,
PopoverContent,
PopoverTrigger,
} from "@/components/ui/popover";
import { import {
Inbox, Inbox,
Calendar, Calendar,
@ -19,12 +15,16 @@ import {
Settings, Settings,
LogOut, LogOut,
} from "lucide-react"; } from "lucide-react";
import { useAppTheme } from "@/components/ThemeProvider";
import { ThemeToggle } from "@/components/ThemeToggle";
export function Header() { export function Header() {
const pathname = usePathname(); const pathname = usePathname();
const router = useRouter(); const router = useRouter();
const [notificationsOpen, setNotificationsOpen] = useState(false); const [notificationsOpen, setNotificationsOpen] = useState(false);
const [userMenuOpen, setUserMenuOpen] = useState(false); const [userMenuOpen, setUserMenuOpen] = useState(false);
const { theme } = useAppTheme();
const isDark = theme === "dark";
// Mock notifications data // Mock notifications data
const notifications = [ const notifications = [
@ -49,15 +49,15 @@ export function Header() {
const unreadCount = notifications.filter((n) => !n.read).length; const unreadCount = notifications.filter((n) => !n.read).length;
return ( return (
<header className="bg-white border-b border-gray-200 fixed top-0 left-0 right-0 z-50"> <header className={`fixed top-0 left-0 right-0 z-50 ${isDark ? "bg-gray-900 border-gray-800" : "bg-white border-gray-200"} border-b`}>
<div className="px-3 sm:px-4 md:px-6 lg:px-8"> <div className="px-3 sm:px-4 md:px-6 lg:px-8">
<div className="flex items-center justify-between h-14 sm:h-16"> <div className="flex items-center justify-between h-14 sm:h-16">
{/* Logo */} {/* Logo */}
<Link href="/" className="flex items-center gap-2 sm:gap-3"> <Link href="/" className="flex items-center gap-2 sm:gap-3">
<div className="flex items-center justify-center w-8 h-8 sm:w-10 sm:h-10 rounded-lg bg-linear-to-r from-rose-100 to-pink-100"> <div className={`flex items-center justify-center w-8 h-8 sm:w-10 sm:h-10 rounded-lg ${isDark ? "bg-gray-800" : "bg-linear-to-r from-rose-100 to-pink-100"}`}>
<Heart className="w-4 h-4 sm:w-6 sm:h-6 text-rose-600" fill="currentColor" /> <Heart className={`w-4 h-4 sm:w-6 sm:h-6 ${isDark ? "text-rose-400" : "text-rose-600"}`} fill="currentColor" />
</div> </div>
<span className="text-base sm:text-lg md:text-xl font-semibold text-gray-900 hidden sm:inline">Attune Heart</span> <span className={`text-base sm:text-lg md:text-xl font-semibold hidden sm:inline ${isDark ? "text-white" : "text-gray-900"}`}>Attune Heart</span>
</Link> </Link>
{/* Navigation Links */} {/* Navigation Links */}
@ -67,7 +67,9 @@ export function Header() {
className={`flex items-center gap-1 sm:gap-2 px-2 sm:px-3 md:px-4 py-1.5 sm:py-2 rounded-lg text-xs sm:text-sm font-medium transition-colors ${ className={`flex items-center gap-1 sm:gap-2 px-2 sm:px-3 md:px-4 py-1.5 sm:py-2 rounded-lg text-xs sm:text-sm font-medium transition-colors ${
pathname === "/admin/dashboard" pathname === "/admin/dashboard"
? "bg-linear-to-r from-rose-500 to-pink-600 text-white" ? "bg-linear-to-r from-rose-500 to-pink-600 text-white"
: "text-gray-600 hover:bg-gray-100" : isDark
? "text-gray-300 hover:bg-gray-800"
: "text-gray-600 hover:bg-gray-100"
}`} }`}
> >
<LayoutGrid className="w-4 h-4 sm:w-5 sm:h-5" /> <LayoutGrid className="w-4 h-4 sm:w-5 sm:h-5" />
@ -78,7 +80,9 @@ export function Header() {
className={`flex items-center gap-1 sm:gap-2 px-2 sm:px-3 md:px-4 py-1.5 sm:py-2 rounded-lg text-xs sm:text-sm font-medium transition-colors ${ className={`flex items-center gap-1 sm:gap-2 px-2 sm:px-3 md:px-4 py-1.5 sm:py-2 rounded-lg text-xs sm:text-sm font-medium transition-colors ${
pathname === "/admin/booking" pathname === "/admin/booking"
? "bg-linear-to-r from-rose-500 to-pink-600 text-white" ? "bg-linear-to-r from-rose-500 to-pink-600 text-white"
: "text-gray-600 hover:bg-gray-100" : isDark
? "text-gray-300 hover:bg-gray-800"
: "text-gray-600 hover:bg-gray-100"
}`} }`}
> >
<Calendar className="w-4 h-4 sm:w-5 sm:h-5" /> <Calendar className="w-4 h-4 sm:w-5 sm:h-5" />
@ -88,23 +92,24 @@ export function Header() {
{/* Right Side Actions */} {/* Right Side Actions */}
<div className="flex items-center gap-1.5 sm:gap-2 md:gap-3"> <div className="flex items-center gap-1.5 sm:gap-2 md:gap-3">
<ThemeToggle />
<Popover open={notificationsOpen} onOpenChange={setNotificationsOpen}> <Popover open={notificationsOpen} onOpenChange={setNotificationsOpen}>
<PopoverTrigger asChild> <PopoverTrigger asChild>
<Button variant="ghost" size="icon" className="relative w-8 h-8 sm:w-9 sm:h-9 md:w-10 md:h-10 cursor-pointer"> <Button variant="ghost" size="icon" className="relative w-8 h-8 sm:w-9 sm:h-9 md:w-10 md:h-10 cursor-pointer">
<Inbox className="w-5 h-5 sm:w-6 sm:h-6 md:w-8 md:h-8 text-gray-600" /> <Inbox className={`w-5 h-5 sm:w-6 sm:h-6 md:w-8 md:h-8 ${isDark ? "text-gray-300" : "text-gray-600"}`} />
{unreadCount > 0 && ( {unreadCount > 0 && (
<span className="absolute top-0.5 right-0.5 sm:top-1 sm:right-1 w-1.5 h-1.5 sm:w-2 sm:h-2 bg-green-500 rounded-full"></span> <span className="absolute top-0.5 right-0.5 sm:top-1 sm:right-1 w-1.5 h-1.5 sm:w-2 sm:h-2 bg-green-500 rounded-full"></span>
)} )}
</Button> </Button>
</PopoverTrigger> </PopoverTrigger>
<PopoverContent className="w-[calc(100vw-2rem)] sm:w-80 md:w-96 p-0 bg-white shadow-xl border border-gray-200" align="end"> <PopoverContent className={`w-[calc(100vw-2rem)] sm:w-80 md:w-96 p-0 shadow-xl border ${isDark ? "bg-gray-900 border-gray-800" : "bg-white border-gray-200"}`} align="end">
{/* Thumbtack Design at Top Right */} {/* Thumbtack Design at Top Right */}
<div className="relative"> <div className="relative">
<div className="absolute -top-2 right-8 w-4 h-4 bg-white border-l border-t border-gray-200 rotate-45"></div> <div className={`absolute -top-2 right-8 w-4 h-4 rotate-45 ${isDark ? "bg-gray-900 border-l border-t border-gray-800" : "bg-white border-l border-t border-gray-200"}`}></div>
<div className="absolute -top-1 right-8 w-2 h-2 bg-white translate-x-1/2"></div> <div className={`absolute -top-1 right-8 w-2 h-2 translate-x-1/2 ${isDark ? "bg-gray-900" : "bg-white"}`}></div>
</div> </div>
<div className="flex items-center justify-between p-4 border-b"> <div className={`flex items-center justify-between p-4 border-b ${isDark ? "border-gray-800" : ""}`}>
<h3 className="font-semibold text-gray-900">Notifications</h3> <h3 className={`font-semibold ${isDark ? "text-white" : "text-gray-900"}`}>Notifications</h3>
{unreadCount > 0 && ( {unreadCount > 0 && (
<span className="px-2 py-1 text-xs font-medium bg-rose-100 text-rose-700 rounded-full"> <span className="px-2 py-1 text-xs font-medium bg-rose-100 text-rose-700 rounded-full">
{unreadCount} new {unreadCount} new
@ -113,24 +118,28 @@ export function Header() {
</div> </div>
<div className="max-h-96 overflow-y-auto"> <div className="max-h-96 overflow-y-auto">
{notifications.length === 0 ? ( {notifications.length === 0 ? (
<div className="p-8 text-center text-gray-500"> <div className={`p-8 text-center ${isDark ? "text-gray-400" : "text-gray-500"}`}>
<Bell className="w-12 h-12 mx-auto mb-2 text-gray-300" /> <Bell className={`w-12 h-12 mx-auto mb-2 ${isDark ? "text-gray-600" : "text-gray-300"}`} />
<p className="text-sm">No notifications</p> <p className="text-sm">No notifications</p>
</div> </div>
) : ( ) : (
<div className="divide-y"> <div className={`divide-y ${isDark ? "divide-gray-800" : ""}`}>
{notifications.map((notification) => { {notifications.map((notification) => {
return ( return (
<div <div
key={notification.id} key={notification.id}
className={`p-4 hover:bg-gray-50 transition-colors cursor-pointer ${ className={`p-4 transition-colors cursor-pointer ${
!notification.read ? "bg-rose-50/50" : "" !notification.read
? isDark
? "bg-rose-500/10"
: "bg-rose-50/50"
: ""
}`} }`}
> >
<div className="flex-1 min-w-0"> <div className="flex-1 min-w-0">
<div className="flex items-start justify-between gap-2"> <div className="flex items-start justify-between gap-2">
<p <p
className={`text-sm font-medium text-gray-900 ${ className={`text-sm font-medium ${isDark ? "text-white" : "text-gray-900"} ${
!notification.read ? "font-semibold" : "" !notification.read ? "font-semibold" : ""
}`} }`}
> >
@ -140,10 +149,10 @@ export function Header() {
<span className="shrink-0 w-2 h-2 bg-green-500 rounded-full mt-1"></span> <span className="shrink-0 w-2 h-2 bg-green-500 rounded-full mt-1"></span>
)} )}
</div> </div>
<p className="text-sm text-gray-600 mt-1"> <p className={`text-sm mt-1 ${isDark ? "text-gray-400" : "text-gray-600"}`}>
{notification.message} {notification.message}
</p> </p>
<p className="text-xs text-gray-400 mt-1"> <p className={`text-xs mt-1 ${isDark ? "text-gray-500" : "text-gray-400"}`}>
{notification.time} {notification.time}
</p> </p>
</div> </div>
@ -153,11 +162,11 @@ export function Header() {
</div> </div>
)} )}
</div> </div>
<div className="p-3 border-t bg-gray-50"> <div className={`p-3 border-t ${isDark ? "border-gray-800 bg-gray-900/80" : "bg-gray-50"}`}>
<Link <Link
href="/admin/notifications" href="/admin/notifications"
onClick={() => setNotificationsOpen(false)} onClick={() => setNotificationsOpen(false)}
className="block w-full text-center text-sm font-medium text-rose-600 hover:text-rose-700 hover:underline transition-colors" className={`block w-full text-center text-sm font-medium hover:underline transition-colors ${isDark ? "text-rose-300 hover:text-rose-200" : "text-rose-600 hover:text-rose-700"}`}
> >
View all notifications View all notifications
</Link> </Link>
@ -166,15 +175,23 @@ export function Header() {
</Popover> </Popover>
<Popover open={userMenuOpen} onOpenChange={setUserMenuOpen}> <Popover open={userMenuOpen} onOpenChange={setUserMenuOpen}>
<PopoverTrigger asChild> <PopoverTrigger asChild>
<Button variant="ghost" size="icon" className="rounded-full bg-linear-to-r from-rose-100 to-pink-100 hover:from-rose-200 hover:to-pink-200 cursor-pointer w-8 h-8 sm:w-9 sm:h-9 md:w-10 md:h-10"> <Button
<UserCog className="w-4 h-4 sm:w-5 sm:h-5 text-rose-600" /> variant="ghost"
size="icon"
className={`rounded-full cursor-pointer w-8 h-8 sm:w-9 sm:h-9 md:w-10 md:h-10 ${
isDark
? "bg-gray-800 hover:bg-gray-700"
: "bg-linear-to-r from-rose-100 to-pink-100 hover:from-rose-200 hover:to-pink-200"
}`}
>
<UserCog className={`w-4 h-4 sm:w-5 sm:h-5 ${isDark ? "text-rose-300" : "text-rose-600"}`} />
</Button> </Button>
</PopoverTrigger> </PopoverTrigger>
<PopoverContent className="w-56 sm:w-64 p-0 bg-white shadow-xl border border-gray-200" align="end"> <PopoverContent className={`w-56 sm:w-64 p-0 shadow-xl border ${isDark ? "bg-gray-900 border-gray-800" : "bg-white border-gray-200"}`} align="end">
{/* Thumbtack Design at Top Right */} {/* Thumbtack Design at Top Right */}
<div className="relative"> <div className="relative">
<div className="absolute -top-2 right-8 w-4 h-4 bg-white border-l border-t border-gray-200 rotate-45"></div> <div className={`absolute -top-2 right-8 w-4 h-4 rotate-45 ${isDark ? "bg-gray-900 border-l border-t border-gray-800" : "bg-white border-l border-t border-gray-200"}`}></div>
<div className="absolute -top-1 right-8 w-2 h-2 bg-white translate-x-1/2"></div> <div className={`absolute -top-1 right-8 w-2 h-2 translate-x-1/2 ${isDark ? "bg-gray-900" : "bg-white"}`}></div>
</div> </div>
<div className="py-2"> <div className="py-2">
<Button <Button
@ -183,10 +200,12 @@ export function Header() {
setUserMenuOpen(false); setUserMenuOpen(false);
router.push("/admin/settings"); router.push("/admin/settings");
}} }}
className="w-full flex items-center gap-3 px-4 py-3 justify-start hover:bg-gray-50 transition-colors cursor-pointer" className={`w-full flex items-center gap-3 px-4 py-3 justify-start transition-colors cursor-pointer ${
isDark ? "hover:bg-gray-800" : "hover:bg-gray-50"
}`}
> >
<Settings className="w-5 h-5 text-gray-600" /> <Settings className={`w-5 h-5 ${isDark ? "text-gray-300" : "text-gray-600"}`} />
<span className="text-sm font-medium text-gray-900">Settings</span> <span className={`text-sm font-medium ${isDark ? "text-white" : "text-gray-900"}`}>Settings</span>
</Button> </Button>
<Button <Button
variant="ghost" variant="ghost"
@ -194,7 +213,9 @@ export function Header() {
setUserMenuOpen(false); setUserMenuOpen(false);
router.push("/"); router.push("/");
}} }}
className="w-full flex items-center gap-3 px-4 py-3 justify-start hover:bg-gray-50 transition-colors cursor-pointer" className={`w-full flex items-center gap-3 px-4 py-3 justify-start transition-colors cursor-pointer ${
isDark ? "hover:bg-gray-800" : "hover:bg-gray-50"
}`}
> >
<LogOut className="w-5 h-5 text-red-500" /> <LogOut className="w-5 h-5 text-red-500" />
<span className="text-sm font-medium text-red-500">Logout</span> <span className="text-sm font-medium text-red-500">Logout</span>

View File

@ -12,6 +12,7 @@ import {
Clock, Clock,
} from "lucide-react"; } from "lucide-react";
import { cn } from "@/lib/utils"; import { cn } from "@/lib/utils";
import { useAppTheme } from "@/components/ThemeProvider";
export interface Notification { export interface Notification {
id: string; id: string;
@ -36,30 +37,32 @@ export function Notifications({
onMarkAllAsRead, onMarkAllAsRead,
}: NotificationsProps) { }: NotificationsProps) {
const unreadCount = notifications.filter((n) => !n.read).length; const unreadCount = notifications.filter((n) => !n.read).length;
const { theme } = useAppTheme();
const isDark = theme === "dark";
const getIcon = (type: Notification["type"]) => { const getIcon = (type: Notification["type"]) => {
switch (type) { switch (type) {
case "success": case "success":
return <CheckCircle className="w-5 h-5 text-green-600" />; return <CheckCircle className={`w-5 h-5 ${isDark ? "text-green-300" : "text-green-600"}`} />;
case "warning": case "warning":
return <AlertCircle className="w-5 h-5 text-orange-600" />; return <AlertCircle className={`w-5 h-5 ${isDark ? "text-orange-300" : "text-orange-600"}`} />;
case "info": case "info":
return <Info className="w-5 h-5 text-blue-600" />; return <Info className={`w-5 h-5 ${isDark ? "text-blue-300" : "text-blue-600"}`} />;
case "appointment": case "appointment":
return <Calendar className="w-5 h-5 text-rose-600" />; return <Calendar className={`w-5 h-5 ${isDark ? "text-rose-300" : "text-rose-600"}`} />;
} }
}; };
const getBgColor = (type: Notification["type"]) => { const getBgColor = (type: Notification["type"]) => {
switch (type) { switch (type) {
case "success": case "success":
return "bg-[#4A90A4]/10 border-[#4A90A4]/30"; return isDark ? "bg-green-500/10 border-green-500/30" : "bg-[#4A90A4]/10 border-[#4A90A4]/30";
case "warning": case "warning":
return "bg-rose-100 border-rose-300"; return isDark ? "bg-rose-500/10 border-rose-400/40" : "bg-rose-100 border-rose-300";
case "info": case "info":
return "bg-pink-50 border-pink-200"; return isDark ? "bg-pink-500/10 border-pink-400/40" : "bg-pink-50 border-pink-200";
case "appointment": case "appointment":
return "bg-gradient-to-br from-rose-50 to-pink-50 border-rose-300"; return isDark ? "bg-rose-500/10 border-rose-400/40" : "bg-gradient-to-br from-rose-50 to-pink-50 border-rose-300";
} }
}; };
@ -68,8 +71,8 @@ export function Notifications({
{/* Header */} {/* Header */}
<div className="flex items-center justify-between mb-6"> <div className="flex items-center justify-between mb-6">
<div className="flex items-center gap-3"> <div className="flex items-center gap-3">
<Bell className="w-6 h-6 text-gray-900" /> <Bell className={`w-6 h-6 ${isDark ? "text-white" : "text-gray-900"}`} />
<h2 className="text-2xl font-bold text-gray-900">Notifications</h2> <h2 className={`text-2xl font-bold ${isDark ? "text-white" : "text-gray-900"}`}>Notifications</h2>
{unreadCount > 0 && ( {unreadCount > 0 && (
<span className="px-2.5 py-0.5 bg-linear-to-r from-rose-500 to-pink-500 text-white text-sm font-medium rounded-full"> <span className="px-2.5 py-0.5 bg-linear-to-r from-rose-500 to-pink-500 text-white text-sm font-medium rounded-full">
{unreadCount} {unreadCount}
@ -77,7 +80,12 @@ export function Notifications({
)} )}
</div> </div>
{unreadCount > 0 && onMarkAllAsRead && ( {unreadCount > 0 && onMarkAllAsRead && (
<Button variant="outline" size="sm" onClick={onMarkAllAsRead}> <Button
variant="outline"
size="sm"
onClick={onMarkAllAsRead}
className={isDark ? "border-gray-700 text-gray-200 hover:bg-gray-800" : ""}
>
Mark all as read Mark all as read
</Button> </Button>
)} )}
@ -87,8 +95,8 @@ export function Notifications({
<div className="space-y-3"> <div className="space-y-3">
{notifications.length === 0 ? ( {notifications.length === 0 ? (
<div className="text-center py-12"> <div className="text-center py-12">
<Bell className="w-12 h-12 text-gray-400 mx-auto mb-4" /> <Bell className={`w-12 h-12 mx-auto mb-4 ${isDark ? "text-gray-600" : "text-gray-400"}`} />
<p className="text-gray-600">No notifications</p> <p className={isDark ? "text-gray-400" : "text-gray-600"}>No notifications</p>
</div> </div>
) : ( ) : (
notifications.map((notification) => ( notifications.map((notification) => (
@ -97,7 +105,7 @@ export function Notifications({
className={cn( className={cn(
"p-4 rounded-lg border-2 transition-all", "p-4 rounded-lg border-2 transition-all",
getBgColor(notification.type), getBgColor(notification.type),
!notification.read && "ring-2 ring-offset-2 ring-rose-300" !notification.read && (isDark ? "ring-2 ring-offset-2 ring-rose-400 ring-offset-gray-900" : "ring-2 ring-offset-2 ring-rose-300")
)} )}
> >
<div className="flex items-start gap-3"> <div className="flex items-start gap-3">
@ -108,13 +116,15 @@ export function Notifications({
<h3 <h3
className={cn( className={cn(
"font-semibold mb-1", "font-semibold mb-1",
!notification.read ? "text-gray-900" : "text-gray-700" !notification.read
? isDark ? "text-white" : "text-gray-900"
: isDark ? "text-gray-300" : "text-gray-700"
)} )}
> >
{notification.title} {notification.title}
</h3> </h3>
<p className="text-sm text-gray-600 mb-2">{notification.message}</p> <p className={`text-sm mb-2 ${isDark ? "text-gray-400" : "text-gray-600"}`}>{notification.message}</p>
<div className="flex items-center gap-2 text-xs text-gray-500"> <div className={`flex items-center gap-2 text-xs ${isDark ? "text-gray-500" : "text-gray-500"}`}>
<Clock className="w-3 h-3" /> <Clock className="w-3 h-3" />
{notification.time} {notification.time}
</div> </div>
@ -125,7 +135,7 @@ export function Notifications({
variant="ghost" variant="ghost"
size="icon-sm" size="icon-sm"
onClick={() => onMarkAsRead(notification.id)} onClick={() => onMarkAsRead(notification.id)}
className="h-7 w-7" className={`h-7 w-7 ${isDark ? "text-gray-300 hover:bg-gray-800" : ""}`}
> >
<CheckCircle className="w-4 h-4" /> <CheckCircle className="w-4 h-4" />
</Button> </Button>
@ -135,7 +145,7 @@ export function Notifications({
variant="ghost" variant="ghost"
size="icon-sm" size="icon-sm"
onClick={() => onDismiss(notification.id)} onClick={() => onDismiss(notification.id)}
className="h-7 w-7" className={`h-7 w-7 ${isDark ? "text-gray-300 hover:bg-gray-800" : ""}`}
> >
<X className="w-4 h-4" /> <X className="w-4 h-4" />
</Button> </Button>

View File

@ -13,6 +13,7 @@ import {
X, X,
Heart, Heart,
} from "lucide-react"; } from "lucide-react";
import { useAppTheme } from "@/components/ThemeProvider";
const navItems = [ const navItems = [
{ label: "Dashboard", icon: LayoutGrid, href: "/admin/dashboard" }, { label: "Dashboard", icon: LayoutGrid, href: "/admin/dashboard" },
@ -23,6 +24,8 @@ export default function SideNav() {
const [open, setOpen] = useState(false); const [open, setOpen] = useState(false);
const pathname = usePathname(); const pathname = usePathname();
const router = useRouter(); const router = useRouter();
const { theme } = useAppTheme();
const isDark = theme === "dark";
const getActiveIndex = () => { const getActiveIndex = () => {
return navItems.findIndex((item) => pathname?.includes(item.href)) ?? -1; return navItems.findIndex((item) => pathname?.includes(item.href)) ?? -1;
@ -43,12 +46,12 @@ export default function SideNav() {
return ( return (
<> <>
{/* Mobile Top Bar */} {/* Mobile Top Bar */}
<div className="flex md:hidden items-center justify-between px-4 py-3 border-b border-gray-200 bg-white z-30 fixed top-0 left-0 right-0"> <div className={`flex md:hidden items-center justify-between px-4 py-3 border-b z-30 fixed top-0 left-0 right-0 ${isDark ? "bg-gray-900 border-gray-800" : "bg-white border-gray-200"}`}>
<Link href="/" className="flex items-center gap-3"> <Link href="/" className="flex items-center gap-3">
<div className="flex items-center justify-center w-8 h-8 rounded-lg bg-linear-to-r from-rose-100 to-pink-100"> <div className={`flex items-center justify-center w-8 h-8 rounded-lg ${isDark ? "bg-gray-800" : "bg-linear-to-r from-rose-100 to-pink-100"}`}>
<Heart className="w-5 h-5 text-rose-600" fill="currentColor" /> <Heart className={`w-5 h-5 ${isDark ? "text-rose-300" : "text-rose-600"}`} fill="currentColor" />
</div> </div>
<span className="text-lg font-semibold text-gray-900">Attune Heart Therapy</span> <span className={`text-lg font-semibold ${isDark ? "text-white" : "text-gray-900"}`}>Attune Heart Therapy</span>
</Link> </Link>
<Button <Button
variant="ghost" variant="ghost"
@ -70,21 +73,21 @@ export default function SideNav() {
{/* Side Navigation */} {/* Side Navigation */}
<aside <aside
className={`fixed top-0 left-0 z-50 h-screen bg-white border-r border-gray-200 flex flex-col transition-transform duration-200 w-[85vw] max-w-[200px] min-w-[160px] md:translate-x-0 md:w-[200px] md:min-w-[200px] md:max-w-[200px] ${ className={`fixed top-0 left-0 z-50 h-screen flex flex-col transition-transform duration-200 w-[85vw] max-w-[200px] min-w-[160px] md:translate-x-0 md:w-[200px] md:min-w-[200px] md:max-w-[200px] ${isDark ? "bg-gray-900 border-r border-gray-800" : "bg-white border-r border-gray-200"} ${
open ? "translate-x-0" : "-translate-x-full" open ? "translate-x-0" : "-translate-x-full"
} md:translate-x-0`} } md:translate-x-0`}
> >
{/* Logo Section */} {/* Logo Section */}
<div className="shrink-0 px-3 pb-4 flex flex-col gap-1 md:block mb-5 pt-16 md:pt-4"> <div className="shrink-0 px-3 pb-4 flex flex-col gap-1 md:block mb-5 pt-16 md:pt-4">
<Link href="/" className="flex items-center gap-2 mb-1 ml-2 md:ml-3"> <Link href="/" className="flex items-center gap-2 mb-1 ml-2 md:ml-3">
<div className="flex items-center justify-center w-8 h-8 rounded-lg bg-linear-to-r from-rose-100 to-pink-100"> <div className={`flex items-center justify-center w-8 h-8 rounded-lg ${isDark ? "bg-gray-800" : "bg-linear-to-r from-rose-100 to-pink-100"}`}>
<Heart className="w-5 h-5 text-rose-600" fill="currentColor" /> <Heart className={`w-5 h-5 ${isDark ? "text-rose-300" : "text-rose-600"}`} fill="currentColor" />
</div> </div>
<span className="text-sm font-semibold text-gray-900">Attune Heart</span> <span className={`text-sm font-semibold ${isDark ? "text-white" : "text-gray-900"}`}>Attune Heart</span>
</Link> </Link>
</div> </div>
<hr className="shrink-0 -mt-10 mb-4 mx-3 border-gray-200 md:block hidden" /> <hr className={`shrink-0 -mt-10 mb-4 mx-3 md:block hidden ${isDark ? "border-gray-800" : "border-gray-200"}`} />
{/* Navigation Items */} {/* Navigation Items */}
<nav className="flex-1 overflow-y-auto flex flex-col gap-2 px-2 md:px-0"> <nav className="flex-1 overflow-y-auto flex flex-col gap-2 px-2 md:px-0">
@ -106,7 +109,9 @@ export default function SideNav() {
className={`group flex items-center gap-2 py-2.5 pl-3 md:pl-3 pr-3 md:pr-3 transition-colors duration-200 focus:outline-none w-[90%] md:w-[90%] ml-1 md:ml-2 cursor-pointer justify-start ${ className={`group flex items-center gap-2 py-2.5 pl-3 md:pl-3 pr-3 md:pr-3 transition-colors duration-200 focus:outline-none w-[90%] md:w-[90%] ml-1 md:ml-2 cursor-pointer justify-start ${
isActive isActive
? "bg-linear-to-r from-rose-500 to-pink-600 text-white border border-rose-500 rounded-[5px] shadow-sm" ? "bg-linear-to-r from-rose-500 to-pink-600 text-white border border-rose-500 rounded-[5px] shadow-sm"
: "bg-transparent text-gray-600 hover:bg-rose-50 hover:text-rose-600 rounded-lg" : isDark
? "bg-transparent text-gray-300 hover:bg-gray-800 hover:text-rose-300 rounded-lg"
: "bg-transparent text-gray-600 hover:bg-rose-50 hover:text-rose-600 rounded-lg"
}`} }`}
style={isActive ? { height: 40 } : {}} style={isActive ? { height: 40 } : {}}
> >
@ -116,7 +121,9 @@ export default function SideNav() {
className={ className={
isActive isActive
? "text-white" ? "text-white"
: "text-gray-700 group-hover:text-rose-600" : isDark
? "text-gray-400 group-hover:text-rose-300"
: "text-gray-700 group-hover:text-rose-600"
} }
/> />
<span <span
@ -131,7 +138,7 @@ export default function SideNav() {
})} })}
{/* Bottom Actions */} {/* Bottom Actions */}
<div className="mt-auto pt-4 pb-4 border-t border-gray-200"> <div className={`mt-auto pt-4 pb-4 border-t ${isDark ? "border-gray-800" : "border-gray-200"}`}>
<div className="relative flex items-center w-full"> <div className="relative flex items-center w-full">
{pathname === "/admin/settings" && ( {pathname === "/admin/settings" && (
<span <span
@ -145,7 +152,9 @@ export default function SideNav() {
className={`group flex items-center gap-2 py-2.5 pl-3 md:pl-3 pr-3 md:pr-3 transition-colors duration-200 w-[90%] md:w-[90%] ml-1 md:ml-2 cursor-pointer justify-start rounded-lg ${ className={`group flex items-center gap-2 py-2.5 pl-3 md:pl-3 pr-3 md:pr-3 transition-colors duration-200 w-[90%] md:w-[90%] ml-1 md:ml-2 cursor-pointer justify-start rounded-lg ${
pathname === "/admin/settings" pathname === "/admin/settings"
? "bg-linear-to-r from-rose-500 to-pink-600 text-white border border-rose-500 rounded-[5px] shadow-sm" ? "bg-linear-to-r from-rose-500 to-pink-600 text-white border border-rose-500 rounded-[5px] shadow-sm"
: "text-gray-600 hover:bg-gray-50 hover:text-gray-900" : isDark
? "text-gray-300 hover:bg-gray-800 hover:text-rose-300"
: "text-gray-600 hover:bg-gray-50 hover:text-gray-900"
}`} }`}
style={pathname === "/admin/settings" ? { height: 40 } : {}} style={pathname === "/admin/settings" ? { height: 40 } : {}}
> >
@ -155,7 +164,9 @@ export default function SideNav() {
className={ className={
pathname === "/admin/settings" pathname === "/admin/settings"
? "text-white" ? "text-white"
: "text-gray-700 group-hover:text-gray-900" : isDark
? "text-gray-400 group-hover:text-rose-300"
: "text-gray-700 group-hover:text-gray-900"
} }
/> />
<span className="font-light leading-none text-[12px]" style={{ fontWeight: 300 }}> <span className="font-light leading-none text-[12px]" style={{ fontWeight: 300 }}>
@ -169,9 +180,13 @@ export default function SideNav() {
setOpen(false); setOpen(false);
router.push("/"); router.push("/");
}} }}
className="group flex items-center gap-2 py-2.5 pl-3 md:pl-3 pr-3 md:pr-3 transition-colors duration-200 w-[90%] md:w-[90%] ml-1 md:ml-2 cursor-pointer justify-start text-gray-600 hover:bg-gray-50 hover:text-gray-900 rounded-lg" className={`group flex items-center gap-2 py-2.5 pl-3 md:pl-3 pr-3 md:pr-3 transition-colors duration-200 w-[90%] md:w-[90%] ml-1 md:ml-2 cursor-pointer justify-start rounded-lg ${
isDark
? "text-gray-300 hover:bg-gray-800 hover:text-rose-300"
: "text-gray-600 hover:bg-gray-50 hover:text-gray-900"
}`}
> >
<LogOut size={18} strokeWidth={1.5} className="text-gray-700 group-hover:text-gray-900" /> <LogOut size={18} strokeWidth={1.5} className={isDark ? "text-gray-400 group-hover:text-rose-300" : "text-gray-700 group-hover:text-gray-900"} />
<span className="font-light leading-none text-[12px]" style={{ fontWeight: 300 }}> <span className="font-light leading-none text-[12px]" style={{ fontWeight: 300 }}>
Logout Logout
</span> </span>

View File

@ -9,6 +9,7 @@ import {
FileText, FileText,
MoreVertical, MoreVertical,
} from "lucide-react"; } from "lucide-react";
import { useAppTheme } from "@/components/ThemeProvider";
interface User { interface User {
ID: number; ID: number;
@ -54,6 +55,8 @@ export default function Booking() {
const [bookings, setBookings] = useState<Booking[]>([]); const [bookings, setBookings] = useState<Booking[]>([]);
const [loading, setLoading] = useState(true); const [loading, setLoading] = useState(true);
const [searchTerm, setSearchTerm] = useState(""); const [searchTerm, setSearchTerm] = useState("");
const { theme } = useAppTheme();
const isDark = theme === "dark";
useEffect(() => { useEffect(() => {
// Simulate API call // Simulate API call
@ -127,7 +130,22 @@ export default function Booking() {
}; };
const getStatusColor = (status: string) => { const getStatusColor = (status: string) => {
switch (status.toLowerCase()) { const normalized = status.toLowerCase();
if (isDark) {
switch (normalized) {
case "scheduled":
return "bg-blue-500/20 text-blue-200";
case "completed":
return "bg-green-500/20 text-green-200";
case "cancelled":
return "bg-red-500/20 text-red-200";
case "pending":
return "bg-yellow-500/20 text-yellow-200";
default:
return "bg-gray-700 text-gray-200";
}
}
switch (normalized) {
case "scheduled": case "scheduled":
return "bg-blue-100 text-blue-700"; return "bg-blue-100 text-blue-700";
case "completed": case "completed":
@ -142,7 +160,20 @@ export default function Booking() {
}; };
const getPaymentStatusColor = (status: string) => { const getPaymentStatusColor = (status: string) => {
switch (status.toLowerCase()) { const normalized = status.toLowerCase();
if (isDark) {
switch (normalized) {
case "paid":
return "bg-green-500/20 text-green-200";
case "pending":
return "bg-yellow-500/20 text-yellow-200";
case "failed":
return "bg-red-500/20 text-red-200";
default:
return "bg-gray-700 text-gray-200";
}
}
switch (normalized) {
case "paid": case "paid":
return "bg-green-100 text-green-700"; return "bg-green-100 text-green-700";
case "pending": case "pending":
@ -166,102 +197,104 @@ export default function Booking() {
); );
return ( return (
<div className="min-h-screen bg-gray-50"> <div className={`min-h-screen ${isDark ? "bg-gray-900" : "bg-gray-50"}`}>
{/* Main Content */} {/* Main Content */}
<main className="p-3 sm:p-4 md:p-6 lg:p-8"> <main className="p-3 sm:p-4 md:p-6 lg:p-8">
{/* Page Header */} {/* Page Header */}
<div className="mb-4 sm:mb-6 flex flex-col sm:flex-row sm:items-center sm:justify-between gap-3 sm:gap-4"> <div className="mb-4 sm:mb-6 flex flex-col sm:flex-row sm:items-center sm:justify-between gap-3 sm:gap-4">
<div> <div>
<h1 className="text-xl sm: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"}`}>
Bookings Bookings
</h1> </h1>
<p className="text-xs sm:text-sm text-gray-500"> <p className={`text-xs sm:text-sm ${isDark ? "text-gray-400" : "text-gray-500"}`}>
Manage and view all appointment bookings Manage and view all appointment bookings
</p> </p>
</div> </div>
<button className="w-full sm:w-auto px-3 sm:px-4 py-2 bg-gray-900 text-white rounded-lg text-xs sm:text-sm font-medium hover:bg-gray-800 transition-colors"> <button className={`w-full sm:w-auto px-3 sm:px-4 py-2 rounded-lg text-xs sm:text-sm font-medium transition-colors ${
isDark ? "bg-rose-500 text-white hover:bg-rose-600" : "bg-gray-900 text-white hover:bg-gray-800"
}`}>
+ New Booking + New Booking
</button> </button>
</div> </div>
{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>
) : filteredBookings.length === 0 ? ( ) : filteredBookings.length === 0 ? (
<div className="bg-white rounded-lg border border-gray-200 p-12 text-center"> <div className={`rounded-lg border p-12 text-center ${isDark ? "bg-gray-800 border-gray-700" : "bg-white border-gray-200"}`}>
<Calendar className="w-12 h-12 text-gray-400 mx-auto mb-4" /> <Calendar className={`w-12 h-12 mx-auto mb-4 ${isDark ? "text-gray-500" : "text-gray-400"}`} />
<p className="text-gray-600 font-medium mb-1">No bookings found</p> <p className={`font-medium mb-1 ${isDark ? "text-gray-200" : "text-gray-600"}`}>No bookings found</p>
<p className="text-sm text-gray-500"> <p className={`text-sm ${isDark ? "text-gray-400" : "text-gray-500"}`}>
{searchTerm {searchTerm
? "Try adjusting your search terms" ? "Try adjusting your search terms"
: "Create a new booking to get started"} : "Create a new booking to get started"}
</p> </p>
</div> </div>
) : ( ) : (
<div className="bg-white rounded-lg border border-gray-200 overflow-hidden"> <div className={`rounded-lg border overflow-hidden ${isDark ? "bg-gray-800 border-gray-700" : "bg-white border-gray-200"}`}>
<div className="overflow-x-auto"> <div className="overflow-x-auto">
<table className="w-full"> <table className="w-full">
<thead className="bg-gray-50 border-b border-gray-200"> <thead className={`${isDark ? "bg-gray-800 border-b border-gray-700" : "bg-gray-50 border-b border-gray-200"}`}>
<tr> <tr>
<th className="px-3 sm:px-4 md:px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"> <th className={`px-3 sm:px-4 md:px-6 py-3 text-left text-xs font-medium uppercase tracking-wider ${isDark ? "text-gray-400" : "text-gray-500"}`}>
Patient Patient
</th> </th>
<th className="px-3 sm:px-4 md:px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider hidden md:table-cell"> <th className={`px-3 sm:px-4 md:px-6 py-3 text-left text-xs font-medium uppercase tracking-wider hidden md:table-cell ${isDark ? "text-gray-400" : "text-gray-500"}`}>
Date & Time Date & Time
</th> </th>
<th className="px-3 sm:px-4 md:px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider hidden lg:table-cell"> <th className={`px-3 sm:px-4 md:px-6 py-3 text-left text-xs font-medium uppercase tracking-wider hidden lg:table-cell ${isDark ? "text-gray-400" : "text-gray-500"}`}>
Duration Duration
</th> </th>
<th className="px-3 sm:px-4 md:px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"> <th className={`px-3 sm:px-4 md:px-6 py-3 text-left text-xs font-medium uppercase tracking-wider ${isDark ? "text-gray-400" : "text-gray-500"}`}>
Status Status
</th> </th>
<th className="px-3 sm:px-4 md:px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider hidden lg:table-cell"> <th className={`px-3 sm:px-4 md:px-6 py-3 text-left text-xs font-medium uppercase tracking-wider hidden lg:table-cell ${isDark ? "text-gray-400" : "text-gray-500"}`}>
Payment Payment
</th> </th>
<th className="px-3 sm:px-4 md:px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider hidden xl:table-cell"> <th className={`px-3 sm:px-4 md:px-6 py-3 text-left text-xs font-medium uppercase tracking-wider hidden xl:table-cell ${isDark ? "text-gray-400" : "text-gray-500"}`}>
Amount Amount
</th> </th>
<th className="px-3 sm:px-4 md:px-6 py-3 text-right text-xs font-medium text-gray-500 uppercase tracking-wider"> <th className={`px-3 sm:px-4 md:px-6 py-3 text-right text-xs font-medium uppercase tracking-wider ${isDark ? "text-gray-400" : "text-gray-500"}`}>
Actions Actions
</th> </th>
</tr> </tr>
</thead> </thead>
<tbody className="bg-white divide-y divide-gray-200"> <tbody className={`${isDark ? "bg-gray-800 divide-gray-700" : "bg-white divide-gray-200"}`}>
{filteredBookings.map((booking) => ( {filteredBookings.map((booking) => (
<tr <tr
key={booking.ID} key={booking.ID}
className="hover:bg-gray-50 transition-colors" className={`transition-colors ${isDark ? "hover:bg-gray-700" : "hover:bg-gray-50"}`}
> >
<td className="px-3 sm:px-4 md:px-6 py-4"> <td className="px-3 sm:px-4 md:px-6 py-4">
<div className="flex items-center"> <div className="flex items-center">
<div className="shrink-0 h-8 w-8 sm:h-10 sm:w-10 rounded-full bg-gray-100 flex items-center justify-center"> <div className={`shrink-0 h-8 w-8 sm:h-10 sm:w-10 rounded-full flex items-center justify-center ${isDark ? "bg-gray-700" : "bg-gray-100"}`}>
<User className="w-4 h-4 sm:w-5 sm:h-5 text-gray-600" /> <User className={`w-4 h-4 sm:w-5 sm:h-5 ${isDark ? "text-gray-200" : "text-gray-600"}`} />
</div> </div>
<div className="ml-2 sm:ml-4 min-w-0"> <div className="ml-2 sm:ml-4 min-w-0">
<div className="text-xs sm:text-sm font-medium text-gray-900 truncate"> <div className={`text-xs sm:text-sm font-medium truncate ${isDark ? "text-white" : "text-gray-900"}`}>
{booking.user.first_name} {booking.user.last_name} {booking.user.first_name} {booking.user.last_name}
</div> </div>
<div className="text-xs sm:text-sm text-gray-500 truncate hidden sm:block"> <div className={`text-xs sm:text-sm truncate hidden sm:block ${isDark ? "text-gray-400" : "text-gray-500"}`}>
{booking.user.email} {booking.user.email}
</div> </div>
<div className="text-xs text-gray-500 sm:hidden mt-0.5"> <div className={`text-xs sm:hidden mt-0.5 ${isDark ? "text-gray-400" : "text-gray-500"}`}>
{formatDate(booking.scheduled_at)} {formatDate(booking.scheduled_at)}
</div> </div>
</div> </div>
</div> </div>
</td> </td>
<td className="px-3 sm:px-4 md:px-6 py-4 whitespace-nowrap hidden md:table-cell"> <td className="px-3 sm:px-4 md:px-6 py-4 whitespace-nowrap hidden md:table-cell">
<div className="text-xs sm:text-sm text-gray-900"> <div className={`text-xs sm:text-sm ${isDark ? "text-white" : "text-gray-900"}`}>
{formatDate(booking.scheduled_at)} {formatDate(booking.scheduled_at)}
</div> </div>
<div className="text-xs sm:text-sm text-gray-500 flex items-center gap-1"> <div className={`text-xs sm:text-sm flex items-center gap-1 ${isDark ? "text-gray-400" : "text-gray-500"}`}>
<Clock className="w-3 h-3" /> <Clock className="w-3 h-3" />
{formatTime(booking.scheduled_at)} {formatTime(booking.scheduled_at)}
</div> </div>
</td> </td>
<td className="px-3 sm:px-4 md:px-6 py-4 whitespace-nowrap text-xs sm:text-sm text-gray-900 hidden lg:table-cell"> <td className={`px-3 sm:px-4 md:px-6 py-4 whitespace-nowrap text-xs sm:text-sm hidden lg:table-cell ${isDark ? "text-white" : "text-gray-900"}`}>
{booking.duration} min {booking.duration} min
</td> </td>
<td className="px-3 sm:px-4 md:px-6 py-4 whitespace-nowrap"> <td className="px-3 sm:px-4 md:px-6 py-4 whitespace-nowrap">
@ -282,7 +315,7 @@ export default function Booking() {
{booking.payment_status} {booking.payment_status}
</span> </span>
</td> </td>
<td className="px-3 sm:px-4 md:px-6 py-4 whitespace-nowrap text-xs sm:text-sm font-medium text-gray-900 hidden xl:table-cell"> <td className={`px-3 sm:px-4 md:px-6 py-4 whitespace-nowrap text-xs sm:text-sm font-medium hidden xl:table-cell ${isDark ? "text-white" : "text-gray-900"}`}>
${booking.amount} ${booking.amount}
</td> </td>
<td className="px-3 sm:px-4 md:px-6 py-4 whitespace-nowrap text-right text-sm font-medium"> <td className="px-3 sm:px-4 md:px-6 py-4 whitespace-nowrap text-right text-sm font-medium">
@ -292,7 +325,7 @@ export default function Booking() {
href={booking.jitsi_room_url} href={booking.jitsi_room_url}
target="_blank" target="_blank"
rel="noopener noreferrer" rel="noopener noreferrer"
className="p-1.5 sm:p-2 text-gray-600 hover:text-gray-900 hover:bg-gray-100 rounded-lg transition-colors" className={`p-1.5 sm:p-2 rounded-lg transition-colors ${isDark ? "text-gray-300 hover:text-white hover:bg-gray-700" : "text-gray-600 hover:text-gray-900 hover:bg-gray-100"}`}
title="Join Meeting" title="Join Meeting"
> >
<Video className="w-4 h-4" /> <Video className="w-4 h-4" />
@ -300,13 +333,13 @@ export default function Booking() {
)} )}
{booking.notes && ( {booking.notes && (
<button <button
className="p-1.5 sm:p-2 text-gray-600 hover:text-gray-900 hover:bg-gray-100 rounded-lg transition-colors" className={`p-1.5 sm:p-2 rounded-lg transition-colors ${isDark ? "text-gray-300 hover:text-white hover:bg-gray-700" : "text-gray-600 hover:text-gray-900 hover:bg-gray-100"}`}
title="View Notes" title="View Notes"
> >
<FileText className="w-4 h-4" /> <FileText className="w-4 h-4" />
</button> </button>
)} )}
<button className="p-1.5 sm:p-2 text-gray-600 hover:text-gray-900 hover:bg-gray-100 rounded-lg transition-colors"> <button className={`p-1.5 sm:p-2 rounded-lg transition-colors ${isDark ? "text-gray-300 hover:text-white hover:bg-gray-700" : "text-gray-600 hover:text-gray-900 hover:bg-gray-100"}`}>
<MoreVertical className="w-4 h-4" /> <MoreVertical className="w-4 h-4" />
</button> </button>
</div> </div>

View File

@ -19,6 +19,7 @@ import {
ArrowUpRight, ArrowUpRight,
ArrowDownRight, ArrowDownRight,
} from "lucide-react"; } from "lucide-react";
import { useAppTheme } from "@/components/ThemeProvider";
interface DashboardStats { interface DashboardStats {
total_users: number; total_users: number;
@ -35,6 +36,8 @@ export default function Dashboard() {
const [stats, setStats] = useState<DashboardStats | null>(null); const [stats, setStats] = useState<DashboardStats | null>(null);
const [loading, setLoading] = useState(true); const [loading, setLoading] = useState(true);
const [timePeriod, setTimePeriod] = useState<string>("last_month"); const [timePeriod, setTimePeriod] = useState<string>("last_month");
const { theme } = useAppTheme();
const isDark = theme === "dark";
useEffect(() => { useEffect(() => {
// Simulate API call // Simulate API call
@ -122,36 +125,43 @@ export default function Dashboard() {
]; ];
const getTrendClasses = (trendUp: boolean) => {
if (isDark) {
return trendUp ? "bg-green-500/20 text-green-200" : "bg-red-500/20 text-red-200";
}
return trendUp ? "bg-green-50 text-green-700" : "bg-red-50 text-red-700";
};
return ( return (
<div className="min-h-screen bg-gray-50"> <div className={`min-h-screen ${isDark ? "bg-gray-900" : "bg-gray-50"}`}>
{/* Main Content */} {/* Main Content */}
<main className="p-4 sm:p-6 lg:p-8 space-y-6"> <main className="p-4 sm:p-6 lg:p-8 space-y-6">
{/* Welcome Section */} {/* Welcome Section */}
<div className="flex items-center justify-between mb-6"> <div className="flex flex-col sm:flex-row sm:items-center sm:justify-between gap-4 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! Hammond Welcome Back! Hammond
</h1> </h1>
<p className="text-sm text-gray-500"> <p className={`text-sm ${isDark ? "text-gray-400" : "text-gray-500"}`}>
Here's an overview of your practice today Here's an overview of your practice today
</p> </p>
</div> </div>
<Select value={timePeriod} onValueChange={setTimePeriod}> <Select value={timePeriod} onValueChange={setTimePeriod}>
<SelectTrigger className="w-[180px] cursor-pointer"> <SelectTrigger className={`w-full sm:w-[200px] cursor-pointer ${isDark ? "bg-gray-800 border-gray-700 text-gray-100" : "bg-white border-gray-200 text-gray-900"}`}>
<SelectValue placeholder="Select period" /> <SelectValue placeholder="Select period" />
</SelectTrigger> </SelectTrigger>
<SelectContent className="bg-white cursor-pointer"> <SelectContent className={`${isDark ? "bg-gray-800 border-gray-700 text-gray-100" : "bg-white border-gray-200 text-gray-900"} cursor-pointer`}>
<SelectItem value="last_week">Last Week</SelectItem> <SelectItem className={isDark ? "focus:bg-gray-700" : ""} value="last_week">Last Week</SelectItem>
<SelectItem value="last_month">Last Month</SelectItem> <SelectItem className={isDark ? "focus:bg-gray-700" : ""} value="last_month">Last Month</SelectItem>
<SelectItem value="last_year">Last Year</SelectItem> <SelectItem className={isDark ? "focus:bg-gray-700" : ""} value="last_year">Last Year</SelectItem>
</SelectContent> </SelectContent>
</Select> </Select>
</div> </div>
{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>
) : ( ) : (
<> <>
@ -162,17 +172,13 @@ export default function Dashboard() {
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-200" : "text-gray-600"}`} />
</div> </div>
<div className={`flex items-center gap-1 px-2 py-1 rounded-full text-xs font-medium ${ <div className={`flex items-center gap-1 px-2 py-1 rounded-full text-xs font-medium ${getTrendClasses(card.trendUp)}`}>
card.trendUp
? "bg-green-50 text-green-700"
: "bg-red-50 text-red-700"
}`}>
{card.trendUp ? ( {card.trendUp ? (
<ArrowUpRight className="w-3 h-3" /> <ArrowUpRight className="w-3 h-3" />
) : ( ) : (
@ -183,13 +189,13 @@ export default function Dashboard() {
</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-300" : "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-500"> <p className={`text-xs ${isDark ? "text-gray-400" : "text-gray-500"}`}>
vs last month vs last month
</p> </p>
</div> </div>

View File

@ -2,6 +2,7 @@
import { useState } from "react"; import { useState } from "react";
import { Bell } from "lucide-react"; import { Bell } from "lucide-react";
import { useAppTheme } from "@/components/ThemeProvider";
interface Notification { interface Notification {
id: string; id: string;
@ -51,16 +52,18 @@ export default function NotificationsPage() {
]); ]);
const unreadCount = notifications.filter((n) => !n.read).length; const unreadCount = notifications.filter((n) => !n.read).length;
const { theme } = useAppTheme();
const isDark = theme === "dark";
return ( return (
<div className="min-h-screen bg-gray-50"> <div className={`min-h-screen ${isDark ? "bg-gray-900" : "bg-gray-50"}`}>
{/* Main Content */} {/* Main Content */}
<main className="p-3 sm:p-4 md:p-6 lg:p-8"> <main className="p-3 sm:p-4 md:p-6 lg:p-8">
{/* Page Header */} {/* Page Header */}
<div className="mb-4 sm:mb-6 flex flex-col sm:flex-row sm:items-center sm:justify-between gap-3 sm:gap-4"> <div className="mb-4 sm:mb-6 flex flex-col sm:flex-row sm:items-center sm:justify-between gap-3 sm:gap-4">
<div className="flex items-center gap-3"> <div className="flex items-center gap-3">
<Bell className="w-5 h-5 sm:w-6 sm:h-6 text-gray-900" /> <Bell className={`w-5 h-5 sm:w-6 sm:h-6 ${isDark ? "text-white" : "text-gray-900"}`} />
<h1 className="text-xl sm:text-2xl font-semibold text-gray-900"> <h1 className={`text-xl sm:text-2xl font-semibold ${isDark ? "text-white" : "text-gray-900"}`}>
Notifications Notifications
</h1> </h1>
{unreadCount > 0 && ( {unreadCount > 0 && (
@ -72,26 +75,30 @@ export default function NotificationsPage() {
</div> </div>
{/* Notifications List */} {/* Notifications List */}
<div className="bg-white rounded-lg border border-gray-200 overflow-hidden"> <div className={`rounded-lg border overflow-hidden ${isDark ? "bg-gray-800 border-gray-700" : "bg-white border-gray-200"}`}>
{notifications.length === 0 ? ( {notifications.length === 0 ? (
<div className="p-8 sm:p-12 text-center text-gray-500"> <div className={`p-8 sm:p-12 text-center ${isDark ? "text-gray-400" : "text-gray-500"}`}>
<Bell className="w-12 h-12 mx-auto mb-2 text-gray-300" /> <Bell className={`w-12 h-12 mx-auto mb-2 ${isDark ? "text-gray-600" : "text-gray-300"}`} />
<p className="text-sm">No notifications</p> <p className="text-sm">No notifications</p>
</div> </div>
) : ( ) : (
<div className="divide-y"> <div className={`divide-y ${isDark ? "divide-gray-700" : ""}`}>
{notifications.map((notification) => { {notifications.map((notification) => {
return ( return (
<div <div
key={notification.id} key={notification.id}
className={`p-4 sm:p-6 hover:bg-gray-50 transition-colors cursor-pointer ${ className={`p-4 sm:p-6 transition-colors cursor-pointer ${
!notification.read ? "bg-rose-50/50" : "" !notification.read
? isDark
? "bg-rose-500/10"
: "bg-rose-50/50"
: ""
}`} }`}
> >
<div className="flex-1 min-w-0"> <div className="flex-1 min-w-0">
<div className="flex items-start justify-between gap-2"> <div className="flex items-start justify-between gap-2">
<p <p
className={`text-sm sm:text-base font-medium text-gray-900 ${ className={`text-sm sm:text-base font-medium ${isDark ? "text-white" : "text-gray-900"} ${
!notification.read ? "font-semibold" : "" !notification.read ? "font-semibold" : ""
}`} }`}
> >
@ -101,10 +108,10 @@ export default function NotificationsPage() {
<span className="shrink-0 w-2 h-2 bg-green-500 rounded-full mt-1"></span> <span className="shrink-0 w-2 h-2 bg-green-500 rounded-full mt-1"></span>
)} )}
</div> </div>
<p className="text-sm text-gray-600 mt-1"> <p className={`text-sm mt-1 ${isDark ? "text-gray-400" : "text-gray-600"}`}>
{notification.message} {notification.message}
</p> </p>
<p className="text-xs text-gray-400 mt-1"> <p className={`text-xs mt-1 ${isDark ? "text-gray-500" : "text-gray-400"}`}>
{notification.time} {notification.time}
</p> </p>
</div> </div>

View File

@ -15,6 +15,7 @@ import {
EyeOff, EyeOff,
} from "lucide-react"; } from "lucide-react";
import Link from "next/link"; import Link from "next/link";
import { useAppTheme } from "@/components/ThemeProvider";
export default function AdminSettingsPage() { export default function AdminSettingsPage() {
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
@ -33,6 +34,8 @@ export default function AdminSettingsPage() {
new: false, new: false,
confirm: false, confirm: false,
}); });
const { theme } = useAppTheme();
const isDark = theme === "dark";
const handleInputChange = (field: string, value: string) => { const handleInputChange = (field: string, value: string) => {
setFormData((prev) => ({ setFormData((prev) => ({
@ -88,22 +91,22 @@ export default function AdminSettingsPage() {
}; };
return ( return (
<div className="min-h-screen bg-gray-50"> <div className={`min-h-screen ${isDark ? "bg-gray-900" : "bg-gray-50"}`}>
{/* Main Content */} {/* Main Content */}
<main className="p-4 sm:p-6 lg:p-8 space-y-6"> <main className="p-4 sm:p-6 lg:p-8 space-y-6">
{/* Header */} {/* Header */}
<div className="flex items-center justify-between mb-6"> <div className="flex flex-col sm:flex-row sm:items-center sm:justify-between gap-4 mb-6">
<div className="flex items-center gap-4"> <div className="flex items-center gap-4">
<Link href="/admin/dashboard"> <Link href="/admin/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 practice information Manage your account settings and practice information
</p> </p>
</div> </div>
@ -111,7 +114,7 @@ export default function AdminSettingsPage() {
<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="w-full sm:w-auto bg-linear-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>
@ -125,60 +128,60 @@ export default function AdminSettingsPage() {
<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-300" : "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" : ""}`}
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" : ""}`}
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" : ""}`}
placeholder="Enter your phone number" placeholder="Enter your phone number"
/> />
</div> </div>
@ -187,34 +190,34 @@ export default function AdminSettingsPage() {
</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-300" : "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" : ""}`}
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" />
@ -226,22 +229,22 @@ export default function AdminSettingsPage() {
</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" : ""}`}
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" />
@ -250,28 +253,28 @@ export default function AdminSettingsPage() {
)} )}
</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" : ""}`}
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" />

View File

@ -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>

View File

@ -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>

View File

@ -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,34 +128,34 @@ 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 */}
<main className="container mx-auto px-4 sm:px-6 lg:px-8 space-y-6 pt-20 sm:pt-24 pb-8"> <main className="container mx-auto px-4 sm:px-6 lg:px-8 space-y-6 pt-20 sm:pt-24 pb-8">
{/* Welcome Section */} {/* Welcome Section */}
<div className="flex items-center justify-between mb-6"> <div className="flex flex-col sm:flex-row sm:items-center sm:justify-between gap-4 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>
<div className="flex items-center gap-3"> <div className="flex items-center gap-3">
<Link href="/user/settings"> <Link href="/user/settings" className="flex-1 sm:flex-initial">
<Button <Button
variant="outline" variant="outline"
className="hover:bg-gray-100" className={`w-full sm:w-auto ${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
</Button> </Button>
</Link> </Link>
<Link href="/book-now"> <Link href="/book-now" className="flex-1 sm:flex-initial">
<Button <Button
className="bg-linear-to-r from-rose-500 to-pink-600 hover:from-rose-600 hover:to-pink-700 text-white" className="w-full sm:w-auto 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>

View File

@ -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,24 +92,24 @@ 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 */}
<main className="container mx-auto px-4 sm:px-6 lg:px-8 space-y-6 pt-20 sm:pt-24 pb-8"> <main className="container mx-auto px-4 sm:px-6 lg:px-8 space-y-6 pt-20 sm:pt-24 pb-8">
{/* Header */} {/* Header */}
<div className="flex items-center justify-between mb-6"> <div className="flex flex-col sm:flex-row sm:items-center sm:justify-between gap-4 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="w-full sm:w-auto 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>

View File

@ -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,19 @@ 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 }}
> >
<<<<<<< HEAD
<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">
=======
<div className="bg-gradient-to-br from-rose-100/15 via-pink-100/15 to-orange-100/15 dark:from-rose-900/15 dark:via-pink-900/15 dark:to-orange-900/15 rounded-3xl p-8 border border-border/50 backdrop-blur-sm"> <div className="bg-gradient-to-br from-rose-100/15 via-pink-100/15 to-orange-100/15 dark:from-rose-900/15 dark:via-pink-900/15 dark:to-orange-900/15 rounded-3xl p-8 border border-border/50 backdrop-blur-sm">
>>>>>>> 79a5bb9fb99183cdae6841a62b115a1f8b88161e
<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 }}

View File

@ -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)",

View File

@ -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

View File

@ -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",

View File

@ -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, LLC. All rights reserved. © {new Date().getFullYear()} Attune Heart Therapy, LLC. All rights reserved.
</p> </p>

View File

@ -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 }}

View File

@ -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",

View File

@ -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>

View File

@ -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,61 +53,155 @@ 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
open={loginDialogOpen} open={loginDialogOpen}

View File

@ -2,28 +2,20 @@
import { motion } from "framer-motion"; import { motion } from "framer-motion";
import { useInView } from "framer-motion"; import { useInView } from "framer-motion";
<<<<<<< HEAD
import { useRef } from "react";
import { Baby, Brain, HeartHandshake, Sparkles, Users2, Shield } from "lucide-react";
import { useAppTheme } from "@/components/ThemeProvider";
=======
import { useRef, useEffect, useState } from "react"; import { useRef, useEffect, useState } from "react";
import { Users, Sparkles, Heart, Users2, Feather, Shield } from "lucide-react"; import { Users, Sparkles, Heart, Users2, Feather, Shield } from "lucide-react";
>>>>>>> 79a5bb9fb99183cdae6841a62b115a1f8b88161e
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 = [
{ {
@ -141,7 +133,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 }}
@ -149,7 +141,7 @@ export function Services() {
Services 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 }}
@ -158,7 +150,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 (
@ -167,7 +159,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"
> >
<div className="relative z-10"> <div className="relative z-10">
<motion.div <motion.div

View File

@ -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",

View File

@ -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"}`} />