Удален файл с тестовым заданием по системе управления сотрудниками. Обновлены зависимости в package.json и package-lock.json, добавлен новый пакет react-resizable-panels. Внесены изменения в компоненты для улучшения работы боковой панели и отображения дат. Добавлены новые функции для обработки дат в формате DateTime в GraphQL.

This commit is contained in:
Bivekich
2025-07-20 22:50:21 +03:00
parent 8d57fcd748
commit cc1f9d8473
16 changed files with 354 additions and 696 deletions

View File

@ -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() {
<div>
<h4 className="font-medium text-white">{category.name}</h4>
<p className="text-white/60 text-xs">
Создано: {new Date(category.createdAt).toLocaleDateString('ru-RU')}
Создано: {formatDate(category.createdAt)}
</p>
</div>
<div className="flex gap-1">

View File

@ -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) => {

View File

@ -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 (
<div className="fixed left-0 top-0 h-full w-56 bg-white/10 backdrop-blur-xl border-r border-white/20 p-3">
<div className={`fixed left-4 top-4 bottom-4 ${isCollapsed ? 'w-14' : 'w-72'} bg-white/10 backdrop-blur-xl border border-white/20 rounded-2xl ${isCollapsed ? 'p-2' : 'p-3'} transition-all duration-300 ease-in-out z-50`}>
<div className="flex flex-col h-full">
{/* Кнопка сворачивания */}
<div className={`flex ${isCollapsed ? 'justify-center' : 'justify-end'} ${isCollapsed ? 'mb-2' : 'mb-3'}`}>
<Button
variant="ghost"
size="icon"
onClick={toggleSidebar}
className={`${isCollapsed ? 'h-7 w-7' : 'h-8 w-8'} text-white/60 hover:text-white hover:bg-white/10 transition-all duration-200`}
>
{isCollapsed ? (
<ChevronRight className="h-4 w-4" />
) : (
<ChevronLeft className="h-4 w-4" />
)}
</Button>
</div>
{/* Информация о пользователе */}
<Card className="bg-gradient-to-br from-white/15 to-white/5 backdrop-blur border border-white/30 p-4 mb-4 shadow-lg">
<div className="flex items-center space-x-3">
<div className="relative">
<Avatar className="h-12 w-12 flex-shrink-0 ring-2 ring-white/20">
{user?.avatar ? (
<AvatarImage
src={user.avatar}
alt="Аватар пользователя"
className="w-full h-full object-cover"
/>
) : null}
<AvatarFallback className="bg-gradient-to-br from-purple-500 to-purple-600 text-white text-sm font-semibold">
{getInitials()}
</AvatarFallback>
</Avatar>
<div className="absolute -bottom-0.5 -right-0.5 w-3 h-3 bg-green-400 rounded-full border-2 border-white/20"></div>
</div>
<div className="flex-1 min-w-0">
<p className="text-white text-sm font-semibold truncate mb-1" title={getOrganizationName()}>
{getOrganizationName()}
</p>
<div className="flex items-center space-x-1">
<div className="w-2 h-2 bg-purple-400 rounded-full flex-shrink-0"></div>
<p className="text-white/70 text-xs font-medium">
{getCabinetType()}
{!isCollapsed ? (
// Развернутое состояние
<div className="flex items-center space-x-3">
<div className="relative flex-shrink-0">
<Avatar className="h-12 w-12 ring-2 ring-white/20">
{user?.avatar ? (
<AvatarImage
src={user.avatar}
alt="Аватар пользователя"
className="w-full h-full object-cover"
/>
) : null}
<AvatarFallback className="bg-gradient-to-br from-purple-500 to-purple-600 text-white text-sm font-semibold">
{getInitials()}
</AvatarFallback>
</Avatar>
<div className="absolute -bottom-0.5 -right-0.5 w-3 h-3 bg-green-400 rounded-full border-2 border-white/20"></div>
</div>
<div className="flex-1 min-w-0">
<p className="text-white text-sm font-semibold mb-1 break-words" title={getOrganizationName()}>
{getOrganizationName()}
</p>
<div className="flex items-center space-x-1">
<div className="w-2 h-2 bg-purple-400 rounded-full flex-shrink-0"></div>
<p className="text-white/70 text-xs font-medium">
{getCabinetType()}
</p>
</div>
</div>
</div>
</div>
) : (
// Свернутое состояние
<div className="flex flex-col items-center">
<div className="relative mb-2">
<Avatar className="h-10 w-10 ring-2 ring-white/20">
{user?.avatar ? (
<AvatarImage
src={user.avatar}
alt="Аватар пользователя"
className="w-full h-full object-cover"
/>
) : null}
<AvatarFallback className="bg-gradient-to-br from-purple-500 to-purple-600 text-white text-xs font-semibold">
{getInitials()}
</AvatarFallback>
</Avatar>
<div className="absolute -bottom-0.5 -right-0.5 w-2.5 h-2.5 bg-green-400 rounded-full border border-white/20"></div>
</div>
<div className="text-center">
<p className="text-white text-[10px] font-semibold leading-tight max-w-full break-words"
title={getOrganizationName()}
style={{ fontSize: '9px', lineHeight: '11px' }}>
{getOrganizationName().length > 12
? getOrganizationName().substring(0, 12) + '...'
: getOrganizationName()
}
</p>
<div className="flex items-center justify-center mt-1">
<div className="w-1.5 h-1.5 bg-purple-400 rounded-full"></div>
</div>
</div>
</div>
)}
</Card>
{/* Навигация */}
<div className="space-y-1 mb-3">
<div className="space-y-1 mb-3 flex-1">
<Button
variant={isMarketActive ? "secondary" : "ghost"}
className={`w-full justify-start text-left transition-all duration-200 h-8 text-xs ${
className={`w-full ${isCollapsed ? 'justify-center px-2 h-9' : 'justify-start h-10'} text-left transition-all duration-200 text-xs ${
isMarketActive
? 'bg-white/20 text-white hover:bg-white/30'
: 'text-white/80 hover:bg-white/10 hover:text-white'
} cursor-pointer`}
onClick={handleMarketClick}
title={isCollapsed ? "Маркет" : ""}
>
<Store className="h-3 w-3 mr-2" />
Маркет
<Store className={`${isCollapsed ? 'h-4 w-4' : 'h-4 w-4'} flex-shrink-0`} />
{!isCollapsed && <span className="ml-3">Маркет</span>}
</Button>
<Button
variant={isMessengerActive ? "secondary" : "ghost"}
className={`w-full justify-start text-left transition-all duration-200 h-8 text-xs ${
className={`w-full ${isCollapsed ? 'justify-center px-2 h-9' : 'justify-start h-10'} text-left transition-all duration-200 text-xs ${
isMessengerActive
? 'bg-white/20 text-white hover:bg-white/30'
: 'text-white/80 hover:bg-white/10 hover:text-white'
} cursor-pointer`}
onClick={handleMessengerClick}
title={isCollapsed ? "Мессенджер" : ""}
>
<MessageCircle className="h-3 w-3 mr-2" />
Мессенджер
<MessageCircle className="h-4 w-4 flex-shrink-0" />
{!isCollapsed && <span className="ml-3">Мессенджер</span>}
</Button>
<Button
variant={isPartnersActive ? "secondary" : "ghost"}
className={`w-full justify-start text-left transition-all duration-200 h-8 text-xs ${
className={`w-full ${isCollapsed ? 'justify-center px-2 h-9' : 'justify-start h-10'} text-left transition-all duration-200 text-xs ${
isPartnersActive
? 'bg-white/20 text-white hover:bg-white/30'
: 'text-white/80 hover:bg-white/10 hover:text-white'
} cursor-pointer`}
onClick={handlePartnersClick}
title={isCollapsed ? "Партнёры" : ""}
>
<Handshake className="h-3 w-3 mr-2" />
Партнёры
<Handshake className="h-4 w-4 flex-shrink-0" />
{!isCollapsed && <span className="ml-3">Партнёры</span>}
</Button>
{/* Услуги - только для фулфилмент центров */}
{user?.organization?.type === 'FULFILLMENT' && (
<Button
variant={isServicesActive ? "secondary" : "ghost"}
className={`w-full justify-start text-left transition-all duration-200 h-8 text-xs ${
className={`w-full ${isCollapsed ? 'justify-center px-2 h-9' : 'justify-start h-10'} text-left transition-all duration-200 text-xs ${
isServicesActive
? 'bg-white/20 text-white hover:bg-white/30'
: 'text-white/80 hover:bg-white/10 hover:text-white'
} cursor-pointer`}
onClick={handleServicesClick}
title={isCollapsed ? "Услуги" : ""}
>
<Wrench className="h-3 w-3 mr-2" />
Услуги
<Wrench className="h-4 w-4 flex-shrink-0" />
{!isCollapsed && <span className="ml-3">Услуги</span>}
</Button>
)}
@ -193,15 +252,16 @@ export function Sidebar() {
{user?.organization?.type === 'FULFILLMENT' && (
<Button
variant={isEmployeesActive ? "secondary" : "ghost"}
className={`w-full justify-start text-left transition-all duration-200 h-8 text-xs ${
className={`w-full ${isCollapsed ? 'justify-center px-2 h-9' : 'justify-start h-10'} text-left transition-all duration-200 text-xs ${
isEmployeesActive
? 'bg-white/20 text-white hover:bg-white/30'
: 'text-white/80 hover:bg-white/10 hover:text-white'
} cursor-pointer`}
onClick={handleEmployeesClick}
title={isCollapsed ? "Сотрудники" : ""}
>
<Users className="h-3 w-3 mr-2" />
Сотрудники
<Users className="h-4 w-4 flex-shrink-0" />
{!isCollapsed && <span className="ml-3">Сотрудники</span>}
</Button>
)}
@ -209,15 +269,16 @@ export function Sidebar() {
{user?.organization?.type === 'SELLER' && (
<Button
variant={isSuppliesActive ? "secondary" : "ghost"}
className={`w-full justify-start text-left transition-all duration-200 h-8 text-xs ${
className={`w-full ${isCollapsed ? 'justify-center px-2 h-9' : 'justify-start h-10'} text-left transition-all duration-200 text-xs ${
isSuppliesActive
? 'bg-white/20 text-white hover:bg-white/30'
: 'text-white/80 hover:bg-white/10 hover:text-white'
} cursor-pointer`}
onClick={handleSuppliesClick}
title={isCollapsed ? "Поставки" : ""}
>
<Truck className="h-3 w-3 mr-2" />
Поставки
<Truck className="h-4 w-4 flex-shrink-0" />
{!isCollapsed && <span className="ml-3">Поставки</span>}
</Button>
)}
@ -225,41 +286,44 @@ export function Sidebar() {
{user?.organization?.type === 'WHOLESALE' && (
<Button
variant={isWarehouseActive ? "secondary" : "ghost"}
className={`w-full justify-start text-left transition-all duration-200 h-8 text-xs ${
className={`w-full ${isCollapsed ? 'justify-center px-2 h-9' : 'justify-start h-10'} text-left transition-all duration-200 text-xs ${
isWarehouseActive
? 'bg-white/20 text-white hover:bg-white/30'
: 'text-white/80 hover:bg-white/10 hover:text-white'
} cursor-pointer`}
onClick={handleWarehouseClick}
title={isCollapsed ? "Склад" : ""}
>
<Warehouse className="h-3 w-3 mr-2" />
Склад
<Warehouse className="h-4 w-4 flex-shrink-0" />
{!isCollapsed && <span className="ml-3">Склад</span>}
</Button>
)}
<Button
variant={isSettingsActive ? "secondary" : "ghost"}
className={`w-full justify-start text-left transition-all duration-200 h-8 text-xs ${
className={`w-full ${isCollapsed ? 'justify-center px-2 h-9' : 'justify-start h-10'} text-left transition-all duration-200 text-xs ${
isSettingsActive
? 'bg-white/20 text-white hover:bg-white/30'
: 'text-white/80 hover:bg-white/10 hover:text-white'
} cursor-pointer`}
onClick={handleSettingsClick}
title={isCollapsed ? "Настройки профиля" : ""}
>
<Settings className="h-3 w-3 mr-2" />
Настройки профиля
<Settings className="h-4 w-4 flex-shrink-0" />
{!isCollapsed && <span className="ml-3">Настройки профиля</span>}
</Button>
</div>
{/* Кнопка выхода */}
<div className="flex-1 flex items-end">
<div>
<Button
variant="ghost"
className="w-full justify-start text-white/80 hover:bg-red-500/20 hover:text-red-300 cursor-pointer h-8 text-xs"
className={`w-full ${isCollapsed ? 'justify-center px-2 h-9' : 'justify-start h-10'} text-white/80 hover:bg-red-500/20 hover:text-red-300 cursor-pointer text-xs transition-all duration-200`}
onClick={logout}
title={isCollapsed ? "Выйти" : ""}
>
<LogOut className="h-3 w-3 mr-2" />
Выйти
<LogOut className="h-4 w-4 flex-shrink-0" />
{!isCollapsed && <span className="ml-3">Выйти</span>}
</Button>
</div>
</div>

View File

@ -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 (
<div className="h-screen flex overflow-hidden">
<Sidebar />
<main className="flex-1 ml-56 px-6 py-4 overflow-hidden">
<main className={`flex-1 ${getSidebarMargin()} px-6 py-4 overflow-hidden transition-all duration-300`}>
<div className="h-full w-full flex flex-col">
{/* Сообщения о сохранении */}
{saveMessage && (

View File

@ -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 (
<div className="h-screen flex overflow-hidden">
<Sidebar />
<main className="flex-1 ml-56 px-6 py-4 overflow-hidden">
<main className={`flex-1 ${getSidebarMargin()} px-6 py-4 overflow-hidden transition-all duration-300`}>
<div className="h-full w-full flex flex-col">
{/* Основной контент с табами */}
<div className="flex-1 overflow-hidden">

View File

@ -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<string | null>(null)
const [leftPanelSize, setLeftPanelSize] = useState<LeftPanelSize>('normal')
const [isResizing, setIsResizing] = useState(false)
const [leftPanelWidth, setLeftPanelWidth] = useState(350)
const resizeRef = useRef<HTMLDivElement>(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 (
<div className="h-screen flex overflow-hidden">
<Sidebar />
<main className="flex-1 ml-56 px-6 py-4 overflow-hidden">
<main className={`flex-1 ${getSidebarMargin()} px-6 py-4 overflow-hidden transition-all duration-300`}>
<div className="h-full w-full flex flex-col">
<div className="flex items-center justify-between mb-4 flex-shrink-0">
<div>
<h1 className="text-xl font-bold text-white mb-1">Мессенджер</h1>
<p className="text-white/70 text-sm">Общение с контрагентами</p>
</div>
</div>
<div className="flex-1 overflow-hidden">
<Card className="glass-card h-full overflow-hidden p-6">
<MessengerEmptyState />
@ -142,115 +60,56 @@ export function MessengerDashboard() {
return (
<div className="h-screen flex overflow-hidden">
<Sidebar />
<main className="flex-1 ml-56 px-6 py-4 overflow-hidden">
<main className={`flex-1 ${getSidebarMargin()} px-6 py-4 overflow-hidden transition-all duration-300`}>
<div className="h-full w-full flex flex-col">
{/* Заголовок с управлением панелями */}
<div className="flex items-center justify-between mb-4 flex-shrink-0">
<div>
<h1 className="text-xl font-bold text-white mb-1">Мессенджер</h1>
<p className="text-white/70 text-sm">Общение с контрагентами</p>
</div>
{/* Управление панелями */}
<div className="flex items-center gap-2">
<Button
variant="outline"
size="sm"
onClick={togglePanelVisibility}
className="bg-white/10 hover:bg-white/20 text-white border-white/20 h-8"
title={leftPanelSize === 'hidden' ? 'Показать список' : 'Скрыть список'}
>
{leftPanelSize === 'hidden' ? (
<PanelLeftOpen className="h-4 w-4" />
) : (
<PanelLeftClose className="h-4 w-4" />
)}
</Button>
{leftPanelSize !== 'hidden' && (
<Button
variant="outline"
size="sm"
onClick={togglePanelSize}
className="bg-white/10 hover:bg-white/20 text-white border-white/20 h-8"
title="Изменить размер списка"
>
{leftPanelSize === 'compact' ? (
<Minimize2 className="h-4 w-4" />
) : leftPanelSize === 'wide' ? (
<Maximize2 className="h-4 w-4" />
) : (
<Settings className="h-4 w-4" />
)}
<span className="ml-1 text-xs">
{leftPanelSize === 'compact' ? 'Компакт' :
leftPanelSize === 'wide' ? 'Широкий' : 'Обычный'}
</span>
</Button>
)}
</div>
</div>
{/* Основной контент */}
<div className="flex-1 overflow-hidden">
<div className="flex gap-4 h-full">
<PanelGroup direction="horizontal" className="h-full">
{/* Левая панель - список контрагентов */}
{leftPanelSize !== 'hidden' && (
<>
<Card
className="glass-card h-full overflow-hidden p-4 transition-all duration-200 ease-in-out"
style={{ width: `${currentWidth}px` }}
>
<MessengerConversations
counterparties={counterparties}
loading={counterpartiesLoading}
selectedCounterparty={selectedCounterparty}
onSelectCounterparty={handleSelectCounterparty}
compact={leftPanelSize === 'compact'}
/>
</Card>
<Panel
defaultSize={30}
minSize={15}
maxSize={50}
className="pr-2"
>
<Card className="glass-card h-full overflow-hidden p-4">
<MessengerConversations
counterparties={counterparties}
loading={counterpartiesLoading}
selectedCounterparty={selectedCounterparty}
onSelectCounterparty={handleSelectCounterparty}
compact={false}
/>
</Card>
</Panel>
{/* Разделитель для изменения размера */}
{leftPanelSize === 'normal' && (
<div
ref={resizeRef}
className="w-1 bg-white/10 hover:bg-white/20 cursor-col-resize transition-colors relative group"
onMouseDown={handleMouseDown}
>
<div className="absolute inset-y-0 -inset-x-1 group-hover:bg-white/5 transition-colors" />
<div className="absolute top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2 w-3 h-8 bg-white/20 rounded-full opacity-0 group-hover:opacity-100 transition-opacity" />
</div>
)}
</>
)}
{/* Разделитель для изменения размера */}
<PanelResizeHandle className="w-2 hover:bg-white/10 transition-colors relative group cursor-col-resize">
<div className="absolute inset-y-0 left-1/2 transform -translate-x-1/2 w-1 bg-white/10 group-hover:bg-white/20 transition-colors" />
<div className="absolute top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2 w-1 h-8 bg-white/30 rounded-full opacity-0 group-hover:opacity-100 transition-opacity" />
</PanelResizeHandle>
{/* Правая панель - чат */}
<Card className="glass-card h-full overflow-hidden flex-1">
{selectedCounterparty && selectedCounterpartyData ? (
<MessengerChat counterparty={selectedCounterpartyData} />
) : (
<div className="flex items-center justify-center h-full">
<div className="text-center">
<div className="w-16 h-16 bg-white/10 rounded-full flex items-center justify-center mx-auto mb-4">
<MessageCircle className="h-8 w-8 text-white/40" />
<Panel defaultSize={70} className="pl-2">
<Card className="glass-card h-full overflow-hidden">
{selectedCounterparty && selectedCounterpartyData ? (
<MessengerChat counterparty={selectedCounterpartyData} />
) : (
<div className="flex items-center justify-center h-full">
<div className="text-center">
<div className="w-16 h-16 bg-white/10 rounded-full flex items-center justify-center mx-auto mb-4">
<MessageCircle className="h-8 w-8 text-white/40" />
</div>
<p className="text-white/60 text-lg mb-2">Выберите контрагента</p>
<p className="text-white/40 text-sm">
Начните беседу с одним из ваших контрагентов
</p>
</div>
<p className="text-white/60 text-lg mb-2">Выберите контрагента</p>
<p className="text-white/40 text-sm">
Начните беседу с одним из ваших контрагентов
</p>
{leftPanelSize === 'hidden' && (
<Button
onClick={togglePanelVisibility}
className="mt-4 bg-purple-600 hover:bg-purple-700 text-white"
>
Показать список контрагентов
</Button>
)}
</div>
</div>
)}
</Card>
</div>
)}
</Card>
</Panel>
</PanelGroup>
</div>
</div>
</main>

View File

@ -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 (
<div className="h-screen flex overflow-hidden">
<Sidebar />
<main className="flex-1 ml-56 px-6 py-4 overflow-hidden">
<main className={`flex-1 ${getSidebarMargin()} px-6 py-4 overflow-hidden transition-all duration-300`}>
<div className="h-full w-full flex flex-col">
{/* Основной контент с табами */}
<div className="flex-1 overflow-hidden">

View File

@ -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 (
<div className="h-screen flex overflow-hidden">
<Sidebar />
<main className="flex-1 ml-56 px-6 py-4 overflow-hidden">
<main className={`flex-1 ${getSidebarMargin()} px-6 py-4 overflow-hidden transition-all duration-300`}>
<div className="h-full w-full flex flex-col">
{/* Основной контент с табами */}
<div className="flex-1 overflow-hidden">

View File

@ -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<Product | null>(null)
const [searchQuery, setSearchQuery] = useState('')
@ -104,7 +106,7 @@ export function WarehouseDashboard() {
return (
<div className="h-screen flex overflow-hidden">
<Sidebar />
<main className="flex-1 ml-56 px-6 py-4 overflow-hidden">
<main className={`flex-1 ${getSidebarMargin()} px-6 py-4 overflow-hidden transition-all duration-300`}>
<div className="h-full w-full flex flex-col">
{/* Заголовок и поиск */}
<div className="flex items-center justify-between mb-4 flex-shrink-0">
@ -114,14 +116,6 @@ export function WarehouseDashboard() {
</div>
<div className="flex gap-2">
<Button
onClick={() => window.open('/admin', '_blank')}
variant="outline"
className="bg-white/10 hover:bg-white/20 text-white border-white/20 hover:border-white/30"
>
Управление категориями
</Button>
<Dialog open={isDialogOpen} onOpenChange={setIsDialogOpen}>
<DialogTrigger asChild>
<Button