Удален файл с тестовым заданием по системе управления сотрудниками. Обновлены зависимости в package.json и package-lock.json, добавлен новый пакет react-resizable-panels. Внесены изменения в компоненты для улучшения работы боковой панели и отображения дат. Добавлены новые функции для обработки дат в формате DateTime в GraphQL.
This commit is contained in:
@ -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>
|
||||
|
Reference in New Issue
Block a user