Initial commit: Open sourcing all of the Maple Open Technologies code.
This commit is contained in:
commit
755d54a99d
2010 changed files with 448675 additions and 0 deletions
302
web/maplefile-frontend/src/components/Navigation.jsx
Normal file
302
web/maplefile-frontend/src/components/Navigation.jsx
Normal file
|
|
@ -0,0 +1,302 @@
|
|||
// File: src/components/Navigation.jsx
|
||||
import React, { useState, useEffect } from "react";
|
||||
import { Link, useNavigate, useLocation } from "react-router";
|
||||
import { useAuth } from "../services/Services";
|
||||
import {
|
||||
LockClosedIcon,
|
||||
HomeIcon,
|
||||
FolderIcon,
|
||||
UserIcon,
|
||||
ArrowRightOnRectangleIcon,
|
||||
Bars3Icon,
|
||||
XMarkIcon,
|
||||
ChevronDownIcon,
|
||||
} from "@heroicons/react/24/outline";
|
||||
|
||||
const Navigation = () => {
|
||||
const navigate = useNavigate();
|
||||
const location = useLocation();
|
||||
const { authManager } = useAuth();
|
||||
const [isMobileMenuOpen, setIsMobileMenuOpen] = useState(false);
|
||||
const [isProfileMenuOpen, setIsProfileMenuOpen] = useState(false);
|
||||
const [isScrolled, setIsScrolled] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
const handleScroll = () => {
|
||||
setIsScrolled(window.scrollY > 10);
|
||||
};
|
||||
|
||||
window.addEventListener("scroll", handleScroll);
|
||||
return () => window.removeEventListener("scroll", handleScroll);
|
||||
}, []);
|
||||
|
||||
const handleLogout = () => {
|
||||
if (authManager?.logout) {
|
||||
authManager.logout();
|
||||
}
|
||||
sessionStorage.clear();
|
||||
localStorage.removeItem("mapleopentech_access_token");
|
||||
localStorage.removeItem("mapleopentech_refresh_token");
|
||||
localStorage.removeItem("mapleopentech_user_email");
|
||||
navigate("/");
|
||||
};
|
||||
|
||||
const isActive = (path) => {
|
||||
if (path === "/file-manager") {
|
||||
return location.pathname.startsWith("/file-manager");
|
||||
}
|
||||
return location.pathname === path;
|
||||
};
|
||||
|
||||
const mainNavItems = [
|
||||
{
|
||||
name: "Dashboard",
|
||||
path: "/dashboard",
|
||||
icon: HomeIcon,
|
||||
description: "Overview",
|
||||
},
|
||||
{
|
||||
name: "My Files",
|
||||
path: "/file-manager",
|
||||
icon: FolderIcon,
|
||||
description: "Your files",
|
||||
},
|
||||
];
|
||||
|
||||
const userEmail = authManager?.getCurrentUserEmail?.() || "User";
|
||||
const userInitial = userEmail.charAt(0).toUpperCase();
|
||||
|
||||
return (
|
||||
<>
|
||||
<nav
|
||||
className={`sticky top-0 z-50 transition-all duration-300 ${
|
||||
isScrolled
|
||||
? "bg-white/95 backdrop-blur-md shadow-lg border-b border-gray-200/50"
|
||||
: "bg-white border-b border-gray-200"
|
||||
}`}
|
||||
>
|
||||
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||
<div className="flex justify-between items-center h-16">
|
||||
{/* Logo */}
|
||||
<Link to="/dashboard" className="flex items-center space-x-3 group">
|
||||
<div className="relative">
|
||||
<div className="absolute inset-0 bg-gradient-to-r from-red-600 to-red-800 rounded-xl opacity-0 group-hover:opacity-100 blur-xl transition-opacity duration-300"></div>
|
||||
<div className="relative flex items-center justify-center h-10 w-10 bg-gradient-to-br from-red-700 to-red-800 rounded-xl shadow-md group-hover:shadow-lg transform group-hover:scale-105 transition-all duration-200">
|
||||
<LockClosedIcon className="h-5 w-5 text-white" />
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<span className="text-xl font-bold text-gray-900 group-hover:text-red-800 transition-colors duration-200">
|
||||
MapleFile
|
||||
</span>
|
||||
<span className="hidden sm:block text-xs text-gray-500">
|
||||
Secure Storage
|
||||
</span>
|
||||
</div>
|
||||
</Link>
|
||||
|
||||
{/* Desktop Navigation */}
|
||||
<div className="hidden lg:flex items-center space-x-1">
|
||||
{mainNavItems.map((item) => (
|
||||
<Link
|
||||
key={item.path}
|
||||
to={item.path}
|
||||
className={`relative px-4 py-2 rounded-lg text-sm font-medium transition-all duration-200 group ${
|
||||
isActive(item.path)
|
||||
? "text-white"
|
||||
: "text-gray-700 hover:text-gray-900 hover:bg-gray-100"
|
||||
}`}
|
||||
>
|
||||
{isActive(item.path) && (
|
||||
<div className="absolute inset-0 bg-gradient-to-r from-red-700 to-red-800 rounded-lg"></div>
|
||||
)}
|
||||
<div className="relative flex items-center space-x-2">
|
||||
<item.icon
|
||||
className={`h-4 w-4 ${
|
||||
isActive(item.path)
|
||||
? "text-white"
|
||||
: "text-gray-500 group-hover:text-gray-700"
|
||||
}`}
|
||||
/>
|
||||
<span>{item.name}</span>
|
||||
</div>
|
||||
{!isActive(item.path) && (
|
||||
<div className="absolute bottom-0 left-0 right-0 h-0.5 bg-red-700 transform scale-x-0 group-hover:scale-x-100 transition-transform duration-200"></div>
|
||||
)}
|
||||
</Link>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* User Menu */}
|
||||
<div className="hidden lg:flex items-center space-x-4">
|
||||
{/* Profile Dropdown */}
|
||||
<div className="relative">
|
||||
<button
|
||||
onClick={() => setIsProfileMenuOpen(!isProfileMenuOpen)}
|
||||
className="flex items-center space-x-3 p-2 hover:bg-gray-100 rounded-lg transition-all duration-200"
|
||||
>
|
||||
<div className="h-8 w-8 bg-gradient-to-br from-red-600 to-red-800 rounded-lg flex items-center justify-center text-white font-semibold text-sm shadow-sm">
|
||||
{userInitial}
|
||||
</div>
|
||||
<div className="hidden md:block text-left">
|
||||
<p className="text-sm font-medium text-gray-900">
|
||||
{userEmail.split("@")[0]}
|
||||
</p>
|
||||
<p className="text-xs text-gray-500">Free Plan</p>
|
||||
</div>
|
||||
<ChevronDownIcon
|
||||
className={`h-4 w-4 text-gray-500 transition-transform duration-200 ${
|
||||
isProfileMenuOpen ? "rotate-180" : ""
|
||||
}`}
|
||||
/>
|
||||
</button>
|
||||
|
||||
{/* Dropdown Menu */}
|
||||
{isProfileMenuOpen && (
|
||||
<>
|
||||
<div
|
||||
className="fixed inset-0 z-10"
|
||||
onClick={() => setIsProfileMenuOpen(false)}
|
||||
></div>
|
||||
<div className="absolute right-0 mt-2 w-56 bg-white rounded-xl shadow-xl border border-gray-200 py-2 z-20 animate-fade-in-down">
|
||||
<div className="px-4 py-3 border-b border-gray-100">
|
||||
<p className="text-sm font-medium text-gray-900">
|
||||
{userEmail}
|
||||
</p>
|
||||
<p className="text-xs text-gray-500 mt-0.5">
|
||||
Free Plan • 5GB Storage
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<Link
|
||||
to="/me"
|
||||
onClick={() => setIsProfileMenuOpen(false)}
|
||||
className="flex items-center px-4 py-2.5 text-sm text-gray-700 hover:bg-gray-50 transition-colors duration-200"
|
||||
>
|
||||
<UserIcon className="h-4 w-4 mr-3 text-gray-500" />
|
||||
My Profile
|
||||
</Link>
|
||||
|
||||
<div className="border-t border-gray-100 mt-2 pt-2">
|
||||
<button
|
||||
onClick={() => {
|
||||
setIsProfileMenuOpen(false);
|
||||
handleLogout();
|
||||
}}
|
||||
className="w-full flex items-center px-4 py-2.5 text-sm text-red-700 hover:bg-red-50 transition-colors duration-200"
|
||||
>
|
||||
<ArrowRightOnRectangleIcon className="h-4 w-4 mr-3 text-red-600" />
|
||||
Sign Out
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Mobile Menu Button */}
|
||||
<button
|
||||
onClick={() => setIsMobileMenuOpen(!isMobileMenuOpen)}
|
||||
className="lg:hidden p-2 rounded-lg text-gray-700 hover:text-gray-900 hover:bg-gray-100 transition-all duration-200"
|
||||
>
|
||||
{isMobileMenuOpen ? (
|
||||
<XMarkIcon className="h-6 w-6" />
|
||||
) : (
|
||||
<Bars3Icon className="h-6 w-6" />
|
||||
)}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
{/* Mobile Menu */}
|
||||
<div
|
||||
className={`lg:hidden fixed inset-0 z-40 transition-opacity duration-300 ${
|
||||
isMobileMenuOpen
|
||||
? "opacity-100 pointer-events-auto"
|
||||
: "opacity-0 pointer-events-none"
|
||||
}`}
|
||||
>
|
||||
<div
|
||||
className="absolute inset-0 bg-gray-900/50 backdrop-blur-sm"
|
||||
onClick={() => setIsMobileMenuOpen(false)}
|
||||
></div>
|
||||
|
||||
<div
|
||||
className={`absolute right-0 top-0 h-full w-72 bg-white shadow-2xl transform transition-transform duration-300 ${
|
||||
isMobileMenuOpen ? "translate-x-0" : "translate-x-full"
|
||||
}`}
|
||||
>
|
||||
<div className="p-6">
|
||||
<div className="flex items-center justify-between mb-8">
|
||||
<h2 className="text-lg font-semibold text-gray-900">Menu</h2>
|
||||
<button
|
||||
onClick={() => setIsMobileMenuOpen(false)}
|
||||
className="p-2 rounded-lg text-gray-500 hover:text-gray-700 hover:bg-gray-100 transition-all duration-200"
|
||||
>
|
||||
<XMarkIcon className="h-5 w-5" />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* Mobile User Info */}
|
||||
<div className="flex items-center space-x-3 p-4 bg-gray-50 rounded-xl mb-6">
|
||||
<div className="h-10 w-10 bg-gradient-to-br from-red-600 to-red-800 rounded-lg flex items-center justify-center text-white font-semibold">
|
||||
{userInitial}
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-sm font-medium text-gray-900">
|
||||
{userEmail.split("@")[0]}
|
||||
</p>
|
||||
<p className="text-xs text-gray-500">{userEmail}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Mobile Navigation Items */}
|
||||
<div className="space-y-1">
|
||||
{mainNavItems.map((item) => (
|
||||
<Link
|
||||
key={item.path}
|
||||
to={item.path}
|
||||
onClick={() => setIsMobileMenuOpen(false)}
|
||||
className={`flex items-center space-x-3 px-4 py-3 rounded-lg text-base font-medium transition-all duration-200 ${
|
||||
isActive(item.path)
|
||||
? "bg-gradient-to-r from-red-700 to-red-800 text-white"
|
||||
: "text-gray-700 hover:bg-gray-100 hover:text-gray-900"
|
||||
}`}
|
||||
>
|
||||
<item.icon className="h-5 w-5" />
|
||||
<span>{item.name}</span>
|
||||
</Link>
|
||||
))}
|
||||
|
||||
<Link
|
||||
to="/me"
|
||||
onClick={() => setIsMobileMenuOpen(false)}
|
||||
className="flex items-center space-x-3 px-4 py-3 rounded-lg text-base font-medium text-gray-700 hover:bg-gray-100 hover:text-gray-900 transition-all duration-200"
|
||||
>
|
||||
<UserIcon className="h-5 w-5" />
|
||||
<span>Profile</span>
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
<div className="absolute bottom-0 left-0 right-0 p-6 border-t border-gray-200">
|
||||
<button
|
||||
onClick={() => {
|
||||
setIsMobileMenuOpen(false);
|
||||
handleLogout();
|
||||
}}
|
||||
className="w-full flex items-center justify-center space-x-3 px-4 py-3 rounded-lg text-base font-medium text-white bg-gradient-to-r from-red-700 to-red-800 hover:from-red-800 hover:to-red-900 transition-all duration-200"
|
||||
>
|
||||
<ArrowRightOnRectangleIcon className="h-5 w-5" />
|
||||
<span>Sign Out</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default Navigation;
|
||||
Loading…
Add table
Add a link
Reference in a new issue