PatientProTokenRepository/src/app/components/Navbar.jsx
2025-04-24 10:32:46 +05:30

375 lines
17 KiB
JavaScript

"use client";
import { FaSearch } from "react-icons/fa";
import { FiLogOut } from "react-icons/fi";
import { useState, useEffect, useRef } from "react";
import { useRouter } from "next/navigation";
import { useTheme } from "../context/ThemeContext";
import { account } from "../lib/appwrite";
import { useAuth } from "../context/AuthContext";
import { MdOutlineSettings, MdSupport } from "react-icons/md";
import { AiOutlineUser } from "react-icons/ai";
import NotificationBell from './NotificationBell';
const Navbar = ({ sidebarOpen, setSidebarOpen, isCollapsed, setIsCollapsed }) => {
const [dropdownOpen, setDropdownOpen] = useState(false);
const { darkMode, toggleDarkMode } = useTheme();
const [currentUser, setCurrentUser] = useState(null);
const router = useRouter();
const dropdownRef = useRef(null);
const { setIsAuthenticated } = useAuth();
// Fetch the logged-in user
useEffect(() => {
const fetchUser = async () => {
try {
const user = await account.get();
setCurrentUser(user);
} catch (error) {
console.error("User not authenticated:", error);
router.push("/signup");
}
};
fetchUser();
}, [router]);
const toggleDropdown = () => {
setDropdownOpen(!dropdownOpen);
};
const handleSidebarToggle = () => {
if (window.innerWidth >= 1024) { // Desktop view
setIsCollapsed(!isCollapsed);
} else { // Mobile view
setSidebarOpen(!sidebarOpen);
}
};
useEffect(() => {
function handleClickOutside(event) {
if (dropdownRef.current && !dropdownRef.current.contains(event.target)) {
setDropdownOpen(false);
}
}
document.addEventListener("mousedown", handleClickOutside);
return () => {
document.removeEventListener("mousedown", handleClickOutside);
};
}, []);
const handleSignOut = async () => {
try {
await account.deleteSession("current");
setCurrentUser(null);
setIsAuthenticated(false);
router.push("/signup");
} catch (error) {
console.error("Logout failed:", error);
}
};
if (darkMode === undefined) {
return (
<nav className="bg-white dark:bg-gray-900 shadow-md sticky top-0 w-full z-30 p-2 sm:p-4 flex items-center justify-between">
<div className="animate-pulse flex space-x-4">
<div className="h-10 w-10 rounded-lg bg-gray-200 dark:bg-gray-700"></div>
<div className="h-10 w-[300px] rounded bg-gray-200 dark:bg-gray-700 hidden md:block"></div>
</div>
<div className="flex items-center gap-4">
<div className="h-8 w-8 rounded-full bg-gray-200 dark:bg-gray-700"></div>
</div>
</nav>
);
}
return (
<nav className={`
${darkMode ? 'bg-gray-900 border-gray-700 shadow-gray-800/30' : 'bg-white border-gray-200 shadow-md'}
sticky top-0 w-full z-30
p-2 sm:p-4
flex items-center justify-between
transition-colors duration-300
`}>
{/* Left Side */}
<div className="flex items-center">
{/* Hamburger Menu Button */}
<button
onClick={handleSidebarToggle}
className={`
flex h-10 w-10 items-center justify-center rounded-lg
border ${darkMode ? 'border-gray-600 bg-gray-800' : 'border-gray-300 bg-white'}
${sidebarOpen || isCollapsed ? (darkMode ? 'bg-gray-700' : 'bg-gray-100') : ''}
text-gray-500 dark:text-gray-300
mr-2
transition-all duration-200
hover:bg-gray-100 dark:hover:bg-gray-700
focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-opacity-50
`}
aria-label="Toggle sidebar"
>
{sidebarOpen || isCollapsed ? (
<svg
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M6.21967 7.28131C5.92678 6.98841 5.92678 6.51354 6.21967 6.22065C6.51256 5.92775 6.98744 5.92775 7.28033 6.22065L11.999 10.9393L16.7176 6.22078C17.0105 5.92789 17.4854 5.92788 17.7782 6.22078C18.0711 6.51367 18.0711 6.98855 17.7782 7.28144L13.0597 12L17.7782 16.7186C18.0711 17.0115 18.0711 17.4863 17.7782 17.7792C17.4854 18.0721 17.0105 18.0721 16.7176 17.7792L11.999 13.0607L7.28033 17.7794C6.98744 18.0722 6.51256 18.0722 6.21967 17.7794C5.92678 17.4865 5.92678 17.0116 6.21967 16.7187L10.9384 12L6.21967 7.28131Z"
fill="currentColor"
/>
</svg>
) : (
<svg
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M3.25 6C3.25 5.58579 3.58579 5.25 4 5.25L20 5.25C20.4142 5.25 20.75 5.58579 20.75 6C20.75 6.41421 20.4142 6.75 20 6.75L4 6.75C3.58579 6.75 3.25 6.41422 3.25 6ZM3.25 18C3.25 17.5858 3.58579 17.25 4 17.25L20 17.25C20.4142 17.25 20.75 17.5858 20.75 18C20.75 18.4142 20.4142 18.75 20 18.75L4 18.75C3.58579 18.75 3.25 18.4142 3.25 18ZM4 11.25C3.58579 11.25 3.25 11.5858 3.25 12C3.25 12.4142 3.58579 12.75 4 12.75L12 12.75C12.4142 12.75 12.75 12.4142 12.75 12C12.75 11.58579 12.4142 11.25 12 11.25L4 11.25Z"
fill="currentColor"
/>
</svg>
)}
</button>
{/* Search Box */}
<div className={`
hidden md:flex items-center gap-[2px]
border ${darkMode ? 'border-gray-600 bg-gray-800' : 'border-gray-300 bg-white'}
p-2 rounded-lg
h-[45px] w-[300px] lg:w-[400px] ml-2
transition-colors duration-300
focus-within:ring-2 focus-within:ring-blue-500 focus-within:ring-opacity-50
${darkMode ? 'focus-within:border-gray-500' : 'focus-within:border-blue-500'}
`}>
<div className="relative flex-1">
<FaSearch className={`
absolute left-3 top-1/2 transform -translate-y-1/2
${darkMode ? 'text-gray-400' : 'text-gray-500'}
`} />
<input
type="text"
placeholder="Search or type command..."
className={`
w-full bg-transparent border-none
pl-10 pr-10 py-2
focus:outline-none
${darkMode ? 'text-gray-200 placeholder-gray-500' : 'text-gray-800 placeholder-gray-400'}
text-sm
`}
/>
</div>
<div className={`
px-2 py-1
${darkMode ? 'bg-gray-700 text-gray-300' : 'bg-gray-100 text-gray-500'}
text-xs rounded-md
font-mono
`}>
K
</div>
</div>
</div>
{/* Right Side */}
<div className="flex items-center gap-2 sm:gap-4">
{/* Mobile Search Button */}
<button className={`
md:hidden p-2 rounded-full
${darkMode ? 'text-gray-300 hover:text-white' : 'text-gray-600 hover:text-gray-800'}
hover:bg-gray-200 dark:hover:bg-gray-700
transition-colors duration-200
`}>
<FaSearch className="text-lg" />
</button>
{/* Dark Mode Toggle */}
<button
onClick={toggleDarkMode}
className={`
p-2 rounded-full
${darkMode ? 'bg-gray-700 hover:bg-gray-600' : 'bg-gray-100 hover:bg-gray-200'}
flex items-center justify-center
w-8 h-8 sm:w-10 sm:h-10
transition-all duration-200
focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-opacity-50
`}
aria-label={darkMode ? "Switch to light mode" : "Switch to dark mode"}
>
{darkMode ? (
<svg
width="20"
height="20"
viewBox="0 0 20 20"
fill="currentColor"
className="text-yellow-400"
>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M9.99998 1.5415C10.4142 1.5415 10.75 1.87729 10.75 2.2915V3.5415C10.75 3.95572 10.4142 4.2915 9.99998 4.2915C9.58577 4.2915 9.24998 3.95572 9.24998 3.5415V2.2915C9.24998 1.87729 9.58577 1.5415 9.99998 1.5415ZM10.0009 6.79327C8.22978 6.79327 6.79402 8.22904 6.79402 10.0001C6.79402 11.7712 8.22978 13.207 10.0009 13.207C11.772 13.207 13.2078 11.7712 13.2078 10.0001C13.2078 8.22904 11.772 6.79327 10.0009 6.79327ZM5.29402 10.0001C5.29402 7.40061 7.40135 5.29327 10.0009 5.29327C12.6004 5.29327 14.7078 7.40061 14.7078 10.0001C14.7078 12.5997 12.6004 14.707 10.0009 14.707C7.40135 14.707 5.29402 12.5997 5.29402 10.0001ZM15.9813 5.08035C16.2742 4.78746 16.2742 4.31258 15.9813 4.01969C15.6884 3.7268 15.2135 3.7268 14.9207 4.01969L14.0368 4.90357C13.7439 5.19647 13.7439 5.67134 14.0368 5.96423C14.3297 6.25713 14.8045 6.25713 15.0974 5.96423L15.9813 5.08035ZM18.4577 10.0001C18.4577 10.4143 18.1219 10.7501 17.7077 10.7501H16.4577C16.0435 10.7501 15.7077 10.4143 15.7077 10.0001C15.7077 9.58592 16.0435 9.25013 16.4577 9.25013H17.7077C18.1219 9.25013 18.4577 9.58592 18.4577 10.0001ZM14.9207 15.9806C15.2135 16.2735 15.6884 16.2735 15.9813 15.9806C16.2742 15.6877 16.2742 15.2128 15.9813 14.9199L15.0974 14.036C14.8045 13.7431 14.3297 13.7431 14.0368 14.036C13.7439 14.3289 13.7439 14.8038 14.0368 15.0967L14.9207 15.9806ZM9.99998 15.7088C10.4142 15.7088 10.75 16.0445 10.75 16.4588V17.7088C10.75 18.123 10.4142 18.4588 9.99998 18.4588C9.58577 18.4588 9.24998 18.123 9.24998 17.7088V16.4588C9.24998 16.0445 9.58577 15.7088 9.99998 15.7088ZM5.96356 15.0972C6.25646 14.8043 6.25646 14.3295 5.96356 14.0366C5.67067 13.7437 5.1958 13.7437 4.9029 14.0366L4.01902 14.9204C3.72613 15.2133 3.72613 15.6882 4.01902 15.9811C4.31191 16.274 4.78679 16.274 5.07968 15.9811L5.96356 15.0972ZM4.29224 10.0001C4.29224 10.4143 3.95645 10.7501 3.54224 10.7501H2.29224C1.87802 10.7501 1.54224 10.4143 1.54224 10.0001C1.54224 9.58592 1.87802 9.25013 2.29224 9.25013H3.54224C3.95645 9.25013 4.29224 9.58592 4.29224 10.0001ZM4.9029 5.9637C5.1958 6.25659 5.67067 6.25659 5.96356 5.9637C6.25646 5.6708 6.25646 5.19593 5.96356 4.90303L5.07968 4.01915C4.78679 3.72626 4.31191 3.72626 4.01902 4.01915C3.72613 4.31204 3.72613 4.78692 4.01902 5.07981L4.9029 5.9637Z"
/>
</svg>
) : (
<svg
width="20"
height="20"
viewBox="0 0 20 20"
fill="currentColor"
className="text-gray-600 dark:text-gray-300"
>
<path
d="M17.4547 11.97L18.1799 12.1611C18.265 11.8383 18.1265 11.4982 17.8401 11.3266C17.5538 11.1551 17.1885 11.1934 16.944 11.4207L17.4547 11.97ZM8.0306 2.5459L8.57989 3.05657C8.80718 2.81209 8.84554 2.44682 8.67398 2.16046C8.50243 1.8741 8.16227 1.73559 7.83948 1.82066L8.0306 2.5459ZM12.9154 13.0035C9.64678 13.0035 6.99707 10.3538 6.99707 7.08524H5.49707C5.49707 11.1823 8.81835 14.5035 12.9154 14.5035V13.0035ZM16.944 11.4207C15.8869 12.4035 14.4721 13.0035 12.9154 13.0035V14.5035C14.8657 14.5035 16.6418 13.7499 17.9654 12.5193L16.944 11.4207ZM16.7295 11.7789C15.9437 14.7607 13.2277 16.9586 10.0003 16.9586V18.4586C13.9257 18.4586 17.2249 15.7853 18.1799 12.1611L16.7295 11.7789ZM10.0003 16.9586C6.15734 16.9586 3.04199 13.8433 3.04199 10.0003H1.54199C1.54199 14.6717 5.32892 18.4586 10.0003 18.4586V16.9586ZM3.04199 10.0003C3.04199 6.77289 5.23988 4.05695 8.22173 3.27114L7.83948 1.82066C4.21532 2.77574 1.54199 6.07486 1.54199 10.0003H3.04199ZM6.99707 7.08524C6.99707 5.52854 7.5971 4.11366 8.57989 3.05657L7.48132 2.03522C6.25073 3.35885 5.49707 5.13487 5.49707 7.08524H6.99707Z"
/>
</svg>
)}
</button>
{/* Notifications */}
<NotificationBell darkMode={darkMode} />
{/* User Profile */}
<div className="relative" ref={dropdownRef}>
<button
onClick={toggleDropdown}
className={`
flex items-center gap-1 sm:gap-2
cursor-pointer
hover:text-blue-500 dark:hover:text-blue-400
transition-colors duration-200
p-1 rounded-full
${dropdownOpen ? (darkMode ? 'bg-gray-700' : 'bg-gray-100') : ''}
`}
aria-label="User menu"
>
<img
src="/images/user/owner.jpg"
alt="User profile"
width={32}
height={32}
className={`
rounded-full w-8 h-8 sm:w-10 sm:h-10
border-2 ${darkMode ? 'border-gray-600' : 'border-gray-200'}
transition-all duration-200
${dropdownOpen ? 'ring-2 ring-blue-500' : ''}
`}
/>
<span className={`
hidden sm:inline
${darkMode ? 'text-gray-500' : 'text-gray-700'}
text-sm font-medium
`}>
{currentUser?.name || "User"}
</span>
</button>
{dropdownOpen && currentUser && (
<div className={`
absolute right-0 mt-2 w-56 sm:w-64
${darkMode ? 'bg-gray-800 border-gray-700' : 'bg-white border-gray-200'}
shadow-lg rounded-lg
p-2 sm:p-4
z-50 border
transition-all duration-200
origin-top-right
animate-scaleIn
`}>
{/* User Info */}
<div className="mb-2 sm:mb-3">
<h3 className={`
font-semibold
${darkMode ? 'text-gray-800' : 'text-gray-500'}
text-sm sm:text-base
`}>
{currentUser.name || "User Name"}
</h3>
<p className={`
text-xs sm:text-sm
${darkMode ? 'text-gray-400' : 'text-gray-500'}
truncate
`}>
{currentUser.email}
</p>
</div>
<hr className={`
${darkMode ? 'border-gray-700' : 'border-gray-200'}
my-1 sm:my-2
`} />
{/* Menu Items */}
<div className="space-y-1 sm:space-y-2">
<button className={`
flex items-center gap-2 w-full text-left
p-1 sm:p-2
${darkMode ? 'hover:bg-gray-700' : 'hover:bg-gray-100'}
rounded-md
text-sm sm:text-base
${darkMode ? 'text-gray-200' : 'text-gray-700'}
transition-colors duration-200
`}>
<AiOutlineUser className={darkMode ? 'text-gray-400' : 'text-gray-500'} />
Edit profile
</button>
<button className={`
flex items-center gap-2 w-full text-left
p-1 sm:p-2
${darkMode ? 'hover:bg-gray-700' : 'hover:bg-gray-100'}
rounded-md
text-sm sm:text-base
${darkMode ? 'text-gray-200' : 'text-gray-700'}
transition-colors duration-200
`}>
<MdOutlineSettings className={darkMode ? 'text-gray-400' : 'text-gray-500'} />
Account settings
</button>
<button className={`
flex items-center gap-2 w-full text-left
p-1 sm:p-2
${darkMode ? 'hover:bg-gray-700' : 'hover:bg-gray-100'}
rounded-md
text-sm sm:text-base
${darkMode ? 'text-gray-200' : 'text-gray-700'}
transition-colors duration-200
`}>
<MdSupport className={darkMode ? 'text-gray-400' : 'text-gray-500'} />
Support
</button>
</div>
<hr className={`
${darkMode ? 'border-gray-700' : 'border-gray-200'}
my-1 sm:my-2
`} />
{/* Sign Out */}
<button
onClick={handleSignOut}
className={`
flex items-center gap-2 w-full text-left
p-1 sm:p-2
text-red-500 hover:text-red-600 dark:hover:text-red-400
${darkMode ? 'hover:bg-gray-700' : 'hover:bg-gray-100'}
rounded-md
text-sm sm:text-base
transition-colors duration-200
`}
>
<FiLogOut />
Sign out
</button>
</div>
)}
</div>
</div>
</nav>
);
};
export default Navbar;