Files
hotelsync/src/layouts/AppLayout.tsx
HotelSync cbddbe6027 feat: collapsible sidebar groups with favorites + chat visibility toggle
- Sidebar: collapsible groups (Основное/Цены/Сервис/Управление/Настройки/Разработчикам) with chevron headers, state persisted in localStorage 'sidebarGroups'
- Sidebar: Избранное group at top, star icon on nav item hover to add/remove favorites, stored in localStorage 'sidebarFavorites'
- Sidebar: active route auto-opens its group on navigation
- SettingsPage: 'Скрыть чат' toggle in Внешний вид section, stored in localStorage 'chatHidden'
- AppLayout: conditionally renders ChatWidget based on chatHidden, listens for storage events for reactive updates

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-26 10:39:33 +03:00

40 lines
1.3 KiB
TypeScript

import { Outlet, Navigate } from 'react-router-dom'
import { useState, useEffect } from 'react'
import { Sidebar } from '../components/layout/Sidebar'
import { Topbar } from '../components/layout/Topbar'
import { ChatWidget } from '../components/chat/ChatWidget'
import { useAuth } from '../contexts/AuthContext'
export function AppLayout() {
const { user } = useAuth()
const [sidebarOpen, setSidebarOpen] = useState(false)
const [chatHidden, setChatHidden] = useState(
() => localStorage.getItem('chatHidden') === 'true',
)
useEffect(() => {
const handleStorage = () => {
setChatHidden(localStorage.getItem('chatHidden') === 'true')
}
window.addEventListener('storage', handleStorage)
return () => window.removeEventListener('storage', handleStorage)
}, [])
if (!user) return <Navigate to="/login" replace />
return (
<div className="flex h-screen bg-slate-50 dark:bg-slate-900 overflow-hidden">
<Sidebar open={sidebarOpen} onClose={() => setSidebarOpen(false)} />
<div className="flex-1 flex flex-col min-w-0">
<Topbar onMenuToggle={() => setSidebarOpen(v => !v)} />
<main className="flex-1 overflow-auto">
<Outlet />
</main>
</div>
{!chatHidden && <ChatWidget />}
</div>
)
}