feat: Enhance UX with animations, loaders, fixes, and doc updates

- Animations: Added global page transitions and smooth sidebar toggle
- Loaders: Implemented branded Loader component and page loading states
- Fixes: Resolved hook order violations in Maps/Dashboard; fixed Layout import
- Docs: Updated README with correct demo credentials and feature list
This commit is contained in:
Satyam
2026-02-01 18:48:47 +05:30
parent 719172372e
commit 96f93d0c93
14 changed files with 209 additions and 63 deletions
+51 -34
View File
@@ -1,12 +1,14 @@
import React, { useRef, useState } from 'react';
import { Outlet, NavLink, useNavigate, useLocation } from 'react-router-dom';
import { useAuth } from '../context/AuthContext';
import { LayoutDashboard, Map, Calendar, LogOut, User, Home, MessageSquare } from 'lucide-react';
import { LayoutDashboard, Map, Calendar, LogOut, User, Home, MessageSquare, ChevronLeft, ChevronRight } from 'lucide-react';
import PageTransition from './PageTransition';
import Chatbot from './Chatbot';
import Logo from '../assets/images/LynkedUp_Pro_F_logo_Y.png';
// Rainbow Sidebar Item Component
const SidebarItem = ({ to, icon: Icon, label }) => {
const SidebarItem = ({ to, icon: Icon, label, isCollapsed }) => {
const divRef = useRef(null);
const [position, setPosition] = useState({ x: 0, y: 0 });
const [opacity, setOpacity] = useState(0);
@@ -33,11 +35,12 @@ const SidebarItem = ({ to, icon: Icon, label }) => {
onMouseEnter={handleMouseEnter}
onMouseLeave={handleMouseLeave}
className={({ isActive }) =>
`relative flex items-center space-x-3 px-4 py-3 rounded-xl transition-all duration-300 group overflow-hidden mb-1 ${isActive
`relative flex items-center ${isCollapsed ? 'justify-center' : 'space-x-3'} px-4 py-3 rounded-xl transition-all duration-300 group overflow-hidden mb-1 ${isActive
? 'text-zinc-900 dark:text-white bg-black/5 dark:bg-white/5 shadow-sm dark:shadow-black/10'
: 'text-zinc-500 hover:text-zinc-900 dark:hover:text-white bg-transparent hover:bg-black/5 dark:hover:bg-white/5'
}`
}
title={isCollapsed ? label : ""}
>
{/*
Rainbow Border Layer
@@ -58,13 +61,15 @@ const SidebarItem = ({ to, icon: Icon, label }) => {
}`} />
{/* Content (Z-index to sit above the mask) */}
<div className="relative z-10 flex items-center space-x-3 w-full">
<Icon size={18} className="transition-transform group-hover:scale-110 duration-300" />
<span className="font-medium text-sm tracking-wide">{label}</span>
<div className={`relative z-10 flex items-center ${isCollapsed ? 'justify-center' : 'space-x-3'} w-full`}>
<Icon size={18} className="transition-transform group-hover:scale-110 duration-300 shrink-0" />
<span className={`font-medium text-sm tracking-wide whitespace-nowrap overflow-hidden transition-all duration-300 ${isCollapsed ? 'w-0 opacity-0' : 'w-auto opacity-100 ml-3'}`}>{label}</span>
</div>
{/* Active Indicator Dot */}
<div className={({ isActive }) => `absolute right-2 w-1.5 h-1.5 rounded-full bg-zinc-900 dark:bg-white transition-all duration-300 ${isActive ? 'opacity-100 scale-100' : 'opacity-0 scale-0'}`} />
{!isCollapsed && (
<div className={({ isActive }) => `absolute right-2 w-1.5 h-1.5 rounded-full bg-zinc-900 dark:bg-white transition-all duration-300 ${isActive ? 'opacity-100 scale-100' : 'opacity-0 scale-0'}`} />
)}
</NavLink>
);
};
@@ -74,6 +79,8 @@ const Layout = () => {
const navigate = useNavigate();
const location = useLocation();
const [isCollapsed, setIsCollapsed] = useState(false);
const handleLogout = () => {
logout();
navigate('/login');
@@ -83,60 +90,67 @@ const Layout = () => {
const isPublic = ['/', '/login'].includes(location.pathname);
if (isPublic) {
return <Outlet />;
return (
<PageTransition>
<Outlet />
</PageTransition>
);
}
return (
<div className="flex h-screen bg-zinc-50 dark:bg-[#050505] text-zinc-900 dark:text-white overflow-hidden font-sans selection:bg-blue-500/20 dark:selection:bg-white/20 transition-colors duration-300">
{/* Sidebar */}
{/* Enhanced glassmorphism with light/dark support */}
<aside className="w-72 bg-white/60 dark:bg-zinc-900/60 backdrop-blur-2xl border-r border-zinc-200 dark:border-white/5 flex flex-col shrink-0 z-30 shadow-[4px_0_24px_rgba(0,0,0,0.05)] dark:shadow-[4px_0_24px_rgba(0,0,0,0.4)] relative">
<aside className={`${isCollapsed ? 'w-20' : 'w-72'} bg-white/60 dark:bg-zinc-900/60 backdrop-blur-2xl border-r border-zinc-200 dark:border-white/5 flex flex-col shrink-0 z-30 shadow-[4px_0_24px_rgba(0,0,0,0.05)] dark:shadow-[4px_0_24px_rgba(0,0,0,0.4)] relative transition-all duration-300 ease-in-out`}>
{/* Collapse Toggle Button */}
<button
onClick={() => setIsCollapsed(!isCollapsed)}
className="absolute -right-3 top-9 w-6 h-6 bg-white dark:bg-zinc-800 border border-zinc-200 dark:border-white/10 rounded-full flex items-center justify-center text-zinc-500 hover:text-zinc-900 dark:hover:text-white shadow-sm z-50 transition-colors"
>
{isCollapsed ? <ChevronRight size={14} /> : <ChevronLeft size={14} />}
</button>
{/* Noise texture */}
<div className="absolute inset-0 bg-[url('https://grainy-gradients.vercel.app/noise.svg')] opacity-[0.03] dark:opacity-5 pointer-events-none mix-blend-overlay"></div>
<div className="p-6 relative z-10">
<div className="flex items-center space-x-3 text-zinc-900 dark:text-white mb-10 pl-2">
<div className="w-10 h-10 rounded-xl bg-gradient-to-br from-zinc-800 to-black dark:from-white dark:to-zinc-400 flex items-center justify-center shadow-lg shadow-black/10 dark:shadow-white/10 ring-1 ring-black/5 dark:ring-white/20">
<Home size={20} className="text-white dark:text-black" />
</div>
<div>
<span className="text-lg font-bold tracking-tight block leading-none">Plano</span>
<span className="text-xs text-zinc-500 dark:text-zinc-400 font-medium tracking-widest uppercase">Realty</span>
<div className="p-4 relative z-10">
<div className={`flex items-center ${isCollapsed ? 'justify-center' : 'space-x-3'} text-zinc-900 dark:text-white mb-10 pl-2 transition-all duration-300`}>
<img src={Logo} alt="LynkedUp Pro" className="w-10 h-10 object-contain shrink-0" />
<div className={`overflow-hidden whitespace-nowrap transition-all duration-300 ${isCollapsed ? 'w-0 opacity-0' : 'w-auto opacity-100'}`}>
<span className="text-lg font-bold tracking-tight block leading-none">LynkedUp</span>
<span className="text-xs text-zinc-500 dark:text-zinc-400 font-medium tracking-widest uppercase">Pro</span>
</div>
</div>
<nav className="space-y-1">
<div className="text-[10px] font-bold text-zinc-400 dark:text-zinc-500 uppercase tracking-widest mb-4 pl-4 pt-2">Menu</div>
<div className={`text-[10px] font-bold text-zinc-400 dark:text-zinc-500 uppercase tracking-widest mb-4 pl-4 pt-2 whitespace-nowrap overflow-hidden transition-all duration-300 ${isCollapsed ? 'opacity-0 h-0 scale-y-0' : 'opacity-100 h-auto scale-y-100'}`}>Menu</div>
{user?.role === 'FIELD_AGENT' || user?.role === 'ADMIN' ? (
<>
<SidebarItem to="/emp/fa/dashboard" icon={LayoutDashboard} label="Dashboard" />
<SidebarItem to="/emp/fa/maps" icon={Map} label="Territory Map" />
<SidebarItem to="/emp/fa/dashboard" icon={LayoutDashboard} label="Dashboard" isCollapsed={isCollapsed} />
<SidebarItem to="/emp/fa/maps" icon={Map} label="Territory Map" isCollapsed={isCollapsed} />
</>
) : null}
{user?.role === 'ADMIN' && (
<SidebarItem to="/admin/schedule" icon={Calendar} label="Team Schedule" />
<SidebarItem to="/admin/schedule" icon={Calendar} label="Team Schedule" isCollapsed={isCollapsed} />
)}
{/* Common Links */}
<div className="pt-6 mt-6 border-t border-zinc-200 dark:border-white/5">
<div className="text-[10px] font-bold text-zinc-400 dark:text-zinc-500 uppercase tracking-widest mb-4 pl-4">System</div>
<SidebarItem to="/" icon={MessageSquare} label="Public Site" />
<div className={`pt-6 mt-6 border-t border-zinc-200 dark:border-white/5 transition-all duration-300 ${isCollapsed ? 'flex justify-center' : ''}`}>
<div className={`text-[10px] font-bold text-zinc-400 dark:text-zinc-500 uppercase tracking-widest mb-4 pl-4 whitespace-nowrap overflow-hidden transition-all duration-300 ${isCollapsed ? 'opacity-0 h-0 scale-y-0' : 'opacity-100 h-auto scale-y-100'}`}>System</div>
<SidebarItem to="/" icon={MessageSquare} label="Public Site" isCollapsed={isCollapsed} />
</div>
</nav>
</div>
<div className="mt-auto p-4 border-t border-zinc-200 dark:border-white/5 bg-zinc-50/50 dark:bg-black/20 relative z-10">
<div className="flex items-center space-x-3 p-3 mb-3 rounded-xl bg-white/40 dark:bg-white/5 border border-zinc-200 dark:border-white/5 backdrop-blur-md transition-colors hover:bg-white/60 dark:hover:bg-white/10 group cursor-default">
<div className="w-10 h-10 rounded-full bg-zinc-200 dark:bg-zinc-800 flex items-center justify-center text-zinc-600 dark:text-zinc-400 border border-zinc-300 dark:border-white/5 group-hover:border-zinc-400 dark:group-hover:border-white/20 transition-colors">
<div className={`flex items-center ${isCollapsed ? 'justify-center' : 'space-x-3'} p-3 mb-3 rounded-xl bg-white/40 dark:bg-white/5 border border-zinc-200 dark:border-white/5 backdrop-blur-md transition-all duration-300 hover:bg-white/60 dark:hover:bg-white/10 group cursor-default`}>
<div className="w-10 h-10 rounded-full bg-zinc-200 dark:bg-zinc-800 flex items-center justify-center text-zinc-600 dark:text-zinc-400 border border-zinc-300 dark:border-white/5 group-hover:border-zinc-400 dark:group-hover:border-white/20 transition-colors shrink-0">
<User size={18} />
</div>
<div className="flex-1 min-w-0">
<div className={`flex-1 min-w-0 overflow-hidden transition-all duration-300 ${isCollapsed ? 'w-0 opacity-0' : 'w-auto opacity-100'}`}>
<p className="text-sm font-bold text-zinc-900 dark:text-white truncate">{user?.name}</p>
<p className="text-[10px] text-zinc-500 dark:text-zinc-400 truncate capitalize tracking-wide font-medium">{user?.role?.replace('_', ' ').toLowerCase()}</p>
</div>
@@ -144,17 +158,20 @@ const Layout = () => {
<button
onClick={handleLogout}
className="flex items-center justify-center space-x-2 w-full px-4 py-3 text-xs font-bold uppercase tracking-wider text-zinc-500 dark:text-zinc-400 hover:text-red-600 dark:hover:text-red-400 hover:bg-red-500/10 rounded-xl transition-all border border-transparent hover:border-red-500/20"
className={`flex items-center ${isCollapsed ? 'justify-center' : 'space-x-2'} w-full px-4 py-3 text-xs font-bold uppercase tracking-wider text-zinc-500 dark:text-zinc-400 hover:text-red-600 dark:hover:text-red-400 hover:bg-red-500/10 rounded-xl transition-all border border-transparent hover:border-red-500/20`}
title={isCollapsed ? "Sign Out" : ""}
>
<LogOut size={14} />
<span>Sign Out</span>
<LogOut size={14} className="shrink-0" />
<span className={`whitespace-nowrap overflow-hidden transition-all duration-300 ${isCollapsed ? 'w-0 opacity-0' : 'w-auto opacity-100'}`}>Sign Out</span>
</button>
</div>
</aside>
{/* Main Content */}
<main className="flex-1 overflow-auto relative bg-zinc-50 dark:bg-[#09090b] scroll-smooth transition-colors duration-300">
<Outlet />
<PageTransition>
<Outlet />
</PageTransition>
</main>
<Chatbot />