import React, { useEffect } from 'react'; import { Outlet, useLocation } from 'react-router-dom'; import { useDemoStore } from '../../mock/store'; import { Sidebar } from '../shell/Sidebar'; import { Topbar } from '../shell/Topbar'; import { RightCommandPanel } from '../shell/RightCommandPanel'; import { BottomNav } from '../shell/BottomNav'; import { CommandPalette } from '../shell/CommandPalette'; import { QuickActionsModal } from '../shell/QuickActionsModal'; import { NotificationsDrawer } from '../shell/NotificationsDrawer'; import { Drawer } from '../../shared/ui'; import { useResponsive } from '../../shared/lib/useResponsive'; import { useCommunicationsStore } from '../../features/communications/services/communicationsService'; export const AppShell: React.FC = () => { const { theme, viewportMode, isAiDrawerOpen, setIsAiDrawerOpen, getActiveProfile, activeLocation, } = useDemoStore(); const { width, isMobile, isTablet, isDesktop } = useResponsive(); const profile = getActiveProfile(); const location = useLocation(); const isCommunicationsPage = location.pathname.startsWith('/communications'); const isMobileChatOpen = useCommunicationsStore((state) => state.isMobileChatOpen); useEffect(() => { document.documentElement.setAttribute('data-theme', theme); const kronaTheme = { light: 'parchment-light', 'soft-light': 'noble-green', dark: 'graphite-forest-dark', }[theme]; document.documentElement.setAttribute('data-krona-theme', kronaTheme); document.documentElement.setAttribute('data-omni-theme', kronaTheme); document.documentElement.setAttribute('data-omni-density', isMobile ? 'touch' : 'workspace'); }, [theme, isMobile]); // Layout resolution: // 1. Real screen size is paramount: // - If actual viewport < 768px -> always Mobile Shell. // - If actual viewport 768-1199px -> always Tablet Shell. // 2. Demo emulation frame is only applied when on real desktop (>= 1200px) and user explicitly chose a demo mode. const isEmulatedOnDesktop = isDesktop && viewportMode !== 'desktop'; const isEffectiveMobile = isMobile || (isDesktop && viewportMode === 'mobile'); const isEffectiveTablet = isTablet || (isDesktop && (viewportMode === 'tablet-portrait' || viewportMode === 'tablet-landscape')); const getViewportContainerStyles = () => { if (!isEmulatedOnDesktop) { return isCommunicationsPage ? 'w-full h-screen max-h-screen overflow-hidden' : 'w-full min-h-screen'; } switch (viewportMode) { case 'mobile': return 'max-w-[420px] h-[880px] my-6 border-8 border-krona-border rounded-[36px] overflow-hidden shadow-2xl relative'; case 'tablet-portrait': return 'max-w-[820px] h-[1050px] my-6 border-8 border-krona-border rounded-[28px] overflow-hidden shadow-2xl relative'; case 'tablet-landscape': return 'max-w-[1180px] h-[800px] my-6 border-8 border-krona-border rounded-[24px] overflow-hidden shadow-2xl relative'; default: return isCommunicationsPage ? 'w-full h-screen max-h-screen overflow-hidden' : 'w-full min-h-screen'; } }; return (
{/* Main Shell Layout */}
{/* Left Sidebar: hidden on mobile, rendered on tablet & desktop */} {!isEffectiveMobile && ( )} {/* Center Work Area */}
{!(isEffectiveMobile && isCommunicationsPage && isMobileChatOpen) && } {/* Main Content Router Outlet */}
{/* Right Command Panel (Only on full Desktop, not on communications which has its own full right card) */} {!isEffectiveMobile && !isEffectiveTablet && width >= 1536 && !isCommunicationsPage && ( )}
{/* Mobile Bottom Navigation (Always present on mobile) */} {isEffectiveMobile && } {/* Global Modals & Drawers */} {/* Standalone AI Drawer */} setIsAiDrawerOpen(false)} title="✦ Krona AI — Бизнес-оркестратор" subtitle={`Контекст: ${profile.name} • ${activeLocation}`} width="md" >
Krona AI: Интеллектуальный помощник подключен к локальной модели данных бизнеса, остаткам, расписанию смен и фискальным отчетам.
Ролевые агенты команды:
Бизнес-аналитик
Food cost, выручка, чеки
Складской агент
Остатки, неснижаемый лимит
Маркетолог
Сегменты, акции, рассылки
HR-ассистент
График смен, переработки
); };