131 lines
5.7 KiB
TypeScript
131 lines
5.7 KiB
TypeScript
import React, { useEffect } from 'react';
|
||
import { Outlet } 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, Button } from '../../shared/ui';
|
||
|
||
export const AppShell: React.FC = () => {
|
||
const {
|
||
theme,
|
||
viewportMode,
|
||
isAiDrawerOpen,
|
||
setIsAiDrawerOpen,
|
||
getActiveProfile,
|
||
activeLocation,
|
||
} = useDemoStore();
|
||
|
||
const profile = getActiveProfile();
|
||
|
||
useEffect(() => {
|
||
document.documentElement.setAttribute('data-theme', theme);
|
||
}, [theme]);
|
||
|
||
// Viewport mode constraints
|
||
const getViewportContainerStyles = () => {
|
||
switch (viewportMode) {
|
||
case 'mobile':
|
||
return 'max-w-[420px] h-[880px] my-6 border-8 border-[#3A3D35] rounded-[36px] overflow-hidden shadow-2xl relative';
|
||
case 'tablet-portrait':
|
||
return 'max-w-[820px] h-[1050px] my-6 border-8 border-[#3A3D35] rounded-[28px] overflow-hidden shadow-2xl relative';
|
||
case 'tablet-landscape':
|
||
return 'max-w-[1180px] h-[800px] my-6 border-8 border-[#3A3D35] rounded-[24px] overflow-hidden shadow-2xl relative';
|
||
case 'desktop':
|
||
default:
|
||
return 'w-full min-h-screen';
|
||
}
|
||
};
|
||
|
||
const isEmulatedDevice = viewportMode !== 'desktop';
|
||
const showMobileNav = viewportMode === 'mobile';
|
||
|
||
return (
|
||
<div className={`min-h-screen bg-[#191B18] flex items-center justify-center ${isEmulatedDevice ? 'p-4' : ''}`}>
|
||
<div className={`w-full bg-krona-app text-krona-primary flex flex-col transition-all duration-300 ${getViewportContainerStyles()}`}>
|
||
{/* App Main Shell Container */}
|
||
<div className="flex-1 flex overflow-hidden w-full h-full relative">
|
||
{/* Left Sidebar (hidden in mobile mode) */}
|
||
{!showMobileNav && <Sidebar />}
|
||
|
||
{/* Center Work Area */}
|
||
<div className="flex-1 flex flex-col min-w-0 overflow-hidden bg-krona-workspace">
|
||
<Topbar />
|
||
|
||
{/* Main Content Router Outlet */}
|
||
<main className={`flex-1 overflow-y-auto ${showMobileNav ? 'pb-20' : 'pb-8'} p-4 md:p-6`}>
|
||
<Outlet />
|
||
</main>
|
||
</div>
|
||
|
||
{/* Right Command Panel (Desktop and Tablet-Landscape) */}
|
||
{viewportMode === 'desktop' && <RightCommandPanel />}
|
||
</div>
|
||
|
||
{/* Mobile Bottom Navigation */}
|
||
{showMobileNav && <BottomNav />}
|
||
|
||
{/* Global Modals & Drawers */}
|
||
<CommandPalette />
|
||
<QuickActionsModal />
|
||
<NotificationsDrawer />
|
||
|
||
{/* Standalone AI Drawer (triggered via Topbar ✦ button on any screen) */}
|
||
<Drawer
|
||
isOpen={isAiDrawerOpen}
|
||
onClose={() => setIsAiDrawerOpen(false)}
|
||
title="✦ Krona AI — Бизнес-оркестратор"
|
||
subtitle={`Контекст: ${profile.name} • ${activeLocation}`}
|
||
width="md"
|
||
>
|
||
<div className="space-y-4">
|
||
<div className="p-3 bg-krona-gold/10 border border-krona-gold/30 rounded-lg text-xs leading-relaxed text-krona-gold-text">
|
||
<strong>Krona AI Core v1:</strong> Интеллектуальный помощник подключен к локальной модели данных бизнеса, остаткам, расписанию смен и фискальным отчетам.
|
||
</div>
|
||
|
||
<div className="space-y-2">
|
||
<span className="text-xs font-semibold text-krona-secondary uppercase tracking-wider">
|
||
Ролевые агенты команды:
|
||
</span>
|
||
<div className="grid grid-cols-2 gap-2 text-xs">
|
||
<div className="p-2.5 rounded-lg border border-krona-border bg-krona-surface">
|
||
<div className="font-semibold text-krona-primary">Бизнес-аналитик</div>
|
||
<div className="text-[11px] text-krona-secondary">Food cost, выручка, чеки</div>
|
||
</div>
|
||
<div className="p-2.5 rounded-lg border border-krona-border bg-krona-surface">
|
||
<div className="font-semibold text-krona-primary">Складской агент</div>
|
||
<div className="text-[11px] text-krona-secondary">Списания, закупки, пороги</div>
|
||
</div>
|
||
<div className="p-2.5 rounded-lg border border-krona-border bg-krona-surface">
|
||
<div className="font-semibold text-krona-primary">Маркетолог</div>
|
||
<div className="text-[11px] text-krona-secondary">Сегменты, LTV, рассылки</div>
|
||
</div>
|
||
<div className="p-2.5 rounded-lg border border-krona-border bg-krona-surface">
|
||
<div className="font-semibold text-krona-primary">Финансовый аудитор</div>
|
||
<div className="text-[11px] text-krona-secondary">Кассовые смены, ДДС, P&L</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div className="pt-2">
|
||
<Button
|
||
variant="primary"
|
||
className="w-full text-xs"
|
||
onClick={() => {
|
||
setIsAiDrawerOpen(false);
|
||
window.location.href = '/ai';
|
||
}}
|
||
>
|
||
Открыть полноэкранный AI-центр →
|
||
</Button>
|
||
</div>
|
||
</div>
|
||
</Drawer>
|
||
</div>
|
||
</div>
|
||
);
|
||
};
|