Добавлены новые поля в модель Supply и обновлены компоненты для работы с расходниками. Реализована логика загрузки и отображения чатов с непрочитанными сообщениями в мессенджере. Обновлены запросы и мутации GraphQL для поддержки новых полей. Исправлены ошибки отображения и добавлены индикаторы для непрочитанных сообщений.
This commit is contained in:
@ -1,91 +1,43 @@
|
||||
"use client"
|
||||
|
||||
import { useState } from 'react'
|
||||
import { useQuery } from '@apollo/client'
|
||||
import { Card } from '@/components/ui/card'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { Badge } from '@/components/ui/badge'
|
||||
import { Input } from '@/components/ui/input'
|
||||
import { Wrench, Plus, Calendar, TrendingUp, AlertCircle, Search, Filter } from 'lucide-react'
|
||||
import { GET_MY_SUPPLIES } from '@/graphql/queries'
|
||||
|
||||
interface MaterialSupply {
|
||||
id: string
|
||||
name: string
|
||||
category: string
|
||||
quantity: number
|
||||
unit: string
|
||||
status: 'planned' | 'in-transit' | 'delivered' | 'in-stock'
|
||||
date: string
|
||||
supplier: string
|
||||
amount: number
|
||||
description?: string
|
||||
minStock: number
|
||||
currentStock: number
|
||||
price: number
|
||||
quantity: number
|
||||
unit?: string
|
||||
category?: string
|
||||
status?: string
|
||||
date: string
|
||||
supplier?: string
|
||||
minStock?: number
|
||||
currentStock?: number
|
||||
imageUrl?: string
|
||||
createdAt: string
|
||||
updatedAt: string
|
||||
}
|
||||
|
||||
const mockMaterialSupplies: MaterialSupply[] = [
|
||||
{
|
||||
id: '1',
|
||||
name: 'Упаковочные коробки 30x20x10',
|
||||
category: 'Упаковка',
|
||||
quantity: 1000,
|
||||
unit: 'шт',
|
||||
status: 'delivered',
|
||||
date: '2024-01-15',
|
||||
supplier: 'ООО "УпакСервис"',
|
||||
amount: 50000,
|
||||
description: 'Картонные коробки для мелких товаров',
|
||||
minStock: 200,
|
||||
currentStock: 350
|
||||
},
|
||||
{
|
||||
id: '2',
|
||||
name: 'Пузырчатая пленка',
|
||||
category: 'Защитная упаковка',
|
||||
quantity: 500,
|
||||
unit: 'м²',
|
||||
status: 'in-transit',
|
||||
date: '2024-01-20',
|
||||
supplier: 'ИП Петров А.В.',
|
||||
amount: 25000,
|
||||
description: 'Пленка для защиты хрупких товаров',
|
||||
minStock: 100,
|
||||
currentStock: 80
|
||||
},
|
||||
{
|
||||
id: '3',
|
||||
name: 'Скотч упаковочный прозрачный',
|
||||
category: 'Клейкая лента',
|
||||
quantity: 200,
|
||||
unit: 'рул',
|
||||
status: 'planned',
|
||||
date: '2024-01-25',
|
||||
supplier: 'ООО "КлейТех"',
|
||||
amount: 15000,
|
||||
description: 'Прозрачный скотч 48мм x 66м',
|
||||
minStock: 50,
|
||||
currentStock: 25
|
||||
},
|
||||
{
|
||||
id: '4',
|
||||
name: 'Этикетки самоклеющиеся',
|
||||
category: 'Маркировка',
|
||||
quantity: 10000,
|
||||
unit: 'шт',
|
||||
status: 'in-stock',
|
||||
date: '2024-01-10',
|
||||
supplier: 'ООО "ЛейблПринт"',
|
||||
amount: 30000,
|
||||
description: 'Белые этикетки 100x70мм',
|
||||
minStock: 2000,
|
||||
currentStock: 3500
|
||||
}
|
||||
]
|
||||
|
||||
export function MaterialsSuppliesTab() {
|
||||
const [searchTerm, setSearchTerm] = useState('')
|
||||
const [categoryFilter, setCategoryFilter] = useState<string>('all')
|
||||
const [statusFilter, setStatusFilter] = useState<string>('all')
|
||||
|
||||
// Загружаем расходники из GraphQL
|
||||
const { data, loading, error, refetch } = useQuery(GET_MY_SUPPLIES, {
|
||||
fetchPolicy: 'cache-and-network', // Всегда проверяем сервер
|
||||
errorPolicy: 'all' // Показываем ошибки
|
||||
})
|
||||
|
||||
const formatCurrency = (amount: number) => {
|
||||
return new Intl.NumberFormat('ru-RU', {
|
||||
style: 'currency',
|
||||
@ -140,10 +92,13 @@ export function MaterialsSuppliesTab() {
|
||||
)
|
||||
}
|
||||
|
||||
const filteredSupplies = mockMaterialSupplies.filter(supply => {
|
||||
// Обрабатываем данные из GraphQL
|
||||
const supplies: MaterialSupply[] = data?.mySupplies || []
|
||||
|
||||
const filteredSupplies = supplies.filter((supply: MaterialSupply) => {
|
||||
const matchesSearch = supply.name.toLowerCase().includes(searchTerm.toLowerCase()) ||
|
||||
supply.category.toLowerCase().includes(searchTerm.toLowerCase()) ||
|
||||
supply.supplier.toLowerCase().includes(searchTerm.toLowerCase())
|
||||
(supply.category || '').toLowerCase().includes(searchTerm.toLowerCase()) ||
|
||||
(supply.supplier || '').toLowerCase().includes(searchTerm.toLowerCase())
|
||||
|
||||
const matchesCategory = categoryFilter === 'all' || supply.category === categoryFilter
|
||||
const matchesStatus = statusFilter === 'all' || supply.status === statusFilter
|
||||
@ -152,18 +107,49 @@ export function MaterialsSuppliesTab() {
|
||||
})
|
||||
|
||||
const getTotalAmount = () => {
|
||||
return filteredSupplies.reduce((sum, supply) => sum + supply.amount, 0)
|
||||
return filteredSupplies.reduce((sum: number, supply: MaterialSupply) => sum + (supply.price * supply.quantity), 0)
|
||||
}
|
||||
|
||||
const getTotalQuantity = () => {
|
||||
return filteredSupplies.reduce((sum, supply) => sum + supply.quantity, 0)
|
||||
return filteredSupplies.reduce((sum: number, supply: MaterialSupply) => sum + supply.quantity, 0)
|
||||
}
|
||||
|
||||
const getLowStockCount = () => {
|
||||
return mockMaterialSupplies.filter(supply => supply.currentStock <= supply.minStock).length
|
||||
return supplies.filter((supply: MaterialSupply) => (supply.currentStock || 0) <= (supply.minStock || 0)).length
|
||||
}
|
||||
|
||||
const categories = Array.from(new Set(mockMaterialSupplies.map(supply => supply.category)))
|
||||
const categories = Array.from(new Set(supplies.map((supply: MaterialSupply) => supply.category)))
|
||||
|
||||
// Показываем индикатор загрузки
|
||||
if (loading) {
|
||||
return (
|
||||
<div className="h-full flex items-center justify-center">
|
||||
<div className="text-center">
|
||||
<div className="animate-spin rounded-full h-8 w-8 border-b-2 border-white mx-auto mb-4"></div>
|
||||
<p className="text-white/60">Загрузка расходников...</p>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
// Показываем ошибку
|
||||
if (error) {
|
||||
return (
|
||||
<div className="h-full flex items-center justify-center">
|
||||
<div className="text-center">
|
||||
<AlertCircle className="h-12 w-12 text-red-400 mx-auto mb-4" />
|
||||
<p className="text-white/60">Ошибка загрузки данных</p>
|
||||
<p className="text-white/40 text-sm mt-2">{error.message}</p>
|
||||
<Button
|
||||
onClick={() => refetch()}
|
||||
className="mt-4 bg-blue-500 hover:bg-blue-600"
|
||||
>
|
||||
Попробовать снова
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="h-full flex flex-col space-y-4 p-4">
|
||||
@ -297,28 +283,28 @@ export function MaterialsSuppliesTab() {
|
||||
</div>
|
||||
</td>
|
||||
<td className="p-2">
|
||||
<span className="text-white/80 text-sm">{supply.category}</span>
|
||||
<span className="text-white/80 text-sm">{supply.category || 'Не указано'}</span>
|
||||
</td>
|
||||
<td className="p-2">
|
||||
<span className="text-white font-semibold text-sm">{supply.quantity} {supply.unit}</span>
|
||||
<span className="text-white font-semibold text-sm">{supply.quantity} {supply.unit || 'шт'}</span>
|
||||
</td>
|
||||
<td className="p-2">
|
||||
<div className="flex flex-col gap-0.5">
|
||||
<span className="text-white font-semibold text-sm">{supply.currentStock} {supply.unit}</span>
|
||||
{getStockStatusBadge(supply.currentStock, supply.minStock)}
|
||||
<span className="text-white font-semibold text-sm">{supply.currentStock || 0} {supply.unit || 'шт'}</span>
|
||||
{getStockStatusBadge(supply.currentStock || 0, supply.minStock || 0)}
|
||||
</div>
|
||||
</td>
|
||||
<td className="p-2">
|
||||
<span className="text-white/80 text-sm">{supply.supplier}</span>
|
||||
<span className="text-white/80 text-sm">{supply.supplier || 'Не указан'}</span>
|
||||
</td>
|
||||
<td className="p-2">
|
||||
<span className="text-white/80 text-sm">{formatDate(supply.date)}</span>
|
||||
<span className="text-white/80 text-sm">{supply.date ? formatDate(supply.date) : 'Не указано'}</span>
|
||||
</td>
|
||||
<td className="p-2">
|
||||
<span className="text-white font-semibold text-sm">{formatCurrency(supply.amount)}</span>
|
||||
<span className="text-white font-semibold text-sm">{formatCurrency(supply.price * supply.quantity)}</span>
|
||||
</td>
|
||||
<td className="p-2">
|
||||
{getStatusBadge(supply.status)}
|
||||
{getStatusBadge(supply.status || 'planned')}
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
|
Reference in New Issue
Block a user