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:
@ -2,8 +2,8 @@ import { FulfillmentDetailedSuppliesTab } from '@/components/fulfillment-supplie
|
|||||||
|
|
||||||
export default function DetailedSuppliesPage() {
|
export default function DetailedSuppliesPage() {
|
||||||
return (
|
return (
|
||||||
<div className="h-full overflow-hidden">
|
<div>
|
||||||
<FulfillmentDetailedSuppliesTab />
|
<FulfillmentDetailedSuppliesTab />
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,18 @@
|
|||||||
'use client'
|
'use client'
|
||||||
|
|
||||||
import { useQuery } from '@apollo/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 Link from 'next/link'
|
||||||
import { usePathname } from 'next/navigation'
|
import { usePathname } from 'next/navigation'
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
@ -26,37 +37,35 @@ function NotificationBadge({ count }: { count: number }) {
|
|||||||
function Breadcrumbs({ pathname }: { pathname: string }) {
|
function Breadcrumbs({ pathname }: { pathname: string }) {
|
||||||
const getBreadcrumbs = (path: string) => {
|
const getBreadcrumbs = (path: string) => {
|
||||||
const segments = path.split('/').filter(Boolean)
|
const segments = path.split('/').filter(Boolean)
|
||||||
|
|
||||||
const breadcrumbs = [
|
const breadcrumbs = [{ name: 'Главная', href: '/dashboard', icon: Home }]
|
||||||
{ name: 'Главная', href: '/dashboard', icon: Home }
|
|
||||||
]
|
|
||||||
|
|
||||||
if (segments[0] === 'fulfillment-supplies') {
|
if (segments[0] === 'fulfillment-supplies') {
|
||||||
breadcrumbs.push({ name: 'Входящие поставки', href: '/fulfillment-supplies', icon: Building2 })
|
breadcrumbs.push({ name: 'Входящие поставки', href: '/fulfillment-supplies', icon: Building2 })
|
||||||
|
|
||||||
if (segments[1]) {
|
if (segments[1]) {
|
||||||
const categoryMap: Record<string, string> = {
|
const categoryMap: Record<string, string> = {
|
||||||
'goods': 'Товары',
|
goods: 'Товары',
|
||||||
'detailed-supplies': 'Расходники фулфилмента',
|
'detailed-supplies': 'Расходники фулфилмента',
|
||||||
'consumables': 'Расходники селлеров',
|
consumables: 'Расходники селлеров',
|
||||||
'returns': 'Возвраты с ПВЗ'
|
returns: 'Возвраты с ПВЗ',
|
||||||
}
|
}
|
||||||
|
|
||||||
const category = categoryMap[segments[1]]
|
const category = categoryMap[segments[1]]
|
||||||
if (category) {
|
if (category) {
|
||||||
breadcrumbs.push({
|
breadcrumbs.push({
|
||||||
name: category,
|
name: category,
|
||||||
href: segments[2] ? `/fulfillment-supplies/${segments[1]}` : path,
|
href: segments[2] ? `/fulfillment-supplies/${segments[1]}` : path,
|
||||||
icon: Package
|
icon: Package,
|
||||||
})
|
})
|
||||||
|
|
||||||
if (segments[1] === 'goods' && segments[2]) {
|
if (segments[1] === 'goods' && segments[2]) {
|
||||||
const subcategoryMap: Record<string, string> = {
|
const subcategoryMap: Record<string, string> = {
|
||||||
'new': 'Новые',
|
new: 'Новые',
|
||||||
'receiving': 'Приёмка',
|
receiving: 'Приёмка',
|
||||||
'received': 'Принято'
|
received: 'Принято',
|
||||||
}
|
}
|
||||||
|
|
||||||
const subcategory = subcategoryMap[segments[2]]
|
const subcategory = subcategoryMap[segments[2]]
|
||||||
if (subcategory) {
|
if (subcategory) {
|
||||||
breadcrumbs.push({ name: subcategory, href: path, icon: Clock })
|
breadcrumbs.push({ name: subcategory, href: path, icon: Clock })
|
||||||
@ -65,7 +74,7 @@ function Breadcrumbs({ pathname }: { pathname: string }) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return breadcrumbs
|
return breadcrumbs
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -82,10 +91,7 @@ function Breadcrumbs({ pathname }: { pathname: string }) {
|
|||||||
{breadcrumb.name}
|
{breadcrumb.name}
|
||||||
</span>
|
</span>
|
||||||
) : (
|
) : (
|
||||||
<Link
|
<Link href={breadcrumb.href} className="hover:text-white transition-colors flex items-center gap-1">
|
||||||
href={breadcrumb.href}
|
|
||||||
className="hover:text-white transition-colors flex items-center gap-1"
|
|
||||||
>
|
|
||||||
{breadcrumb.icon && <breadcrumb.icon className="h-4 w-4" />}
|
{breadcrumb.icon && <breadcrumb.icon className="h-4 w-4" />}
|
||||||
{breadcrumb.name}
|
{breadcrumb.name}
|
||||||
</Link>
|
</Link>
|
||||||
@ -101,7 +107,11 @@ export function FulfillmentSuppliesLayout({ children }: { children: React.ReactN
|
|||||||
const pathname = usePathname()
|
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',
|
fetchPolicy: 'cache-first',
|
||||||
errorPolicy: 'ignore',
|
errorPolicy: 'ignore',
|
||||||
onError: (error) => {
|
onError: (error) => {
|
||||||
@ -165,13 +175,13 @@ export function FulfillmentSuppliesLayout({ children }: { children: React.ReactN
|
|||||||
<div className="h-full w-full flex flex-col space-y-4">
|
<div className="h-full w-full flex flex-col space-y-4">
|
||||||
{/* Breadcrumbs */}
|
{/* Breadcrumbs */}
|
||||||
<Breadcrumbs pathname={pathname} />
|
<Breadcrumbs pathname={pathname} />
|
||||||
|
|
||||||
{/* БЛОК 1: ТАБЫ ВСЕХ УРОВНЕЙ */}
|
{/* БЛОК 1: ТАБЫ ВСЕХ УРОВНЕЙ */}
|
||||||
<div className="bg-white/10 backdrop-blur-xl border border-white/20 rounded-2xl p-6">
|
<div className="bg-white/10 backdrop-blur-xl border border-white/20 rounded-2xl p-6">
|
||||||
{/* УРОВЕНЬ 1: Главные табы */}
|
{/* УРОВЕНЬ 1: Главные табы */}
|
||||||
<div className="mb-4">
|
<div className="mb-4">
|
||||||
<div className="grid w-full grid-cols-2 bg-white/15 backdrop-blur border-white/30 rounded-xl h-11 p-2">
|
<div className="grid w-full grid-cols-2 bg-white/15 backdrop-blur border-white/30 rounded-xl h-11 p-2">
|
||||||
<Link
|
<Link
|
||||||
href="/fulfillment-supplies/goods/new"
|
href="/fulfillment-supplies/goods/new"
|
||||||
className={`flex items-center gap-2 text-sm font-semibold transition-all duration-200 rounded-lg px-3 ${
|
className={`flex items-center gap-2 text-sm font-semibold transition-all duration-200 rounded-lg px-3 ${
|
||||||
activeTab === 'fulfillment'
|
activeTab === 'fulfillment'
|
||||||
@ -296,14 +306,12 @@ export function FulfillmentSuppliesLayout({ children }: { children: React.ReactN
|
|||||||
|
|
||||||
{/* БЛОК 2: ОСНОВНОЙ КОНТЕНТ */}
|
{/* БЛОК 2: ОСНОВНОЙ КОНТЕНТ */}
|
||||||
<div className="flex-1 overflow-hidden">
|
<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="bg-white/10 backdrop-blur-xl border border-white/20 rounded-2xl h-full overflow-y-auto p-6">
|
||||||
<div className="h-full">
|
<div className="h-full">{children}</div>
|
||||||
{children}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -1,15 +1,16 @@
|
|||||||
'use client'
|
'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 { TrendingUp, Wrench, Plus, Package2, Clock, CheckCircle, XCircle, Truck } from 'lucide-react'
|
||||||
import { useRouter } from 'next/navigation'
|
import { useRouter } from 'next/navigation'
|
||||||
import React, { useState } from 'react'
|
import React, { useState } from 'react'
|
||||||
|
import { toast } from 'sonner'
|
||||||
|
|
||||||
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 { FULFILLMENT_RECEIVE_CONSUMABLE_SUPPLY } from '@/graphql/mutations/fulfillment-receive-v2'
|
||||||
import { GET_MY_FULFILLMENT_CONSUMABLE_SUPPLIES } from '@/graphql/queries/fulfillment-consumables-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 { StatsCard } from '../../supplies/ui/stats-card'
|
||||||
import { StatsGrid } from '../../supplies/ui/stats-grid'
|
import { StatsGrid } from '../../supplies/ui/stats-grid'
|
||||||
@ -155,7 +156,6 @@ const formatDate = (dateString: string) => {
|
|||||||
|
|
||||||
export function FulfillmentDetailedSuppliesTab() {
|
export function FulfillmentDetailedSuppliesTab() {
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const { user } = useAuth()
|
|
||||||
const [expandedSupplies, setExpandedSupplies] = useState<Set<string>>(new Set())
|
const [expandedSupplies, setExpandedSupplies] = useState<Set<string>>(new Set())
|
||||||
|
|
||||||
// Загружаем поставки расходников через новый API v2
|
// Загружаем поставки расходников через новый API v2
|
||||||
@ -171,6 +171,22 @@ export function FulfillmentDetailedSuppliesTab() {
|
|||||||
// Получаем поставки из нового API
|
// Получаем поставки из нового API
|
||||||
const supplies: FulfillmentConsumableSupply[] = data?.myFulfillmentConsumableSupplies || []
|
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 toggleExpanded = (supplyId: string) => {
|
||||||
const newExpanded = new Set(expandedSupplies)
|
const newExpanded = new Set(expandedSupplies)
|
||||||
@ -182,6 +198,29 @@ export function FulfillmentDetailedSuppliesTab() {
|
|||||||
setExpandedSupplies(newExpanded)
|
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 totalSupplies = supplies.length
|
||||||
const totalAmount = supplies.reduce((sum, supply) => {
|
const totalAmount = supplies.reduce((sum, supply) => {
|
||||||
@ -190,27 +229,29 @@ export function FulfillmentDetailedSuppliesTab() {
|
|||||||
const totalItems = supplies.reduce((sum, supply) => {
|
const totalItems = supplies.reduce((sum, supply) => {
|
||||||
return sum + supply.items.reduce((itemSum, item) => itemSum + item.requestedQuantity, 0)
|
return sum + supply.items.reduce((itemSum, item) => itemSum + item.requestedQuantity, 0)
|
||||||
}, 0)
|
}, 0)
|
||||||
const deliveredCount = supplies.filter(supply => supply.status === 'DELIVERED').length
|
const deliveredCount = supplies.filter((supply) => supply.status === 'DELIVERED').length
|
||||||
|
|
||||||
if (loading) {
|
if (loading) {
|
||||||
return (
|
return (
|
||||||
<div className="flex items-center justify-center h-64">
|
<div className="h-full flex items-center justify-center">
|
||||||
<div className="animate-spin rounded-full h-8 w-8 border-2 border-white border-t-transparent"></div>
|
<div className="text-center">
|
||||||
<span className="ml-3 text-white/60">Загрузка расходников фулфилмента...</span>
|
<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>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (error) {
|
if (error) {
|
||||||
return (
|
return (
|
||||||
<div className="flex items-center justify-center h-64">
|
<div className="h-full flex items-center justify-center">
|
||||||
<div className="text-center">
|
<div className="text-center">
|
||||||
<Wrench className="h-12 w-12 text-red-400 mx-auto mb-4" />
|
<Wrench className="h-12 w-12 text-red-400 mx-auto mb-4" />
|
||||||
<p className="text-red-400 font-medium">Ошибка загрузки расходников</p>
|
<p className="text-red-400 font-medium">Ошибка загрузки расходников</p>
|
||||||
<p className="text-white/60 text-sm mt-2">{error.message}</p>
|
<p className="text-white/60 text-sm mt-2">{error.message}</p>
|
||||||
<Button
|
<Button
|
||||||
onClick={() => refetch()}
|
onClick={() => refetch()}
|
||||||
variant="outline"
|
variant="outline"
|
||||||
className="mt-4 border-white/20 text-white/80 hover:bg-white/10"
|
className="mt-4 border-white/20 text-white/80 hover:bg-white/10"
|
||||||
>
|
>
|
||||||
Повторить попытку
|
Повторить попытку
|
||||||
@ -283,8 +324,8 @@ export function FulfillmentDetailedSuppliesTab() {
|
|||||||
<Wrench className="h-16 w-16 text-white/20 mx-auto mb-4" />
|
<Wrench className="h-16 w-16 text-white/20 mx-auto mb-4" />
|
||||||
<h3 className="text-lg font-semibold text-white mb-2">Пока нет поставок расходников</h3>
|
<h3 className="text-lg font-semibold text-white mb-2">Пока нет поставок расходников</h3>
|
||||||
<p className="text-white/60">
|
<p className="text-white/60">
|
||||||
Здесь будут отображаться поставки расходников для вашего фулфилмент-центра.
|
Здесь будут отображаться поставки расходников для вашего фулфилмент-центра. Создайте новую поставку через
|
||||||
Создайте новую поставку через кнопку "Создать поставку".
|
кнопку "Создать поставку".
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</Card>
|
</Card>
|
||||||
@ -293,17 +334,29 @@ export function FulfillmentDetailedSuppliesTab() {
|
|||||||
{supplies.map((supply) => (
|
{supplies.map((supply) => (
|
||||||
<Card key={supply.id} className="bg-white/10 backdrop-blur border-white/20 overflow-hidden">
|
<Card key={supply.id} className="bg-white/10 backdrop-blur border-white/20 overflow-hidden">
|
||||||
{/* Основная информация о поставке */}
|
{/* Основная информация о поставке */}
|
||||||
<div
|
<div
|
||||||
className="p-6 cursor-pointer hover:bg-white/5 transition-colors"
|
className="p-6 cursor-pointer hover:bg-white/5 transition-colors"
|
||||||
onClick={() => toggleExpanded(supply.id)}
|
onClick={() => toggleExpanded(supply.id)}
|
||||||
>
|
>
|
||||||
<div className="flex items-center justify-between">
|
<div className="flex items-center justify-between">
|
||||||
<div className="flex-1">
|
<div className="flex-1">
|
||||||
<div className="flex items-center gap-4 mb-2">
|
<div className="flex items-center gap-4 mb-2">
|
||||||
<h3 className="text-lg font-semibold text-white">
|
<h3 className="text-lg font-semibold text-white">Поставка #{supply.id.slice(-8)}</h3>
|
||||||
Поставка #{supply.id.slice(-8)}
|
|
||||||
</h3>
|
|
||||||
{getStatusBadge(supply.status)}
|
{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>
|
||||||
<div className="grid grid-cols-2 md:grid-cols-4 gap-4 text-sm">
|
<div className="grid grid-cols-2 md:grid-cols-4 gap-4 text-sm">
|
||||||
<div>
|
<div>
|
||||||
@ -327,9 +380,11 @@ export function FulfillmentDetailedSuppliesTab() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="ml-4">
|
<div className="ml-4">
|
||||||
<Clock className={`h-5 w-5 transition-transform ${
|
<Clock
|
||||||
expandedSupplies.has(supply.id) ? 'rotate-180' : ''
|
className={`h-5 w-5 transition-transform ${
|
||||||
} text-white/60`} />
|
expandedSupplies.has(supply.id) ? 'rotate-180' : ''
|
||||||
|
} text-white/60`}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -357,7 +412,8 @@ export function FulfillmentDetailedSuppliesTab() {
|
|||||||
<div className="text-right">
|
<div className="text-right">
|
||||||
<p className="text-white">Количество: {item.requestedQuantity}</p>
|
<p className="text-white">Количество: {item.requestedQuantity}</p>
|
||||||
<p className="text-white/60 text-sm">
|
<p className="text-white/60 text-sm">
|
||||||
{formatCurrency(item.unitPrice)} × {item.requestedQuantity} = {formatCurrency(item.totalPrice)}
|
{formatCurrency(item.unitPrice)} × {item.requestedQuantity} ={' '}
|
||||||
|
{formatCurrency(item.totalPrice)}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -399,4 +455,4 @@ export function FulfillmentDetailedSuppliesTab() {
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user