| 1 | import { useEffect, useRef, useState } from "react";
|
|---|
| 2 | import { Link } from "react-router-dom";
|
|---|
| 3 | import { toast } from "react-toastify";
|
|---|
| 4 | import axiosInstance, { baseURL } from "../api/axiosInstance";
|
|---|
| 5 | import Logo from "../assets/logo-finkwave.png";
|
|---|
| 6 | import { useAuth } from "../context/authContext";
|
|---|
| 7 |
|
|---|
| 8 | interface NavProps {
|
|---|
| 9 | isSidebarOpen?: boolean;
|
|---|
| 10 | onToggleSidebar?: () => void;
|
|---|
| 11 | }
|
|---|
| 12 |
|
|---|
| 13 | const Nav = ({ isSidebarOpen = false, onToggleSidebar }: NavProps) => {
|
|---|
| 14 | const { user, setUser, isAuthLoading } = useAuth();
|
|---|
| 15 | const [isDropdownOpen, setIsDropdownOpen] = useState(false);
|
|---|
| 16 | const dropdownRef = useRef<HTMLDivElement>(null);
|
|---|
| 17 |
|
|---|
| 18 | const handleLogout = async (e: React.MouseEvent<HTMLButtonElement>) => {
|
|---|
| 19 | e.preventDefault();
|
|---|
| 20 | try {
|
|---|
| 21 | await axiosInstance.post("/auth/logout");
|
|---|
| 22 | setUser(undefined);
|
|---|
| 23 | setIsDropdownOpen(false);
|
|---|
| 24 | toast.success("Logout successful!");
|
|---|
| 25 | } catch (error) {
|
|---|
| 26 | console.error("Logout failed:", error);
|
|---|
| 27 | toast.error("Logout failed!");
|
|---|
| 28 | }
|
|---|
| 29 | };
|
|---|
| 30 |
|
|---|
| 31 | useEffect(() => {
|
|---|
| 32 | const handleClickOutside = (event: MouseEvent) => {
|
|---|
| 33 | if (
|
|---|
| 34 | dropdownRef.current &&
|
|---|
| 35 | !dropdownRef.current.contains(event.target as Node)
|
|---|
| 36 | ) {
|
|---|
| 37 | setIsDropdownOpen(false);
|
|---|
| 38 | }
|
|---|
| 39 | };
|
|---|
| 40 |
|
|---|
| 41 | document.addEventListener("mousedown", handleClickOutside);
|
|---|
| 42 | return () => {
|
|---|
| 43 | document.removeEventListener("mousedown", handleClickOutside);
|
|---|
| 44 | };
|
|---|
| 45 | }, []);
|
|---|
| 46 |
|
|---|
| 47 | return (
|
|---|
| 48 | <div
|
|---|
| 49 | className={`bg-gray-800 p-4 flex justify-between items-center fixed top-0 right-0 z-50 transition-all duration-300 ${
|
|---|
| 50 | isSidebarOpen ? "left-64" : "left-0"
|
|---|
| 51 | }`}
|
|---|
| 52 | >
|
|---|
| 53 | <div className="flex items-center gap-4">
|
|---|
| 54 | {onToggleSidebar && user && (
|
|---|
| 55 | <button
|
|---|
| 56 | onClick={onToggleSidebar}
|
|---|
| 57 | className="text-white hover:text-[#1db954] transition-colors p-2"
|
|---|
| 58 | aria-label="Toggle sidebar"
|
|---|
| 59 | >
|
|---|
| 60 | <svg
|
|---|
| 61 | className="w-6 h-6"
|
|---|
| 62 | fill="none"
|
|---|
| 63 | stroke="currentColor"
|
|---|
| 64 | viewBox="0 0 24 24"
|
|---|
| 65 | >
|
|---|
| 66 | <path
|
|---|
| 67 | strokeLinecap="round"
|
|---|
| 68 | strokeLinejoin="round"
|
|---|
| 69 | strokeWidth={2}
|
|---|
| 70 | d="M4 6h16M4 12h16M4 18h16"
|
|---|
| 71 | />
|
|---|
| 72 | </svg>
|
|---|
| 73 | </button>
|
|---|
| 74 | )}
|
|---|
| 75 | <Link to="/" className="text-white text-lg font-semibold">
|
|---|
| 76 | <img src={Logo} alt="Finkwave Logo" className="h-12 w-auto" />
|
|---|
| 77 | </Link>
|
|---|
| 78 | </div>
|
|---|
| 79 |
|
|---|
| 80 | <div className="flex items-center space-x-4">
|
|---|
| 81 | {!isAuthLoading && (
|
|---|
| 82 | <div className="flex items-center space-x-3">
|
|---|
| 83 | {user ? (
|
|---|
| 84 | <div className="relative" ref={dropdownRef}>
|
|---|
| 85 | <button
|
|---|
| 86 | onClick={() => setIsDropdownOpen(!isDropdownOpen)}
|
|---|
| 87 | className="focus:outline-none focus:ring-2 focus:ring-blue-500 rounded-full"
|
|---|
| 88 | >
|
|---|
| 89 | {user.profilePhoto ? (
|
|---|
| 90 | <img
|
|---|
| 91 | src={`${baseURL}/${user.profilePhoto}`}
|
|---|
| 92 | alt={`${user.username}'s profile`}
|
|---|
| 93 | className="w-10 h-10 rounded-full object-cover cursor-pointer hover:ring-2 hover:ring-blue-400 transition-all"
|
|---|
| 94 | />
|
|---|
| 95 | ) : (
|
|---|
| 96 | <div className="w-10 h-10 bg-blue-500 rounded-full flex items-center justify-center cursor-pointer hover:ring-2 hover:ring-blue-400 transition-all">
|
|---|
| 97 | <span className="text-white text-lg font-semibold">
|
|---|
| 98 | {user.username.charAt(0).toUpperCase()}
|
|---|
| 99 | </span>
|
|---|
| 100 | </div>
|
|---|
| 101 | )}
|
|---|
| 102 | </button>
|
|---|
| 103 |
|
|---|
| 104 | {isDropdownOpen && (
|
|---|
| 105 | <div className="absolute right-0 mt-2 w-48 bg-gray-700 rounded-lg shadow-lg py-1 z-50">
|
|---|
| 106 | <Link
|
|---|
| 107 | to="/me"
|
|---|
| 108 | className="block px-4 py-2 text-sm text-white hover:bg-gray-600 transition-colors"
|
|---|
| 109 | onClick={() => setIsDropdownOpen(false)}
|
|---|
| 110 | >
|
|---|
| 111 | Account
|
|---|
| 112 | </Link>
|
|---|
| 113 | <button
|
|---|
| 114 | onClick={handleLogout}
|
|---|
| 115 | className="w-full text-left px-4 py-2 text-sm text-white hover:bg-gray-600 transition-colors"
|
|---|
| 116 | >
|
|---|
| 117 | Log out
|
|---|
| 118 | </button>
|
|---|
| 119 | </div>
|
|---|
| 120 | )}
|
|---|
| 121 | </div>
|
|---|
| 122 | ) : (
|
|---|
| 123 | <div className="flex items-center space-x-4">
|
|---|
| 124 | <Link
|
|---|
| 125 | to="/login"
|
|---|
| 126 | className="bg-blue-500 hover:bg-blue-600 text-white px-4 py-2 rounded-lg text-sm
|
|---|
| 127 | font-medium transition-colors duration-200 cursor-pointer"
|
|---|
| 128 | >
|
|---|
| 129 | Login
|
|---|
| 130 | </Link>
|
|---|
| 131 | <Link
|
|---|
| 132 | to="/register"
|
|---|
| 133 | className="bg-green-500 hover:bg-green-600 text-white px-4 py-2 rounded-lg text-sm
|
|---|
| 134 | font-medium transition-colors duration-200 cursor-pointer"
|
|---|
| 135 | >
|
|---|
| 136 | Register
|
|---|
| 137 | </Link>
|
|---|
| 138 | </div>
|
|---|
| 139 | )}
|
|---|
| 140 | </div>
|
|---|
| 141 | )}
|
|---|
| 142 | </div>
|
|---|
| 143 | </div>
|
|---|
| 144 | );
|
|---|
| 145 | };
|
|---|
| 146 |
|
|---|
| 147 | export default Nav;
|
|---|