fix(ui): исправить проблемы со scroll и layout в кабинете фулфилмента

- Убрать конфликты overflow и принудительные высоты
- Исправить дублирование Sidebar в layout
- Изменить структуру с overflow-hidden на overflow-y-auto
- Удалить style={{ minHeight: '200vh' }} вызывавший смещение контента

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Veronika Smirnova
2025-08-27 12:23:43 +03:00
parent d200885ff5
commit a80dee9758
3 changed files with 119 additions and 55 deletions

View File

@ -2,7 +2,7 @@ import { FulfillmentDetailedSuppliesTab } from '@/components/fulfillment-supplie
export default function DetailedSuppliesPage() {
return (
<div className="h-full overflow-hidden">
<div>
<FulfillmentDetailedSuppliesTab />
</div>
)

View File

@ -1,7 +1,18 @@
'use client'
import { useQuery } from '@apollo/client'
import { Building2, ShoppingCart, Package, Wrench, RotateCcw, Clock, FileText, CheckCircle, ChevronRight, Home } from 'lucide-react'
import {
Building2,
ShoppingCart,
Package,
Wrench,
RotateCcw,
Clock,
FileText,
CheckCircle,
ChevronRight,
Home,
} from 'lucide-react'
import Link from 'next/link'
import { usePathname } from 'next/navigation'
import React from 'react'
@ -27,19 +38,17 @@ function Breadcrumbs({ pathname }: { pathname: string }) {
const getBreadcrumbs = (path: string) => {
const segments = path.split('/').filter(Boolean)
const breadcrumbs = [
{ name: 'Главная', href: '/dashboard', icon: Home }
]
const breadcrumbs = [{ name: 'Главная', href: '/dashboard', icon: Home }]
if (segments[0] === 'fulfillment-supplies') {
breadcrumbs.push({ name: 'Входящие поставки', href: '/fulfillment-supplies', icon: Building2 })
if (segments[1]) {
const categoryMap: Record<string, string> = {
'goods': 'Товары',
goods: 'Товары',
'detailed-supplies': 'Расходники фулфилмента',
'consumables': 'Расходники селлеров',
'returns': 'Возвраты с ПВЗ'
consumables: 'Расходники селлеров',
returns: 'Возвраты с ПВЗ',
}
const category = categoryMap[segments[1]]
@ -47,14 +56,14 @@ function Breadcrumbs({ pathname }: { pathname: string }) {
breadcrumbs.push({
name: category,
href: segments[2] ? `/fulfillment-supplies/${segments[1]}` : path,
icon: Package
icon: Package,
})
if (segments[1] === 'goods' && segments[2]) {
const subcategoryMap: Record<string, string> = {
'new': 'Новые',
'receiving': 'Приёмка',
'received': 'Принято'
new: 'Новые',
receiving: 'Приёмка',
received: 'Принято',
}
const subcategory = subcategoryMap[segments[2]]
@ -82,10 +91,7 @@ function Breadcrumbs({ pathname }: { pathname: string }) {
{breadcrumb.name}
</span>
) : (
<Link
href={breadcrumb.href}
className="hover:text-white transition-colors flex items-center gap-1"
>
<Link href={breadcrumb.href} className="hover:text-white transition-colors flex items-center gap-1">
{breadcrumb.icon && <breadcrumb.icon className="h-4 w-4" />}
{breadcrumb.name}
</Link>
@ -101,7 +107,11 @@ export function FulfillmentSuppliesLayout({ children }: { children: React.ReactN
const pathname = usePathname()
// Загружаем данные о непринятых поставках
const { data: pendingData, error: pendingError, refetch: refetchPending } = useQuery(GET_PENDING_SUPPLIES_COUNT, {
const {
data: pendingData,
error: pendingError,
refetch: refetchPending,
} = useQuery(GET_PENDING_SUPPLIES_COUNT, {
fetchPolicy: 'cache-first',
errorPolicy: 'ignore',
onError: (error) => {
@ -296,10 +306,8 @@ export function FulfillmentSuppliesLayout({ children }: { children: React.ReactN
{/* БЛОК 2: ОСНОВНОЙ КОНТЕНТ */}
<div className="flex-1 overflow-hidden">
<div className="bg-white/10 backdrop-blur-xl border border-white/20 rounded-2xl h-full overflow-hidden p-6">
<div className="h-full">
{children}
</div>
<div className="bg-white/10 backdrop-blur-xl border border-white/20 rounded-2xl h-full overflow-y-auto p-6">
<div className="h-full">{children}</div>
</div>
</div>
</div>

View File

@ -1,15 +1,16 @@
'use client'
import { useQuery } from '@apollo/client'
import { useQuery, useMutation } from '@apollo/client'
import { TrendingUp, Wrench, Plus, Package2, Clock, CheckCircle, XCircle, Truck } from 'lucide-react'
import { useRouter } from 'next/navigation'
import React, { useState } from 'react'
import { toast } from 'sonner'
import { Badge } from '@/components/ui/badge'
import { Button } from '@/components/ui/button'
import { Card } from '@/components/ui/card'
import { FULFILLMENT_RECEIVE_CONSUMABLE_SUPPLY } from '@/graphql/mutations/fulfillment-receive-v2'
import { GET_MY_FULFILLMENT_CONSUMABLE_SUPPLIES } from '@/graphql/queries/fulfillment-consumables-v2'
import { useAuth } from '@/hooks/useAuth'
import { StatsCard } from '../../supplies/ui/stats-card'
import { StatsGrid } from '../../supplies/ui/stats-grid'
@ -155,7 +156,6 @@ const formatDate = (dateString: string) => {
export function FulfillmentDetailedSuppliesTab() {
const router = useRouter()
const { user } = useAuth()
const [expandedSupplies, setExpandedSupplies] = useState<Set<string>>(new Set())
// Загружаем поставки расходников через новый API v2
@ -171,6 +171,22 @@ export function FulfillmentDetailedSuppliesTab() {
// Получаем поставки из нового API
const supplies: FulfillmentConsumableSupply[] = data?.myFulfillmentConsumableSupplies || []
// Мутация для приемки поставки
const [fulfillmentReceiveSupply] = useMutation(FULFILLMENT_RECEIVE_CONSUMABLE_SUPPLY, {
refetchQueries: [{ query: GET_MY_FULFILLMENT_CONSUMABLE_SUPPLIES }],
onCompleted: (data) => {
if (data.fulfillmentReceiveConsumableSupply.success) {
toast.success(data.fulfillmentReceiveConsumableSupply.message)
} else {
toast.error(data.fulfillmentReceiveConsumableSupply.message)
}
},
onError: (error) => {
console.error('Error receiving supply:', error)
toast.error('Ошибка при приемке поставки')
},
})
// Функция для переключения развернутого состояния поставки
const toggleExpanded = (supplyId: string) => {
const newExpanded = new Set(expandedSupplies)
@ -182,6 +198,29 @@ export function FulfillmentDetailedSuppliesTab() {
setExpandedSupplies(newExpanded)
}
// Обработчик приемки поставки
const handleReceiveSupply = async (supply: FulfillmentConsumableSupply) => {
try {
// Создаем items для приемки (принимаем все заказанные количества без брака)
const items = supply.items.map((item) => ({
id: item.id,
receivedQuantity: item.requestedQuantity, // Принимаем все заказанное количество
defectQuantity: 0, // Без брака
}))
await fulfillmentReceiveSupply({
variables: {
id: supply.id,
items: items,
notes: 'Поставка принята на склад',
},
})
} catch (error) {
console.error('Error receiving supply:', error)
toast.error('Ошибка при приемке поставки')
}
}
// Вычисляем статистику
const totalSupplies = supplies.length
const totalAmount = supplies.reduce((sum, supply) => {
@ -190,20 +229,22 @@ export function FulfillmentDetailedSuppliesTab() {
const totalItems = supplies.reduce((sum, supply) => {
return sum + supply.items.reduce((itemSum, item) => itemSum + item.requestedQuantity, 0)
}, 0)
const deliveredCount = supplies.filter(supply => supply.status === 'DELIVERED').length
const deliveredCount = supplies.filter((supply) => supply.status === 'DELIVERED').length
if (loading) {
return (
<div className="flex items-center justify-center h-64">
<div className="animate-spin rounded-full h-8 w-8 border-2 border-white border-t-transparent"></div>
<span className="ml-3 text-white/60">Загрузка расходников фулфилмента...</span>
<div className="h-full flex items-center justify-center">
<div className="text-center">
<div className="animate-spin rounded-full h-8 w-8 border-2 border-white border-t-transparent mx-auto mb-3"></div>
<span className="text-white/60">Загрузка расходников фулфилмента...</span>
</div>
</div>
)
}
if (error) {
return (
<div className="flex items-center justify-center h-64">
<div className="h-full flex items-center justify-center">
<div className="text-center">
<Wrench className="h-12 w-12 text-red-400 mx-auto mb-4" />
<p className="text-red-400 font-medium">Ошибка загрузки расходников</p>
@ -283,8 +324,8 @@ export function FulfillmentDetailedSuppliesTab() {
<Wrench className="h-16 w-16 text-white/20 mx-auto mb-4" />
<h3 className="text-lg font-semibold text-white mb-2">Пока нет поставок расходников</h3>
<p className="text-white/60">
Здесь будут отображаться поставки расходников для вашего фулфилмент-центра.
Создайте новую поставку через кнопку &quot;Создать поставку&quot;.
Здесь будут отображаться поставки расходников для вашего фулфилмент-центра. Создайте новую поставку через
кнопку &quot;Создать поставку&quot;.
</p>
</div>
</Card>
@ -300,10 +341,22 @@ export function FulfillmentDetailedSuppliesTab() {
<div className="flex items-center justify-between">
<div className="flex-1">
<div className="flex items-center gap-4 mb-2">
<h3 className="text-lg font-semibold text-white">
Поставка #{supply.id.slice(-8)}
</h3>
<h3 className="text-lg font-semibold text-white">Поставка #{supply.id.slice(-8)}</h3>
{getStatusBadge(supply.status)}
{/* Кнопка приемки для статуса SHIPPED */}
{supply.status === 'SHIPPED' && (
<Button
size="sm"
onClick={(e) => {
e.stopPropagation()
handleReceiveSupply(supply)
}}
className="bg-green-500/20 hover:bg-green-500/30 text-green-300 border border-green-500/30"
>
<Package2 className="h-3 w-3 mr-1" />
Принять на склад
</Button>
)}
</div>
<div className="grid grid-cols-2 md:grid-cols-4 gap-4 text-sm">
<div>
@ -327,9 +380,11 @@ export function FulfillmentDetailedSuppliesTab() {
</div>
</div>
<div className="ml-4">
<Clock className={`h-5 w-5 transition-transform ${
<Clock
className={`h-5 w-5 transition-transform ${
expandedSupplies.has(supply.id) ? 'rotate-180' : ''
} text-white/60`} />
} text-white/60`}
/>
</div>
</div>
</div>
@ -357,7 +412,8 @@ export function FulfillmentDetailedSuppliesTab() {
<div className="text-right">
<p className="text-white">Количество: {item.requestedQuantity}</p>
<p className="text-white/60 text-sm">
{formatCurrency(item.unitPrice)} × {item.requestedQuantity} = {formatCurrency(item.totalPrice)}
{formatCurrency(item.unitPrice)} × {item.requestedQuantity} ={' '}
{formatCurrency(item.totalPrice)}
</p>
</div>
</div>