Files
citycards-partnerweb/src/components/Sidebar.tsx
priyanshuvish b8068d1a52 first commit
2025-09-18 15:03:41 +05:30

225 lines
9.4 KiB
TypeScript

import { Home, Calendar, Users, CreditCard, HelpCircle, LogOut, ChevronDown, Settings, CalendarDays, Table2 } from "lucide-react";
import { motion } from "motion/react";
import { Avatar, AvatarFallback, AvatarImage } from "./ui/avatar";
import { useState } from "react";
interface SidebarProps {
activeItem: string;
onItemSelect: (item: string) => void;
}
export default function Sidebar({ activeItem, onItemSelect }: SidebarProps) {
const [isProfileOpen, setIsProfileOpen] = useState(false);
const [isBookingExpanded, setIsBookingExpanded] = useState(activeItem === 'booking-table' || activeItem === 'booking-calendar');
const navigationItems = [
{ id: 'dashboard', label: 'Dashboard', icon: Home },
{
id: 'booking-management',
label: 'Booking Management',
icon: Calendar,
subItems: [
{ id: 'booking-calendar', label: 'Calendar View', icon: CalendarDays },
{ id: 'booking-table', label: 'Table View', icon: Table2 }
]
},
{ id: 'staff', label: 'Staff Management', icon: Users },
{ id: 'redemptions', label: 'Redemptions', icon: CreditCard },
{ id: 'support', label: 'Support', icon: HelpCircle },
];
return (
<div className="fixed left-0 top-0 w-[280px] h-full bg-white border-r border-gray-200 flex flex-col z-50">
{/* Logo Section */}
<div className="p-6 border-b border-gray-100">
<div className="flex items-center gap-3">
<div className="w-8 h-8 bg-gradient-to-br from-blue-600 to-purple-600 rounded-lg flex items-center justify-center">
<span className="text-white font-bold text-sm">CC</span>
</div>
<span className="text-xl font-bold text-gray-900">CityCards</span>
</div>
</div>
{/* Navigation Items */}
<nav className="flex-1 p-4">
<div className="space-y-2">
{navigationItems.map((item) => {
const Icon = item.icon;
const hasActiveSubItem = item.subItems && item.subItems.some(sub => activeItem === sub.id);
const isDirectlyActive = activeItem === item.id;
const isExpanded = item.id === 'booking-management' ? isBookingExpanded : false;
const hasSubItems = item.subItems && item.subItems.length > 0;
return (
<div key={item.id} className={`${
hasActiveSubItem || isDirectlyActive
? 'bg-slate-800 rounded-lg shadow-sm mx-2'
: isExpanded && hasSubItems
? 'bg-gray-100 rounded-lg mx-2'
: ''
}`}>
<motion.button
onClick={() => {
if (hasSubItems) {
if (item.id === 'booking-management') {
setIsBookingExpanded(!isBookingExpanded);
// Default to table view when expanding
if (!isBookingExpanded) {
onItemSelect('booking-table');
}
}
} else {
onItemSelect(item.id);
}
}}
className={`w-full flex items-center gap-3 px-3 py-3 text-left transition-all duration-200 ${
hasActiveSubItem || isDirectlyActive
? 'text-white'
: isExpanded && hasSubItems
? 'text-gray-700'
: 'text-gray-700 hover:bg-gray-50 hover:text-gray-900 rounded-lg mx-2'
}`}
whileHover={{ x: (hasActiveSubItem || isDirectlyActive) ? 0 : 4 }}
whileTap={{ scale: 0.98 }}
>
<Icon className={`h-5 w-5 ${
hasActiveSubItem
? 'text-white'
: isDirectlyActive
? 'text-white'
: isExpanded && hasSubItems
? 'text-gray-600'
: 'text-gray-500'
}`} />
<span className="font-medium">{item.label}</span>
{/* Expand/Collapse arrow for sub-items */}
{hasSubItems && (
<motion.div
animate={{ rotate: isExpanded ? 90 : 0 }}
transition={{ duration: 0.2 }}
className="ml-auto"
>
<ChevronDown className={`h-4 w-4 ${
hasActiveSubItem
? 'text-white'
: isDirectlyActive
? 'text-white'
: isExpanded && hasSubItems
? 'text-gray-600'
: 'text-gray-400'
}`} />
</motion.div>
)}
</motion.button>
{/* Sub-items */}
{hasSubItems && (
<motion.div
initial={{ height: 0, opacity: 0 }}
animate={{
height: isExpanded ? 'auto' : 0,
opacity: isExpanded ? 1 : 0
}}
transition={{ duration: 0.2 }}
className="overflow-hidden"
>
<div className="px-4 pt-1 pb-3 space-y-2">
{item.subItems.map((subItem) => {
const SubIcon = subItem.icon;
const isSubActive = activeItem === subItem.id;
return (
<motion.button
key={subItem.id}
onClick={() => onItemSelect(subItem.id)}
className={`w-full flex items-center px-3 py-2 text-left transition-all duration-200 ${
isSubActive
? 'bg-white text-gray-900 rounded-lg shadow-sm'
: hasActiveSubItem
? 'text-gray-300 hover:text-white hover:bg-slate-700/50 rounded-lg'
: 'text-gray-600 hover:text-gray-800 hover:bg-gray-200/50 rounded-lg'
}`}
whileHover={{ x: isSubActive ? 0 : 2 }}
whileTap={{ scale: 0.98 }}
>
<div className="w-1.5 h-1.5 rounded-full flex-shrink-0 mr-3">
{isSubActive && (
<motion.div
initial={{ scale: 0 }}
animate={{ scale: 1 }}
className="w-full h-full bg-blue-500 rounded-full"
/>
)}
</div>
<span className="text-sm font-medium">{subItem.label}</span>
</motion.button>
);
})}
</div>
</motion.div>
)}
</div>
);
})}
</div>
</nav>
{/* Profile Section */}
<div className="p-4 border-t border-gray-100">
<motion.button
onClick={() => setIsProfileOpen(!isProfileOpen)}
className="w-full flex items-center gap-3 p-3 rounded-lg hover:bg-gray-50 transition-colors"
whileTap={{ scale: 0.98 }}
>
<Avatar className="h-10 w-10">
<AvatarImage src="/api/placeholder/40/40" alt="Profile" />
<AvatarFallback className="bg-gradient-to-br from-blue-600 to-purple-600 text-white">
KA
</AvatarFallback>
</Avatar>
<div className="flex-1 text-left">
<p className="font-medium text-gray-900">Kassandra</p>
<p className="text-sm text-gray-500">Admin</p>
</div>
<motion.div
animate={{ rotate: isProfileOpen ? 180 : 0 }}
transition={{ duration: 0.2 }}
>
<ChevronDown className="h-4 w-4 text-gray-400" />
</motion.div>
</motion.button>
{/* Profile Dropdown */}
<motion.div
initial={{ height: 0, opacity: 0 }}
animate={{
height: isProfileOpen ? 'auto' : 0,
opacity: isProfileOpen ? 1 : 0
}}
transition={{ duration: 0.2 }}
className="overflow-hidden"
>
<div className="pt-2 space-y-1">
<motion.button
whileHover={{ x: 4 }}
className="w-full flex items-center gap-3 px-3 py-2 text-left text-gray-700 hover:bg-gray-50 rounded-lg transition-colors"
>
<Settings className="h-4 w-4 text-gray-500" />
<span className="text-sm">Account Settings</span>
</motion.button>
<motion.button
whileHover={{ x: 4 }}
className="w-full flex items-center gap-3 px-3 py-2 text-left text-red-600 hover:bg-red-50 rounded-lg transition-colors"
>
<LogOut className="h-4 w-4 text-red-500" />
<span className="text-sm">Sign Out</span>
</motion.button>
</div>
</motion.div>
</div>
</div>
);
}