feat: Release CRM V2 (Clean History)

Squashed commits for V2 release including Dark Mode, Chatbot, and Landing Page enhancements.
This commit is contained in:
Satyam
2026-02-01 03:49:20 +05:30
commit 8a749d3041
42 changed files with 12518 additions and 0 deletions
+165
View File
@@ -0,0 +1,165 @@
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 Chatbot from './Chatbot';
// Rainbow Sidebar Item Component
const SidebarItem = ({ to, icon: Icon, label }) => {
const divRef = useRef(null);
const [position, setPosition] = useState({ x: 0, y: 0 });
const [opacity, setOpacity] = useState(0);
const handleMouseMove = (e) => {
if (!divRef.current) return;
const div = divRef.current;
const rect = div.getBoundingClientRect();
setPosition({ x: e.clientX - rect.left, y: e.clientY - rect.top });
};
const handleFocus = () => setOpacity(1);
const handleBlur = () => setOpacity(0);
const handleMouseEnter = () => setOpacity(1);
const handleMouseLeave = () => setOpacity(0);
return (
<NavLink
to={to}
ref={divRef}
onMouseMove={handleMouseMove}
onFocus={handleFocus}
onBlur={handleBlur}
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
? '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'
}`
}
>
{/*
Rainbow Border Layer
- Only visible on Hover (opacity controlled by state)
*/}
<div
className='pointer-events-none absolute -inset-px opacity-0 transition duration-300 z-0'
style={{
opacity,
background: `conic-gradient(from 0deg, #ff0000, #ff8800, #ffff00, #00ff00, #00ffff, #0000ff, #ff00ff, #ff0000)`,
WebkitMaskImage: `radial-gradient(150px circle at ${position.x}px ${position.y}px, black, transparent 80%)`,
maskImage: `radial-gradient(150px circle at ${position.x}px ${position.y}px, black, transparent 80%)`,
}}
/>
{/* Inner Mask (The "Button" surface) */}
<div className={`absolute inset-[1px] rounded-[11px] z-0 pointer-events-none transition-colors duration-300 ${opacity > 0 ? 'bg-zinc-50 dark:bg-[#121214]' : 'bg-transparent'
}`} />
{/* 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>
{/* 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'}`} />
</NavLink>
);
};
const Layout = () => {
const { user, logout } = useAuth();
const navigate = useNavigate();
const location = useLocation();
const handleLogout = () => {
logout();
navigate('/login');
};
// Determine standard layout vs full screen for Landing/Login
const isPublic = ['/', '/login'].includes(location.pathname);
if (isPublic) {
return <Outlet />;
}
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">
{/* 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>
</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>
{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" />
</>
) : null}
{user?.role === 'ADMIN' && (
<SidebarItem to="/admin/schedule" icon={Calendar} label="Team Schedule" />
)}
{/* 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>
</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">
<User size={18} />
</div>
<div className="flex-1 min-w-0">
<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>
</div>
<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"
>
<LogOut size={14} />
<span>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 />
</main>
<Chatbot />
</div>
);
};
export default Layout;