diff --git a/components/Header.tsx b/components/Header.tsx index 7292f5e..ad579be 100644 --- a/components/Header.tsx +++ b/components/Header.tsx @@ -3,8 +3,11 @@ import { useState } from 'react'; import Link from 'next/link'; import { Menu, X } from 'lucide-react'; +import { useAuthStore } from '@/store/authStore'; +import ProfileDropdown from './ProfileDropdown'; export default function Header() { const [mobileOpen, setMobileOpen] = useState(false); + const { isAuthenticated } = useAuthStore(); return (
- - Sign In - - - Get Started - + {isAuthenticated ? ( + + ) : ( + <> + + Sign In + + + Get Started + + + )} {/* Mobile hamburger */} @@ -57,20 +66,28 @@ export default function Header() { className="md:hidden border-t border-gray-200 px-4 py-4 space-y-3" style={{ backgroundColor: '#eef3fa' }} > - setMobileOpen(false)} - className="block text-sm font-medium text-gray-700 hover:text-gray-900 py-2 transition-colors" - > - Sign In - - setMobileOpen(false)} - className="block w-full text-center text-sm font-semibold text-white bg-[#1a3a6b] hover:bg-[#15305a] rounded-lg px-5 py-2.5 transition-colors" - > - Get Started - + {isAuthenticated ? ( +
+ +
+ ) : ( + <> + setMobileOpen(false)} + className="block text-sm font-medium text-gray-700 hover:text-gray-900 py-2 transition-colors" + > + Sign In + + setMobileOpen(false)} + className="block w-full text-center text-sm font-semibold text-white bg-[#1a3a6b] hover:bg-[#15305a] rounded-lg px-5 py-2.5 transition-colors" + > + Get Started + + + )} )}
diff --git a/components/ProfileDropdown.tsx b/components/ProfileDropdown.tsx new file mode 100644 index 0000000..cd3c041 --- /dev/null +++ b/components/ProfileDropdown.tsx @@ -0,0 +1,192 @@ +'use client'; + +import { useState, useRef, useEffect } from 'react'; +import { useRouter } from 'next/navigation'; +import Link from 'next/link'; +import { User, Settings, LayoutDashboard, LogOut, ChevronDown } from 'lucide-react'; +import { useAuthStore } from '@/store/authStore'; + +export default function ProfileDropdown() { + const [isOpen, setIsOpen] = useState(false); + const dropdownRef = useRef(null); + const router = useRouter(); + const { user, logout } = useAuthStore(); + + // Close dropdown when clicking outside + useEffect(() => { + function handleClickOutside(event: MouseEvent) { + if (dropdownRef.current && !dropdownRef.current.contains(event.target as Node)) { + setIsOpen(false); + } + } + + document.addEventListener('mousedown', handleClickOutside); + return () => { + document.removeEventListener('mousedown', handleClickOutside); + }; + }, []); + + // Handle keyboard navigation + useEffect(() => { + function handleEscape(event: KeyboardEvent) { + if (event.key === 'Escape') { + setIsOpen(false); + } + } + + document.addEventListener('keydown', handleEscape); + return () => { + document.removeEventListener('keydown', handleEscape); + }; + }, []); + + const handleLogout = async () => { + try { + // Clear auth store + logout(); + + // Clear any persisted auth data + localStorage.removeItem('auth-storage'); + + // Redirect to home + router.push('/'); + + // Close dropdown + setIsOpen(false); + } catch (error) { + console.error('Logout error:', error); + } + }; + + const menuItems = [ + { + icon: User, + label: 'Profile', + href: '/profile', + description: 'View your profile' + }, + { + icon: Settings, + label: 'Settings', + href: '/settings', + description: 'Account settings' + }, + { + icon: LayoutDashboard, + label: 'Dashboard', + href: '/dashboard', + description: 'Go to dashboard' + }, + { + icon: LogOut, + label: 'Logout', + href: '#', + description: 'Sign out of your account', + onClick: handleLogout, + isDanger: true + } + ]; + + const getUserInitials = (name: string) => { + return name + .split(' ') + .map(word => word.charAt(0).toUpperCase()) + .join('') + .slice(0, 2); + }; + + return ( +
+ {/* Trigger Button */} + + + {/* Dropdown Menu */} +
+ {/* User Info Header */} +
+

{user?.name || 'User'}

+

{user?.email}

+
+ + {/* Menu Items */} +
+ {menuItems.map((item, index) => { + const Icon = item.icon; + + if (item.onClick) { + // Logout button + return ( + + ); + } else { + // Regular navigation links + return ( + setIsOpen(false)} + className="flex items-center gap-3 px-4 py-2 text-sm text-gray-700 hover:bg-gray-50 transition-colors duration-150 focus:outline-none focus:bg-gray-50" + > + +
+

{item.label}

+

{item.description}

+
+ + ); + } + })} +
+
+
+ ); +} diff --git a/package-lock.json b/package-lock.json index 8aeed4c..d928f63 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1133,7 +1133,6 @@ "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.28.tgz", "integrity": "sha512-z9VXpC7MWrhfWipitjNdgCauoMLRdIILQsAEV+ZesIzBq/oUlxk0m3ApZuMFCXdnS4U7KrI+l3WRUEGQ8K1QKw==", "license": "MIT", - "peer": true, "dependencies": { "@types/prop-types": "*", "csstype": "^3.2.2" @@ -1605,7 +1604,6 @@ "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==", "dev": true, "license": "MIT", - "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -2107,7 +2105,6 @@ } ], "license": "MIT", - "peer": true, "dependencies": { "baseline-browser-mapping": "^2.9.0", "caniuse-lite": "^1.0.30001759", @@ -2839,7 +2836,6 @@ "deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.6.1", @@ -5664,7 +5660,6 @@ } ], "license": "MIT", - "peer": true, "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", @@ -5887,7 +5882,6 @@ "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", "license": "MIT", - "peer": true, "dependencies": { "loose-envify": "^1.1.0" }, @@ -5900,7 +5894,6 @@ "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==", "license": "MIT", - "peer": true, "dependencies": { "loose-envify": "^1.1.0", "scheduler": "^0.23.2" @@ -5914,7 +5907,6 @@ "resolved": "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.72.0.tgz", "integrity": "sha512-V4v6jubaf6JAurEaVnT9aUPKFbNtDgohj5CIgVGyPHvT9wRx5OZHVjz31GsxnPNI278XMu+ruFz+wGOscHaLKw==", "license": "MIT", - "peer": true, "engines": { "node": ">=18.0.0" }, @@ -5946,7 +5938,6 @@ "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-9.2.0.tgz", "integrity": "sha512-ROY9fvHhwOD9ySfrF0wmvu//bKCQ6AeZZq1nJNtbDC+kk5DuSuNX/n6YWYF/SYy7bSba4D4FSz8DJeKY/S/r+g==", "license": "MIT", - "peer": true, "dependencies": { "@types/use-sync-external-store": "^0.0.6", "use-sync-external-store": "^1.4.0" @@ -5992,8 +5983,7 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/redux/-/redux-5.0.1.tgz", "integrity": "sha512-M9/ELqF6fy8FwmkpnF0S3YKOqMyoWJ4+CS5Efg2ct3oY9daQvd/Pc71FpGZsVsbl3Cpb+IIcjBDUnnyBdQbq4w==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/redux-thunk": { "version": "3.1.0", @@ -6988,7 +6978,6 @@ "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=12" }, @@ -7183,7 +7172,6 @@ "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "dev": true, "license": "Apache-2.0", - "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver"