Добавлена поддержка входящих заявок в компоненты панели и мессенджера. Обновлены запросы GraphQL для получения данных о новых заявках, добавлены индикаторы для отображения количества входящих заявок в интерфейсе. Оптимизирован код для улучшения читаемости и взаимодействия с пользователем.
This commit is contained in:
57
src/app/employees/new/page.tsx
Normal file
57
src/app/employees/new/page.tsx
Normal 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>
|
||||
)
|
||||
}
|
@ -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>
|
||||
|
||||
{/* Услуги - только для фулфилмент центров */}
|
||||
|
@ -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" />
|
||||
|
@ -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">
|
||||
|
@ -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"
|
||||
|
Reference in New Issue
Block a user