krona-front/src/app/layouts/AppShell.tsx

131 lines
5.7 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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>
);
};