import { NavLink, useLocation, useNavigate } from 'react-router-dom' import { useState, useEffect } from 'react' import { CalendarDays, BookOpen, BedDouble, Globe, Settings, FileText, LayoutDashboard, Building2, Users, X, Hotel, Puzzle, CalendarRange, Map, LayoutGrid, UserCog, UsersRound, TrendingUp, Tag, Award, Wrench, Utensils, CalendarClock, Zap, ChevronRight, Star, CreditCard, } from 'lucide-react' import { useAuth } from '../../contexts/AuthContext' import { useModules } from '../../contexts/ModulesContext' import { MODULES_DATA } from '../../data/modulesData' import { cn } from '../../lib/utils' interface SidebarProps { open: boolean onClose: () => void } // ── Favorites helpers ────────────────────────────────────────────────────── function loadFavorites(): string[] { try { return JSON.parse(localStorage.getItem('sidebarFavorites') || '[]') } catch { return [] } } function saveFavorites(favs: string[]) { localStorage.setItem('sidebarFavorites', JSON.stringify(favs)) } // ── Group state helpers ──────────────────────────────────────────────────── type GroupKey = 'basics' | 'prices' | 'service' | 'management' | 'settingsGroup' | 'devGroup' const DEFAULT_GROUPS: Record = { basics: true, prices: true, service: true, management: true, settingsGroup: true, devGroup: true, } function loadGroups(): Record { try { const stored = JSON.parse(localStorage.getItem('sidebarGroups') || '{}') return { ...DEFAULT_GROUPS, ...stored } } catch { return { ...DEFAULT_GROUPS } } } function saveGroups(groups: Record) { localStorage.setItem('sidebarGroups', JSON.stringify(groups)) } // ── NavItem with star ────────────────────────────────────────────────────── function NavItem({ to, icon: Icon, label, onClick, favorites, onToggleFavorite, }: { to: string icon: React.ElementType label: string onClick?: () => void favorites: string[] onToggleFavorite: (path: string) => void }) { const isFav = favorites.includes(to) return (
cn('sidebar-link', isActive && 'active')} > {label}
) } // ── Group header ─────────────────────────────────────────────────────────── function GroupHeader({ label, isOpen, onToggle, }: { label: string isOpen: boolean onToggle: () => void }) { return ( ) } // ── Main Sidebar ─────────────────────────────────────────────────────────── export function Sidebar({ open, onClose }: SidebarProps) { const { user } = useAuth() const { statuses } = useModules() const navigate = useNavigate() const location = useLocation() const isAdmin = user?.role === 'super_admin' const isModuleActive = (id: string) => statuses[id] === 'active' || statuses[id] === 'trial' // Role-based permission map const ROLE_PERMS: Record = { hotel_admin: ['*'], manager: ['*'], receptionist: ['calendar','bookings','guests','rooms','availability','housekeeping','room_service','rental','pos','reviews','reports'], housekeeper: ['calendar','housekeeping','maintenance','rooms'], accountant: ['calendar','reports','pos','discounts','tariffs','pricing','loyalty'], security: ['calendar','bookings'], technician: ['calendar','housekeeping','maintenance','rooms'], } const can = (permission: string) => { const role = user?.role ?? 'housekeeper' const perms = ROLE_PERMS[role] ?? [] return perms.includes('*') || perms.includes(permission) } // Core modules with dedicated nav positions (not shown in generic module list) const CORE_MODULE_IDS = ['channel-manager', 'rental'] // Module items for the "Сервис" sidebar section const activeModuleItems = MODULES_DATA.filter( m => m.sidebarItem && !m.sidebarItem.showForHousekeeper && !CORE_MODULE_IDS.includes(m.id) && isModuleActive(m.id), ) // ── Favorites state ────────────────────────────────────────────────────── const [favorites, setFavorites] = useState(loadFavorites) const toggleFavorite = (path: string) => { setFavorites(prev => { const next = prev.includes(path) ? prev.filter(p => p !== path) : [...prev, path] saveFavorites(next) return next }) } // ── Group open/close state ─────────────────────────────────────────────── const [groups, setGroups] = useState>(loadGroups) const toggleGroup = (key: GroupKey) => { setGroups(prev => { const next = { ...prev, [key]: !prev[key] } saveGroups(next) return next }) } // Auto-open the group containing the currently active route useEffect(() => { const path = location.pathname const groupPaths: Record = { basics: ['/calendar', '/bookings', '/guests', '/rooms', '/room-categories', '/availability'], prices: ['/tariffs', '/dynamic-pricing', '/discounts', '/rental'], service: ['/housekeeping', '/technical', ...activeModuleItems.map(m => m.sidebarItem!.path)], management: ['/users', '/schedule', '/loyalty', '/maintenance', '/floor-map', '/channels'], settingsGroup:['/modules', '/settings', '/billing'], devGroup: ['/api-docs'], } const matchedGroup = (Object.keys(groupPaths) as GroupKey[]).find( key => groupPaths[key].some(p => path === p || path.startsWith(p + '/')), ) if (matchedGroup) { setGroups(prev => { if (prev[matchedGroup]) return prev const next = { ...prev, [matchedGroup]: true } saveGroups(next) return next }) } // eslint-disable-next-line react-hooks/exhaustive-deps }, [location.pathname]) // All items metadata for favorites lookup const allItems: { to: string; icon: React.ElementType; label: string }[] = [ // basics { to: '/calendar', icon: CalendarDays, label: 'Шахматка' }, { to: '/bookings', icon: BookOpen, label: 'Бронирования' }, { to: '/guests', icon: UsersRound, label: 'Гости' }, { to: '/rooms', icon: BedDouble, label: 'Номера' }, { to: '/room-categories', icon: LayoutGrid, label: 'Категории номеров' }, { to: '/availability', icon: CalendarRange, label: 'Доступность' }, // prices { to: '/tariffs', icon: Utensils, label: 'Тарифы' }, { to: '/dynamic-pricing', icon: TrendingUp, label: 'Динамические цены' }, { to: '/discounts', icon: Tag, label: 'Скидки' }, { to: '/rental', icon: CalendarClock, label: 'Аренда объектов' }, // service { to: '/housekeeping', icon: MODULES_DATA.find(m => m.id === 'housekeeping')?.sidebarItem?.icon ?? Wrench, label: 'Уборка' }, { to: '/technical', icon: Zap, label: 'Тех. задачи' }, ...activeModuleItems.map(m => ({ to: m.sidebarItem!.path, icon: m.sidebarItem!.icon, label: m.sidebarItem!.label })), // management { to: '/users', icon: UserCog, label: 'Сотрудники' }, { to: '/schedule', icon: CalendarClock, label: 'График работы' }, { to: '/loyalty', icon: Award, label: 'Лояльность' }, { to: '/maintenance', icon: Wrench, label: 'Тех. перерывы' }, { to: '/floor-map', icon: Map, label: 'План этажей' }, { to: '/channels', icon: Globe, label: 'Каналы' }, // settings { to: '/modules', icon: Puzzle, label: 'Модули' }, { to: '/settings', icon: Settings, label: 'Настройки' }, // dev { to: '/api-docs', icon: FileText, label: 'API & Документация' }, ] const navItemProps = { favorites, onToggleFavorite: toggleFavorite, onClick: onClose } return ( <> {open && (
)} ) }