first commit

This commit is contained in:
Veronika Smirnova
2025-08-22 18:05:11 +03:00
parent 6e3201f491
commit 6ff4ca20db
26 changed files with 244 additions and 130 deletions

View File

@ -6,8 +6,8 @@ import React, { useState } from 'react'
import { Sidebar } from '@/components/dashboard/sidebar'
import { GET_PENDING_SUPPLIES_COUNT } from '@/graphql/queries'
import { useSidebar } from '@/hooks/useSidebar'
import { useRealtime } from '@/hooks/useRealtime'
import { useSidebar } from '@/hooks/useSidebar'
// Импорты компонентов подразделов
import { FulfillmentConsumablesOrdersTab } from './fulfillment-supplies/fulfillment-consumables-orders-tab'

View File

@ -137,12 +137,12 @@ const formatCurrency = (amount: number) => {
}
// Функция для форматирования даты
const formatDate = (dateString: string) => {
const _formatDate = (dateString: string) => {
return new Date(dateString).toLocaleDateString('ru-RU')
}
// Функция для отображения статуса
const getStatusBadge = (status: string) => {
const _getStatusBadge = (status: string) => {
const statusConfig = {
PENDING: {
label: 'Ожидает одобрения поставщика',
@ -217,7 +217,7 @@ export function FulfillmentDetailedSuppliesTab() {
// Получаем поставки с многоуровневой структурой для фулфилмента
// Фильтруем поставки где мы являемся получателем (фулфилмент-центром)
// И это расходники фулфилмента (FULFILLMENT_CONSUMABLES)
const ourSupplyOrders: SupplyOrder[] = (data?.mySupplyOrders || []).filter((order: any) => {
const ourSupplyOrders: SupplyOrder[] = (data?.mySupplyOrders || []).filter((order: SupplyOrder) => {
// Проверяем что order существует и имеет нужные поля
if (!order || !order.fulfillmentCenterId) return false
@ -246,11 +246,11 @@ export function FulfillmentDetailedSuppliesTab() {
break
case 'cancel':
// Отменить поставку (если разрешено)
console.log('Отмена поставки:', supplyId)
console.warn('Отмена поставки:', supplyId)
toast.info('Функция отмены поставки в разработке')
break
default:
console.log('Неизвестное действие фулфилмента:', action, supplyId)
console.warn('Неизвестное действие фулфилмента:', action, supplyId)
}
} catch (error) {
console.error('Ошибка при выполнении действия фулфилмента:', error)
@ -260,7 +260,7 @@ export function FulfillmentDetailedSuppliesTab() {
// Функция для приема заказа фулфилментом
const handleReceiveOrder = async (orderId: string) => {
const _handleReceiveOrder = async (orderId: string) => {
try {
await fulfillmentReceiveOrder({
variables: { id: orderId },

View File

@ -18,6 +18,7 @@ import { Badge } from '@/components/ui/badge'
import { Button } from '@/components/ui/button'
import { Card } from '@/components/ui/card'
import { Separator } from '@/components/ui/separator'
import { ASSIGN_LOGISTICS_TO_SUPPLY } from '@/graphql/mutations'
import {
GET_SUPPLY_ORDERS,
GET_MY_EMPLOYEES,
@ -26,7 +27,6 @@ import {
GET_PENDING_SUPPLIES_COUNT,
GET_WAREHOUSE_PRODUCTS,
} from '@/graphql/queries'
import { ASSIGN_LOGISTICS_TO_SUPPLY } from '@/graphql/mutations'
import { useAuth } from '@/hooks/useAuth'
interface SupplyOrder {

View File

@ -1,6 +1,7 @@
'use client'
import { TrendingUp, TrendingDown } from 'lucide-react'
import { Card } from '@/components/ui/card'
interface StatCardProps {

View File

@ -1,5 +1,5 @@
import React from 'react'
import { ChevronDown, ChevronRight, Eye } from 'lucide-react'
import React from 'react'
import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar'
import { Button } from '@/components/ui/button'

View File

@ -1,10 +1,10 @@
import React from 'react'
import { Search } from 'lucide-react'
import React from 'react'
import { Button } from '@/components/ui/button'
import { Input } from '@/components/ui/input'
import { TableHeader } from '../components/TableHeader'
import { TableHeader } from '../components/TableHeader'
import type { TableHeadersBlockProps, StoreDataField } from '../types'
/**

View File

@ -1,8 +1,7 @@
import React from 'react'
import { Package, Box, AlertTriangle, RotateCcw, Users, Wrench } from 'lucide-react'
import React from 'react'
import { StatCard } from '../components/StatCard'
import type { WarehouseStatsBlockProps } from '../types'
/**
@ -19,7 +18,7 @@ import type { WarehouseStatsBlockProps } from '../types'
export const WarehouseStatsBlock = React.memo<WarehouseStatsBlockProps>(({
warehouseStats,
warehouseStatsData,
isStatsLoading
isStatsLoading,
}) => {
return (
<div className="grid grid-cols-2 md:grid-cols-3 xl:grid-cols-6 gap-3">

View File

@ -19,7 +19,7 @@ export function useStoreData(
sellerSupplies: any[],
searchTerm: string,
sortField: StoreDataField,
sortOrder: 'asc' | 'desc'
sortOrder: 'asc' | 'desc',
): UseStoreDataReturn {
// === СОЗДАНИЕ СТРУКТУРИРОВАННЫХ ДАННЫХ СКЛАДА ===
@ -206,7 +206,7 @@ export function useStoreData(
// Фильтрация по поисковому термину
if (searchTerm) {
filtered = filtered.filter(store =>
store.name.toLowerCase().includes(searchTerm.toLowerCase())
store.name.toLowerCase().includes(searchTerm.toLowerCase()),
)
}
@ -258,7 +258,7 @@ export function useStoreData(
defectsChange: 0,
sellerSuppliesChange: 0,
pvzReturnsChange: 0,
}
},
)
}, [filteredAndSortedStores])

View File

@ -15,7 +15,7 @@ export function useWarehouseStats(
supplyOrders: any[],
warehouseStatsData: any,
warehouseStatsLoading: boolean,
sellerSupplies: any[] = []
sellerSupplies: any[] = [],
): UseWarehouseStatsReturn {
// === РАСЧЕТ ПОСТУПЛЕНИЙ РАСХОДНИКОВ ЗА СУТКИ ===

View File

@ -47,10 +47,11 @@ import {
GET_SUPPLY_MOVEMENTS, // Движения товаров (прибыло/убыло)
} from '@/graphql/queries'
import { useAuth } from '@/hooks/useAuth'
import { useSidebar } from '@/hooks/useSidebar'
import { useRealtime } from '@/hooks/useRealtime'
import { useSidebar } from '@/hooks/useSidebar'
import { WbReturnClaims } from '../wb-return-claims'
import { StatCard } from './blocks/StatCard'
// Типы данных для 3-уровневой иерархии
@ -284,37 +285,37 @@ export function FulfillmentWarehouseDashboard() {
current: stats.products?.current || 0,
change: stats.products?.change || 0,
arrived: movements?.arrived?.products || 0,
departed: movements?.departed?.products || 0
departed: movements?.departed?.products || 0,
},
goods: {
current: stats.goods?.current || 0,
change: stats.goods?.change || 0,
arrived: movements?.arrived?.goods || 0,
departed: movements?.departed?.goods || 0
departed: movements?.departed?.goods || 0,
},
defects: {
current: stats.defects?.current || 0,
change: stats.defects?.change || 0,
arrived: movements?.arrived?.defects || 0,
departed: movements?.departed?.defects || 0
departed: movements?.departed?.defects || 0,
},
pvzReturns: {
current: stats.pvzReturns?.current || 0,
change: stats.pvzReturns?.change || 0,
arrived: movements?.arrived?.pvzReturns || 0,
departed: movements?.departed?.pvzReturns || 0
departed: movements?.departed?.pvzReturns || 0,
},
fulfillmentSupplies: {
current: stats.fulfillmentSupplies?.current || 0,
change: stats.fulfillmentSupplies?.change || 0,
arrived: movements?.arrived?.fulfillmentSupplies || 0,
departed: movements?.departed?.fulfillmentSupplies || 0
departed: movements?.departed?.fulfillmentSupplies || 0,
},
sellerSupplies: {
current: stats.sellerSupplies?.current || 0,
change: stats.sellerSupplies?.change || 0,
arrived: movements?.arrived?.sellerSupplies || 0,
departed: movements?.departed?.sellerSupplies || 0
departed: movements?.departed?.sellerSupplies || 0,
},
}
}
@ -329,37 +330,37 @@ export function FulfillmentWarehouseDashboard() {
current: warehouseProducts.filter((p: any) => p.type === 'PRODUCT').length,
change: 0,
arrived: movements?.arrived?.products || 0,
departed: movements?.departed?.products || 0
departed: movements?.departed?.products || 0,
},
goods: {
current: warehouseProducts.filter((p: any) => p.type === 'GOODS').length,
change: 0,
arrived: movements?.arrived?.goods || 0,
departed: movements?.departed?.goods || 0
departed: movements?.departed?.goods || 0,
},
defects: {
current: warehouseProducts.filter((p: any) => p.type === 'DEFECTS').length,
change: 0,
arrived: movements?.arrived?.defects || 0,
departed: movements?.departed?.defects || 0
departed: movements?.departed?.defects || 0,
},
pvzReturns: {
current: warehouseProducts.filter((p: any) => p.type === 'PVZ_RETURNS').length,
change: 0,
arrived: movements?.arrived?.pvzReturns || 0,
departed: movements?.departed?.pvzReturns || 0
departed: movements?.departed?.pvzReturns || 0,
},
fulfillmentSupplies: {
current: fulfillmentSupplies.length,
change: 0,
arrived: movements?.arrived?.fulfillmentSupplies || 0,
departed: movements?.departed?.fulfillmentSupplies || 0
departed: movements?.departed?.fulfillmentSupplies || 0,
},
sellerSupplies: {
current: sellerSupplies.length,
change: 0,
arrived: movements?.arrived?.sellerSupplies || 0,
departed: movements?.departed?.sellerSupplies || 0
departed: movements?.departed?.sellerSupplies || 0,
},
}
}, [warehouseStatsData, warehouseData, sellerSuppliesData, fulfillmentSuppliesData, supplyMovementsData])
@ -448,7 +449,7 @@ export function FulfillmentWarehouseDashboard() {
sellerSuppliesQuantity: 0,
pvzReturnsQuantity: 0,
sellerSuppliesOwners: [],
variants: []
variants: [],
}
}
@ -464,7 +465,7 @@ export function FulfillmentWarehouseDashboard() {
// КРИТИЧНО: Группировка расходников селлера по ВЛАДЕЛЬЦУ (не по названию!)
const sellerSuppliesForThisSeller = sellerSupplies.filter((supply: any) =>
supply.type === 'SELLER_CONSUMABLES' &&
supply.sellerId === sellerId
supply.sellerId === sellerId,
)
console.warn(`📦 Расходники для селлера ${sellerName}:`, sellerSuppliesForThisSeller.length)
@ -484,7 +485,7 @@ export function FulfillmentWarehouseDashboard() {
sellerSuppliesQuantity: 0,
pvzReturnsQuantity: 0,
sellerSuppliesOwners: [ownerKey],
variants: []
variants: [],
}
}
@ -504,7 +505,7 @@ export function FulfillmentWarehouseDashboard() {
sellerSupplies: acc.sellerSupplies + (item.sellerSuppliesQuantity || 0),
pvzReturns: acc.pvzReturns + (item.pvzReturnsQuantity || 0),
}),
{ products: 0, goods: 0, defects: 0, sellerSupplies: 0, pvzReturns: 0 }
{ products: 0, goods: 0, defects: 0, sellerSupplies: 0, pvzReturns: 0 },
)
console.warn(`📊 Итоги для ${sellerName}:`, totals)
@ -541,7 +542,7 @@ export function FulfillmentWarehouseDashboard() {
if (searchTerm) {
filtered = filtered.filter((store) =>
store.name.toLowerCase().includes(searchTerm.toLowerCase())
store.name.toLowerCase().includes(searchTerm.toLowerCase()),
)
}
@ -573,7 +574,7 @@ export function FulfillmentWarehouseDashboard() {
sellerSupplies: acc.sellerSupplies + store.sellerSupplies,
pvzReturns: acc.pvzReturns + store.pvzReturns,
}),
{ products: 0, goods: 0, defects: 0, sellerSupplies: 0, pvzReturns: 0 }
{ products: 0, goods: 0, defects: 0, sellerSupplies: 0, pvzReturns: 0 },
)
}, [filteredAndSortedStores])

View File

@ -0,0 +1,57 @@
'use client'
import { Building, Users, Target, Briefcase } from 'lucide-react'
import { Card } from '@/components/ui/card'
export function MarketBusiness() {
return (
<div className="h-full flex flex-col space-y-4 overflow-hidden">
{/* Заголовок с иконкой */}
<div className="flex items-center space-x-3 flex-shrink-0 mb-4">
<Briefcase className="h-6 w-6 text-orange-400" />
<div>
<h3 className="text-lg font-semibold text-white">Бизнес</h3>
<p className="text-white/60 text-sm">Бизнес-возможности и развитие</p>
</div>
</div>
{/* Контент раздела */}
<div className="flex-1 overflow-auto space-y-4">
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
<Card className="bg-white/5 backdrop-blur border-white/10 p-6">
<div className="flex items-center space-x-3 mb-4">
<Building className="h-8 w-8 text-orange-400" />
<h4 className="text-lg font-semibold text-white">Франшизы</h4>
</div>
<p className="text-white/60 text-sm">Готовые бизнес-решения и франшизы в сфере логистики и торговли</p>
</Card>
<Card className="bg-white/5 backdrop-blur border-white/10 p-6">
<div className="flex items-center space-x-3 mb-4">
<Users className="h-8 w-8 text-blue-400" />
<h4 className="text-lg font-semibold text-white">Партнёрство</h4>
</div>
<p className="text-white/60 text-sm">Поиск бизнес-партнёров для совместных проектов и развития</p>
</Card>
<Card className="bg-white/5 backdrop-blur border-white/10 p-6">
<div className="flex items-center space-x-3 mb-4">
<Target className="h-8 w-8 text-green-400" />
<h4 className="text-lg font-semibold text-white">Консалтинг</h4>
</div>
<p className="text-white/60 text-sm">Бизнес-консультации и стратегическое планирование развития</p>
</Card>
</div>
<div className="text-center py-8">
<div className="w-16 h-16 bg-white/10 rounded-full flex items-center justify-center mx-auto mb-4">
<Briefcase 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>
</div>
</div>
)
}

View File

@ -0,0 +1,61 @@
'use client'
import { TrendingUp, DollarSign, BarChart3 } from 'lucide-react'
import { Card } from '@/components/ui/card'
export function MarketInvestments() {
return (
<div className="h-full flex flex-col space-y-4 overflow-hidden">
{/* Заголовок с иконкой */}
<div className="flex items-center space-x-3 flex-shrink-0 mb-4">
<TrendingUp className="h-6 w-6 text-green-400" />
<div>
<h3 className="text-lg font-semibold text-white">Инвестиции</h3>
<p className="text-white/60 text-sm">Инвестиционные возможности и проекты</p>
</div>
</div>
{/* Контент раздела */}
<div className="flex-1 overflow-auto space-y-4">
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
<Card className="bg-white/5 backdrop-blur border-white/10 p-6">
<div className="flex items-center space-x-3 mb-4">
<DollarSign className="h-8 w-8 text-green-400" />
<h4 className="text-lg font-semibold text-white">Инвестиционные проекты</h4>
</div>
<p className="text-white/60 text-sm">
Поиск и анализ перспективных инвестиционных проектов в сфере логистики и e-commerce
</p>
</Card>
<Card className="bg-white/5 backdrop-blur border-white/10 p-6">
<div className="flex items-center space-x-3 mb-4">
<BarChart3 className="h-8 w-8 text-blue-400" />
<h4 className="text-lg font-semibold text-white">Аналитика рынка</h4>
</div>
<p className="text-white/60 text-sm">
Исследования и аналитические отчёты для принятия инвестиционных решений
</p>
</Card>
<Card className="bg-white/5 backdrop-blur border-white/10 p-6">
<div className="flex items-center space-x-3 mb-4">
<TrendingUp className="h-8 w-8 text-purple-400" />
<h4 className="text-lg font-semibold text-white">Доходность</h4>
</div>
<p className="text-white/60 text-sm">Отслеживание доходности инвестиций и планирование бюджета</p>
</Card>
</div>
<div className="text-center py-8">
<div className="w-16 h-16 bg-white/10 rounded-full flex items-center justify-center mx-auto mb-4">
<TrendingUp 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>
</div>
</div>
)
}

View File

@ -22,9 +22,9 @@ import {
} from '@/graphql/mutations'
import { GET_MESSAGES, GET_CONVERSATIONS } from '@/graphql/queries'
import { useAuth } from '@/hooks/useAuth'
import { useRealtime } from '@/hooks/useRealtime'
import { MessengerAttachments } from './messenger-attachments'
import { useRealtime } from '@/hooks/useRealtime'
interface Organization {
id: string

View File

@ -9,8 +9,8 @@ import { Sidebar } from '@/components/dashboard/sidebar'
import { Button } from '@/components/ui/button'
import { Card } from '@/components/ui/card'
import { GET_CONVERSATIONS, GET_MY_COUNTERPARTIES } from '@/graphql/queries'
import { useSidebar } from '@/hooks/useSidebar'
import { useRealtime } from '@/hooks/useRealtime'
import { useSidebar } from '@/hooks/useSidebar'
import { MessengerChat } from './messenger-chat'
import { MessengerConversations } from './messenger-conversations'

View File

@ -6,8 +6,8 @@ import { Sidebar } from '@/components/dashboard/sidebar'
import { Card } from '@/components/ui/card'
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'
import { GET_INCOMING_REQUESTS } from '@/graphql/queries'
import { useSidebar } from '@/hooks/useSidebar'
import { useRealtime } from '@/hooks/useRealtime'
import { useSidebar } from '@/hooks/useSidebar'
import { MarketCounterparties } from '../market/market-counterparties'
import { MarketFulfillment } from '../market/market-fulfillment'

View File

@ -3,18 +3,19 @@
import { useQuery, useMutation } from '@apollo/client'
import { Clock, CheckCircle, Settings, Truck, Package, Calendar, Search } from 'lucide-react'
import { useState, useMemo } from 'react'
import { toast } from 'sonner'
import { MultiLevelSuppliesTable } from '@/components/supplies/multilevel-supplies-table'
import { Badge } from '@/components/ui/badge'
import { Button } from '@/components/ui/button'
import { Card } from '@/components/ui/card'
import { Input } from '@/components/ui/input'
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'
import { GET_SUPPLY_ORDERS, GET_MY_SUPPLY_ORDERS } from '@/graphql/queries'
import { SUPPLIER_APPROVE_ORDER, SUPPLIER_REJECT_ORDER, SUPPLIER_SHIP_ORDER } from '@/graphql/mutations'
import { GET_SUPPLY_ORDERS, GET_MY_SUPPLY_ORDERS } from '@/graphql/queries'
import { useAuth } from '@/hooks/useAuth'
import { toast } from 'sonner'
import { MultiLevelSuppliesTable } from '@/components/supplies/multilevel-supplies-table'
import { SupplierOrderStats } from './supplier-order-stats'
import { SupplierOrdersSearch } from './supplier-orders-search'

View File

@ -263,7 +263,7 @@ function ProductTableRow({
console.log('🎯 Услуги ФФ:', {
fulfillmentServicesCount: fulfillmentServices.length,
fulfillmentServices: fulfillmentServices,
selectedFulfillment: selectedFulfillment
selectedFulfillment: selectedFulfillment,
})
return null
})()}
@ -288,7 +288,7 @@ function ProductTableRow({
productId: product.id,
serviceName: service.name,
isSelected: isSelected,
newRecipe: newRecipe
newRecipe: newRecipe,
})
onRecipeChange(product.id, newRecipe)
@ -453,7 +453,7 @@ function MarketplaceCardSelector({ productId, onCardSelect, selectedCardId }: Ma
dataExists: !!data,
warehouseDataExists: !!data?.getWBWarehouseData,
cacheExists: !!data?.getWBWarehouseData?.cache,
rawData: data
rawData: data,
})
// Извлекаем карточки из кеша склада WB, как на странице склада
@ -464,7 +464,7 @@ function MarketplaceCardSelector({ productId, onCardSelect, selectedCardId }: Ma
hasWBData: !!data?.getWBWarehouseData,
hasCache: !!data?.getWBWarehouseData?.cache,
cache: data?.getWBWarehouseData?.cache,
cacheData: data?.getWBWarehouseData?.cache?.data
cacheData: data?.getWBWarehouseData?.cache?.data,
})
const cacheData = data?.getWBWarehouseData?.cache?.data
@ -512,7 +512,7 @@ function MarketplaceCardSelector({ productId, onCardSelect, selectedCardId }: Ma
}}
>
<SelectTrigger className="glass-input h-7 text-xs border-white/20">
<SelectValue placeholder={loading ? "..." : "WB"} />
<SelectValue placeholder={loading ? '...' : 'WB'} />
</SelectTrigger>
<SelectContent className="glass-card border-white/20 max-h-[200px] overflow-y-auto">
<SelectItem value="none">Не выбрано</SelectItem>

View File

@ -145,7 +145,7 @@ export function useSupplyCart({ selectedSupplier, allCounterparties, productReci
console.log('🔎 Проверка услуг для товаров:', {
selectedGoods: selectedGoods.map(item => ({ id: item.id, name: item.name })),
productRecipesKeys: Object.keys(productRecipes),
productRecipes: productRecipes
productRecipes: productRecipes,
})
const result = selectedGoods.every((item) => {
@ -215,7 +215,7 @@ export function useSupplyCart({ selectedSupplier, allCounterparties, productReci
fulfillmentConsumables: recipe.selectedFFConsumables || [],
sellerConsumables: recipe.selectedSellerConsumables || [],
marketplaceCardId: recipe.selectedWBCard || null,
}
},
}
}),
notes: selectedGoods
@ -232,7 +232,7 @@ export function useSupplyCart({ selectedSupplier, allCounterparties, productReci
selectedGoodsCount: selectedGoods.length,
deliveryDateType: typeof deliveryDate,
deliveryDateValue: deliveryDate,
convertedDate: new Date(deliveryDate).toISOString()
convertedDate: new Date(deliveryDate).toISOString(),
})
console.warn('🔍 ДЕТАЛЬНАЯ ПРОВЕРКА inputData перед отправкой:', JSON.stringify(inputData, null, 2))

View File

@ -147,7 +147,7 @@ export function CreateSuppliersSupplyPage() {
(productId: string, recipe: ProductRecipe) => {
console.log('📝 handleRecipeChange вызван:', {
productId: productId,
recipe: recipe
recipe: recipe,
})
setProductRecipes((prev) => ({

View File

@ -6,45 +6,38 @@ import {
Calendar,
DollarSign,
Search,
Filter,
ChevronDown,
ChevronRight,
Smartphone,
Eye,
MoreHorizontal,
MapPin,
TrendingUp,
AlertTriangle,
Warehouse,
} from 'lucide-react'
import React, { useState } from 'react'
import { Badge } from '@/components/ui/badge'
import { Button } from '@/components/ui/button'
import { Card } from '@/components/ui/card'
import { Input } from '@/components/ui/input'
import { formatCurrency } from '@/lib/utils'
// Простые компоненты таблицы
const Table = ({ children, ...props }: any) => (
const Table = ({ children, ...props }: { children: React.ReactNode; [key: string]: unknown }) => (
<div className="w-full overflow-auto" {...props}>
<table className="w-full">{children}</table>
</div>
)
const TableHeader = ({ children, ...props }: any) => <thead {...props}>{children}</thead>
const TableBody = ({ children, ...props }: any) => <tbody {...props}>{children}</tbody>
const TableRow = ({ children, className, ...props }: any) => (
const TableHeader = ({ children, ...props }: { children: React.ReactNode; [key: string]: unknown }) => <thead {...props}>{children}</thead>
const TableBody = ({ children, ...props }: { children: React.ReactNode; [key: string]: unknown }) => <tbody {...props}>{children}</tbody>
const TableRow = ({ children, className, ...props }: { children: React.ReactNode; className?: string; [key: string]: unknown }) => (
<tr className={className} {...props}>
{children}
</tr>
)
const TableHead = ({ children, className, ...props }: any) => (
const TableHead = ({ children, className, ...props }: { children: React.ReactNode; className?: string; [key: string]: unknown }) => (
<th className={`px-4 py-3 text-left font-medium ${className}`} {...props}>
{children}
</th>
)
const TableCell = ({ children, className, ...props }: any) => (
const TableCell = ({ children, className, ...props }: { children: React.ReactNode; className?: string; [key: string]: unknown }) => (
<td className={`px-4 py-3 ${className}`} {...props}>
{children}
</td>
@ -94,7 +87,7 @@ interface GoodsSupplyRoute {
}
// Основной интерфейс поставки товаров согласно rules2.md 9.5.4
interface GoodsSupply {
interface _GoodsSupply {
id: string
number: string
creationMethod: 'cards' | 'suppliers' // 📱 карточки / 🏢 поставщик
@ -243,7 +236,7 @@ function CreationMethodIcon({ method }: { method: 'cards' | 'suppliers' }) {
}
// Компонент для статуса поставки
function StatusBadge({ status }: { status: string }) {
function _StatusBadge({ status }: { status: string }) {
const getStatusColor = (status: string) => {
switch (status.toLowerCase()) {
case 'pending':

View File

@ -3,9 +3,6 @@
import {
Package,
Building2,
DollarSign,
ChevronDown,
ChevronRight,
MapPin,
Truck,
Clock,
@ -17,7 +14,6 @@ import { createPortal } from 'react-dom'
import { Badge } from '@/components/ui/badge'
import { Button } from '@/components/ui/button'
import { Card } from '@/components/ui/card'
import {
Dialog,
DialogContent,
@ -137,25 +133,25 @@ interface MultiLevelSuppliesTableProps {
}
// Простые компоненты таблицы
const Table = ({ children, ...props }: any) => (
const Table = ({ children, ...props }: { children: React.ReactNode; [key: string]: unknown }) => (
<div className="w-full" {...props}>
<table className="w-full">{children}</table>
</div>
)
const TableHeader = ({ children, ...props }: any) => <thead {...props}>{children}</thead>
const TableBody = ({ children, ...props }: any) => <tbody {...props}>{children}</tbody>
const TableRow = ({ children, className, ...props }: any) => (
const TableHeader = ({ children, ...props }: { children: React.ReactNode; [key: string]: unknown }) => <thead {...props}>{children}</thead>
const TableBody = ({ children, ...props }: { children: React.ReactNode; [key: string]: unknown }) => <tbody {...props}>{children}</tbody>
const TableRow = ({ children, className, ...props }: { children: React.ReactNode; className?: string; [key: string]: unknown }) => (
<tr className={className} {...props}>
{children}
</tr>
)
const TableHead = ({ children, className, ...props }: any) => (
const TableHead = ({ children, className, ...props }: { children: React.ReactNode; className?: string; [key: string]: unknown }) => (
<th className={`px-4 py-3 text-left ${className}`} {...props}>
{children}
</th>
)
const TableCell = ({ children, className, ...props }: any) => (
const TableCell = ({ children, className, ...props }: { children: React.ReactNode; className?: string; [key: string]: unknown }) => (
<td className={`px-4 py-3 ${className}`} {...props}>
{children}
</td>
@ -214,7 +210,7 @@ function ContextMenu({
isOpen,
position,
onClose,
onCancel
onCancel,
}: {
isOpen: boolean
position: { x: number; y: number }
@ -241,7 +237,7 @@ function ContextMenu({
top: position.y,
zIndex: 9999,
backgroundColor: 'rgb(17, 24, 39)',
borderColor: 'rgba(255, 255, 255, 0.2)'
borderColor: 'rgba(255, 255, 255, 0.2)',
}}
>
<button
@ -266,7 +262,7 @@ function CancelConfirmDialog({
isOpen,
onClose,
onConfirm,
supplyId
supplyId,
}: {
isOpen: boolean
onClose: () => void
@ -306,7 +302,7 @@ function CancelConfirmDialog({
// Основной компонент многоуровневой таблицы поставок
export function MultiLevelSuppliesTable({
supplies = [],
loading = false,
loading: _loading = false,
userRole = 'SELLER',
onSupplyAction,
}: MultiLevelSuppliesTableProps) {
@ -323,24 +319,26 @@ export function MultiLevelSuppliesTable({
}>({
isOpen: false,
position: { x: 0, y: 0 },
supplyId: null
supplyId: null,
})
const [cancelDialogOpen, setCancelDialogOpen] = useState(false)
// Безопасная диагностика данных услуг ФФ
console.log('🔍 ДИАГНОСТИКА: Данные поставок и рецептур:', supplies.map(supply => ({
id: supply.id,
itemsCount: supply.items?.length || 0,
items: supply.items?.slice(0, 2).map(item => ({ // Берем только первые 2 товара для диагностики
id: item.id,
productName: item.product?.name,
hasRecipe: !!item.recipe,
recipe: item.recipe, // Полная структура рецептуры
services: item.services, // Массив ID услуг
fulfillmentConsumables: item.fulfillmentConsumables, // Массив ID расходников ФФ
sellerConsumables: item.sellerConsumables // Массив ID расходников селлера
}))
})))
// Диагностика данных услуг ФФ (только в dev режиме)
if (process.env.NODE_ENV === 'development') {
console.warn('🔍 ДИАГНОСТИКА: Данные поставок и рецептур:', supplies.map(supply => ({
id: supply.id,
itemsCount: supply.items?.length || 0,
items: supply.items?.slice(0, 2).map(item => ({
id: item.id,
productName: item.product?.name,
hasRecipe: !!item.recipe,
recipe: item.recipe,
services: item.services,
fulfillmentConsumables: item.fulfillmentConsumables,
sellerConsumables: item.sellerConsumables,
})),
})))
}
// Массив цветов для различения поставок (с лучшим контрастом)
const supplyColors = [
@ -351,13 +349,13 @@ export function MultiLevelSuppliesTable({
'rgba(248, 113, 113, 0.8)', // Красный
'rgba(34, 211, 238, 0.8)', // Голубой
'rgba(74, 222, 128, 0.8)', // Зеленый (переместил на 7 позицию)
'rgba(250, 204, 21, 0.8)' // Желтый
'rgba(250, 204, 21, 0.8)', // Желтый
]
const getSupplyColor = (index: number) => supplyColors[index % supplyColors.length]
// Функция для получения цвета фона строки в зависимости от уровня иерархии
const getLevelBackgroundColor = (level: number, supplyIndex: number) => {
const getLevelBackgroundColor = (level: number, _supplyIndex: number) => {
const alpha = 0.08 + (level * 0.03) // Больше прозрачности: начальное значение 0.08, шаг 0.03
// Цвета для разных уровней (соответствуют цветам точек)
@ -366,7 +364,7 @@ export function MultiLevelSuppliesTable({
2: 'rgba(96, 165, 250, ', // Синий для маршрута
3: 'rgba(74, 222, 128, ', // Зеленый для поставщика
4: 'rgba(244, 114, 182, ', // Розовый для товара
5: 'rgba(250, 204, 21, ' // Желтый для рецептуры
5: 'rgba(250, 204, 21, ', // Желтый для рецептуры
}
const baseColor = levelColors[level as keyof typeof levelColors] || 'rgba(75, 85, 99, '
@ -429,7 +427,7 @@ export function MultiLevelSuppliesTable({
setContextMenu({
isOpen: true,
position: { x: e.clientX, y: e.clientY },
supplyId: supply.id
supplyId: supply.id,
})
}
@ -449,7 +447,7 @@ export function MultiLevelSuppliesTable({
}
// Функция для отображения действий в зависимости от роли пользователя
const renderActionButtons = (supply: SupplyOrderFromGraphQL) => {
const _renderActionButtons = (supply: SupplyOrderFromGraphQL) => {
const { status, id } = supply
switch (userRole) {
@ -499,11 +497,14 @@ export function MultiLevelSuppliesTable({
case 'SELLER': // Селлер
return (
<CancelButton
supplyId={id}
status={status}
onCancel={handleCancelSupply}
/>
<Button
size="sm"
variant="outline"
className="text-red-400 border-red-400 hover:bg-red-400/10"
onClick={() => handleCancelSupply(id)}
>
Отменить
</Button>
)
case 'FULFILLMENT': // Фулфилмент
@ -666,7 +667,7 @@ export function MultiLevelSuppliesTable({
MozUserSelect: 'none',
msUserSelect: 'none',
userSelect: 'none',
backgroundColor: getLevelBackgroundColor(1, index)
backgroundColor: getLevelBackgroundColor(1, index),
}}
onClick={() => {
toggleSupplyExpansion(supply.id)
@ -787,9 +788,9 @@ export function MultiLevelSuppliesTable({
: [{
id: `route-${supply.id}`,
createdDate: supply.deliveryDate,
fromLocation: "Садовод",
toLocation: "SFERAV Logistics ФФ",
price: 0
fromLocation: 'Садовод',
toLocation: 'SFERAV Logistics ФФ',
price: 0,
}]
return mockRoutes.map((route) => {
@ -1033,7 +1034,7 @@ export function MultiLevelSuppliesTable({
item.totalPrice +
(item.recipe?.services || []).reduce((sum, service) => sum + service.price * item.quantity, 0) +
(item.recipe?.fulfillmentConsumables || []).reduce((sum, consumable) => sum + consumable.price * item.quantity, 0) +
(item.recipe?.sellerConsumables || []).reduce((sum, consumable) => sum + consumable.price * item.quantity, 0)
(item.recipe?.sellerConsumables || []).reduce((sum, consumable) => sum + consumable.price * item.quantity, 0),
)}
</span>
</TableCell>

View File

@ -10,8 +10,8 @@ import { Alert, AlertDescription } from '@/components/ui/alert'
import { Button } from '@/components/ui/button'
import { GET_PENDING_SUPPLIES_COUNT, GET_MY_SUPPLY_ORDERS } from '@/graphql/queries'
import { useAuth } from '@/hooks/useAuth'
import { useSidebar } from '@/hooks/useSidebar'
import { useRealtime } from '@/hooks/useRealtime'
import { useSidebar } from '@/hooks/useSidebar'
import { AllSuppliesTab } from './fulfillment-supplies/all-supplies-tab'
import { RealSupplyOrdersTab } from './fulfillment-supplies/real-supply-orders-tab'