# 📋 ПРАВИЛА АРХИТЕКТУРЫ SIDEBAR КОМПОНЕНТОВ SFERA > **Статус**: ✅ Архитектурный стандарт SFERA > **Дата создания**: 28.08.2025 > **Связанные документы**: > - [URL_ROUTING_RULES.md](./URL_ROUTING_RULES.md) > - [COMPONENT_ARCHITECTURE.md](./COMPONENT_ARCHITECTURE.md) > - [DOMAIN_MODEL.md](../core/DOMAIN_MODEL.md) --- ## 🎯 ПРОБЛЕМА И РЕШЕНИЕ ### ❌ ТЕКУЩАЯ ПРОБЛЕМА - **Монолитный sidebar.tsx** - 740 строк кода - **Смешанная логика** всех ролей в одном файле - **Условная навигация** с `user?.organization?.type === 'ROLE'` - **Сложность поддержки** и добавления новых пунктов меню - **Отсутствие изоляции** между ролями ### ✅ АРХИТЕКТУРНОЕ РЕШЕНИЕ - **4 независимых sidebar** компонента для каждой роли - **Изолированная навигация** без условий - **Базовый компонент** с общей логикой - **Ролевой роутер** для автоматического выбора sidebar --- ## 🏗️ АРХИТЕКТУРНЫЕ ПРИНЦИПЫ ### ПРИНЦИП 1: ИЗОЛЯЦИЯ ПО РОЛЯМ ```typescript // ❌ СТАРЫЙ ПОДХОД - условия в одном файле {user?.organization?.type === 'SELLER' && } {user?.organization?.type === 'FULFILLMENT' && } // ✅ НОВЫЙ ПОДХОД - отдельные компоненты // Только навигация селлера // Только навигация фулфилмента // Только навигация логистики // Только навигация поставщика ``` ### ПРИНЦИП 2: ЕДИНАЯ БАЗОВАЯ АРХИТЕКТУРА Все sidebar наследуют от базового компонента: ```typescript export function BaseSidebar({ navigationItems, user, notifications }: BaseSidebarProps) { return (
// Общий для всех // Общий для всех // Уникальный для роли // Уникальный для роли // Общий для всех
) } ``` ### ПРИНЦИП 3: СООТВЕТСТВИЕ URL ROUTING RULES Каждый пункт навигации использует новые ролевые URL: ```typescript // ✅ Правильные URL согласно URL_ROUTING_RULES const SELLER_NAVIGATION = [ { path: '/seller/home' }, // не /home { path: '/seller/supplies/goods/cards' }, // не /supplies { path: '/seller/warehouse' }, // не /wb-warehouse { path: '/seller/statistics' } // не /seller-statistics ] ``` --- ## 📁 ФАЙЛОВАЯ СТРУКТУРА ``` src/components/dashboard/sidebar/ ├── index.tsx # 🔄 Роутер sidebar (выбор по роли) ├── BaseSidebar.tsx # 🔧 Базовый компонент ├── SellerSidebar.tsx # 🛒 Навигация селлера ├── FulfillmentSidebar.tsx # 🏭 Навигация фулфилмента ├── WholesaleSidebar.tsx # 🏪 Навигация поставщика ├── LogistSidebar.tsx # 🚛 Навигация логистики ├── types.ts # 🔷 TypeScript интерфейсы └── components/ # 📦 Общие компоненты ├── UserProfile.tsx ├── CollapseButton.tsx ├── Navigation.tsx ├── Notifications.tsx └── LogoutButton.tsx ``` --- ## 🔧 ТЕХНИЧЕСКАЯ РЕАЛИЗАЦИЯ ### 1. БАЗОВЫЕ ТИПЫ ```typescript // types.ts export interface NavigationItem { id: string label: string icon: React.ComponentType<{ className?: string }> path: string badge?: number notification?: React.ComponentType isActive?: boolean } export interface SidebarUser { id: string organization: { type: 'SELLER' | 'FULFILLMENT' | 'WHOLESALE' | 'LOGIST' name: string } avatar?: string managerName?: string } export interface NotificationConfig { supplies?: number orders?: number messages?: number requests?: number } export interface BaseSidebarProps { navigationItems: NavigationItem[] user: SidebarUser notifications: NotificationConfig isCollapsed: boolean onToggle: () => void } ``` ### 2. БАЗОВЫЙ КОМПОНЕНТ ```typescript // BaseSidebar.tsx import { UserProfile } from './components/UserProfile' import { CollapseButton } from './components/CollapseButton' import { Navigation } from './components/Navigation' import { Notifications } from './components/Notifications' import { LogoutButton } from './components/LogoutButton' export function BaseSidebar({ navigationItems, user, notifications, isCollapsed, onToggle }: BaseSidebarProps) { return (
) } ``` ### 3. РОЛЕВОЙ SIDEBAR (ПРИМЕР) ```typescript // LogistSidebar.tsx import { Home, Truck, Map, MessageCircle, DollarSign, Handshake, Store, TrendingUp, Settings } from 'lucide-react' import { useAuth } from '@/hooks/useAuth' import { usePathname } from 'next/navigation' import { useSidebar } from '@/hooks/useSidebar' import { BaseSidebar } from './BaseSidebar' import { NavigationItem } from './types' export function LogistSidebar() { const { user } = useAuth() const pathname = usePathname() const { isCollapsed, toggleSidebar } = useSidebar() const navigationItems: NavigationItem[] = [ { id: 'home', label: 'Главная', icon: Home, path: '/logistics/home', isActive: pathname === '/logistics/home' }, { id: 'orders', label: 'Перевозки', icon: Truck, path: '/logistics/orders/pending', isActive: pathname.startsWith('/logistics/orders') }, { id: 'routes', label: 'Маршруты', icon: Map, path: '/logistics/routes', isActive: pathname.startsWith('/logistics/routes') }, { id: 'messenger', label: 'Мессенджер', icon: MessageCircle, path: '/logistics/messenger', isActive: pathname.startsWith('/logistics/messenger') }, { id: 'economics', label: 'Экономика', icon: DollarSign, path: '/logistics/economics', isActive: pathname.startsWith('/logistics/economics') }, { id: 'partners', label: 'Партнёры', icon: Handshake, path: '/logistics/partners', isActive: pathname.startsWith('/logistics/partners') }, { id: 'market', label: 'Маркет', icon: Store, path: '/logistics/market', isActive: pathname.startsWith('/logistics/market') }, { id: 'exchange', label: 'Биржа', icon: TrendingUp, path: '/logistics/exchange', isActive: pathname.startsWith('/logistics/exchange') }, { id: 'settings', label: 'Настройки', icon: Settings, path: '/logistics/settings', isActive: pathname.startsWith('/logistics/settings') } ] return ( ) } ``` ### 4. ГЛАВНЫЙ РОУТЕР ```typescript // index.tsx import { useAuth } from '@/hooks/useAuth' import { SellerSidebar } from './SellerSidebar' import { FulfillmentSidebar } from './FulfillmentSidebar' import { WholesaleSidebar } from './WholesaleSidebar' import { LogistSidebar } from './LogistSidebar' export function Sidebar() { const { user } = useAuth() if (!user?.organization?.type) { return (
Загрузка...
) } // Роутинг на основе типа организации switch (user.organization.type) { case 'SELLER': return case 'FULFILLMENT': return case 'WHOLESALE': return case 'LOGIST': return default: return (
Неизвестный тип организации: {user.organization.type}
) } } ``` --- ## 📋 НАВИГАЦИОННЫЕ СПЕЦИФИКАЦИИ ПО РОЛЯМ ### 🛒 SELLER SIDEBAR ```typescript const SELLER_NAVIGATION = [ { id: 'home', label: 'Главная', icon: Home, path: '/seller/home' }, { id: 'supplies', label: 'Мои поставки', icon: Truck, path: '/seller/supplies/goods/cards' }, { id: 'warehouse', label: 'Склад WB', icon: Warehouse, path: '/seller/warehouse' }, { id: 'statistics', label: 'Статистика', icon: BarChart3, path: '/seller/statistics' }, { id: 'messenger', label: 'Мессенджер', icon: MessageCircle, path: '/seller/messenger' }, { id: 'economics', label: 'Экономика', icon: DollarSign, path: '/seller/economics' }, { id: 'partners', label: 'Партнёры', icon: Handshake, path: '/seller/partners' }, { id: 'market', label: 'Маркет', icon: Store, path: '/seller/market' }, { id: 'exchange', label: 'Биржа', icon: TrendingUp, path: '/seller/exchange' }, { id: 'settings', label: 'Настройки', icon: Settings, path: '/seller/settings' } ] ``` ### 🏭 FULFILLMENT SIDEBAR ```typescript const FULFILLMENT_NAVIGATION = [ { id: 'home', label: 'Главная', icon: Home, path: '/fulfillment/home' }, { id: 'supplies', label: 'Входящие поставки', icon: Truck, path: '/fulfillment/supplies/goods/receiving' }, { id: 'warehouse', label: 'Склад', icon: Warehouse, path: '/fulfillment/warehouse' }, { id: 'services', label: 'Услуги', icon: Wrench, path: '/fulfillment/services' }, { id: 'employees', label: 'Сотрудники', icon: Users, path: '/fulfillment/employees' }, { id: 'statistics', label: 'Статистика', icon: BarChart3, path: '/fulfillment/statistics' }, { id: 'messenger', label: 'Мессенджер', icon: MessageCircle, path: '/fulfillment/messenger' }, { id: 'economics', label: 'Экономика', icon: DollarSign, path: '/fulfillment/economics' }, { id: 'partners', label: 'Партнёры', icon: Handshake, path: '/fulfillment/partners' }, { id: 'market', label: 'Маркет', icon: Store, path: '/fulfillment/market' }, { id: 'exchange', label: 'Биржа', icon: TrendingUp, path: '/fulfillment/exchange' }, { id: 'settings', label: 'Настройки', icon: Settings, path: '/fulfillment/settings' } ] ``` ### 🏪 WHOLESALE SIDEBAR ```typescript const WHOLESALE_NAVIGATION = [ { id: 'home', label: 'Главная', icon: Home, path: '/wholesale/home' }, { id: 'orders', label: 'Входящие заказы', icon: Truck, path: '/wholesale/orders' }, { id: 'catalog', label: 'Каталог товаров', icon: Store, path: '/wholesale/catalog/goods' }, { id: 'warehouse', label: 'Склад', icon: Warehouse, path: '/wholesale/warehouse' }, { id: 'messenger', label: 'Мессенджер', icon: MessageCircle, path: '/wholesale/messenger' }, { id: 'economics', label: 'Экономика', icon: DollarSign, path: '/wholesale/economics' }, { id: 'partners', label: 'Партнёры', icon: Handshake, path: '/wholesale/partners' }, { id: 'market', label: 'Маркет', icon: Store, path: '/wholesale/market' }, { id: 'exchange', label: 'Биржа', icon: TrendingUp, path: '/wholesale/exchange' }, { id: 'settings', label: 'Настройки', icon: Settings, path: '/wholesale/settings' } ] ``` ### 🚛 LOGIST SIDEBAR ```typescript const LOGIST_NAVIGATION = [ { id: 'home', label: 'Главная', icon: Home, path: '/logistics/home' }, { id: 'orders', label: 'Перевозки', icon: Truck, path: '/logistics/orders/pending' }, { id: 'routes', label: 'Маршруты', icon: Map, path: '/logistics/routes' }, { id: 'messenger', label: 'Мессенджер', icon: MessageCircle, path: '/logistics/messenger' }, { id: 'economics', label: 'Экономика', icon: DollarSign, path: '/logistics/economics' }, { id: 'partners', label: 'Партнёры', icon: Handshake, path: '/logistics/partners' }, { id: 'market', label: 'Маркет', icon: Store, path: '/logistics/market' }, { id: 'exchange', label: 'Биржа', icon: TrendingUp, path: '/logistics/exchange' }, { id: 'settings', label: 'Настройки', icon: Settings, path: '/logistics/settings' } ] ``` --- ## 🎯 ПРЕИМУЩЕСТВА НОВОЙ АРХИТЕКТУРЫ ### 📊 ТЕХНИЧЕСКИЕ ПРЕИМУЩЕСТВА ✅ **Читаемость** - каждая роль в отдельном файле (~100 строк vs 740) ✅ **Поддержка** - легко изменить навигацию конкретной роли ✅ **Тестирование** - изолированное тестирование каждой роли ✅ **Производительность** - загружается только нужная навигация ✅ **Безопасность** - физическая невозможность показать чужие пункты ### 🏗️ АРХИТЕКТУРНЫЕ ПРЕИМУЩЕСТВА ✅ **Масштабируемость** - легко добавлять новые роли ✅ **Изоляция** - изменения в одной роли не влияют на другие ✅ **Переиспользование** - общая логика в BaseSidebar ✅ **Типизация** - строгие типы для каждой роли ✅ **Консистентность** - единый интерфейс для всех sidebar ### 💼 БИЗНЕС ПРЕИМУЩЕСТВА ✅ **UX** - каждая роль видит только релевантную навигацию ✅ **Скорость разработки** - параллельная работа над разными ролями ✅ **Качество** - меньше ошибок из-за изоляции кода ✅ **Гибкость** - быстрая адаптация навигации под потребности роли --- ## 📈 ПЛАН МИГРАЦИИ ### ФАЗА 1: ПОДГОТОВКА (1-2 дня) - [ ] Создать папку `src/components/dashboard/sidebar/` - [ ] Реализовать `types.ts` с базовыми интерфейсами - [ ] Создать `BaseSidebar.tsx` с общей логикой - [ ] Создать базовые компоненты (`UserProfile`, `Navigation`, etc.) ### ФАЗА 2: ПЕРВЫЙ РОЛЕВОЙ SIDEBAR (2-3 дня) - [ ] Реализовать `LogistSidebar.tsx` как пилотный проект - [ ] Протестировать работу с существующими хуками - [ ] Убедиться в корректной работе уведомлений - [ ] Проверить соответствие URL_ROUTING_RULES ### ФАЗА 3: ОСТАЛЬНЫЕ РОЛЕВЫЕ SIDEBAR (3-4 дня) - [ ] Создать `SellerSidebar.tsx` - [ ] Создать `FulfillmentSidebar.tsx` - [ ] Создать `WholesaleSidebar.tsx` - [ ] Протестировать все роли ### ФАЗА 4: ИНТЕГРАЦИЯ И CLEANUP (1-2 дня) - [ ] Создать главный роутер `index.tsx` - [ ] Обновить импорты в layout компонентах - [ ] Удалить старый `sidebar.tsx` - [ ] Провести финальное тестирование ### ФАЗА 5: ТЕСТИРОВАНИЕ (2-3 дня) - [ ] Unit тесты для каждого ролевого sidebar - [ ] Интеграционные тесты навигации - [ ] E2E тесты пользовательских сценариев - [ ] Проверка производительности --- ## ⚠️ ВАЖНЫЕ ЗАМЕЧАНИЯ ### ОБРАТНАЯ СОВМЕСТИМОСТЬ - Старые хуки (`useSidebar`, `useAuth`) должны работать без изменений - GraphQL запросы для уведомлений остаются теми же - Стили и анимации сохраняются ### БЕЗОПАСНОСТЬ - Каждый sidebar имеет доступ только к своим данным - Невозможно случайно показать навигацию другой роли - Строгая типизация предотвращает ошибки ### ПРОИЗВОДИТЕЛЬНОСТЬ - Bundle splitting по ролям - Lazy loading неиспользуемых sidebar - Мемоизация навигационных элементов --- ## 🧪 ТЕСТИРОВАНИЕ ### Unit тесты ```typescript // LogistSidebar.test.tsx describe('LogistSidebar', () => { it('should render correct navigation items', () => { render() expect(screen.getByText('Перевозки')).toBeInTheDocument() expect(screen.getByText('Маршруты')).toBeInTheDocument() expect(screen.queryByText('Услуги')).not.toBeInTheDocument() // только для фулфилмента }) it('should highlight active navigation item', () => { mockPathname('/logistics/orders/pending') render() expect(screen.getByText('Перевозки')).toHaveClass('active') }) }) ``` ### Интеграционные тесты ```typescript // Sidebar.test.tsx describe('Sidebar Routing', () => { it('should render LogistSidebar for LOGIST users', () => { mockUser({ organization: { type: 'LOGIST' } }) render() expect(screen.getByText('Перевозки')).toBeInTheDocument() expect(screen.queryByText('Входящие поставки')).not.toBeInTheDocument() }) }) ``` --- ## 📝 ИСТОРИЯ ИЗМЕНЕНИЙ | Дата | Версия | Описание | Автор | |------------|--------|---------------------------------------|----------| | 28.08.2025 | 1.0 | Первая версия правил sidebar | AI | | 28.08.2025 | 1.1 | Добавлены спецификации по ролям | AI | | 28.08.2025 | 1.2 | Добавлен план миграции | AI | --- **Создано**: В рамках унификации URL системы SFERA **Связано**: Модульная архитектура компонентов и ролевая маршрутизация **Цель**: Изоляция навигации по ролям для улучшения UX и поддержки кода