first commit
This commit is contained in:
@ -63,7 +63,7 @@ export async function GET(req: NextRequest) {
|
|||||||
// Heartbeat to keep connection alive
|
// Heartbeat to keep connection alive
|
||||||
const intervalId = setInterval(() => {
|
const intervalId = setInterval(() => {
|
||||||
try {
|
try {
|
||||||
controller.enqueue(encoder.encode(`:\n\n`))
|
controller.enqueue(encoder.encode(':\n\n'))
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
clearInterval(intervalId)
|
clearInterval(intervalId)
|
||||||
}
|
}
|
||||||
|
@ -25,20 +25,20 @@ const server = new ApolloServer<Context>({
|
|||||||
operationType,
|
operationType,
|
||||||
operationName,
|
operationName,
|
||||||
timestamp: new Date().toISOString(),
|
timestamp: new Date().toISOString(),
|
||||||
variables: requestContext.request.variables
|
variables: requestContext.request.variables,
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
didEncounterErrors(requestContext) {
|
didEncounterErrors(requestContext) {
|
||||||
console.error('❌ GraphQL ERROR:', {
|
console.error('❌ GraphQL ERROR:', {
|
||||||
errors: requestContext.errors?.map(e => e.message),
|
errors: requestContext.errors?.map(e => e.message),
|
||||||
operationName: requestContext.request.operationName,
|
operationName: requestContext.request.operationName,
|
||||||
timestamp: new Date().toISOString()
|
timestamp: new Date().toISOString(),
|
||||||
})
|
})
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
]
|
],
|
||||||
})
|
})
|
||||||
|
|
||||||
// Создаем Next.js handler
|
// Создаем Next.js handler
|
||||||
|
@ -6,8 +6,8 @@ import React, { useState } from 'react'
|
|||||||
|
|
||||||
import { Sidebar } from '@/components/dashboard/sidebar'
|
import { Sidebar } from '@/components/dashboard/sidebar'
|
||||||
import { GET_PENDING_SUPPLIES_COUNT } from '@/graphql/queries'
|
import { GET_PENDING_SUPPLIES_COUNT } from '@/graphql/queries'
|
||||||
import { useSidebar } from '@/hooks/useSidebar'
|
|
||||||
import { useRealtime } from '@/hooks/useRealtime'
|
import { useRealtime } from '@/hooks/useRealtime'
|
||||||
|
import { useSidebar } from '@/hooks/useSidebar'
|
||||||
|
|
||||||
// Импорты компонентов подразделов
|
// Импорты компонентов подразделов
|
||||||
import { FulfillmentConsumablesOrdersTab } from './fulfillment-supplies/fulfillment-consumables-orders-tab'
|
import { FulfillmentConsumablesOrdersTab } from './fulfillment-supplies/fulfillment-consumables-orders-tab'
|
||||||
|
@ -137,12 +137,12 @@ const formatCurrency = (amount: number) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Функция для форматирования даты
|
// Функция для форматирования даты
|
||||||
const formatDate = (dateString: string) => {
|
const _formatDate = (dateString: string) => {
|
||||||
return new Date(dateString).toLocaleDateString('ru-RU')
|
return new Date(dateString).toLocaleDateString('ru-RU')
|
||||||
}
|
}
|
||||||
|
|
||||||
// Функция для отображения статуса
|
// Функция для отображения статуса
|
||||||
const getStatusBadge = (status: string) => {
|
const _getStatusBadge = (status: string) => {
|
||||||
const statusConfig = {
|
const statusConfig = {
|
||||||
PENDING: {
|
PENDING: {
|
||||||
label: 'Ожидает одобрения поставщика',
|
label: 'Ожидает одобрения поставщика',
|
||||||
@ -217,7 +217,7 @@ export function FulfillmentDetailedSuppliesTab() {
|
|||||||
// Получаем поставки с многоуровневой структурой для фулфилмента
|
// Получаем поставки с многоуровневой структурой для фулфилмента
|
||||||
// Фильтруем поставки где мы являемся получателем (фулфилмент-центром)
|
// Фильтруем поставки где мы являемся получателем (фулфилмент-центром)
|
||||||
// И это расходники фулфилмента (FULFILLMENT_CONSUMABLES)
|
// И это расходники фулфилмента (FULFILLMENT_CONSUMABLES)
|
||||||
const ourSupplyOrders: SupplyOrder[] = (data?.mySupplyOrders || []).filter((order: any) => {
|
const ourSupplyOrders: SupplyOrder[] = (data?.mySupplyOrders || []).filter((order: SupplyOrder) => {
|
||||||
// Проверяем что order существует и имеет нужные поля
|
// Проверяем что order существует и имеет нужные поля
|
||||||
if (!order || !order.fulfillmentCenterId) return false
|
if (!order || !order.fulfillmentCenterId) return false
|
||||||
|
|
||||||
@ -246,11 +246,11 @@ export function FulfillmentDetailedSuppliesTab() {
|
|||||||
break
|
break
|
||||||
case 'cancel':
|
case 'cancel':
|
||||||
// Отменить поставку (если разрешено)
|
// Отменить поставку (если разрешено)
|
||||||
console.log('Отмена поставки:', supplyId)
|
console.warn('Отмена поставки:', supplyId)
|
||||||
toast.info('Функция отмены поставки в разработке')
|
toast.info('Функция отмены поставки в разработке')
|
||||||
break
|
break
|
||||||
default:
|
default:
|
||||||
console.log('Неизвестное действие фулфилмента:', action, supplyId)
|
console.warn('Неизвестное действие фулфилмента:', action, supplyId)
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Ошибка при выполнении действия фулфилмента:', error)
|
console.error('Ошибка при выполнении действия фулфилмента:', error)
|
||||||
@ -260,7 +260,7 @@ export function FulfillmentDetailedSuppliesTab() {
|
|||||||
|
|
||||||
|
|
||||||
// Функция для приема заказа фулфилментом
|
// Функция для приема заказа фулфилментом
|
||||||
const handleReceiveOrder = async (orderId: string) => {
|
const _handleReceiveOrder = async (orderId: string) => {
|
||||||
try {
|
try {
|
||||||
await fulfillmentReceiveOrder({
|
await fulfillmentReceiveOrder({
|
||||||
variables: { id: orderId },
|
variables: { id: orderId },
|
||||||
|
@ -18,6 +18,7 @@ import { Badge } from '@/components/ui/badge'
|
|||||||
import { Button } from '@/components/ui/button'
|
import { Button } from '@/components/ui/button'
|
||||||
import { Card } from '@/components/ui/card'
|
import { Card } from '@/components/ui/card'
|
||||||
import { Separator } from '@/components/ui/separator'
|
import { Separator } from '@/components/ui/separator'
|
||||||
|
import { ASSIGN_LOGISTICS_TO_SUPPLY } from '@/graphql/mutations'
|
||||||
import {
|
import {
|
||||||
GET_SUPPLY_ORDERS,
|
GET_SUPPLY_ORDERS,
|
||||||
GET_MY_EMPLOYEES,
|
GET_MY_EMPLOYEES,
|
||||||
@ -26,7 +27,6 @@ import {
|
|||||||
GET_PENDING_SUPPLIES_COUNT,
|
GET_PENDING_SUPPLIES_COUNT,
|
||||||
GET_WAREHOUSE_PRODUCTS,
|
GET_WAREHOUSE_PRODUCTS,
|
||||||
} from '@/graphql/queries'
|
} from '@/graphql/queries'
|
||||||
import { ASSIGN_LOGISTICS_TO_SUPPLY } from '@/graphql/mutations'
|
|
||||||
import { useAuth } from '@/hooks/useAuth'
|
import { useAuth } from '@/hooks/useAuth'
|
||||||
|
|
||||||
interface SupplyOrder {
|
interface SupplyOrder {
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
'use client'
|
'use client'
|
||||||
|
|
||||||
import { TrendingUp, TrendingDown } from 'lucide-react'
|
import { TrendingUp, TrendingDown } from 'lucide-react'
|
||||||
|
|
||||||
import { Card } from '@/components/ui/card'
|
import { Card } from '@/components/ui/card'
|
||||||
|
|
||||||
interface StatCardProps {
|
interface StatCardProps {
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import React from 'react'
|
|
||||||
import { ChevronDown, ChevronRight, Eye } from 'lucide-react'
|
import { ChevronDown, ChevronRight, Eye } from 'lucide-react'
|
||||||
|
import React from 'react'
|
||||||
|
|
||||||
import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar'
|
import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar'
|
||||||
import { Button } from '@/components/ui/button'
|
import { Button } from '@/components/ui/button'
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
import React from 'react'
|
|
||||||
import { Search } from 'lucide-react'
|
import { Search } from 'lucide-react'
|
||||||
|
import React from 'react'
|
||||||
|
|
||||||
import { Button } from '@/components/ui/button'
|
import { Button } from '@/components/ui/button'
|
||||||
import { Input } from '@/components/ui/input'
|
import { Input } from '@/components/ui/input'
|
||||||
import { TableHeader } from '../components/TableHeader'
|
|
||||||
|
|
||||||
|
import { TableHeader } from '../components/TableHeader'
|
||||||
import type { TableHeadersBlockProps, StoreDataField } from '../types'
|
import type { TableHeadersBlockProps, StoreDataField } from '../types'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1,8 +1,7 @@
|
|||||||
import React from 'react'
|
|
||||||
import { Package, Box, AlertTriangle, RotateCcw, Users, Wrench } from 'lucide-react'
|
import { Package, Box, AlertTriangle, RotateCcw, Users, Wrench } from 'lucide-react'
|
||||||
|
import React from 'react'
|
||||||
|
|
||||||
import { StatCard } from '../components/StatCard'
|
import { StatCard } from '../components/StatCard'
|
||||||
|
|
||||||
import type { WarehouseStatsBlockProps } from '../types'
|
import type { WarehouseStatsBlockProps } from '../types'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -19,7 +18,7 @@ import type { WarehouseStatsBlockProps } from '../types'
|
|||||||
export const WarehouseStatsBlock = React.memo<WarehouseStatsBlockProps>(({
|
export const WarehouseStatsBlock = React.memo<WarehouseStatsBlockProps>(({
|
||||||
warehouseStats,
|
warehouseStats,
|
||||||
warehouseStatsData,
|
warehouseStatsData,
|
||||||
isStatsLoading
|
isStatsLoading,
|
||||||
}) => {
|
}) => {
|
||||||
return (
|
return (
|
||||||
<div className="grid grid-cols-2 md:grid-cols-3 xl:grid-cols-6 gap-3">
|
<div className="grid grid-cols-2 md:grid-cols-3 xl:grid-cols-6 gap-3">
|
||||||
|
@ -19,7 +19,7 @@ export function useStoreData(
|
|||||||
sellerSupplies: any[],
|
sellerSupplies: any[],
|
||||||
searchTerm: string,
|
searchTerm: string,
|
||||||
sortField: StoreDataField,
|
sortField: StoreDataField,
|
||||||
sortOrder: 'asc' | 'desc'
|
sortOrder: 'asc' | 'desc',
|
||||||
): UseStoreDataReturn {
|
): UseStoreDataReturn {
|
||||||
|
|
||||||
// === СОЗДАНИЕ СТРУКТУРИРОВАННЫХ ДАННЫХ СКЛАДА ===
|
// === СОЗДАНИЕ СТРУКТУРИРОВАННЫХ ДАННЫХ СКЛАДА ===
|
||||||
@ -206,7 +206,7 @@ export function useStoreData(
|
|||||||
// Фильтрация по поисковому термину
|
// Фильтрация по поисковому термину
|
||||||
if (searchTerm) {
|
if (searchTerm) {
|
||||||
filtered = filtered.filter(store =>
|
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,
|
defectsChange: 0,
|
||||||
sellerSuppliesChange: 0,
|
sellerSuppliesChange: 0,
|
||||||
pvzReturnsChange: 0,
|
pvzReturnsChange: 0,
|
||||||
}
|
},
|
||||||
)
|
)
|
||||||
}, [filteredAndSortedStores])
|
}, [filteredAndSortedStores])
|
||||||
|
|
||||||
|
@ -15,7 +15,7 @@ export function useWarehouseStats(
|
|||||||
supplyOrders: any[],
|
supplyOrders: any[],
|
||||||
warehouseStatsData: any,
|
warehouseStatsData: any,
|
||||||
warehouseStatsLoading: boolean,
|
warehouseStatsLoading: boolean,
|
||||||
sellerSupplies: any[] = []
|
sellerSupplies: any[] = [],
|
||||||
): UseWarehouseStatsReturn {
|
): UseWarehouseStatsReturn {
|
||||||
|
|
||||||
// === РАСЧЕТ ПОСТУПЛЕНИЙ РАСХОДНИКОВ ЗА СУТКИ ===
|
// === РАСЧЕТ ПОСТУПЛЕНИЙ РАСХОДНИКОВ ЗА СУТКИ ===
|
||||||
|
@ -47,10 +47,11 @@ import {
|
|||||||
GET_SUPPLY_MOVEMENTS, // Движения товаров (прибыло/убыло)
|
GET_SUPPLY_MOVEMENTS, // Движения товаров (прибыло/убыло)
|
||||||
} from '@/graphql/queries'
|
} from '@/graphql/queries'
|
||||||
import { useAuth } from '@/hooks/useAuth'
|
import { useAuth } from '@/hooks/useAuth'
|
||||||
import { useSidebar } from '@/hooks/useSidebar'
|
|
||||||
import { useRealtime } from '@/hooks/useRealtime'
|
import { useRealtime } from '@/hooks/useRealtime'
|
||||||
|
import { useSidebar } from '@/hooks/useSidebar'
|
||||||
|
|
||||||
import { WbReturnClaims } from '../wb-return-claims'
|
import { WbReturnClaims } from '../wb-return-claims'
|
||||||
|
|
||||||
import { StatCard } from './blocks/StatCard'
|
import { StatCard } from './blocks/StatCard'
|
||||||
|
|
||||||
// Типы данных для 3-уровневой иерархии
|
// Типы данных для 3-уровневой иерархии
|
||||||
@ -284,37 +285,37 @@ export function FulfillmentWarehouseDashboard() {
|
|||||||
current: stats.products?.current || 0,
|
current: stats.products?.current || 0,
|
||||||
change: stats.products?.change || 0,
|
change: stats.products?.change || 0,
|
||||||
arrived: movements?.arrived?.products || 0,
|
arrived: movements?.arrived?.products || 0,
|
||||||
departed: movements?.departed?.products || 0
|
departed: movements?.departed?.products || 0,
|
||||||
},
|
},
|
||||||
goods: {
|
goods: {
|
||||||
current: stats.goods?.current || 0,
|
current: stats.goods?.current || 0,
|
||||||
change: stats.goods?.change || 0,
|
change: stats.goods?.change || 0,
|
||||||
arrived: movements?.arrived?.goods || 0,
|
arrived: movements?.arrived?.goods || 0,
|
||||||
departed: movements?.departed?.goods || 0
|
departed: movements?.departed?.goods || 0,
|
||||||
},
|
},
|
||||||
defects: {
|
defects: {
|
||||||
current: stats.defects?.current || 0,
|
current: stats.defects?.current || 0,
|
||||||
change: stats.defects?.change || 0,
|
change: stats.defects?.change || 0,
|
||||||
arrived: movements?.arrived?.defects || 0,
|
arrived: movements?.arrived?.defects || 0,
|
||||||
departed: movements?.departed?.defects || 0
|
departed: movements?.departed?.defects || 0,
|
||||||
},
|
},
|
||||||
pvzReturns: {
|
pvzReturns: {
|
||||||
current: stats.pvzReturns?.current || 0,
|
current: stats.pvzReturns?.current || 0,
|
||||||
change: stats.pvzReturns?.change || 0,
|
change: stats.pvzReturns?.change || 0,
|
||||||
arrived: movements?.arrived?.pvzReturns || 0,
|
arrived: movements?.arrived?.pvzReturns || 0,
|
||||||
departed: movements?.departed?.pvzReturns || 0
|
departed: movements?.departed?.pvzReturns || 0,
|
||||||
},
|
},
|
||||||
fulfillmentSupplies: {
|
fulfillmentSupplies: {
|
||||||
current: stats.fulfillmentSupplies?.current || 0,
|
current: stats.fulfillmentSupplies?.current || 0,
|
||||||
change: stats.fulfillmentSupplies?.change || 0,
|
change: stats.fulfillmentSupplies?.change || 0,
|
||||||
arrived: movements?.arrived?.fulfillmentSupplies || 0,
|
arrived: movements?.arrived?.fulfillmentSupplies || 0,
|
||||||
departed: movements?.departed?.fulfillmentSupplies || 0
|
departed: movements?.departed?.fulfillmentSupplies || 0,
|
||||||
},
|
},
|
||||||
sellerSupplies: {
|
sellerSupplies: {
|
||||||
current: stats.sellerSupplies?.current || 0,
|
current: stats.sellerSupplies?.current || 0,
|
||||||
change: stats.sellerSupplies?.change || 0,
|
change: stats.sellerSupplies?.change || 0,
|
||||||
arrived: movements?.arrived?.sellerSupplies || 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,
|
current: warehouseProducts.filter((p: any) => p.type === 'PRODUCT').length,
|
||||||
change: 0,
|
change: 0,
|
||||||
arrived: movements?.arrived?.products || 0,
|
arrived: movements?.arrived?.products || 0,
|
||||||
departed: movements?.departed?.products || 0
|
departed: movements?.departed?.products || 0,
|
||||||
},
|
},
|
||||||
goods: {
|
goods: {
|
||||||
current: warehouseProducts.filter((p: any) => p.type === 'GOODS').length,
|
current: warehouseProducts.filter((p: any) => p.type === 'GOODS').length,
|
||||||
change: 0,
|
change: 0,
|
||||||
arrived: movements?.arrived?.goods || 0,
|
arrived: movements?.arrived?.goods || 0,
|
||||||
departed: movements?.departed?.goods || 0
|
departed: movements?.departed?.goods || 0,
|
||||||
},
|
},
|
||||||
defects: {
|
defects: {
|
||||||
current: warehouseProducts.filter((p: any) => p.type === 'DEFECTS').length,
|
current: warehouseProducts.filter((p: any) => p.type === 'DEFECTS').length,
|
||||||
change: 0,
|
change: 0,
|
||||||
arrived: movements?.arrived?.defects || 0,
|
arrived: movements?.arrived?.defects || 0,
|
||||||
departed: movements?.departed?.defects || 0
|
departed: movements?.departed?.defects || 0,
|
||||||
},
|
},
|
||||||
pvzReturns: {
|
pvzReturns: {
|
||||||
current: warehouseProducts.filter((p: any) => p.type === 'PVZ_RETURNS').length,
|
current: warehouseProducts.filter((p: any) => p.type === 'PVZ_RETURNS').length,
|
||||||
change: 0,
|
change: 0,
|
||||||
arrived: movements?.arrived?.pvzReturns || 0,
|
arrived: movements?.arrived?.pvzReturns || 0,
|
||||||
departed: movements?.departed?.pvzReturns || 0
|
departed: movements?.departed?.pvzReturns || 0,
|
||||||
},
|
},
|
||||||
fulfillmentSupplies: {
|
fulfillmentSupplies: {
|
||||||
current: fulfillmentSupplies.length,
|
current: fulfillmentSupplies.length,
|
||||||
change: 0,
|
change: 0,
|
||||||
arrived: movements?.arrived?.fulfillmentSupplies || 0,
|
arrived: movements?.arrived?.fulfillmentSupplies || 0,
|
||||||
departed: movements?.departed?.fulfillmentSupplies || 0
|
departed: movements?.departed?.fulfillmentSupplies || 0,
|
||||||
},
|
},
|
||||||
sellerSupplies: {
|
sellerSupplies: {
|
||||||
current: sellerSupplies.length,
|
current: sellerSupplies.length,
|
||||||
change: 0,
|
change: 0,
|
||||||
arrived: movements?.arrived?.sellerSupplies || 0,
|
arrived: movements?.arrived?.sellerSupplies || 0,
|
||||||
departed: movements?.departed?.sellerSupplies || 0
|
departed: movements?.departed?.sellerSupplies || 0,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}, [warehouseStatsData, warehouseData, sellerSuppliesData, fulfillmentSuppliesData, supplyMovementsData])
|
}, [warehouseStatsData, warehouseData, sellerSuppliesData, fulfillmentSuppliesData, supplyMovementsData])
|
||||||
@ -448,7 +449,7 @@ export function FulfillmentWarehouseDashboard() {
|
|||||||
sellerSuppliesQuantity: 0,
|
sellerSuppliesQuantity: 0,
|
||||||
pvzReturnsQuantity: 0,
|
pvzReturnsQuantity: 0,
|
||||||
sellerSuppliesOwners: [],
|
sellerSuppliesOwners: [],
|
||||||
variants: []
|
variants: [],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -464,7 +465,7 @@ export function FulfillmentWarehouseDashboard() {
|
|||||||
// КРИТИЧНО: Группировка расходников селлера по ВЛАДЕЛЬЦУ (не по названию!)
|
// КРИТИЧНО: Группировка расходников селлера по ВЛАДЕЛЬЦУ (не по названию!)
|
||||||
const sellerSuppliesForThisSeller = sellerSupplies.filter((supply: any) =>
|
const sellerSuppliesForThisSeller = sellerSupplies.filter((supply: any) =>
|
||||||
supply.type === 'SELLER_CONSUMABLES' &&
|
supply.type === 'SELLER_CONSUMABLES' &&
|
||||||
supply.sellerId === sellerId
|
supply.sellerId === sellerId,
|
||||||
)
|
)
|
||||||
|
|
||||||
console.warn(`📦 Расходники для селлера ${sellerName}:`, sellerSuppliesForThisSeller.length)
|
console.warn(`📦 Расходники для селлера ${sellerName}:`, sellerSuppliesForThisSeller.length)
|
||||||
@ -484,7 +485,7 @@ export function FulfillmentWarehouseDashboard() {
|
|||||||
sellerSuppliesQuantity: 0,
|
sellerSuppliesQuantity: 0,
|
||||||
pvzReturnsQuantity: 0,
|
pvzReturnsQuantity: 0,
|
||||||
sellerSuppliesOwners: [ownerKey],
|
sellerSuppliesOwners: [ownerKey],
|
||||||
variants: []
|
variants: [],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -504,7 +505,7 @@ export function FulfillmentWarehouseDashboard() {
|
|||||||
sellerSupplies: acc.sellerSupplies + (item.sellerSuppliesQuantity || 0),
|
sellerSupplies: acc.sellerSupplies + (item.sellerSuppliesQuantity || 0),
|
||||||
pvzReturns: acc.pvzReturns + (item.pvzReturnsQuantity || 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)
|
console.warn(`📊 Итоги для ${sellerName}:`, totals)
|
||||||
@ -541,7 +542,7 @@ export function FulfillmentWarehouseDashboard() {
|
|||||||
|
|
||||||
if (searchTerm) {
|
if (searchTerm) {
|
||||||
filtered = filtered.filter((store) =>
|
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,
|
sellerSupplies: acc.sellerSupplies + store.sellerSupplies,
|
||||||
pvzReturns: acc.pvzReturns + store.pvzReturns,
|
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])
|
}, [filteredAndSortedStores])
|
||||||
|
|
||||||
|
57
src/components/market/market-business.tsx.backup
Normal file
57
src/components/market/market-business.tsx.backup
Normal 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>
|
||||||
|
)
|
||||||
|
}
|
61
src/components/market/market-investments.tsx.backup
Normal file
61
src/components/market/market-investments.tsx.backup
Normal 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>
|
||||||
|
)
|
||||||
|
}
|
@ -22,9 +22,9 @@ import {
|
|||||||
} from '@/graphql/mutations'
|
} from '@/graphql/mutations'
|
||||||
import { GET_MESSAGES, GET_CONVERSATIONS } from '@/graphql/queries'
|
import { GET_MESSAGES, GET_CONVERSATIONS } from '@/graphql/queries'
|
||||||
import { useAuth } from '@/hooks/useAuth'
|
import { useAuth } from '@/hooks/useAuth'
|
||||||
|
import { useRealtime } from '@/hooks/useRealtime'
|
||||||
|
|
||||||
import { MessengerAttachments } from './messenger-attachments'
|
import { MessengerAttachments } from './messenger-attachments'
|
||||||
import { useRealtime } from '@/hooks/useRealtime'
|
|
||||||
|
|
||||||
interface Organization {
|
interface Organization {
|
||||||
id: string
|
id: string
|
||||||
|
@ -9,8 +9,8 @@ import { Sidebar } from '@/components/dashboard/sidebar'
|
|||||||
import { Button } from '@/components/ui/button'
|
import { Button } from '@/components/ui/button'
|
||||||
import { Card } from '@/components/ui/card'
|
import { Card } from '@/components/ui/card'
|
||||||
import { GET_CONVERSATIONS, GET_MY_COUNTERPARTIES } from '@/graphql/queries'
|
import { GET_CONVERSATIONS, GET_MY_COUNTERPARTIES } from '@/graphql/queries'
|
||||||
import { useSidebar } from '@/hooks/useSidebar'
|
|
||||||
import { useRealtime } from '@/hooks/useRealtime'
|
import { useRealtime } from '@/hooks/useRealtime'
|
||||||
|
import { useSidebar } from '@/hooks/useSidebar'
|
||||||
|
|
||||||
import { MessengerChat } from './messenger-chat'
|
import { MessengerChat } from './messenger-chat'
|
||||||
import { MessengerConversations } from './messenger-conversations'
|
import { MessengerConversations } from './messenger-conversations'
|
||||||
|
@ -6,8 +6,8 @@ import { Sidebar } from '@/components/dashboard/sidebar'
|
|||||||
import { Card } from '@/components/ui/card'
|
import { Card } from '@/components/ui/card'
|
||||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'
|
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'
|
||||||
import { GET_INCOMING_REQUESTS } from '@/graphql/queries'
|
import { GET_INCOMING_REQUESTS } from '@/graphql/queries'
|
||||||
import { useSidebar } from '@/hooks/useSidebar'
|
|
||||||
import { useRealtime } from '@/hooks/useRealtime'
|
import { useRealtime } from '@/hooks/useRealtime'
|
||||||
|
import { useSidebar } from '@/hooks/useSidebar'
|
||||||
|
|
||||||
import { MarketCounterparties } from '../market/market-counterparties'
|
import { MarketCounterparties } from '../market/market-counterparties'
|
||||||
import { MarketFulfillment } from '../market/market-fulfillment'
|
import { MarketFulfillment } from '../market/market-fulfillment'
|
||||||
|
@ -3,18 +3,19 @@
|
|||||||
import { useQuery, useMutation } from '@apollo/client'
|
import { useQuery, useMutation } from '@apollo/client'
|
||||||
import { Clock, CheckCircle, Settings, Truck, Package, Calendar, Search } from 'lucide-react'
|
import { Clock, CheckCircle, Settings, Truck, Package, Calendar, Search } from 'lucide-react'
|
||||||
import { useState, useMemo } from '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 { Badge } from '@/components/ui/badge'
|
||||||
import { Button } from '@/components/ui/button'
|
import { Button } from '@/components/ui/button'
|
||||||
import { Card } from '@/components/ui/card'
|
import { Card } from '@/components/ui/card'
|
||||||
import { Input } from '@/components/ui/input'
|
import { Input } from '@/components/ui/input'
|
||||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'
|
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 { 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 { useAuth } from '@/hooks/useAuth'
|
||||||
import { toast } from 'sonner'
|
|
||||||
|
|
||||||
import { MultiLevelSuppliesTable } from '@/components/supplies/multilevel-supplies-table'
|
|
||||||
import { SupplierOrderStats } from './supplier-order-stats'
|
import { SupplierOrderStats } from './supplier-order-stats'
|
||||||
import { SupplierOrdersSearch } from './supplier-orders-search'
|
import { SupplierOrdersSearch } from './supplier-orders-search'
|
||||||
|
|
||||||
|
@ -263,7 +263,7 @@ function ProductTableRow({
|
|||||||
console.log('🎯 Услуги ФФ:', {
|
console.log('🎯 Услуги ФФ:', {
|
||||||
fulfillmentServicesCount: fulfillmentServices.length,
|
fulfillmentServicesCount: fulfillmentServices.length,
|
||||||
fulfillmentServices: fulfillmentServices,
|
fulfillmentServices: fulfillmentServices,
|
||||||
selectedFulfillment: selectedFulfillment
|
selectedFulfillment: selectedFulfillment,
|
||||||
})
|
})
|
||||||
return null
|
return null
|
||||||
})()}
|
})()}
|
||||||
@ -288,7 +288,7 @@ function ProductTableRow({
|
|||||||
productId: product.id,
|
productId: product.id,
|
||||||
serviceName: service.name,
|
serviceName: service.name,
|
||||||
isSelected: isSelected,
|
isSelected: isSelected,
|
||||||
newRecipe: newRecipe
|
newRecipe: newRecipe,
|
||||||
})
|
})
|
||||||
|
|
||||||
onRecipeChange(product.id, newRecipe)
|
onRecipeChange(product.id, newRecipe)
|
||||||
@ -453,7 +453,7 @@ function MarketplaceCardSelector({ productId, onCardSelect, selectedCardId }: Ma
|
|||||||
dataExists: !!data,
|
dataExists: !!data,
|
||||||
warehouseDataExists: !!data?.getWBWarehouseData,
|
warehouseDataExists: !!data?.getWBWarehouseData,
|
||||||
cacheExists: !!data?.getWBWarehouseData?.cache,
|
cacheExists: !!data?.getWBWarehouseData?.cache,
|
||||||
rawData: data
|
rawData: data,
|
||||||
})
|
})
|
||||||
|
|
||||||
// Извлекаем карточки из кеша склада WB, как на странице склада
|
// Извлекаем карточки из кеша склада WB, как на странице склада
|
||||||
@ -464,7 +464,7 @@ function MarketplaceCardSelector({ productId, onCardSelect, selectedCardId }: Ma
|
|||||||
hasWBData: !!data?.getWBWarehouseData,
|
hasWBData: !!data?.getWBWarehouseData,
|
||||||
hasCache: !!data?.getWBWarehouseData?.cache,
|
hasCache: !!data?.getWBWarehouseData?.cache,
|
||||||
cache: data?.getWBWarehouseData?.cache,
|
cache: data?.getWBWarehouseData?.cache,
|
||||||
cacheData: data?.getWBWarehouseData?.cache?.data
|
cacheData: data?.getWBWarehouseData?.cache?.data,
|
||||||
})
|
})
|
||||||
|
|
||||||
const 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">
|
<SelectTrigger className="glass-input h-7 text-xs border-white/20">
|
||||||
<SelectValue placeholder={loading ? "..." : "WB"} />
|
<SelectValue placeholder={loading ? '...' : 'WB'} />
|
||||||
</SelectTrigger>
|
</SelectTrigger>
|
||||||
<SelectContent className="glass-card border-white/20 max-h-[200px] overflow-y-auto">
|
<SelectContent className="glass-card border-white/20 max-h-[200px] overflow-y-auto">
|
||||||
<SelectItem value="none">Не выбрано</SelectItem>
|
<SelectItem value="none">Не выбрано</SelectItem>
|
||||||
|
@ -145,7 +145,7 @@ export function useSupplyCart({ selectedSupplier, allCounterparties, productReci
|
|||||||
console.log('🔎 Проверка услуг для товаров:', {
|
console.log('🔎 Проверка услуг для товаров:', {
|
||||||
selectedGoods: selectedGoods.map(item => ({ id: item.id, name: item.name })),
|
selectedGoods: selectedGoods.map(item => ({ id: item.id, name: item.name })),
|
||||||
productRecipesKeys: Object.keys(productRecipes),
|
productRecipesKeys: Object.keys(productRecipes),
|
||||||
productRecipes: productRecipes
|
productRecipes: productRecipes,
|
||||||
})
|
})
|
||||||
|
|
||||||
const result = selectedGoods.every((item) => {
|
const result = selectedGoods.every((item) => {
|
||||||
@ -215,7 +215,7 @@ export function useSupplyCart({ selectedSupplier, allCounterparties, productReci
|
|||||||
fulfillmentConsumables: recipe.selectedFFConsumables || [],
|
fulfillmentConsumables: recipe.selectedFFConsumables || [],
|
||||||
sellerConsumables: recipe.selectedSellerConsumables || [],
|
sellerConsumables: recipe.selectedSellerConsumables || [],
|
||||||
marketplaceCardId: recipe.selectedWBCard || null,
|
marketplaceCardId: recipe.selectedWBCard || null,
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
notes: selectedGoods
|
notes: selectedGoods
|
||||||
@ -232,7 +232,7 @@ export function useSupplyCart({ selectedSupplier, allCounterparties, productReci
|
|||||||
selectedGoodsCount: selectedGoods.length,
|
selectedGoodsCount: selectedGoods.length,
|
||||||
deliveryDateType: typeof deliveryDate,
|
deliveryDateType: typeof deliveryDate,
|
||||||
deliveryDateValue: deliveryDate,
|
deliveryDateValue: deliveryDate,
|
||||||
convertedDate: new Date(deliveryDate).toISOString()
|
convertedDate: new Date(deliveryDate).toISOString(),
|
||||||
})
|
})
|
||||||
|
|
||||||
console.warn('🔍 ДЕТАЛЬНАЯ ПРОВЕРКА inputData перед отправкой:', JSON.stringify(inputData, null, 2))
|
console.warn('🔍 ДЕТАЛЬНАЯ ПРОВЕРКА inputData перед отправкой:', JSON.stringify(inputData, null, 2))
|
||||||
|
@ -147,7 +147,7 @@ export function CreateSuppliersSupplyPage() {
|
|||||||
(productId: string, recipe: ProductRecipe) => {
|
(productId: string, recipe: ProductRecipe) => {
|
||||||
console.log('📝 handleRecipeChange вызван:', {
|
console.log('📝 handleRecipeChange вызван:', {
|
||||||
productId: productId,
|
productId: productId,
|
||||||
recipe: recipe
|
recipe: recipe,
|
||||||
})
|
})
|
||||||
|
|
||||||
setProductRecipes((prev) => ({
|
setProductRecipes((prev) => ({
|
||||||
|
@ -6,45 +6,38 @@ import {
|
|||||||
Calendar,
|
Calendar,
|
||||||
DollarSign,
|
DollarSign,
|
||||||
Search,
|
Search,
|
||||||
Filter,
|
|
||||||
ChevronDown,
|
ChevronDown,
|
||||||
ChevronRight,
|
ChevronRight,
|
||||||
Smartphone,
|
Smartphone,
|
||||||
Eye,
|
|
||||||
MoreHorizontal,
|
|
||||||
MapPin,
|
MapPin,
|
||||||
TrendingUp,
|
|
||||||
AlertTriangle,
|
|
||||||
Warehouse,
|
|
||||||
} from 'lucide-react'
|
} from 'lucide-react'
|
||||||
import React, { useState } from 'react'
|
import React, { useState } from 'react'
|
||||||
|
|
||||||
import { Badge } from '@/components/ui/badge'
|
import { Badge } from '@/components/ui/badge'
|
||||||
import { Button } from '@/components/ui/button'
|
|
||||||
import { Card } from '@/components/ui/card'
|
import { Card } from '@/components/ui/card'
|
||||||
import { Input } from '@/components/ui/input'
|
import { Input } from '@/components/ui/input'
|
||||||
import { formatCurrency } from '@/lib/utils'
|
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}>
|
<div className="w-full overflow-auto" {...props}>
|
||||||
<table className="w-full">{children}</table>
|
<table className="w-full">{children}</table>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|
||||||
const TableHeader = ({ children, ...props }: any) => <thead {...props}>{children}</thead>
|
const TableHeader = ({ children, ...props }: { children: React.ReactNode; [key: string]: unknown }) => <thead {...props}>{children}</thead>
|
||||||
const TableBody = ({ children, ...props }: any) => <tbody {...props}>{children}</tbody>
|
const TableBody = ({ children, ...props }: { children: React.ReactNode; [key: string]: unknown }) => <tbody {...props}>{children}</tbody>
|
||||||
const TableRow = ({ children, className, ...props }: any) => (
|
const TableRow = ({ children, className, ...props }: { children: React.ReactNode; className?: string; [key: string]: unknown }) => (
|
||||||
<tr className={className} {...props}>
|
<tr className={className} {...props}>
|
||||||
{children}
|
{children}
|
||||||
</tr>
|
</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}>
|
<th className={`px-4 py-3 text-left font-medium ${className}`} {...props}>
|
||||||
{children}
|
{children}
|
||||||
</th>
|
</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}>
|
<td className={`px-4 py-3 ${className}`} {...props}>
|
||||||
{children}
|
{children}
|
||||||
</td>
|
</td>
|
||||||
@ -94,7 +87,7 @@ interface GoodsSupplyRoute {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Основной интерфейс поставки товаров согласно rules2.md 9.5.4
|
// Основной интерфейс поставки товаров согласно rules2.md 9.5.4
|
||||||
interface GoodsSupply {
|
interface _GoodsSupply {
|
||||||
id: string
|
id: string
|
||||||
number: string
|
number: string
|
||||||
creationMethod: 'cards' | 'suppliers' // 📱 карточки / 🏢 поставщик
|
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) => {
|
const getStatusColor = (status: string) => {
|
||||||
switch (status.toLowerCase()) {
|
switch (status.toLowerCase()) {
|
||||||
case 'pending':
|
case 'pending':
|
||||||
|
@ -3,9 +3,6 @@
|
|||||||
import {
|
import {
|
||||||
Package,
|
Package,
|
||||||
Building2,
|
Building2,
|
||||||
DollarSign,
|
|
||||||
ChevronDown,
|
|
||||||
ChevronRight,
|
|
||||||
MapPin,
|
MapPin,
|
||||||
Truck,
|
Truck,
|
||||||
Clock,
|
Clock,
|
||||||
@ -17,7 +14,6 @@ import { createPortal } from 'react-dom'
|
|||||||
|
|
||||||
import { Badge } from '@/components/ui/badge'
|
import { Badge } from '@/components/ui/badge'
|
||||||
import { Button } from '@/components/ui/button'
|
import { Button } from '@/components/ui/button'
|
||||||
import { Card } from '@/components/ui/card'
|
|
||||||
import {
|
import {
|
||||||
Dialog,
|
Dialog,
|
||||||
DialogContent,
|
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}>
|
<div className="w-full" {...props}>
|
||||||
<table className="w-full">{children}</table>
|
<table className="w-full">{children}</table>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|
||||||
const TableHeader = ({ children, ...props }: any) => <thead {...props}>{children}</thead>
|
const TableHeader = ({ children, ...props }: { children: React.ReactNode; [key: string]: unknown }) => <thead {...props}>{children}</thead>
|
||||||
const TableBody = ({ children, ...props }: any) => <tbody {...props}>{children}</tbody>
|
const TableBody = ({ children, ...props }: { children: React.ReactNode; [key: string]: unknown }) => <tbody {...props}>{children}</tbody>
|
||||||
const TableRow = ({ children, className, ...props }: any) => (
|
const TableRow = ({ children, className, ...props }: { children: React.ReactNode; className?: string; [key: string]: unknown }) => (
|
||||||
<tr className={className} {...props}>
|
<tr className={className} {...props}>
|
||||||
{children}
|
{children}
|
||||||
</tr>
|
</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}>
|
<th className={`px-4 py-3 text-left ${className}`} {...props}>
|
||||||
{children}
|
{children}
|
||||||
</th>
|
</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}>
|
<td className={`px-4 py-3 ${className}`} {...props}>
|
||||||
{children}
|
{children}
|
||||||
</td>
|
</td>
|
||||||
@ -214,7 +210,7 @@ function ContextMenu({
|
|||||||
isOpen,
|
isOpen,
|
||||||
position,
|
position,
|
||||||
onClose,
|
onClose,
|
||||||
onCancel
|
onCancel,
|
||||||
}: {
|
}: {
|
||||||
isOpen: boolean
|
isOpen: boolean
|
||||||
position: { x: number; y: number }
|
position: { x: number; y: number }
|
||||||
@ -241,7 +237,7 @@ function ContextMenu({
|
|||||||
top: position.y,
|
top: position.y,
|
||||||
zIndex: 9999,
|
zIndex: 9999,
|
||||||
backgroundColor: 'rgb(17, 24, 39)',
|
backgroundColor: 'rgb(17, 24, 39)',
|
||||||
borderColor: 'rgba(255, 255, 255, 0.2)'
|
borderColor: 'rgba(255, 255, 255, 0.2)',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<button
|
<button
|
||||||
@ -266,7 +262,7 @@ function CancelConfirmDialog({
|
|||||||
isOpen,
|
isOpen,
|
||||||
onClose,
|
onClose,
|
||||||
onConfirm,
|
onConfirm,
|
||||||
supplyId
|
supplyId,
|
||||||
}: {
|
}: {
|
||||||
isOpen: boolean
|
isOpen: boolean
|
||||||
onClose: () => void
|
onClose: () => void
|
||||||
@ -306,7 +302,7 @@ function CancelConfirmDialog({
|
|||||||
// Основной компонент многоуровневой таблицы поставок
|
// Основной компонент многоуровневой таблицы поставок
|
||||||
export function MultiLevelSuppliesTable({
|
export function MultiLevelSuppliesTable({
|
||||||
supplies = [],
|
supplies = [],
|
||||||
loading = false,
|
loading: _loading = false,
|
||||||
userRole = 'SELLER',
|
userRole = 'SELLER',
|
||||||
onSupplyAction,
|
onSupplyAction,
|
||||||
}: MultiLevelSuppliesTableProps) {
|
}: MultiLevelSuppliesTableProps) {
|
||||||
@ -323,24 +319,26 @@ export function MultiLevelSuppliesTable({
|
|||||||
}>({
|
}>({
|
||||||
isOpen: false,
|
isOpen: false,
|
||||||
position: { x: 0, y: 0 },
|
position: { x: 0, y: 0 },
|
||||||
supplyId: null
|
supplyId: null,
|
||||||
})
|
})
|
||||||
const [cancelDialogOpen, setCancelDialogOpen] = useState(false)
|
const [cancelDialogOpen, setCancelDialogOpen] = useState(false)
|
||||||
|
|
||||||
// Безопасная диагностика данных услуг ФФ
|
// Диагностика данных услуг ФФ (только в dev режиме)
|
||||||
console.log('🔍 ДИАГНОСТИКА: Данные поставок и рецептур:', supplies.map(supply => ({
|
if (process.env.NODE_ENV === 'development') {
|
||||||
id: supply.id,
|
console.warn('🔍 ДИАГНОСТИКА: Данные поставок и рецептур:', supplies.map(supply => ({
|
||||||
itemsCount: supply.items?.length || 0,
|
id: supply.id,
|
||||||
items: supply.items?.slice(0, 2).map(item => ({ // Берем только первые 2 товара для диагностики
|
itemsCount: supply.items?.length || 0,
|
||||||
id: item.id,
|
items: supply.items?.slice(0, 2).map(item => ({
|
||||||
productName: item.product?.name,
|
id: item.id,
|
||||||
hasRecipe: !!item.recipe,
|
productName: item.product?.name,
|
||||||
recipe: item.recipe, // Полная структура рецептуры
|
hasRecipe: !!item.recipe,
|
||||||
services: item.services, // Массив ID услуг
|
recipe: item.recipe,
|
||||||
fulfillmentConsumables: item.fulfillmentConsumables, // Массив ID расходников ФФ
|
services: item.services,
|
||||||
sellerConsumables: item.sellerConsumables // Массив ID расходников селлера
|
fulfillmentConsumables: item.fulfillmentConsumables,
|
||||||
}))
|
sellerConsumables: item.sellerConsumables,
|
||||||
})))
|
})),
|
||||||
|
})))
|
||||||
|
}
|
||||||
|
|
||||||
// Массив цветов для различения поставок (с лучшим контрастом)
|
// Массив цветов для различения поставок (с лучшим контрастом)
|
||||||
const supplyColors = [
|
const supplyColors = [
|
||||||
@ -351,13 +349,13 @@ export function MultiLevelSuppliesTable({
|
|||||||
'rgba(248, 113, 113, 0.8)', // Красный
|
'rgba(248, 113, 113, 0.8)', // Красный
|
||||||
'rgba(34, 211, 238, 0.8)', // Голубой
|
'rgba(34, 211, 238, 0.8)', // Голубой
|
||||||
'rgba(74, 222, 128, 0.8)', // Зеленый (переместил на 7 позицию)
|
'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 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
|
const alpha = 0.08 + (level * 0.03) // Больше прозрачности: начальное значение 0.08, шаг 0.03
|
||||||
|
|
||||||
// Цвета для разных уровней (соответствуют цветам точек)
|
// Цвета для разных уровней (соответствуют цветам точек)
|
||||||
@ -366,7 +364,7 @@ export function MultiLevelSuppliesTable({
|
|||||||
2: 'rgba(96, 165, 250, ', // Синий для маршрута
|
2: 'rgba(96, 165, 250, ', // Синий для маршрута
|
||||||
3: 'rgba(74, 222, 128, ', // Зеленый для поставщика
|
3: 'rgba(74, 222, 128, ', // Зеленый для поставщика
|
||||||
4: 'rgba(244, 114, 182, ', // Розовый для товара
|
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, '
|
const baseColor = levelColors[level as keyof typeof levelColors] || 'rgba(75, 85, 99, '
|
||||||
@ -429,7 +427,7 @@ export function MultiLevelSuppliesTable({
|
|||||||
setContextMenu({
|
setContextMenu({
|
||||||
isOpen: true,
|
isOpen: true,
|
||||||
position: { x: e.clientX, y: e.clientY },
|
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
|
const { status, id } = supply
|
||||||
|
|
||||||
switch (userRole) {
|
switch (userRole) {
|
||||||
@ -499,11 +497,14 @@ export function MultiLevelSuppliesTable({
|
|||||||
|
|
||||||
case 'SELLER': // Селлер
|
case 'SELLER': // Селлер
|
||||||
return (
|
return (
|
||||||
<CancelButton
|
<Button
|
||||||
supplyId={id}
|
size="sm"
|
||||||
status={status}
|
variant="outline"
|
||||||
onCancel={handleCancelSupply}
|
className="text-red-400 border-red-400 hover:bg-red-400/10"
|
||||||
/>
|
onClick={() => handleCancelSupply(id)}
|
||||||
|
>
|
||||||
|
Отменить
|
||||||
|
</Button>
|
||||||
)
|
)
|
||||||
|
|
||||||
case 'FULFILLMENT': // Фулфилмент
|
case 'FULFILLMENT': // Фулфилмент
|
||||||
@ -666,7 +667,7 @@ export function MultiLevelSuppliesTable({
|
|||||||
MozUserSelect: 'none',
|
MozUserSelect: 'none',
|
||||||
msUserSelect: 'none',
|
msUserSelect: 'none',
|
||||||
userSelect: 'none',
|
userSelect: 'none',
|
||||||
backgroundColor: getLevelBackgroundColor(1, index)
|
backgroundColor: getLevelBackgroundColor(1, index),
|
||||||
}}
|
}}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
toggleSupplyExpansion(supply.id)
|
toggleSupplyExpansion(supply.id)
|
||||||
@ -787,9 +788,9 @@ export function MultiLevelSuppliesTable({
|
|||||||
: [{
|
: [{
|
||||||
id: `route-${supply.id}`,
|
id: `route-${supply.id}`,
|
||||||
createdDate: supply.deliveryDate,
|
createdDate: supply.deliveryDate,
|
||||||
fromLocation: "Садовод",
|
fromLocation: 'Садовод',
|
||||||
toLocation: "SFERAV Logistics ФФ",
|
toLocation: 'SFERAV Logistics ФФ',
|
||||||
price: 0
|
price: 0,
|
||||||
}]
|
}]
|
||||||
|
|
||||||
return mockRoutes.map((route) => {
|
return mockRoutes.map((route) => {
|
||||||
@ -1033,7 +1034,7 @@ export function MultiLevelSuppliesTable({
|
|||||||
item.totalPrice +
|
item.totalPrice +
|
||||||
(item.recipe?.services || []).reduce((sum, service) => sum + service.price * item.quantity, 0) +
|
(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?.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>
|
</span>
|
||||||
</TableCell>
|
</TableCell>
|
||||||
|
@ -10,8 +10,8 @@ import { Alert, AlertDescription } from '@/components/ui/alert'
|
|||||||
import { Button } from '@/components/ui/button'
|
import { Button } from '@/components/ui/button'
|
||||||
import { GET_PENDING_SUPPLIES_COUNT, GET_MY_SUPPLY_ORDERS } from '@/graphql/queries'
|
import { GET_PENDING_SUPPLIES_COUNT, GET_MY_SUPPLY_ORDERS } from '@/graphql/queries'
|
||||||
import { useAuth } from '@/hooks/useAuth'
|
import { useAuth } from '@/hooks/useAuth'
|
||||||
import { useSidebar } from '@/hooks/useSidebar'
|
|
||||||
import { useRealtime } from '@/hooks/useRealtime'
|
import { useRealtime } from '@/hooks/useRealtime'
|
||||||
|
import { useSidebar } from '@/hooks/useSidebar'
|
||||||
|
|
||||||
import { AllSuppliesTab } from './fulfillment-supplies/all-supplies-tab'
|
import { AllSuppliesTab } from './fulfillment-supplies/all-supplies-tab'
|
||||||
import { RealSupplyOrdersTab } from './fulfillment-supplies/real-supply-orders-tab'
|
import { RealSupplyOrdersTab } from './fulfillment-supplies/real-supply-orders-tab'
|
||||||
|
@ -84,7 +84,7 @@ const autoCreateWarehouseEntry = async (sellerId: string, fulfillmentId: string)
|
|||||||
products: [], // Пустой массив продуктов
|
products: [], // Пустой массив продуктов
|
||||||
}
|
}
|
||||||
|
|
||||||
console.warn(`✅ AUTO WAREHOUSE ENTRY CREATED:`, {
|
console.warn('✅ AUTO WAREHOUSE ENTRY CREATED:', {
|
||||||
sellerId,
|
sellerId,
|
||||||
storeName: warehouseEntry.storeName,
|
storeName: warehouseEntry.storeName,
|
||||||
storeOwner: warehouseEntry.storeOwner,
|
storeOwner: warehouseEntry.storeOwner,
|
||||||
@ -933,7 +933,7 @@ export const resolvers = {
|
|||||||
userId: context.user.id,
|
userId: context.user.id,
|
||||||
organizationType: currentUser.organization.type,
|
organizationType: currentUser.organization.type,
|
||||||
organizationId: currentUser.organization.id,
|
organizationId: currentUser.organization.id,
|
||||||
organizationName: currentUser.organization.name
|
organizationName: currentUser.organization.name,
|
||||||
})
|
})
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@ -990,7 +990,7 @@ export const resolvers = {
|
|||||||
acc[order.status] = (acc[order.status] || 0) + 1
|
acc[order.status] = (acc[order.status] || 0) + 1
|
||||||
return acc
|
return acc
|
||||||
}, {}),
|
}, {}),
|
||||||
orderIds: orders.map(o => o.id)
|
orderIds: orders.map(o => o.id),
|
||||||
})
|
})
|
||||||
|
|
||||||
return orders
|
return orders
|
||||||
@ -1770,7 +1770,7 @@ export const resolvers = {
|
|||||||
// Получаем всех партнеров-селлеров
|
// Получаем всех партнеров-селлеров
|
||||||
const counterparties = await prisma.counterparty.findMany({
|
const counterparties = await prisma.counterparty.findMany({
|
||||||
where: {
|
where: {
|
||||||
organizationId: currentUser.organization.id
|
organizationId: currentUser.organization.id,
|
||||||
},
|
},
|
||||||
include: {
|
include: {
|
||||||
counterparty: true,
|
counterparty: true,
|
||||||
@ -2678,14 +2678,14 @@ export const resolvers = {
|
|||||||
...item,
|
...item,
|
||||||
recipe,
|
recipe,
|
||||||
}
|
}
|
||||||
})
|
}),
|
||||||
)
|
)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...order,
|
...order,
|
||||||
items: processedItems,
|
items: processedItems,
|
||||||
}
|
}
|
||||||
})
|
}),
|
||||||
)
|
)
|
||||||
|
|
||||||
console.warn('✅ Данные обработаны для многоуровневой таблицы')
|
console.warn('✅ Данные обработаны для многоуровневой таблицы')
|
||||||
@ -3868,7 +3868,7 @@ export const resolvers = {
|
|||||||
await autoCreateWarehouseEntry(request.senderId, request.receiverId)
|
await autoCreateWarehouseEntry(request.senderId, request.receiverId)
|
||||||
console.warn(`✅ AUTO WAREHOUSE ENTRY: Created for seller ${request.senderId} with fulfillment ${request.receiverId}`)
|
console.warn(`✅ AUTO WAREHOUSE ENTRY: Created for seller ${request.senderId} with fulfillment ${request.receiverId}`)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(`❌ AUTO WAREHOUSE ENTRY ERROR:`, error)
|
console.error('❌ AUTO WAREHOUSE ENTRY ERROR:', error)
|
||||||
// Не прерываем основной процесс, если не удалось создать запись склада
|
// Не прерываем основной процесс, если не удалось создать запись склада
|
||||||
}
|
}
|
||||||
} else if (request.sender.type === 'FULFILLMENT' && request.receiver.type === 'SELLER') {
|
} else if (request.sender.type === 'FULFILLMENT' && request.receiver.type === 'SELLER') {
|
||||||
@ -3877,7 +3877,7 @@ export const resolvers = {
|
|||||||
await autoCreateWarehouseEntry(request.receiverId, request.senderId)
|
await autoCreateWarehouseEntry(request.receiverId, request.senderId)
|
||||||
console.warn(`✅ AUTO WAREHOUSE ENTRY: Created for seller ${request.receiverId} with fulfillment ${request.senderId}`)
|
console.warn(`✅ AUTO WAREHOUSE ENTRY: Created for seller ${request.receiverId} with fulfillment ${request.senderId}`)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(`❌ AUTO WAREHOUSE ENTRY ERROR:`, error)
|
console.error('❌ AUTO WAREHOUSE ENTRY ERROR:', error)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -5089,7 +5089,7 @@ export const resolvers = {
|
|||||||
console.warn('🔍 Автоматическое определение типа расходников:', {
|
console.warn('🔍 Автоматическое определение типа расходников:', {
|
||||||
organizationType: currentUser.organization.type,
|
organizationType: currentUser.organization.type,
|
||||||
consumableType: consumableType,
|
consumableType: consumableType,
|
||||||
inputType: args.input.consumableType // Для отладки
|
inputType: args.input.consumableType, // Для отладки
|
||||||
})
|
})
|
||||||
|
|
||||||
// Подготавливаем данные для создания заказа
|
// Подготавливаем данные для создания заказа
|
||||||
@ -5188,7 +5188,7 @@ export const resolvers = {
|
|||||||
toAddress: fulfillmentCenterId ?
|
toAddress: fulfillmentCenterId ?
|
||||||
(await prisma.organization.findUnique({
|
(await prisma.organization.findUnique({
|
||||||
where: { id: fulfillmentCenterId },
|
where: { id: fulfillmentCenterId },
|
||||||
select: { addressFull: true, address: true }
|
select: { addressFull: true, address: true },
|
||||||
}))?.addressFull || null : null,
|
}))?.addressFull || null : null,
|
||||||
status: 'pending',
|
status: 'pending',
|
||||||
createdDate: new Date(),
|
createdDate: new Date(),
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
"use client"
|
'use client'
|
||||||
|
|
||||||
import { useEffect, useRef } from 'react'
|
import { useEffect, useRef } from 'react'
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user