Добавлены новые функции для управления категориями: реализованы мутации для создания, обновления и удаления категорий. Обновлены компоненты админ-панели для отображения и управления категориями, улучшен интерфейс и адаптивность. Добавлены новые кнопки и обработчики событий для взаимодействия с категориями.

This commit is contained in:
Bivekich
2025-07-19 17:09:40 +03:00
parent 965482b617
commit 8d57fcd748
12 changed files with 1733 additions and 67 deletions

View File

@ -0,0 +1,518 @@
"use client"
import { useState } from 'react'
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'
import { Badge } from '@/components/ui/badge'
import { Button } from '@/components/ui/button'
import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar'
import { Progress } from '@/components/ui/progress'
import { Input } from '@/components/ui/input'
import {
Calendar,
Check,
X,
Clock,
User,
Package,
Star,
Heart,
ShoppingCart,
Edit,
Trash2,
Phone,
Mail,
MapPin,
Building,
TrendingUp,
Award,
Users,
Briefcase,
Eye,
Plus,
Minus
} from 'lucide-react'
export function BusinessDemo() {
const [selectedProduct] = useState(null)
const [cartQuantity, setCartQuantity] = useState(1)
// Данные для демонстрации
const scheduleData = Array.from({ length: 30 }, (_, i) => ({
day: i + 1,
status: ['work', 'work', 'work', 'work', 'work', 'weekend', 'weekend'][i % 7],
hours: [8, 8, 8, 8, 8, 0, 0][i % 7]
}))
const products = [
{
id: '1',
name: 'iPhone 15 Pro Max 256GB',
article: 'APL-IP15PM-256',
price: 89990,
oldPrice: 99990,
quantity: 45,
category: 'Электроника',
brand: 'Apple',
rating: 4.8,
reviews: 1234,
image: '/placeholder-phone.jpg',
seller: 'TechStore Moscow',
isNew: true,
inStock: true
},
{
id: '2',
name: 'Беспроводные наушники AirPods Pro',
article: 'APL-APP-PRO',
price: 24990,
quantity: 23,
category: 'Аксессуары',
brand: 'Apple',
rating: 4.6,
reviews: 856,
image: '/placeholder-headphones.jpg',
seller: 'Audio Expert',
isNew: false,
inStock: true
},
{
id: '3',
name: 'Ноутбук MacBook Air M2',
article: 'APL-MBA-M2',
price: 0,
quantity: 0,
category: 'Компьютеры',
brand: 'Apple',
rating: 4.9,
reviews: 445,
image: '/placeholder-laptop.jpg',
seller: 'Digital World',
isNew: false,
inStock: false
}
]
const wholesalers = [
{
id: '1',
name: 'ТехноОпт Москва',
fullName: 'ООО "Технологии Оптом"',
inn: '7735123456',
type: 'WHOLESALE',
avatar: '/placeholder-company.jpg',
rating: 4.8,
reviewsCount: 2345,
productsCount: 15670,
completedOrders: 8934,
responseTime: '2 часа',
categories: ['Электроника', 'Компьютеры', 'Аксессуары'],
location: 'Москва, Россия',
workingSince: '2018',
verifiedBadges: ['verified', 'premium', 'fast-delivery'],
description: 'Крупнейший поставщик электроники и компьютерной техники в России',
specialOffers: 3,
minOrder: 50000
},
{
id: '2',
name: 'СтройБаза Регион',
fullName: 'ИП Строительные материалы',
inn: '7735987654',
type: 'WHOLESALE',
avatar: '/placeholder-construction.jpg',
rating: 4.5,
reviewsCount: 1876,
productsCount: 8430,
completedOrders: 5621,
responseTime: '4 часа',
categories: ['Стройматериалы', 'Инструменты', 'Сантехника'],
location: 'Екатеринбург, Россия',
workingSince: '2015',
verifiedBadges: ['verified', 'eco-friendly'],
description: 'Надежный поставщик строительных материалов по всей России',
specialOffers: 1,
minOrder: 30000
}
]
const getStatusColor = (status: string) => {
switch (status) {
case 'work': return 'bg-green-500'
case 'weekend': return 'bg-gray-400'
case 'vacation': return 'bg-blue-500'
case 'sick': return 'bg-yellow-500'
case 'absent': return 'bg-red-500'
default: return 'bg-gray-400'
}
}
const getStatusText = (status: string) => {
switch (status) {
case 'work': return 'Работа'
case 'weekend': return 'Выходной'
case 'vacation': return 'Отпуск'
case 'sick': return 'Больничный'
case 'absent': return 'Прогул'
default: return 'Неизвестно'
}
}
const formatPrice = (price: number) => {
return new Intl.NumberFormat('ru-RU', {
style: 'currency',
currency: 'RUB',
minimumFractionDigits: 0
}).format(price)
}
return (
<div className="space-y-6">
{/* Табель рабочего времени */}
<Card className="glass-card border-white/10">
<CardHeader>
<CardTitle className="text-white">Табель рабочего времени</CardTitle>
</CardHeader>
<CardContent className="space-y-6">
{/* Заголовок табеля */}
<div className="flex items-center justify-between">
<div className="flex items-center space-x-3">
<Avatar className="h-10 w-10">
<AvatarImage src="/placeholder-employee.jpg" />
<AvatarFallback className="bg-purple-600 text-white">ИИ</AvatarFallback>
</Avatar>
<div>
<h4 className="text-white font-medium">Иванов Иван Иванович</h4>
<p className="text-white/60 text-sm">Менеджер по продажам Март 2024</p>
</div>
</div>
<div className="text-right">
<p className="text-white text-lg font-bold">176 часов</p>
<p className="text-white/60 text-sm">Отработано в месяце</p>
</div>
</div>
{/* Календарь */}
<div className="space-y-4">
<div className="grid grid-cols-7 gap-2 text-center text-sm text-white/70">
<div>Пн</div>
<div>Вт</div>
<div>Ср</div>
<div>Чт</div>
<div>Пт</div>
<div>Сб</div>
<div>Вс</div>
</div>
<div className="grid grid-cols-7 gap-2">
{scheduleData.map((day, index) => (
<div
key={index}
className={`
relative p-3 rounded-lg border border-white/10 text-center transition-all hover:border-white/30
${day.status === 'work' ? 'bg-green-500/20' : ''}
${day.status === 'weekend' ? 'bg-gray-500/20' : ''}
`}
>
<div className="text-white text-sm font-medium">{day.day}</div>
<div className={`w-2 h-2 rounded-full mx-auto mt-1 ${getStatusColor(day.status)}`}></div>
{day.hours > 0 && (
<div className="text-white/60 text-xs mt-1">{day.hours}ч</div>
)}
</div>
))}
</div>
</div>
{/* Легенда */}
<div className="flex flex-wrap gap-4 text-sm">
<div className="flex items-center gap-2">
<div className="w-3 h-3 rounded-full bg-green-500"></div>
<span className="text-white/70">Работа</span>
</div>
<div className="flex items-center gap-2">
<div className="w-3 h-3 rounded-full bg-gray-400"></div>
<span className="text-white/70">Выходной</span>
</div>
<div className="flex items-center gap-2">
<div className="w-3 h-3 rounded-full bg-blue-500"></div>
<span className="text-white/70">Отпуск</span>
</div>
<div className="flex items-center gap-2">
<div className="w-3 h-3 rounded-full bg-yellow-500"></div>
<span className="text-white/70">Больничный</span>
</div>
<div className="flex items-center gap-2">
<div className="w-3 h-3 rounded-full bg-red-500"></div>
<span className="text-white/70">Прогул</span>
</div>
</div>
{/* Статистика */}
<div className="grid grid-cols-1 md:grid-cols-4 gap-4">
<div className="glass-card p-3 rounded-lg border border-white/10">
<div className="text-white/60 text-xs">Рабочие дни</div>
<div className="text-white text-lg font-bold">22</div>
</div>
<div className="glass-card p-3 rounded-lg border border-white/10">
<div className="text-white/60 text-xs">Выходные</div>
<div className="text-white text-lg font-bold">8</div>
</div>
<div className="glass-card p-3 rounded-lg border border-white/10">
<div className="text-white/60 text-xs">Отпуск</div>
<div className="text-white text-lg font-bold">0</div>
</div>
<div className="glass-card p-3 rounded-lg border border-white/10">
<div className="text-white/60 text-xs">Опозданий</div>
<div className="text-white text-lg font-bold">2</div>
</div>
</div>
</CardContent>
</Card>
{/* Карточки товаров */}
<Card className="glass-card border-white/10">
<CardHeader>
<CardTitle className="text-white">Карточки товаров</CardTitle>
</CardHeader>
<CardContent className="space-y-6">
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
{products.map((product) => (
<div key={product.id} className="glass-card p-4 rounded-lg border border-white/10 group hover:border-white/30 transition-all">
{/* Изображение товара */}
<div className="relative mb-3">
<div className="w-full h-40 bg-white/10 rounded-lg flex items-center justify-center">
<Package className="h-16 w-16 text-white/40" />
</div>
{/* Бейджи */}
<div className="absolute top-2 left-2 flex flex-col gap-1">
{product.isNew && (
<Badge className="bg-green-600 text-white text-xs">Новинка</Badge>
)}
{product.oldPrice && (
<Badge className="bg-red-600 text-white text-xs">Скидка</Badge>
)}
</div>
{/* Кнопки действий */}
<div className="absolute top-2 right-2 flex flex-col gap-1 opacity-0 group-hover:opacity-100 transition-opacity">
<Button size="sm" variant="outline" className="bg-white/20 hover:bg-white/30 text-white border-white/30 h-8 w-8 p-0">
<Heart className="h-3 w-3" />
</Button>
<Button size="sm" variant="outline" className="bg-white/20 hover:bg-white/30 text-white border-white/30 h-8 w-8 p-0">
<Eye className="h-3 w-3" />
</Button>
</div>
</div>
{/* Информация о товаре */}
<div className="space-y-2">
<div>
<h4 className="text-white font-medium text-sm line-clamp-2">{product.name}</h4>
<p className="text-white/60 text-xs">Артикул: {product.article}</p>
</div>
{/* Рейтинг и отзывы */}
<div className="flex items-center gap-2">
<div className="flex items-center gap-1">
<Star className="h-3 w-3 fill-yellow-400 text-yellow-400" />
<span className="text-white/80 text-xs">{product.rating}</span>
</div>
<span className="text-white/60 text-xs">({product.reviews} отзывов)</span>
</div>
{/* Категория и бренд */}
<div className="flex flex-wrap gap-1">
<Badge variant="outline" className="text-xs border-white/30 text-white/70">
{product.category}
</Badge>
<Badge variant="outline" className="text-xs border-white/30 text-white/70">
{product.brand}
</Badge>
</div>
{/* Цена */}
<div className="space-y-1">
{product.price > 0 ? (
<div className="flex items-center gap-2">
<span className="text-white font-bold">{formatPrice(product.price)}</span>
{product.oldPrice && (
<span className="text-white/60 text-sm line-through">{formatPrice(product.oldPrice)}</span>
)}
</div>
) : (
<span className="text-red-400 font-medium">Нет в наличии</span>
)}
{product.inStock && product.quantity > 0 && (
<p className="text-green-400 text-xs">В наличии: {product.quantity} шт.</p>
)}
</div>
{/* Продавец */}
<div className="text-white/60 text-xs">
Продавец: {product.seller}
</div>
{/* Кнопки */}
<div className="flex gap-2 pt-2">
{product.inStock && product.price > 0 ? (
<>
<div className="flex items-center border border-white/30 rounded">
<Button size="sm" variant="ghost" className="h-7 w-7 p-0 text-white hover:bg-white/20">
<Minus className="h-3 w-3" />
</Button>
<span className="px-2 text-white text-sm">{cartQuantity}</span>
<Button size="sm" variant="ghost" className="h-7 w-7 p-0 text-white hover:bg-white/20">
<Plus className="h-3 w-3" />
</Button>
</div>
<Button size="sm" className="flex-1 bg-purple-600 hover:bg-purple-700 text-white">
<ShoppingCart className="h-3 w-3 mr-1" />
В корзину
</Button>
</>
) : (
<Button size="sm" disabled className="w-full bg-gray-600 text-white/50">
Недоступно
</Button>
)}
</div>
</div>
</div>
))}
</div>
</CardContent>
</Card>
{/* Карточки оптовиков */}
<Card className="glass-card border-white/10">
<CardHeader>
<CardTitle className="text-white">Карточки оптовиков</CardTitle>
</CardHeader>
<CardContent className="space-y-6">
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
{wholesalers.map((wholesaler) => (
<div key={wholesaler.id} className="glass-card p-6 rounded-lg border border-white/10 hover:border-white/30 transition-all">
{/* Заголовок карточки */}
<div className="flex items-start gap-4 mb-4">
<Avatar className="h-16 w-16">
<AvatarImage src={wholesaler.avatar} />
<AvatarFallback className="bg-purple-600 text-white text-lg">
{wholesaler.name.charAt(0)}
</AvatarFallback>
</Avatar>
<div className="flex-1 min-w-0">
<div className="flex items-center gap-2 mb-1">
<h4 className="text-white font-semibold text-lg truncate">{wholesaler.name}</h4>
{wholesaler.verifiedBadges.includes('verified') && (
<Badge className="bg-green-600 text-white text-xs">Проверен</Badge>
)}
{wholesaler.verifiedBadges.includes('premium') && (
<Badge className="bg-yellow-600 text-white text-xs">Premium</Badge>
)}
</div>
<p className="text-white/70 text-sm mb-2">{wholesaler.fullName}</p>
<p className="text-white/60 text-xs">ИНН: {wholesaler.inn}</p>
{/* Рейтинг и статистика */}
<div className="flex items-center gap-4 mt-2 text-sm">
<div className="flex items-center gap-1">
<Star className="h-4 w-4 fill-yellow-400 text-yellow-400" />
<span className="text-white">{wholesaler.rating}</span>
<span className="text-white/60">({wholesaler.reviewsCount})</span>
</div>
<div className="text-white/60">
{wholesaler.completedOrders} заказов
</div>
</div>
</div>
</div>
{/* Описание */}
<p className="text-white/70 text-sm mb-4 line-clamp-2">
{wholesaler.description}
</p>
{/* Статистика */}
<div className="grid grid-cols-2 gap-4 mb-4">
<div className="bg-white/5 rounded-lg p-3">
<div className="flex items-center gap-2 mb-1">
<Package className="h-4 w-4 text-purple-400" />
<span className="text-white/70 text-xs">Товаров</span>
</div>
<div className="text-white font-bold">{wholesaler.productsCount.toLocaleString()}</div>
</div>
<div className="bg-white/5 rounded-lg p-3">
<div className="flex items-center gap-2 mb-1">
<Clock className="h-4 w-4 text-green-400" />
<span className="text-white/70 text-xs">Ответ</span>
</div>
<div className="text-white font-bold">{wholesaler.responseTime}</div>
</div>
</div>
{/* Категории */}
<div className="mb-4">
<p className="text-white/70 text-xs mb-2">Категории:</p>
<div className="flex flex-wrap gap-1">
{wholesaler.categories.map((category, index) => (
<Badge key={index} variant="outline" className="text-xs border-white/30 text-white/70">
{category}
</Badge>
))}
</div>
</div>
{/* Дополнительная информация */}
<div className="space-y-2 mb-4 text-sm">
<div className="flex items-center gap-2 text-white/60">
<MapPin className="h-3 w-3" />
<span>{wholesaler.location}</span>
</div>
<div className="flex items-center gap-2 text-white/60">
<Calendar className="h-3 w-3" />
<span>Работает с {wholesaler.workingSince} года</span>
</div>
<div className="flex items-center gap-2 text-white/60">
<Briefcase className="h-3 w-3" />
<span>Мин. заказ: {formatPrice(wholesaler.minOrder)}</span>
</div>
</div>
{/* Специальные предложения */}
{wholesaler.specialOffers > 0 && (
<div className="bg-orange-500/20 border border-orange-500/30 rounded-lg p-3 mb-4">
<div className="flex items-center gap-2">
<Award className="h-4 w-4 text-orange-400" />
<span className="text-orange-200 font-medium text-sm">
{wholesaler.specialOffers} специальных предложения
</span>
</div>
</div>
)}
{/* Кнопки действий */}
<div className="flex gap-2">
<Button className="flex-1 bg-purple-600 hover:bg-purple-700 text-white">
<Eye className="h-4 w-4 mr-2" />
Смотреть товары
</Button>
<Button variant="outline" className="bg-white/10 hover:bg-white/20 text-white border-white/30">
<Phone className="h-4 w-4" />
</Button>
<Button variant="outline" className="bg-white/10 hover:bg-white/20 text-white border-white/30">
<Mail className="h-4 w-4" />
</Button>
</div>
</div>
))}
</div>
</CardContent>
</Card>
</div>
)
}