Добавлена поддержка входящих заявок в компоненты панели и мессенджера. Обновлены запросы 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 { Avatar, AvatarImage, AvatarFallback } from '@/components/ui/avatar'
import { useRouter, usePathname } from 'next/navigation' import { useRouter, usePathname } from 'next/navigation'
import { useQuery } from '@apollo/client' import { useQuery } from '@apollo/client'
import { GET_CONVERSATIONS } from '@/graphql/queries' import { GET_CONVERSATIONS, GET_INCOMING_REQUESTS } from '@/graphql/queries'
import { import {
Settings, Settings,
LogOut, LogOut,
@ -36,8 +36,18 @@ export function Sidebar() {
notifyOnNetworkStatusChange: false, // Плавные обновления без мерцания notifyOnNetworkStatusChange: false, // Плавные обновления без мерцания
}) })
// Загружаем входящие заявки для подсчета новых запросов
const { data: incomingRequestsData } = useQuery(GET_INCOMING_REQUESTS, {
pollInterval: 60000, // Обновляем каждую минуту
fetchPolicy: 'cache-first',
errorPolicy: 'ignore',
notifyOnNetworkStatusChange: false,
})
const conversations = conversationsData?.conversations || [] const conversations = conversationsData?.conversations || []
const incomingRequests = incomingRequestsData?.incomingRequests || []
const totalUnreadCount = conversations.reduce((sum: number, conv: { unreadCount?: number }) => sum + (conv.unreadCount || 0), 0) const totalUnreadCount = conversations.reduce((sum: number, conv: { unreadCount?: number }) => sum + (conv.unreadCount || 0), 0)
const incomingRequestsCount = incomingRequests.length
const getInitials = () => { const getInitials = () => {
const orgName = getOrganizationName() const orgName = getOrganizationName()
@ -258,7 +268,7 @@ export function Sidebar() {
<Button <Button
variant={isPartnersActive ? "secondary" : "ghost"} 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 isPartnersActive
? 'bg-white/20 text-white hover:bg-white/30' ? 'bg-white/20 text-white hover:bg-white/30'
: 'text-white/80 hover:bg-white/10 hover:text-white' : '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" /> <Handshake className="h-4 w-4 flex-shrink-0" />
{!isCollapsed && <span className="ml-3">Партнёры</span>} {!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> </Button>
{/* Услуги - только для фулфилмент центров */} {/* Услуги - только для фулфилмент центров */}

View File

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

View File

@ -338,17 +338,17 @@ export function MessengerChat({ counterparty, onMessagesRead }: MessengerChatPro
</AvatarFallback> </AvatarFallback>
</Avatar> </Avatar>
<div> <div>
<h3 className="font-semibold text-white">
{getOrganizationName(counterparty)}
</h3>
<div className="flex items-center space-x-2"> <div className="flex items-center space-x-2">
<h3 className="font-semibold text-white">
{getOrganizationName(counterparty)}
</h3>
<Badge className={`${getTypeColor(counterparty.type)} text-xs`}> <Badge className={`${getTypeColor(counterparty.type)} text-xs`}>
{getTypeLabel(counterparty.type)} {getTypeLabel(counterparty.type)}
</Badge> </Badge>
<p className="text-white/60 text-xs">
{getShortCompanyName(counterparty.fullName || '')}
</p>
</div> </div>
<p className="text-white/60 text-xs">
{getShortCompanyName(counterparty.fullName || '')}
</p>
</div> </div>
</div> </div>

View File

@ -4,6 +4,8 @@ import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'
import { Card } from '@/components/ui/card' import { Card } from '@/components/ui/card'
import { Sidebar } from '@/components/dashboard/sidebar' import { Sidebar } from '@/components/dashboard/sidebar'
import { useSidebar } from '@/hooks/useSidebar' import { useSidebar } from '@/hooks/useSidebar'
import { useQuery } from '@apollo/client'
import { GET_INCOMING_REQUESTS } from '@/graphql/queries'
import { MarketCounterparties } from '../market/market-counterparties' import { MarketCounterparties } from '../market/market-counterparties'
import { MarketFulfillment } from '../market/market-fulfillment' import { MarketFulfillment } from '../market/market-fulfillment'
import { MarketSellers } from '../market/market-sellers' import { MarketSellers } from '../market/market-sellers'
@ -12,6 +14,17 @@ import { MarketWholesale } from '../market/market-wholesale'
export function PartnersDashboard() { export function PartnersDashboard() {
const { getSidebarMargin } = useSidebar() 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 ( return (
<div className="h-screen flex overflow-hidden"> <div className="h-screen flex overflow-hidden">
<Sidebar /> <Sidebar />
@ -20,12 +33,15 @@ export function PartnersDashboard() {
{/* Основной контент с табами */} {/* Основной контент с табами */}
<div className="flex-1 overflow-hidden"> <div className="flex-1 overflow-hidden">
<Tabs defaultValue="counterparties" className="h-full flex flex-col"> <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 <TabsTrigger
value="counterparties" 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>
<TabsTrigger <TabsTrigger
value="fulfillment" value="fulfillment"