2025-11-06 12:34:29 +00:00
|
|
|
"use client";
|
|
|
|
|
|
|
|
|
|
import React, { useState, useEffect } from "react";
|
2025-11-07 13:45:14 +00:00
|
|
|
import { usePathname, useRouter } from "next/navigation";
|
2025-11-06 12:34:29 +00:00
|
|
|
import Link from "next/link";
|
2025-11-07 13:45:14 +00:00
|
|
|
import { Button } from "@/components/ui/button";
|
2025-11-06 12:34:29 +00:00
|
|
|
import {
|
|
|
|
|
LayoutGrid,
|
|
|
|
|
Calendar,
|
|
|
|
|
Settings,
|
|
|
|
|
LogOut,
|
|
|
|
|
Menu,
|
|
|
|
|
X,
|
|
|
|
|
Heart,
|
2025-11-24 17:48:57 +00:00
|
|
|
FileText,
|
2025-11-06 12:34:29 +00:00
|
|
|
} from "lucide-react";
|
2025-11-13 11:42:56 +00:00
|
|
|
import { useAppTheme } from "@/components/ThemeProvider";
|
2025-11-23 21:13:18 +00:00
|
|
|
import { useAuth } from "@/hooks/useAuth";
|
|
|
|
|
import { toast } from "sonner";
|
2025-11-06 12:34:29 +00:00
|
|
|
|
|
|
|
|
const navItems = [
|
2025-11-12 00:28:29 +00:00
|
|
|
{ label: "Dashboard", icon: LayoutGrid, href: "/admin/dashboard" },
|
|
|
|
|
{ label: "Book Appointment", icon: Calendar, href: "/admin/booking" },
|
2025-11-24 17:48:57 +00:00
|
|
|
{ label: "Deliverables", icon: FileText, href: "/deliverables" },
|
2025-11-06 12:34:29 +00:00
|
|
|
];
|
|
|
|
|
|
|
|
|
|
export default function SideNav() {
|
|
|
|
|
const [open, setOpen] = useState(false);
|
|
|
|
|
const pathname = usePathname();
|
2025-11-07 13:45:14 +00:00
|
|
|
const router = useRouter();
|
2025-11-13 11:42:56 +00:00
|
|
|
const { theme } = useAppTheme();
|
|
|
|
|
const isDark = theme === "dark";
|
2025-11-23 21:13:18 +00:00
|
|
|
const { logout } = useAuth();
|
|
|
|
|
|
|
|
|
|
const handleLogout = () => {
|
|
|
|
|
setOpen(false);
|
|
|
|
|
logout();
|
|
|
|
|
toast.success("Logged out successfully");
|
|
|
|
|
router.push("/");
|
|
|
|
|
};
|
2025-11-06 12:34:29 +00:00
|
|
|
|
|
|
|
|
const getActiveIndex = () => {
|
|
|
|
|
return navItems.findIndex((item) => pathname?.includes(item.href)) ?? -1;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Handle body scroll when mobile menu is open
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
if (open) {
|
|
|
|
|
document.body.classList.add("menu-open");
|
|
|
|
|
} else {
|
|
|
|
|
document.body.classList.remove("menu-open");
|
|
|
|
|
}
|
|
|
|
|
return () => {
|
|
|
|
|
document.body.classList.remove("menu-open");
|
|
|
|
|
};
|
|
|
|
|
}, [open]);
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<>
|
|
|
|
|
{/* Mobile Top Bar */}
|
2025-11-13 11:42:56 +00:00
|
|
|
<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"}`}>
|
2025-11-12 12:33:36 +00:00
|
|
|
<Link href="/" className="flex items-center gap-3">
|
2025-11-13 11:42:56 +00:00
|
|
|
<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 ${isDark ? "text-rose-300" : "text-rose-600"}`} fill="currentColor" />
|
2025-11-06 12:34:29 +00:00
|
|
|
</div>
|
2025-11-13 11:42:56 +00:00
|
|
|
<span className={`text-lg font-semibold ${isDark ? "text-white" : "text-gray-900"}`}>Attune Heart Therapy</span>
|
2025-11-12 12:33:36 +00:00
|
|
|
</Link>
|
2025-11-07 13:45:14 +00:00
|
|
|
<Button
|
|
|
|
|
variant="ghost"
|
|
|
|
|
size="icon"
|
|
|
|
|
onClick={() => setOpen((v) => !v)}
|
|
|
|
|
aria-label="Open menu"
|
|
|
|
|
>
|
2025-11-06 12:34:29 +00:00
|
|
|
{open ? <X size={28} /> : <Menu size={28} />}
|
2025-11-07 13:45:14 +00:00
|
|
|
</Button>
|
2025-11-06 12:34:29 +00:00
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
{/* Mobile Drawer Overlay */}
|
|
|
|
|
<div
|
|
|
|
|
className={`fixed inset-0 z-40 bg-black/30 transition-opacity duration-200 md:hidden ${
|
|
|
|
|
open ? "opacity-100" : "opacity-0 pointer-events-none"
|
|
|
|
|
}`}
|
|
|
|
|
onClick={() => setOpen(false)}
|
|
|
|
|
/>
|
|
|
|
|
|
|
|
|
|
{/* Side Navigation */}
|
|
|
|
|
<aside
|
2025-11-13 11:42:56 +00:00
|
|
|
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"} ${
|
2025-11-06 12:34:29 +00:00
|
|
|
open ? "translate-x-0" : "-translate-x-full"
|
|
|
|
|
} md:translate-x-0`}
|
|
|
|
|
>
|
|
|
|
|
{/* Logo Section */}
|
2025-11-07 13:45:14 +00:00
|
|
|
<div className="shrink-0 px-3 pb-4 flex flex-col gap-1 md:block mb-5 pt-16 md:pt-4">
|
2025-11-12 12:33:36 +00:00
|
|
|
<Link href="/" className="flex items-center gap-2 mb-1 ml-2 md:ml-3">
|
2025-11-13 11:42:56 +00:00
|
|
|
<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 ${isDark ? "text-rose-300" : "text-rose-600"}`} fill="currentColor" />
|
2025-11-06 12:34:29 +00:00
|
|
|
</div>
|
2025-11-13 11:42:56 +00:00
|
|
|
<span className={`text-sm font-semibold ${isDark ? "text-white" : "text-gray-900"}`}>Attune Heart</span>
|
2025-11-12 12:33:36 +00:00
|
|
|
</Link>
|
2025-11-06 12:34:29 +00:00
|
|
|
</div>
|
|
|
|
|
|
2025-11-13 11:42:56 +00:00
|
|
|
<hr className={`shrink-0 -mt-10 mb-4 mx-3 md:block hidden ${isDark ? "border-gray-800" : "border-gray-200"}`} />
|
2025-11-06 12:34:29 +00:00
|
|
|
|
|
|
|
|
{/* Navigation Items */}
|
|
|
|
|
<nav className="flex-1 overflow-y-auto flex flex-col gap-2 px-2 md:px-0">
|
|
|
|
|
{navItems.map((item, idx) => {
|
|
|
|
|
const Icon = item.icon;
|
|
|
|
|
const isActive = idx === getActiveIndex();
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<div className="relative flex items-center w-full" key={item.label}>
|
|
|
|
|
{isActive && (
|
|
|
|
|
<span
|
2025-11-07 13:45:14 +00:00
|
|
|
className="absolute left-0 top-0 h-[40px] w-[3px] bg-linear-to-r from-rose-500 to-pink-600"
|
2025-11-06 12:34:29 +00:00
|
|
|
style={{ left: 0 }}
|
|
|
|
|
/>
|
|
|
|
|
)}
|
|
|
|
|
<Link
|
|
|
|
|
href={item.href}
|
|
|
|
|
onClick={() => setOpen(false)}
|
2025-11-06 14:40:30 +00:00
|
|
|
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 ${
|
2025-11-06 12:34:29 +00:00
|
|
|
isActive
|
2025-11-07 13:45:14 +00:00
|
|
|
? "bg-linear-to-r from-rose-500 to-pink-600 text-white border border-rose-500 rounded-[5px] shadow-sm"
|
2025-11-13 11:42:56 +00:00
|
|
|
: 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"
|
2025-11-06 12:34:29 +00:00
|
|
|
}`}
|
2025-11-06 14:40:30 +00:00
|
|
|
style={isActive ? { height: 40 } : {}}
|
2025-11-06 12:34:29 +00:00
|
|
|
>
|
|
|
|
|
<Icon
|
2025-11-06 14:40:30 +00:00
|
|
|
size={18}
|
2025-11-06 12:34:29 +00:00
|
|
|
strokeWidth={isActive ? 2.2 : 1.5}
|
|
|
|
|
className={
|
|
|
|
|
isActive
|
|
|
|
|
? "text-white"
|
2025-11-13 11:42:56 +00:00
|
|
|
: isDark
|
|
|
|
|
? "text-gray-400 group-hover:text-rose-300"
|
|
|
|
|
: "text-gray-700 group-hover:text-rose-600"
|
2025-11-06 12:34:29 +00:00
|
|
|
}
|
|
|
|
|
/>
|
|
|
|
|
<span
|
2025-11-06 14:40:30 +00:00
|
|
|
className="font-light leading-none text-[12px] md:text-[12px]"
|
2025-11-06 12:34:29 +00:00
|
|
|
style={{ fontWeight: 300 }}
|
|
|
|
|
>
|
|
|
|
|
{item.label}
|
|
|
|
|
</span>
|
|
|
|
|
</Link>
|
|
|
|
|
</div>
|
|
|
|
|
);
|
|
|
|
|
})}
|
|
|
|
|
|
|
|
|
|
{/* Bottom Actions */}
|
2025-11-13 11:42:56 +00:00
|
|
|
<div className={`mt-auto pt-4 pb-4 border-t ${isDark ? "border-gray-800" : "border-gray-200"}`}>
|
2025-11-12 00:28:29 +00:00
|
|
|
<div className="relative flex items-center w-full">
|
|
|
|
|
{pathname === "/admin/settings" && (
|
|
|
|
|
<span
|
|
|
|
|
className="absolute left-0 top-0 h-[40px] w-[3px] bg-linear-to-r from-rose-500 to-pink-600"
|
|
|
|
|
style={{ left: 0 }}
|
|
|
|
|
/>
|
|
|
|
|
)}
|
|
|
|
|
<Link
|
|
|
|
|
href="/admin/settings"
|
|
|
|
|
onClick={() => setOpen(false)}
|
|
|
|
|
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"
|
|
|
|
|
? "bg-linear-to-r from-rose-500 to-pink-600 text-white border border-rose-500 rounded-[5px] shadow-sm"
|
2025-11-13 11:42:56 +00:00
|
|
|
: isDark
|
|
|
|
|
? "text-gray-300 hover:bg-gray-800 hover:text-rose-300"
|
|
|
|
|
: "text-gray-600 hover:bg-gray-50 hover:text-gray-900"
|
2025-11-12 00:28:29 +00:00
|
|
|
}`}
|
|
|
|
|
style={pathname === "/admin/settings" ? { height: 40 } : {}}
|
|
|
|
|
>
|
|
|
|
|
<Settings
|
|
|
|
|
size={18}
|
|
|
|
|
strokeWidth={pathname === "/admin/settings" ? 2.2 : 1.5}
|
|
|
|
|
className={
|
|
|
|
|
pathname === "/admin/settings"
|
|
|
|
|
? "text-white"
|
2025-11-13 11:42:56 +00:00
|
|
|
: isDark
|
|
|
|
|
? "text-gray-400 group-hover:text-rose-300"
|
|
|
|
|
: "text-gray-700 group-hover:text-gray-900"
|
2025-11-12 00:28:29 +00:00
|
|
|
}
|
|
|
|
|
/>
|
|
|
|
|
<span className="font-light leading-none text-[12px]" style={{ fontWeight: 300 }}>
|
|
|
|
|
Settings
|
|
|
|
|
</span>
|
|
|
|
|
</Link>
|
|
|
|
|
</div>
|
2025-11-07 13:45:14 +00:00
|
|
|
<Button
|
|
|
|
|
variant="ghost"
|
2025-11-23 21:13:18 +00:00
|
|
|
onClick={handleLogout}
|
2025-11-13 11:42:56 +00:00
|
|
|
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"
|
|
|
|
|
}`}
|
2025-11-06 12:34:29 +00:00
|
|
|
>
|
2025-11-13 11:42:56 +00:00
|
|
|
<LogOut size={18} strokeWidth={1.5} className={isDark ? "text-gray-400 group-hover:text-rose-300" : "text-gray-700 group-hover:text-gray-900"} />
|
2025-11-06 14:40:30 +00:00
|
|
|
<span className="font-light leading-none text-[12px]" style={{ fontWeight: 300 }}>
|
2025-11-06 12:34:29 +00:00
|
|
|
Logout
|
|
|
|
|
</span>
|
2025-11-07 13:45:14 +00:00
|
|
|
</Button>
|
2025-11-06 12:34:29 +00:00
|
|
|
</div>
|
|
|
|
|
</nav>
|
|
|
|
|
</aside>
|
|
|
|
|
</>
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|