diff --git a/EMPLOYEE_TEST_TASK.md b/EMPLOYEE_TEST_TASK.md
deleted file mode 100644
index 146b7b5..0000000
--- a/EMPLOYEE_TEST_TASK.md
+++ /dev/null
@@ -1,379 +0,0 @@
-# Тестовое задание: Система управления сотрудниками
-
-## Описание задачи
-
-Необходимо разработать с нуля полнофункциональную систему управления сотрудниками компании. Система должна позволять добавлять, редактировать, удалять сотрудников, а также управлять их расписанием и просматривать статистику.
-
-## Технический стек
-
-- **Frontend**: Next.js 15+ (App Router)
-- **Backend**: GraphQL с Apollo Server
-- **Database**: PostgreSQL с Prisma ORM
-- **Styling**: TailwindCSS с glassmorphism эффектами
-- **UI Components**: Radix UI (через shadcn/ui)
-- **TypeScript**: строгая типизация
-- **Icons**: Lucide React
-- **Notifications**: Sonner
-
-## Дизайн-система и стили
-
-### Цветовая схема
-- Основные цвета: оттенки фиолетового (oklch(0.75 0.32 315) до oklch(0.68 0.28 280))
-- Фон: тёмный градиент `bg-gradient-smooth`
-- Карточки: стеклянный эффект `glass-card`
-- Текст: белый и оттенки белого/серого
-
-### Glassmorphism стили
-```css
-.glass-card {
- background: rgba(255, 255, 255, 0.12);
- backdrop-filter: blur(20px);
- border: 1px solid rgba(255, 255, 255, 0.2);
- box-shadow:
- 0 8px 32px rgba(168, 85, 247, 0.18),
- 0 4px 16px rgba(147, 51, 234, 0.12),
- inset 0 1px 0 rgba(255, 255, 255, 0.3);
-}
-
-.glass-card:hover {
- background: rgba(255, 255, 255, 0.15);
- border: 1px solid rgba(255, 255, 255, 0.3);
- box-shadow:
- 0 12px 40px rgba(168, 85, 247, 0.25),
- 0 6px 20px rgba(147, 51, 234, 0.18);
-}
-```
-
-### Анимации
-- Плавные переходы (0.3s ease)
-- Hover-эффекты с изменением opacity и shadow
-- Анимация появления форм
-- Интерактивные элементы с transform
-
-## Структура базы данных
-
-```prisma
-model Employee {
- id String @id @default(cuid())
- firstName String
- lastName String
- middleName String?
- birthDate DateTime?
- avatar String?
- passportPhoto String?
- passportSeries String?
- passportNumber String?
- passportIssued String?
- passportDate DateTime?
- address String?
- position String
- department String?
- hireDate DateTime
- salary Float?
- status EmployeeStatus @default(ACTIVE)
- phone String
- email String?
- telegram String?
- whatsapp String?
- emergencyContact String?
- emergencyPhone String?
- organizationId String
- createdAt DateTime @default(now())
- updatedAt DateTime @updatedAt
- scheduleRecords EmployeeSchedule[]
- organization Organization @relation(fields: [organizationId], references: [id])
-}
-
-model EmployeeSchedule {
- id String @id @default(cuid())
- date DateTime
- status ScheduleStatus
- hoursWorked Float?
- notes String?
- employeeId String
- createdAt DateTime @default(now())
- updatedAt DateTime @updatedAt
- employee Employee @relation(fields: [employeeId], references: [id])
-}
-
-enum EmployeeStatus {
- ACTIVE
- VACATION
- SICK
- FIRED
-}
-
-enum ScheduleStatus {
- WORK
- WEEKEND
- VACATION
- SICK
- ABSENT
-}
-```
-
-## Функциональные требования
-
-### 1. Основная страница сотрудников (/employees)
-
-**Макет страницы:**
-- Sidebar слева (аналогично dashboard)
-- Основной контент справа
-- Поиск по ФИО, телефону, должности
-- Табы: "Сотрудники", "Расписание", "Статистика"
-
-**Таб "Сотрудники":**
-- Карточки сотрудников в grid layout (3-4 в ряд)
-- Каждая карточка содержит:
- - Аватар (или инициалы, если нет фото)
- - ФИО
- - Должность
- - Телефон
- - Email (если есть)
- - Статус (бейдж с цветом)
- - Кнопки: "Редактировать", "Уволить"
-- Кнопка "Добавить сотрудника" вверху
-- Фильтры по статусу
-- Пагинация при большом количестве
-
-### 2. Добавление сотрудника
-
-**Inline форма (появляется как первая карточка):**
-- Обязательные поля:
- - Имя, Фамилия
- - Должность
- - Дата приёма
- - Телефон
-- Необязательные поля:
- - Отчество
- - Email
- - Дата рождения
- - Адрес
- - Оклад
- - Паспортные данные
- - Контакты для экстренной связи
- - Telegram/WhatsApp
-- Загрузка аватара
-- Валидация всех полей
-- Красивые маски ввода (телефон, паспорт, ЗП)
-
-### 3. Редактирование сотрудника
-
-**Inline редактирование:**
-- Форма заменяет карточку сотрудника
-- Все те же поля, что при создании
-- Предзаполненные данные
-- Возможность изменить статус
-- Кнопки "Сохранить" / "Отмена"
-
-### 4. Управление расписанием
-
-**Таб "Расписание":**
-- Календарь на месяц
-- Выбор сотрудника из dropdown
-- Отметки на каждый день:
- - Работа (зелёный)
- - Выходной (серый)
- - Отпуск (синий)
- - Больничный (жёлтый)
- - Прогул (красный)
-- Возможность массово отметить период
-- Подсчёт отработанных часов/дней
-
-### 5. Статистика
-
-**Таб "Статистика":**
-- Общее количество сотрудников
-- Распределение по статусам
-- Средний возраст
-- Средняя зарплата
-- График найма по месяцам
-- Топ-должности
-- Статистика посещаемости
-
-## GraphQL API
-
-### Queries
-```graphql
-type Query {
- employees: [Employee!]!
- employee(id: ID!): Employee
- employeeSchedule(employeeId: ID!, year: Int!, month: Int!): [EmployeeSchedule!]!
- employeeStats: EmployeeStats!
-}
-```
-
-### Mutations
-```graphql
-type Mutation {
- createEmployee(input: CreateEmployeeInput!): CreateEmployeeResponse!
- updateEmployee(id: ID!, input: UpdateEmployeeInput!): UpdateEmployeeResponse!
- deleteEmployee(id: ID!): Boolean!
- updateEmployeeSchedule(input: UpdateScheduleInput!): Boolean!
-}
-```
-
-### Types
-```graphql
-type Employee {
- id: ID!
- firstName: String!
- lastName: String!
- middleName: String
- birthDate: DateTime
- avatar: String
- position: String!
- department: String
- hireDate: DateTime!
- salary: Float
- status: EmployeeStatus!
- phone: String!
- email: String
- telegram: String
- whatsapp: String
- emergencyContact: String
- emergencyPhone: String
- createdAt: DateTime!
- updatedAt: DateTime!
- scheduleRecords: [EmployeeSchedule!]!
-}
-```
-
-## Требования к реализации
-
-### 1. Файловая структура
-```
-src/
-├── app/
-│ └── employees/
-│ └── page.tsx
-├── components/
-│ └── employees/
-│ ├── employees-dashboard.tsx
-│ ├── employee-card.tsx
-│ ├── employee-form.tsx
-│ ├── employee-schedule.tsx
-│ └── employee-stats.tsx
-├── graphql/
-│ ├── queries.ts
-│ ├── mutations.ts
-│ ├── typedefs.ts
-│ └── resolvers.ts
-└── lib/
- ├── validations.ts
- └── input-masks.ts
-```
-
-### 2. Компоненты
-
-**EmployeesDashboard** - основной контейнер:
-- Управление состоянием
-- GraphQL операции
-- Переключение между табами
-
-**EmployeeCard** - карточка сотрудника:
-- Отображение информации
-- Кнопки действий
-- Анимации hover
-
-**EmployeeForm** - форма создания/редактирования:
-- Валидация полей
-- Маски ввода
-- Загрузка файлов
-- Обработка ошибок
-
-**EmployeeSchedule** - календарь:
-- Интерактивный календарь
-- Управление статусами дней
-- Подсчёт статистики
-
-### 3. Требования к UX/UI
-
-**Интерактивность:**
-- Плавные анимации при hover
-- Loading состояния для всех операций
-- Оптимистичные обновления
-- Toast уведомления об успехе/ошибке
-
-**Адаптивность:**
-- Корректное отображение на мобильных устройствах
-- Responsive grid для карточек
-- Адаптивная форма
-
-**Доступность:**
-- Поддержка клавиатурной навигации
-- ARIA атрибуты
-- Семантическая разметка
-
-### 4. Валидация
-
-**Клиентская валидация:**
-- Обязательные поля
-- Форматы email, телефона
-- Валидные даты
-- Паспортные данные
-
-**Серверная валидация:**
-- Дублирование всех проверок
-- Уникальность телефона
-- Проверка существования сотрудника
-
-### 5. Обработка ошибок
-
-- Graceful обработка всех ошибок API
-- Понятные сообщения пользователю
-- Retry механизм для сетевых ошибок
-- Fallback состояния
-
-## Дополнительные фичи (nice to have)
-
-1. **Экспорт данных** - выгрузка списка сотрудников в Excel/PDF
-2. **Массовые операции** - выбор нескольких сотрудников для действий
-3. **Фильтры** - по отделу, статусу, дате приёма
-4. **Сортировка** - по ФИО, дате приёма, зарплате
-5. **История изменений** - лог всех изменений сотрудника
-6. **Интеграция с мессенджерами** - отправка уведомлений
-
-## Критерии оценки
-
-### Обязательно (must have):
-- ✅ Все основные функции работают
-- ✅ Соответствие дизайн-системе
-- ✅ Чистый и понятный код
-- ✅ TypeScript без any
-- ✅ Обработка ошибок
-- ✅ Валидация данных
-- ✅ Адаптивная вёрстка
-
-### Дополнительные баллы:
-- ⭐ Оптимизация производительности
-- ⭐ Тесты (unit/integration)
-- ⭐ Документация API
-- ⭐ Инновационные решения UX
-- ⭐ Дополнительные фичи
-
-## Ресурсы
-
-### Дизайн-референсы:
-- Glassmorphism: https://css.glass/
-- UI Patterns: https://ui-patterns.com/
-
-### Технические ресурсы:
-- Next.js App Router: https://nextjs.org/docs
-- Prisma: https://www.prisma.io/docs
-- Apollo GraphQL: https://www.apollographql.com/docs
-- shadcn/ui: https://ui.shadcn.com/
-
-## Дедлайн
-
-**Время выполнения:** 2 часа
-
-**Что предоставить:**
-1. GitHub репозиторий с кодом
-2. README с инструкциями по запуску
-3. Демо на Vercel/Netlify (по возможности)
-4. Краткое описание архитектурных решений
-
----
-
-*Удачи в разработке! 🚀*
\ No newline at end of file
diff --git a/package-lock.json b/package-lock.json
index 389ce34..8cf86a9 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -48,6 +48,7 @@
"react": "19.1.0",
"react-dom": "19.1.0",
"react-imask": "^7.6.1",
+ "react-resizable-panels": "^3.0.3",
"sonner": "^2.0.6",
"tailwind-merge": "^3.3.1"
},
@@ -9634,6 +9635,16 @@
}
}
},
+ "node_modules/react-resizable-panels": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/react-resizable-panels/-/react-resizable-panels-3.0.3.tgz",
+ "integrity": "sha512-7HA8THVBHTzhDK4ON0tvlGXyMAJN1zBeRpuyyremSikgYh2ku6ltD7tsGQOcXx4NKPrZtYCm/5CBr+dkruTGQw==",
+ "license": "MIT",
+ "peerDependencies": {
+ "react": "^16.14.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc",
+ "react-dom": "^16.14.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc"
+ }
+ },
"node_modules/react-style-singleton": {
"version": "2.2.3",
"resolved": "https://registry.npmjs.org/react-style-singleton/-/react-style-singleton-2.2.3.tgz",
diff --git a/package.json b/package.json
index 01a5d5b..8dc80d4 100644
--- a/package.json
+++ b/package.json
@@ -49,6 +49,7 @@
"react": "19.1.0",
"react-dom": "19.1.0",
"react-imask": "^7.6.1",
+ "react-resizable-panels": "^3.0.3",
"sonner": "^2.0.6",
"tailwind-merge": "^3.3.1"
},
diff --git a/src/app/providers.tsx b/src/app/providers.tsx
index 1bf0abb..b381976 100644
--- a/src/app/providers.tsx
+++ b/src/app/providers.tsx
@@ -2,6 +2,7 @@
import { ApolloProvider } from '@apollo/client'
import { apolloClient } from '@/lib/apollo-client'
+import { SidebarProvider } from '@/hooks/useSidebar'
export function Providers({
children,
@@ -10,7 +11,9 @@ export function Providers({
}) {
return (
- {children}
+
+ {children}
+
)
}
\ No newline at end of file
diff --git a/src/components/admin/categories-section.tsx b/src/components/admin/categories-section.tsx
index 87c93bc..a7bf216 100644
--- a/src/components/admin/categories-section.tsx
+++ b/src/components/admin/categories-section.tsx
@@ -27,6 +27,22 @@ export function CategoriesSection() {
const [newCategoryName, setNewCategoryName] = useState('')
const [editCategoryName, setEditCategoryName] = useState('')
+ const formatDate = (dateString: string) => {
+ try {
+ const date = new Date(dateString)
+ if (isNaN(date.getTime())) {
+ return 'Неизвестно'
+ }
+ return date.toLocaleDateString('ru-RU', {
+ day: '2-digit',
+ month: '2-digit',
+ year: 'numeric'
+ })
+ } catch (error) {
+ return 'Неизвестно'
+ }
+ }
+
const { data, loading, error, refetch } = useQuery(GET_CATEGORIES)
const [createCategory, { loading: creating }] = useMutation(CREATE_CATEGORY)
const [updateCategory, { loading: updating }] = useMutation(UPDATE_CATEGORY)
@@ -266,7 +282,7 @@ export function CategoriesSection() {
{category.name}
- Создано: {new Date(category.createdAt).toLocaleDateString('ru-RU')}
+ Создано: {formatDate(category.createdAt)}
diff --git a/src/components/admin/users-section.tsx b/src/components/admin/users-section.tsx
index b061c83..9729501 100644
--- a/src/components/admin/users-section.tsx
+++ b/src/components/admin/users-section.tsx
@@ -96,13 +96,21 @@ export function UsersSection() {
}
const formatDate = (dateString: string) => {
- return new Date(dateString).toLocaleDateString('ru-RU', {
- day: '2-digit',
- month: '2-digit',
- year: 'numeric',
- hour: '2-digit',
- minute: '2-digit'
- })
+ try {
+ const date = new Date(dateString)
+ if (isNaN(date.getTime())) {
+ return 'Неизвестно'
+ }
+ return date.toLocaleDateString('ru-RU', {
+ day: '2-digit',
+ month: '2-digit',
+ year: 'numeric',
+ hour: '2-digit',
+ minute: '2-digit'
+ })
+ } catch (error) {
+ return 'Неизвестно'
+ }
}
const getInitials = (name?: string, phone?: string) => {
diff --git a/src/components/dashboard/sidebar.tsx b/src/components/dashboard/sidebar.tsx
index 43c82b4..f613a82 100644
--- a/src/components/dashboard/sidebar.tsx
+++ b/src/components/dashboard/sidebar.tsx
@@ -1,6 +1,7 @@
"use client"
import { useAuth } from '@/hooks/useAuth'
+import { useSidebar } from '@/hooks/useSidebar'
import { Button } from '@/components/ui/button'
import { Card } from '@/components/ui/card'
import { Avatar, AvatarImage, AvatarFallback } from '@/components/ui/avatar'
@@ -14,13 +15,16 @@ import {
Warehouse,
Users,
Truck,
- Handshake
+ Handshake,
+ ChevronLeft,
+ ChevronRight
} from 'lucide-react'
export function Sidebar() {
const { user, logout } = useAuth()
const router = useRouter()
const pathname = usePathname()
+ const { isCollapsed, toggleSidebar } = useSidebar()
const getInitials = () => {
const orgName = getOrganizationName()
@@ -86,6 +90,8 @@ export function Sidebar() {
router.push('/partners')
}
+
+
const isSettingsActive = pathname === '/settings'
const isMarketActive = pathname.startsWith('/market')
const isMessengerActive = pathname.startsWith('/messenger')
@@ -96,96 +102,149 @@ export function Sidebar() {
const isPartnersActive = pathname.startsWith('/partners')
return (
-
+
-
+ {/* Кнопка сворачивания */}
+
+
+
{/* Информация о пользователе */}
-
-
-
- {user?.avatar ? (
-
- ) : null}
-
- {getInitials()}
-
-
-
-
-
-
- {getOrganizationName()}
-
-
-
-
- {getCabinetType()}
+ {!isCollapsed ? (
+ // Развернутое состояние
+
+
+
+ {user?.avatar ? (
+
+ ) : null}
+
+ {getInitials()}
+
+
+
+
+
+
+ {getOrganizationName()}
+
+
+
+ {getCabinetType()}
+
+
-
+ ) : (
+ // Свернутое состояние
+
+
+
+ {user?.avatar ? (
+
+ ) : null}
+
+ {getInitials()}
+
+
+
+
+
+
+ {getOrganizationName().length > 12
+ ? getOrganizationName().substring(0, 12) + '...'
+ : getOrganizationName()
+ }
+
+
+
+
+ )}
{/* Навигация */}
-
+
{/* Услуги - только для фулфилмент центров */}
{user?.organization?.type === 'FULFILLMENT' && (
)}
@@ -193,15 +252,16 @@ export function Sidebar() {
{user?.organization?.type === 'FULFILLMENT' && (
)}
@@ -209,15 +269,16 @@ export function Sidebar() {
{user?.organization?.type === 'SELLER' && (
)}
@@ -225,41 +286,44 @@ export function Sidebar() {
{user?.organization?.type === 'WHOLESALE' && (
)}
{/* Кнопка выхода */}
-
+
diff --git a/src/components/dashboard/user-settings.tsx b/src/components/dashboard/user-settings.tsx
index 7703b47..a7f7c4d 100644
--- a/src/components/dashboard/user-settings.tsx
+++ b/src/components/dashboard/user-settings.tsx
@@ -14,6 +14,7 @@ import { Badge } from '@/components/ui/badge'
import { Alert, AlertDescription } from '@/components/ui/alert'
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'
import { Sidebar } from './sidebar'
+import { useSidebar } from '@/hooks/useSidebar'
import {
User,
Building2,
@@ -38,6 +39,7 @@ import { useState, useEffect } from 'react'
import Image from 'next/image'
export function UserSettings() {
+ const { getSidebarMargin } = useSidebar()
const { user } = useAuth()
const [updateUserProfile, { loading: isSaving }] = useMutation(UPDATE_USER_PROFILE)
const [updateOrganizationByInn, { loading: isUpdatingOrganization }] = useMutation(UPDATE_ORGANIZATION_BY_INN)
@@ -552,7 +554,7 @@ export function UserSettings() {
return (
-
+
{/* Сообщения о сохранении */}
{saveMessage && (
diff --git a/src/components/market/market-dashboard.tsx b/src/components/market/market-dashboard.tsx
index d2c894b..e7cd994 100644
--- a/src/components/market/market-dashboard.tsx
+++ b/src/components/market/market-dashboard.tsx
@@ -4,6 +4,7 @@ import { useState } from 'react'
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'
import { Card } from '@/components/ui/card'
import { Sidebar } from '@/components/dashboard/sidebar'
+import { useSidebar } from '@/hooks/useSidebar'
import { MarketProducts } from './market-products'
import { MarketCategories } from './market-categories'
import { MarketRequests } from './market-requests'
@@ -12,6 +13,7 @@ import { MarketBusiness } from './market-business'
import { FavoritesDashboard } from '../favorites/favorites-dashboard'
export function MarketDashboard() {
+ const { getSidebarMargin } = useSidebar()
const [productsView, setProductsView] = useState<'categories' | 'products' | 'cart' | 'favorites'>('categories')
const [selectedCategory, setSelectedCategory] = useState<{ id: string; name: string } | null>(null)
@@ -38,7 +40,7 @@ export function MarketDashboard() {
return (
-
+
{/* Основной контент с табами */}
diff --git a/src/components/messenger/messenger-dashboard.tsx b/src/components/messenger/messenger-dashboard.tsx
index 47d655d..fa56874 100644
--- a/src/components/messenger/messenger-dashboard.tsx
+++ b/src/components/messenger/messenger-dashboard.tsx
@@ -1,22 +1,17 @@
"use client"
-import React, { useState, useRef, useCallback } from 'react'
+import React, { useState } from 'react'
import { useQuery } from '@apollo/client'
import { Card } from '@/components/ui/card'
import { Button } from '@/components/ui/button'
import { Sidebar } from '@/components/dashboard/sidebar'
+import { useSidebar } from '@/hooks/useSidebar'
import { MessengerConversations } from './messenger-conversations'
import { MessengerChat } from './messenger-chat'
import { MessengerEmptyState } from './messenger-empty-state'
import { GET_MY_COUNTERPARTIES } from '@/graphql/queries'
-import {
- MessageCircle,
- PanelLeftOpen,
- PanelLeftClose,
- Maximize2,
- Minimize2,
- Settings
-} from 'lucide-react'
+import { Panel, PanelGroup, PanelResizeHandle } from 'react-resizable-panels'
+import { MessageCircle } from 'lucide-react'
interface Organization {
id: string
@@ -31,14 +26,9 @@ interface Organization {
createdAt: string
}
-type LeftPanelSize = 'compact' | 'normal' | 'wide' | 'hidden'
-
export function MessengerDashboard() {
+ const { getSidebarMargin } = useSidebar()
const [selectedCounterparty, setSelectedCounterparty] = useState
(null)
- const [leftPanelSize, setLeftPanelSize] = useState('normal')
- const [isResizing, setIsResizing] = useState(false)
- const [leftPanelWidth, setLeftPanelWidth] = useState(350)
- const resizeRef = useRef(null)
const { data: counterpartiesData, loading: counterpartiesLoading } = useQuery(GET_MY_COUNTERPARTIES)
const counterparties = counterpartiesData?.myCounterparties || []
@@ -49,85 +39,13 @@ export function MessengerDashboard() {
const selectedCounterpartyData = counterparties.find((cp: Organization) => cp.id === selectedCounterparty)
- // Получение ширины для разных размеров панели
- const getPanelWidth = (size: LeftPanelSize) => {
- switch (size) {
- case 'hidden': return 0
- case 'compact': return 280
- case 'normal': return 350
- case 'wide': return 450
- default: return 350
- }
- }
-
- const currentWidth = leftPanelSize === 'normal' ? leftPanelWidth : getPanelWidth(leftPanelSize)
-
- // Обработка изменения размера панели
- const handleMouseDown = useCallback((e: React.MouseEvent) => {
- setIsResizing(true)
- e.preventDefault()
- }, [])
-
- const handleMouseMove = useCallback((e: MouseEvent) => {
- if (!isResizing) return
-
- const newWidth = Math.min(Math.max(280, e.clientX - 56 - 24), 600) // 56px sidebar + 24px padding
- setLeftPanelWidth(newWidth)
- setLeftPanelSize('normal')
- }, [isResizing])
-
- const handleMouseUp = useCallback(() => {
- setIsResizing(false)
- }, [])
-
- // Добавляем глобальные обработчики для изменения размера
- React.useEffect(() => {
- if (isResizing) {
- document.addEventListener('mousemove', handleMouseMove)
- document.addEventListener('mouseup', handleMouseUp)
- document.body.style.cursor = 'col-resize'
- document.body.style.userSelect = 'none'
- } else {
- document.removeEventListener('mousemove', handleMouseMove)
- document.removeEventListener('mouseup', handleMouseUp)
- document.body.style.cursor = ''
- document.body.style.userSelect = ''
- }
-
- return () => {
- document.removeEventListener('mousemove', handleMouseMove)
- document.removeEventListener('mouseup', handleMouseUp)
- document.body.style.cursor = ''
- document.body.style.userSelect = ''
- }
- }, [isResizing, handleMouseMove, handleMouseUp])
-
- // Переключение размеров панели
- const togglePanelSize = () => {
- const sizes: LeftPanelSize[] = ['compact', 'normal', 'wide']
- const currentIndex = sizes.indexOf(leftPanelSize)
- const nextIndex = (currentIndex + 1) % sizes.length
- setLeftPanelSize(sizes[nextIndex])
- }
-
- const togglePanelVisibility = () => {
- setLeftPanelSize(leftPanelSize === 'hidden' ? 'normal' : 'hidden')
- }
-
// Если нет контрагентов, показываем заглушку
if (!counterpartiesLoading && counterparties.length === 0) {
return (
-
+
-
-
-
Мессенджер
-
Общение с контрагентами
-
-
-
@@ -142,115 +60,56 @@ export function MessengerDashboard() {
return (
-
+
- {/* Заголовок с управлением панелями */}
-
-
-
Мессенджер
-
Общение с контрагентами
-
-
- {/* Управление панелями */}
-
-
-
- {leftPanelSize !== 'hidden' && (
-
- )}
-
-
-
{/* Основной контент */}
-
+
{/* Левая панель - список контрагентов */}
- {leftPanelSize !== 'hidden' && (
- <>
-
-
-
+
+
+
+
+
- {/* Разделитель для изменения размера */}
- {leftPanelSize === 'normal' && (
-
- )}
- >
- )}
+ {/* Разделитель для изменения размера */}
+
+
+
+
{/* Правая панель - чат */}
-
- {selectedCounterparty && selectedCounterpartyData ? (
-
- ) : (
-
-
-
-
+
+
+ {selectedCounterparty && selectedCounterpartyData ? (
+
+ ) : (
+
+
+
+
+
+
Выберите контрагента
+
+ Начните беседу с одним из ваших контрагентов
+
-
Выберите контрагента
-
- Начните беседу с одним из ваших контрагентов
-
- {leftPanelSize === 'hidden' && (
-
- )}
-
- )}
-
-
+ )}
+
+
+
diff --git a/src/components/partners/partners-dashboard.tsx b/src/components/partners/partners-dashboard.tsx
index 59cd4eb..5321459 100644
--- a/src/components/partners/partners-dashboard.tsx
+++ b/src/components/partners/partners-dashboard.tsx
@@ -3,6 +3,7 @@
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'
import { Card } from '@/components/ui/card'
import { Sidebar } from '@/components/dashboard/sidebar'
+import { useSidebar } from '@/hooks/useSidebar'
import { MarketCounterparties } from '../market/market-counterparties'
import { MarketFulfillment } from '../market/market-fulfillment'
import { MarketSellers } from '../market/market-sellers'
@@ -10,10 +11,11 @@ import { MarketLogistics } from '../market/market-logistics'
import { MarketWholesale } from '../market/market-wholesale'
export function PartnersDashboard() {
+ const { getSidebarMargin } = useSidebar()
return (
-
+
{/* Основной контент с табами */}
diff --git a/src/components/services/services-dashboard.tsx b/src/components/services/services-dashboard.tsx
index 93c0dd8..cca22f7 100644
--- a/src/components/services/services-dashboard.tsx
+++ b/src/components/services/services-dashboard.tsx
@@ -2,15 +2,17 @@
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'
import { Sidebar } from '@/components/dashboard/sidebar'
+import { useSidebar } from '@/hooks/useSidebar'
import { ServicesTab } from './services-tab'
import { SuppliesTab } from './supplies-tab'
import { LogisticsTab } from './logistics-tab'
export function ServicesDashboard() {
+ const { getSidebarMargin } = useSidebar()
return (
-
+
{/* Основной контент с табами */}
diff --git a/src/components/warehouse/warehouse-dashboard.tsx b/src/components/warehouse/warehouse-dashboard.tsx
index bf4e311..bd2c85d 100644
--- a/src/components/warehouse/warehouse-dashboard.tsx
+++ b/src/components/warehouse/warehouse-dashboard.tsx
@@ -6,6 +6,7 @@ import { Card } from '@/components/ui/card'
import { Button } from '@/components/ui/button'
import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogTrigger } from '@/components/ui/dialog'
import { Sidebar } from '@/components/dashboard/sidebar'
+import { useSidebar } from '@/hooks/useSidebar'
import { ProductForm } from './product-form'
import { ProductCard } from './product-card'
import { GET_MY_PRODUCTS } from '@/graphql/queries'
@@ -34,6 +35,7 @@ interface Product {
}
export function WarehouseDashboard() {
+ const { getSidebarMargin } = useSidebar()
const [isDialogOpen, setIsDialogOpen] = useState(false)
const [editingProduct, setEditingProduct] = useState
(null)
const [searchQuery, setSearchQuery] = useState('')
@@ -104,7 +106,7 @@ export function WarehouseDashboard() {
return (
-
+
{/* Заголовок и поиск */}
@@ -114,14 +116,6 @@ export function WarehouseDashboard() {
-
-