Добавлена поддержка входящих заявок в компоненты панели и мессенджера. Обновлены запросы GraphQL для получения данных о новых заявках, добавлены индикаторы для отображения количества входящих заявок в интерфейсе. Оптимизирован код для улучшения читаемости и взаимодействия с пользователем.

This commit is contained in:
Bivekich
2025-07-21 15:52:09 +03:00
parent 674eb33e5a
commit 85b1758950
5 changed files with 107 additions and 11 deletions

View File

@ -0,0 +1,57 @@
"use client"
import { AuthGuard } from '@/components/auth-guard'
import { EmployeeForm } from '@/components/employees/employee-form'
import { Card } from '@/components/ui/card'
import { Sidebar } from '@/components/dashboard/sidebar'
import { useSidebar } from '@/hooks/useSidebar'
import { UserPlus, ArrowLeft } from 'lucide-react'
import { Button } from '@/components/ui/button'
import { useRouter } from 'next/navigation'
export default function NewEmployeePage() {
const { getSidebarMargin } = useSidebar()
const router = useRouter()
return (
<AuthGuard>
<div className="h-screen flex overflow-hidden">
<Sidebar />
<main className={`flex-1 ${getSidebarMargin()} px-6 py-4 overflow-auto transition-all duration-300`}>
<div className="max-w-4xl mx-auto">
{/* Заголовок */}
<div className="flex items-center space-x-4 mb-6">
<Button
variant="ghost"
onClick={() => router.back()}
className="text-white/80 hover:text-white hover:bg-white/10"
>
<ArrowLeft className="h-4 w-4 mr-2" />
Назад
</Button>
<div className="flex items-center space-x-3">
<UserPlus className="h-6 w-6 text-blue-400" />
<div>
<h1 className="text-2xl font-bold text-white">Добавить сотрудника</h1>
<p className="text-white/60">Создание нового сотрудника организации</p>
</div>
</div>
</div>
{/* Форма */}
<Card className="glass-card p-6">
<EmployeeForm
onSave={(employeeData) => {
// TODO: Добавить создание сотрудника
console.log('Создание сотрудника:', employeeData)
router.push('/employees')
}}
onCancel={() => router.push('/employees')}
/>
</Card>
</div>
</main>
</div>
</AuthGuard>
)
}

View File

@ -7,7 +7,7 @@ import { Card } from '@/components/ui/card'
import { Avatar, AvatarImage, AvatarFallback } from '@/components/ui/avatar'
import { useRouter, usePathname } from 'next/navigation'
import { useQuery } from '@apollo/client'
import { GET_CONVERSATIONS } from '@/graphql/queries'
import { GET_CONVERSATIONS, GET_INCOMING_REQUESTS } from '@/graphql/queries'
import {
Settings,
LogOut,
@ -36,8 +36,18 @@ export function Sidebar() {
notifyOnNetworkStatusChange: false, // Плавные обновления без мерцания
})
// Загружаем входящие заявки для подсчета новых запросов
const { data: incomingRequestsData } = useQuery(GET_INCOMING_REQUESTS, {
pollInterval: 60000, // Обновляем каждую минуту
fetchPolicy: 'cache-first',
errorPolicy: 'ignore',
notifyOnNetworkStatusChange: false,
})
const conversations = conversationsData?.conversations || []
const incomingRequests = incomingRequestsData?.incomingRequests || []
const totalUnreadCount = conversations.reduce((sum: number, conv: { unreadCount?: number }) => sum + (conv.unreadCount || 0), 0)
const incomingRequestsCount = incomingRequests.length
const getInitials = () => {
const orgName = getOrganizationName()
@ -258,7 +268,7 @@ export function Sidebar() {
<Button
variant={isPartnersActive ? "secondary" : "ghost"}
className={`w-full ${isCollapsed ? 'justify-center px-2 h-9' : 'justify-start h-10'} text-left transition-all duration-200 text-xs ${
className={`w-full ${isCollapsed ? 'justify-center px-2 h-9' : 'justify-start h-10'} text-left transition-all duration-200 text-xs relative ${
isPartnersActive
? 'bg-white/20 text-white hover:bg-white/30'
: 'text-white/80 hover:bg-white/10 hover:text-white'
@ -268,6 +278,16 @@ export function Sidebar() {
>
<Handshake className="h-4 w-4 flex-shrink-0" />
{!isCollapsed && <span className="ml-3">Партнёры</span>}
{/* Индикатор входящих заявок */}
{incomingRequestsCount > 0 && (
<div className={`absolute ${
isCollapsed
? 'top-1 right-1 w-3 h-3'
: 'top-2 right-2 w-4 h-4'
} bg-red-500 text-white text-xs rounded-full flex items-center justify-center font-bold`}>
{isCollapsed ? '' : (incomingRequestsCount > 99 ? '99+' : incomingRequestsCount)}
</div>
)}
</Button>
{/* Услуги - только для фулфилмент центров */}

View File

@ -167,9 +167,12 @@ export function MarketCounterparties() {
<Users className="h-4 w-4 mr-2" />
Контрагенты ({counterparties.length})
</TabsTrigger>
<TabsTrigger value="incoming" className="data-[state=active]:bg-green-500/20 data-[state=active]:text-green-300">
<TabsTrigger value="incoming" className={`data-[state=active]:bg-green-500/20 data-[state=active]:text-green-300 relative ${incomingRequests.length > 0 ? 'ring-2 ring-green-400/50 animate-pulse' : ''}`}>
<ArrowDownCircle className="h-4 w-4 mr-2" />
Входящие ({incomingRequests.length})
{incomingRequests.length > 0 && (
<div className="absolute -top-1 -right-1 w-3 h-3 bg-green-500 rounded-full"></div>
)}
</TabsTrigger>
<TabsTrigger value="outgoing" className="data-[state=active]:bg-orange-500/20 data-[state=active]:text-orange-300">
<ArrowUpCircle className="h-4 w-4 mr-2" />

View File

@ -338,19 +338,19 @@ export function MessengerChat({ counterparty, onMessagesRead }: MessengerChatPro
</AvatarFallback>
</Avatar>
<div>
<div className="flex items-center space-x-2">
<h3 className="font-semibold text-white">
{getOrganizationName(counterparty)}
</h3>
<div className="flex items-center space-x-2">
<Badge className={`${getTypeColor(counterparty.type)} text-xs`}>
{getTypeLabel(counterparty.type)}
</Badge>
</div>
<p className="text-white/60 text-xs">
{getShortCompanyName(counterparty.fullName || '')}
</p>
</div>
</div>
</div>
{/* Переключатель между чатом и вложениями */}
<div className="flex items-center space-x-2">

View File

@ -4,6 +4,8 @@ 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 { useQuery } from '@apollo/client'
import { GET_INCOMING_REQUESTS } from '@/graphql/queries'
import { MarketCounterparties } from '../market/market-counterparties'
import { MarketFulfillment } from '../market/market-fulfillment'
import { MarketSellers } from '../market/market-sellers'
@ -12,6 +14,17 @@ import { MarketWholesale } from '../market/market-wholesale'
export function PartnersDashboard() {
const { getSidebarMargin } = useSidebar()
// Загружаем входящие заявки для подсветки
const { data: incomingRequestsData } = useQuery(GET_INCOMING_REQUESTS, {
pollInterval: 30000, // Обновляем каждые 30 секунд
fetchPolicy: 'cache-first',
errorPolicy: 'ignore',
})
const incomingRequests = incomingRequestsData?.incomingRequests || []
const hasIncomingRequests = incomingRequests.length > 0
return (
<div className="h-screen flex overflow-hidden">
<Sidebar />
@ -20,12 +33,15 @@ export function PartnersDashboard() {
{/* Основной контент с табами */}
<div className="flex-1 overflow-hidden">
<Tabs defaultValue="counterparties" className="h-full flex flex-col">
<TabsList className="grid w-full grid-cols-5 bg-white/5 backdrop-blur border-white/10 flex-shrink-0">
<TabsList className={`grid w-full grid-cols-5 bg-white/5 backdrop-blur border-white/10 flex-shrink-0 ${hasIncomingRequests ? 'ring-2 ring-blue-400/50' : ''}`}>
<TabsTrigger
value="counterparties"
className="data-[state=active]:bg-white/20 data-[state=active]:text-white text-white/70"
className={`data-[state=active]:bg-white/20 data-[state=active]:text-white text-white/70 relative ${hasIncomingRequests ? 'animate-pulse' : ''}`}
>
Мои контрагенты
{hasIncomingRequests && (
<div className="absolute -top-1 -right-1 w-3 h-3 bg-blue-500 rounded-full"></div>
)}
</TabsTrigger>
<TabsTrigger
value="fulfillment"