Files
edr-platform/apps/edr-passenger-web/portal/src/components/AppHeader.tsx
Abubeker Yasin 8e19089e89 Merge pull request #407 from Tria-plc/passenger/feat/iam
feat: ( iam ) add portal auth  registration, forgot/change password, …
2026-07-02 16:39:13 +03:00

215 lines
8.5 KiB
TypeScript

"use client";
import { Menu, X, Moon, Sun, HelpCircle, KeyRound, LogOut, ChevronDown } from "lucide-react";
import Link from "next/link";
import Image from "next/image";
import { useEffect, useState } from "react";
import { useAuthStore } from "@/lib/auth-store";
import ChangePasswordModal from "@/components/ChangePasswordModal";
export default function AppHeader() {
const [isOpen, setIsOpen] = useState(false);
const [isDark, setIsDark] = useState(false);
const [showUserMenu, setShowUserMenu] = useState(false);
const [showChangePassword, setShowChangePassword] = useState(false);
const { user, isAuthenticated, initialize, logout } = useAuthStore();
useEffect(() => {
const isDarkMode = document.documentElement.classList.contains("dark");
setIsDark(isDarkMode);
initialize();
}, [initialize]);
const toggleTheme = () => {
const html = document.documentElement;
const isDarkMode = html.classList.contains("dark");
if (isDarkMode) {
html.classList.remove("dark");
setIsDark(false);
localStorage.setItem("theme", "light");
} else {
html.classList.add("dark");
setIsDark(true);
localStorage.setItem("theme", "dark");
}
};
return (
<header className="sticky top-0 z-[60] bg-[rgb(20_113_76)] dark:bg-gray-900 border-b border-[rgb(16_89_60)] dark:border-gray-800 shadow-sm">
<div className="container mx-auto px-4">
<div className="max-w-6xl mx-auto">
<div className="flex items-center justify-between h-16">
{/* Logo */}
<Link
href="/"
className="flex items-center hover:opacity-80 transition-opacity"
>
<Image
src="/edr-logo.png"
alt="Ethio-Djibouti Railway"
width={140}
height={48}
className="h-14 w-auto"
priority
/>
</Link>
{/* Desktop Menu */}
<div className="hidden md:flex items-center gap-8">
<Link
href="/booking/lookup"
className="text-sm font-medium text-gray-100 hover:text-white transition-colors"
>
My Booking
</Link>
</div>
{/* Right Actions */}
<div className="flex items-center gap-2">
{/* Help Link */}
<Link
href="/help"
className="hidden sm:flex items-center justify-center p-2 text-gray-100 hover:bg-white hover:bg-opacity-10 rounded-lg transition-colors"
title="Help & FAQ"
>
<HelpCircle className="w-5 h-5" />
</Link>
{/* Theme Toggler */}
<button
onClick={toggleTheme}
className="p-2 text-gray-100 hover:bg-white hover:bg-opacity-10 rounded-lg transition-colors"
title={isDark ? "Light mode" : "Dark mode"}
>
{isDark ? (
<Sun className="w-5 h-5" />
) : (
<Moon className="w-5 h-5" />
)}
</button>
{/* Auth */}
{isAuthenticated && user ? (
<div className="relative">
<button
onClick={() => setShowUserMenu(!showUserMenu)}
className="flex items-center gap-2 p-1.5 hover:bg-white hover:bg-opacity-10 rounded-lg transition-colors"
>
<div className="flex h-8 w-8 items-center justify-center rounded-full bg-white text-[rgb(20_113_76)] font-semibold text-sm">
{user.fullName?.toUpperCase().charAt(0) || 'U'}
</div>
<ChevronDown className="hidden sm:block w-4 h-4 text-gray-100" />
</button>
{showUserMenu && (
<div className="absolute right-0 top-full mt-2 w-56 z-50 rounded-lg border border-gray-200 dark:border-gray-700 bg-white dark:bg-gray-800 shadow-xl">
<div className="p-3 border-b border-gray-200 dark:border-gray-700">
<p className="text-sm font-medium text-gray-900 dark:text-gray-100 truncate">{user.fullName}</p>
<p className="text-xs text-gray-500 dark:text-gray-400 truncate">{user.email}</p>
</div>
<div className="p-2">
<button
onClick={() => {
setShowUserMenu(false);
setShowChangePassword(true);
}}
className="flex w-full items-center gap-2 rounded-lg px-3 py-2 text-sm text-gray-700 dark:text-gray-200 hover:bg-gray-100 dark:hover:bg-gray-700 transition-colors"
>
<KeyRound className="h-4 w-4" />
Change Password
</button>
<button
onClick={() => {
setShowUserMenu(false);
logout();
}}
className="flex w-full items-center gap-2 rounded-lg px-3 py-2 text-sm text-[rgb(20_113_76)] dark:text-emerald-400 hover:bg-green-50 dark:hover:bg-green-900/20 transition-colors"
>
<LogOut className="h-4 w-4" />
Sign out
</button>
</div>
</div>
)}
</div>
) : (
<div className="hidden md:flex items-center gap-2">
<Link
href="/login"
className="px-3 py-1.5 text-sm font-medium text-gray-100 hover:bg-white hover:bg-opacity-10 rounded-lg transition-colors"
>
Sign in
</Link>
<Link
href="/register"
className="px-3 py-1.5 text-sm font-medium bg-white text-[rgb(20_113_76)] hover:bg-gray-100 rounded-lg transition-colors"
>
Register
</Link>
</div>
)}
{/* Mobile Menu Button */}
<button
onClick={() => setIsOpen(!isOpen)}
className="md:hidden p-2 text-gray-100 hover:bg-white hover:bg-opacity-10 rounded-lg"
>
{isOpen ? (
<X className="w-5 h-5" />
) : (
<Menu className="w-5 h-5" />
)}
</button>
</div>
</div>
{/* Mobile Menu */}
{isOpen && (
<div className="md:hidden border-t border-white border-opacity-20 dark:border-gray-700 py-4 space-y-2 animate-in slide-in-from-top-2 duration-200">
<Link
href="/booking/lookup"
className="block px-4 py-2 text-gray-100 dark:text-gray-300 hover:bg-white hover:bg-opacity-10 dark:hover:bg-gray-800 rounded-lg transition-colors"
onClick={() => setIsOpen(false)}
>
My Booking
</Link>
<Link
href="/help"
className="block px-4 py-2 text-gray-100 dark:text-gray-300 hover:bg-white hover:bg-opacity-10 dark:hover:bg-gray-800 rounded-lg transition-colors"
onClick={() => setIsOpen(false)}
>
Help
</Link>
{!isAuthenticated && (
<>
<Link
href="/login"
className="block px-4 py-2 text-gray-100 dark:text-gray-300 hover:bg-white hover:bg-opacity-10 dark:hover:bg-gray-800 rounded-lg transition-colors"
onClick={() => setIsOpen(false)}
>
Sign in
</Link>
<Link
href="/register"
className="block px-4 py-2 text-gray-100 dark:text-gray-300 hover:bg-white hover:bg-opacity-10 dark:hover:bg-gray-800 rounded-lg transition-colors"
onClick={() => setIsOpen(false)}
>
Register
</Link>
</>
)}
</div>
)}
</div>
</div>
<ChangePasswordModal
isOpen={showChangePassword}
onClose={() => setShowChangePassword(false)}
/>
</header>
);
}