Добавлены модели товаров и корзины для оптовиков, реализованы соответствующие мутации и запросы в GraphQL. Обновлен API для загрузки файлов с учетом новых типов данных. Улучшена обработка ошибок и добавлены новые функции для работы с категориями товаров.

This commit is contained in:
Bivekich
2025-07-17 16:36:07 +03:00
parent 6a94d51032
commit f377fbab5f
21 changed files with 3958 additions and 34 deletions

View File

@ -0,0 +1,207 @@
"use client"
import { useState } from 'react'
import { useQuery } from '@apollo/client'
import { Card } from '@/components/ui/card'
import { Button } from '@/components/ui/button'
import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogTrigger } from '@/components/ui/dialog'
import { Sidebar } from '@/components/dashboard/sidebar'
import { ProductForm } from './product-form'
import { ProductCard } from './product-card'
import { GET_MY_PRODUCTS } from '@/graphql/queries'
import { Plus, Search, Package } from 'lucide-react'
import { Input } from '@/components/ui/input'
interface Product {
id: string
name: string
article: string
description: string
price: number
quantity: number
category: { id: string; name: string } | null
brand: string
color: string
size: string
weight: number
dimensions: string
material: string
images: string[]
mainImage: string
isActive: boolean
createdAt: string
updatedAt: string
}
export function WarehouseDashboard() {
const [isDialogOpen, setIsDialogOpen] = useState(false)
const [editingProduct, setEditingProduct] = useState<Product | null>(null)
const [searchQuery, setSearchQuery] = useState('')
const { data, loading, error, refetch } = useQuery(GET_MY_PRODUCTS, {
errorPolicy: 'all'
})
const products: Product[] = data?.myProducts || []
// Фильтрация товаров по поисковому запросу
const filteredProducts = products.filter(product =>
product.name.toLowerCase().includes(searchQuery.toLowerCase()) ||
product.article.toLowerCase().includes(searchQuery.toLowerCase()) ||
product.category?.name?.toLowerCase().includes(searchQuery.toLowerCase()) ||
product.brand?.toLowerCase().includes(searchQuery.toLowerCase())
)
const handleCreateProduct = () => {
setEditingProduct(null)
setIsDialogOpen(true)
}
const handleEditProduct = (product: Product) => {
setEditingProduct(product)
setIsDialogOpen(true)
}
const handleProductSaved = () => {
setIsDialogOpen(false)
setEditingProduct(null)
refetch()
}
const handleProductDeleted = () => {
refetch()
}
if (error) {
return (
<div className="h-screen bg-gradient-smooth flex overflow-hidden">
<Sidebar />
<main className="flex-1 ml-56 px-6 py-4 overflow-hidden">
<div className="h-full w-full flex flex-col">
<Card className="flex-1 bg-white/5 backdrop-blur border-white/10 p-6">
<div className="flex items-center justify-center h-full">
<div className="text-center">
<Package className="h-16 w-16 text-white/40 mx-auto mb-4" />
<h3 className="text-lg font-medium text-white mb-2">Ошибка загрузки</h3>
<p className="text-white/60 text-sm mb-4">
{error.message || 'Не удалось загрузить товары'}
</p>
<Button
onClick={() => refetch()}
className="bg-purple-600 hover:bg-purple-700 text-white"
>
Попробовать снова
</Button>
</div>
</div>
</Card>
</div>
</main>
</div>
)
}
return (
<div className="h-screen bg-gradient-smooth flex overflow-hidden">
<Sidebar />
<main className="flex-1 ml-56 px-6 py-4 overflow-hidden">
<div className="h-full w-full flex flex-col">
{/* Заголовок и поиск */}
<div className="flex items-center justify-between mb-4 flex-shrink-0">
<div>
<h1 className="text-xl font-bold text-white mb-1">Склад товаров</h1>
<p className="text-white/70 text-sm">Управление ассортиментом вашего склада</p>
</div>
<Dialog open={isDialogOpen} onOpenChange={setIsDialogOpen}>
<DialogTrigger asChild>
<Button
onClick={handleCreateProduct}
className="bg-gradient-to-r from-purple-600 to-pink-600 hover:from-purple-700 hover:to-pink-700 text-white border-0 shadow-lg shadow-purple-500/25 transition-all duration-300"
>
<Plus className="w-4 h-4 mr-2" />
Добавить товар
</Button>
</DialogTrigger>
<DialogContent className="glass-card max-w-4xl max-h-[90vh] overflow-y-auto">
<DialogHeader>
<DialogTitle className="text-white">
{editingProduct ? 'Редактировать товар' : 'Добавить новый товар'}
</DialogTitle>
</DialogHeader>
<ProductForm
product={editingProduct}
onSave={handleProductSaved}
onCancel={() => setIsDialogOpen(false)}
/>
</DialogContent>
</Dialog>
</div>
{/* Поиск */}
<div className="mb-4 flex-shrink-0">
<div className="relative max-w-md">
<Search className="absolute left-3 top-1/2 transform -translate-y-1/2 h-4 w-4 text-white/50" />
<Input
type="text"
placeholder="Поиск по названию, артикулу, категории..."
value={searchQuery}
onChange={(e) => setSearchQuery(e.target.value)}
className="glass-input text-white placeholder:text-white/50 pl-10 h-10"
/>
</div>
</div>
{/* Основной контент */}
<Card className="flex-1 bg-white/5 backdrop-blur border-white/10 p-6 overflow-hidden">
{loading ? (
<div className="flex items-center justify-center h-full">
<div className="text-center">
<div className="animate-spin rounded-full h-16 w-16 border-4 border-white border-t-transparent mx-auto mb-4"></div>
<p className="text-white/70">Загрузка товаров...</p>
</div>
</div>
) : filteredProducts.length === 0 ? (
<div className="flex items-center justify-center h-full">
<div className="text-center">
<Package className="h-16 w-16 text-white/40 mx-auto mb-4" />
<h3 className="text-lg font-medium text-white mb-2">
{searchQuery ? 'Товары не найдены' : 'Склад пуст'}
</h3>
<p className="text-white/60 text-sm mb-4">
{searchQuery
? 'Попробуйте изменить критерии поиска'
: 'Добавьте ваш первый товар на склад'
}
</p>
{!searchQuery && (
<Button
onClick={handleCreateProduct}
className="bg-purple-600 hover:bg-purple-700 text-white"
>
<Plus className="w-4 h-4 mr-2" />
Добавить товар
</Button>
)}
</div>
</div>
) : (
<div className="h-full overflow-y-auto">
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-4">
{filteredProducts.map((product) => (
<ProductCard
key={product.id}
product={product}
onEdit={handleEditProduct}
onDeleted={handleProductDeleted}
/>
))}
</div>
</div>
)}
</Card>
</div>
</main>
</div>
)
}