@ -7,6 +7,7 @@ import { Input } from '@/components/ui/input'
import { Badge } from '@/components/ui/badge'
import { Label } from '@/components/ui/label'
import { Select , SelectContent , SelectItem , SelectTrigger , SelectValue } from '@/components/ui/select'
import { Dialog , DialogContent , DialogHeader , DialogTitle } from '@/components/ui/dialog'
import {
Search ,
Plus ,
@ -20,7 +21,9 @@ import {
Wrench ,
ArrowLeft ,
Check ,
X
Eye ,
ChevronLeft ,
ChevronRight
} from 'lucide-react'
import { WildberriesService } from '@/services/wildberries-service'
import { useAuth } from '@/hooks/useAuth'
@ -82,6 +85,176 @@ export function WBProductCards({ onBack, onComplete }: WBProductCardsProps) {
const [ selectedCards , setSelectedCards ] = useState < SelectedCard [ ] > ( [ ] )
const [ showSummary , setShowSummary ] = useState ( false )
const [ fulfillmentServices , setFulfillmentServices ] = useState < FulfillmentService [ ] > ( [ ] )
const [ selectedCardForDetails , setSelectedCardForDetails ] = useState < WildberriesCard | null > ( null )
const [ currentImageIndex , setCurrentImageIndex ] = useState ( 0 )
// Моковые товары для демонстрации
const getMockCards = ( ) : WildberriesCard [ ] = > [
{
nmID : 123456789 ,
vendorCode : 'SKU001' ,
title : 'Смартфон Samsung Galaxy A54' ,
description : 'Современный смартфон с отличной камерой и долгим временем автономной работы' ,
brand : 'Samsung' ,
object : 'Смартфоны' ,
parent : 'Электроника' ,
countryProduction : 'Корея' ,
supplierVendorCode : 'SUPPLIER-001' ,
mediaFiles : [ '/api/placeholder/400/400' , '/api/placeholder/400/401' , '/api/placeholder/400/402' ] ,
sizes : [
{
chrtID : 123456 ,
techSize : '128GB' ,
wbSize : '128GB Черный' ,
price : 25990 ,
discountedPrice : 22990 ,
quantity : 15
}
]
} ,
{
nmID : 987654321 ,
vendorCode : 'SKU002' ,
title : 'Наушники Apple AirPods Pro' ,
description : 'Беспроводные наушники с активным шумоподавлением и пространственным звуком' ,
brand : 'Apple' ,
object : 'Наушники' ,
parent : 'Электроника' ,
countryProduction : 'Китай' ,
supplierVendorCode : 'SUPPLIER-002' ,
mediaFiles : [ '/api/placeholder/400/403' , '/api/placeholder/400/404' ] ,
sizes : [
{
chrtID : 987654 ,
techSize : 'Standard' ,
wbSize : 'Белый' ,
price : 24990 ,
discountedPrice : 19990 ,
quantity : 8
}
]
} ,
{
nmID : 555666777 ,
vendorCode : 'SKU003' ,
title : 'Кроссовки Nike Air Max 270' ,
description : 'Спортивные кроссовки с современным дизайном и комфортной посадкой' ,
brand : 'Nike' ,
object : 'Кроссовки' ,
parent : 'Обувь' ,
countryProduction : 'Вьетнам' ,
supplierVendorCode : 'SUPPLIER-003' ,
mediaFiles : [ '/api/placeholder/400/405' , '/api/placeholder/400/406' , '/api/placeholder/400/407' ] ,
sizes : [
{
chrtID : 555666 ,
techSize : '42' ,
wbSize : '42 EU' ,
price : 12990 ,
discountedPrice : 9990 ,
quantity : 25
} ,
{
chrtID : 555667 ,
techSize : '43' ,
wbSize : '43 EU' ,
price : 12990 ,
discountedPrice : 9990 ,
quantity : 20
}
]
} ,
{
nmID : 444333222 ,
vendorCode : 'SKU004' ,
title : 'Футболка Adidas Originals' ,
description : 'Классическая футболка из органического хлопка с логотипом бренда' ,
brand : 'Adidas' ,
object : 'Футболки' ,
parent : 'Одежда' ,
countryProduction : 'Бангладеш' ,
supplierVendorCode : 'SUPPLIER-004' ,
mediaFiles : [ '/api/placeholder/400/408' , '/api/placeholder/400/409' ] ,
sizes : [
{
chrtID : 444333 ,
techSize : 'M' ,
wbSize : 'M' ,
price : 2990 ,
discountedPrice : 2490 ,
quantity : 50
} ,
{
chrtID : 444334 ,
techSize : 'L' ,
wbSize : 'L' ,
price : 2990 ,
discountedPrice : 2490 ,
quantity : 45
} ,
{
chrtID : 444335 ,
techSize : 'XL' ,
wbSize : 'XL' ,
price : 2990 ,
discountedPrice : 2490 ,
quantity : 30
}
]
} ,
{
nmID : 111222333 ,
vendorCode : 'SKU005' ,
title : 'Рюкзак для ноутбука Xiaomi' ,
description : 'Стильный и функциональный рюкзак для ноутбука до 15.6 дюймов' ,
brand : 'Xiaomi' ,
object : 'Рюкзаки' ,
parent : 'Аксессуары' ,
countryProduction : 'Китай' ,
supplierVendorCode : 'SUPPLIER-005' ,
mediaFiles : [ '/api/placeholder/400/410' ] ,
sizes : [
{
chrtID : 111222 ,
techSize : '15.6"' ,
wbSize : 'Черный' ,
price : 4990 ,
discountedPrice : 3990 ,
quantity : 35
}
]
} ,
{
nmID : 777888999 ,
vendorCode : 'SKU006' ,
title : 'Умные часы Apple Watch Series 9' ,
description : 'Новейшие умные часы с передовыми функциями здоровья и фитнеса' ,
brand : 'Apple' ,
object : 'Умные часы' ,
parent : 'Электроника' ,
countryProduction : 'Китай' ,
supplierVendorCode : 'SUPPLIER-006' ,
mediaFiles : [ '/api/placeholder/400/411' , '/api/placeholder/400/412' , '/api/placeholder/400/413' ] ,
sizes : [
{
chrtID : 777888 ,
techSize : '41mm' ,
wbSize : '41mm GPS' ,
price : 39990 ,
discountedPrice : 35990 ,
quantity : 12
} ,
{
chrtID : 777889 ,
techSize : '45mm' ,
wbSize : '45mm GPS' ,
price : 42990 ,
discountedPrice : 38990 ,
quantity : 8
}
]
}
]
// Загружаем контрагентов-фулфилментов
const { data : counterpartiesData } = useQuery ( GET_MY_COUNTERPARTIES )
@ -113,12 +286,19 @@ export function WBProductCards({ onBack, onComplete }: WBProductCardsProps) {
useEffect ( ( ) = > {
// Загружаем услуги фулфилмента из контрагентов
if ( counterpartiesData ? . myCounterparties ) {
interface Organization {
id : string
name? : string
fullName? : string
type : string
}
const fulfillmentOrganizations = counterpartiesData . myCounterparties . filter (
( org : any ) = > org . type === 'FULFILLMENT'
( org : Organization ) = > org . type === 'FULFILLMENT'
)
// В реальном приложении здесь был бы запрос услуг для каждой организации
const mockServices : FulfillmentService [ ] = fulfillmentOrganizations . flatMap ( ( org : any ) = > [
const mockServices : FulfillmentService [ ] = fulfillmentOrganizations . flatMap ( ( org : Organization ) = > [
{
id : ` ${ org . id } -packaging ` ,
name : 'Упаковка товаров' ,
@ -146,83 +326,155 @@ export function WBProductCards({ onBack, onComplete }: WBProductCardsProps) {
}
} , [ counterpartiesData ] )
const searchCards = async ( ) = > {
if ( ! searchTerm . trim ( ) ) return
// Автоматически загружаем товары при открытии компонента
useEffect ( ( ) = > {
const loadCards = async ( ) = > {
setLoading ( true )
try {
const wbApiKey = user ? . organization ? . apiKeys ? . find ( key = > key . marketplace === 'WILDBERRIES' )
console . log ( 'WB API Key found:' , ! ! wbApiKey )
console . log ( 'WB API Key active:' , wbApiKey ? . isActive )
console . log ( 'WB API Key validationData:' , wbApiKey ? . validationData )
if ( wbApiKey ? . isActive ) {
// Попытка загрузить реальные данные из API Wildberries
const validationData = wbApiKey . validationData as Record < string , string >
// API ключ может храниться в разных местах
const apiToken = validationData ? . token ||
validationData ? . apiKey ||
validationData ? . key ||
( wbApiKey as { apiKey? : string } ) . apiKey // Прямое поле apiKey из базы
console . log ( 'API Token extracted:' , ! ! apiToken )
console . log ( 'API Token length:' , apiToken ? . length )
if ( apiToken ) {
console . log ( 'Загружаем карточки из WB API...' )
const cards = await WildberriesService . getAllCards ( apiToken , 50 )
setWbCards ( cards )
console . log ( 'Загружено карточек из WB API:' , cards . length )
return
}
}
// Если API ключ не настроен, оставляем пустое состояние
console . log ( 'API ключ WB не настроен, показываем пустое состояние' )
setWbCards ( [ ] )
} catch ( error ) {
console . error ( 'Ошибка загрузки карточек WB:' , error )
// При ошибке API показываем пустое состояние
setWbCards ( [ ] )
} finally {
setLoading ( false )
}
}
loadCards ( )
} , [ user ] )
const loadAllCards = async ( ) = > {
setLoading ( true )
try {
const wbApiKey = user ? . organization ? . apiKeys ? . find ( key = > key . marketplace === 'WILDBERRIES' )
if ( ! wbApiKey ? . isActive ) {
throw new Error ( 'API ключ Wildberries не настроен' )
}
const validationData = wbApiKey . validationData as Record < string , string >
const apiToken = validationData ? . token || validationData ? . apiKey
if ( ! apiToken ) {
throw new Error ( 'API токен не найден' )
}
const cards = await WildberriesService . searchCards ( apiToken , searchTerm )
setWbCards ( cards )
} catch ( error ) {
console . error ( 'Ошибка поиска карточек:' , error )
// Для демо загрузим моковые данные
setWbCards ( [
{
nmID : 123456789 ,
vendorCode : 'SKU001' ,
title : 'Смартфон Samsung Galaxy A54' ,
description : 'Современный смартфон с отличной камерой' ,
brand : 'Samsung' ,
object : 'Смартфоны' ,
parent : 'Электроника' ,
countryProduction : 'Корея' ,
supplierVendorCode : 'SUPPLIER-001' ,
mediaFiles : [ '/api/placeholder/300/300' ] ,
sizes : [
{
chrtID : 123456 ,
techSize : '128GB' ,
wbSize : '128GB Черный' ,
price : 25990 ,
discountedPrice : 22990 ,
quantity : 10
}
]
} ,
{
nmID : 987654321 ,
vendorCode : 'SKU002' ,
title : 'Наушники Apple AirPods Pro' ,
description : 'Беспроводные наушники с шумоподавлением' ,
brand : 'Apple' ,
object : 'Наушники' ,
parent : 'Электроника' ,
countryProduction : 'Китай' ,
supplierVendorCode : 'SUPPLIER-002' ,
mediaFiles : [ '/api/placeholder/300/300' ] ,
sizes : [
{
chrtID : 987654 ,
techSize : 'Standart' ,
wbSize : 'Белый' ,
price : 24990 ,
discountedPrice : 19990 ,
quantity : 5
}
]
if ( wbApiKey ? . isActive ) {
// Попытка загрузить реальные данные из API Wildberries
const validationData = wbApiKey . validationData as Record < string , string >
const apiToken = validationData ? . token ||
validationData ? . apiKey ||
validationData ? . key ||
( wbApiKey as { apiKey? : string } ) . apiKey
if ( apiToken ) {
console . log ( 'Загружаем все карточки из WB API...' )
const cards = await WildberriesService . getAllCards ( apiToken , 100 )
setWbCards ( cards )
console . log ( 'Загружено карточек из WB API:' , cards . length )
return
}
] )
}
// Если API ключ не настроен, загружаем моковые данные
console . log ( 'API ключ WB не настроен, загружаем моковые данные' )
const allCards = getMockCards ( )
setWbCards ( allCards )
console . log ( 'Загружены моковые товары:' , allCards . length )
} catch ( error ) {
console . error ( 'Ошибка загрузки всех карточек WB:' , error )
// При ошибке загружаем моковые данные
const allCards = getMockCards ( )
setWbCards ( allCards )
console . log ( 'Загружены моковые товары (fallback):' , allCards . length )
} finally {
setLoading ( false )
}
}
const updateCardSelection = ( card : WildberriesCard , field : keyof SelectedCard , value : any ) = > {
const searchCards = async ( ) = > {
if ( ! searchTerm . trim ( ) ) {
loadAllCards ( )
return
}
setLoading ( true )
try {
const wbApiKey = user ? . organization ? . apiKeys ? . find ( key = > key . marketplace === 'WILDBERRIES' )
if ( wbApiKey ? . isActive ) {
// Попытка поиска в реальном API Wildberries
const validationData = wbApiKey . validationData as Record < string , string >
const apiToken = validationData ? . token ||
validationData ? . apiKey ||
validationData ? . key ||
( wbApiKey as { apiKey? : string } ) . apiKey
if ( apiToken ) {
console . log ( 'Поиск в WB API:' , searchTerm )
const cards = await WildberriesService . searchCards ( apiToken , searchTerm , 50 )
setWbCards ( cards )
console . log ( 'Найдено карточек в WB API:' , cards . length )
return
}
}
// Если API ключ не настроен, ищем в моковых данных
console . log ( 'API ключ WB не настроен, поиск в моковых данных:' , searchTerm )
const mockCards = getMockCards ( )
// Фильтруем товары по поисковому запросу
const filteredCards = mockCards . filter ( card = >
card . title . toLowerCase ( ) . includes ( searchTerm . toLowerCase ( ) ) ||
card . brand . toLowerCase ( ) . includes ( searchTerm . toLowerCase ( ) ) ||
card . vendorCode . toLowerCase ( ) . includes ( searchTerm . toLowerCase ( ) ) ||
card . object . toLowerCase ( ) . includes ( searchTerm . toLowerCase ( ) )
)
setWbCards ( filteredCards )
console . log ( 'Найдено моковых товаров:' , filteredCards . length )
} catch ( error ) {
console . error ( 'Ошибка поиска карточек WB:' , error )
// При ошибке ищем в моковых данных
const mockCards = getMockCards ( )
const filteredCards = mockCards . filter ( card = >
card . title . toLowerCase ( ) . includes ( searchTerm . toLowerCase ( ) ) ||
card . brand . toLowerCase ( ) . includes ( searchTerm . toLowerCase ( ) ) ||
card . vendorCode . toLowerCase ( ) . includes ( searchTerm . toLowerCase ( ) ) ||
card . object . toLowerCase ( ) . includes ( searchTerm . toLowerCase ( ) )
)
setWbCards ( filteredCards )
console . log ( 'Найдено моковых товаров (fallback):' , filteredCards . length )
} finally {
setLoading ( false )
}
}
const updateCardSelection = ( card : WildberriesCard , field : keyof SelectedCard , value : string | number | string [ ] ) = > {
setSelectedCards ( prev = > {
const existing = prev . find ( sc = > sc . card . nmID === card . nmID )
if ( field === 'selectedQuantity' && value === 0 ) {
if ( field === 'selectedQuantity' && typeof value === 'number' && value === 0 ) {
return prev . filter ( sc = > sc . card . nmID !== card . nmID )
}
@ -232,10 +484,10 @@ export function WBProductCards({ onBack, onComplete }: WBProductCardsProps) {
? { . . . sc , [ field ] : value }
: sc
)
} else if ( field === 'selectedQuantity' && value > 0 ) {
} else if ( field === 'selectedQuantity' && typeof value === 'number' && value > 0 ) {
const newSelectedCard : SelectedCard = {
card ,
selectedQuantity : value ,
selectedQuantity : value as number ,
selectedMarket : '' ,
selectedPlace : '' ,
sellerName : '' ,
@ -284,6 +536,28 @@ export function WBProductCards({ onBack, onComplete }: WBProductCardsProps) {
)
}
const handleCardClick = ( card : WildberriesCard ) = > {
setSelectedCardForDetails ( card )
setCurrentImageIndex ( 0 )
}
const closeDetailsModal = ( ) = > {
setSelectedCardForDetails ( null )
setCurrentImageIndex ( 0 )
}
const nextImage = ( ) = > {
if ( selectedCardForDetails && selectedCardForDetails . mediaFiles ? . length > 1 ) {
setCurrentImageIndex ( ( prev ) = > ( prev + 1 ) % selectedCardForDetails . mediaFiles . length )
}
}
const prevImage = ( ) = > {
if ( selectedCardForDetails && selectedCardForDetails . mediaFiles ? . length > 1 ) {
setCurrentImageIndex ( ( prev ) = > ( prev - 1 + selectedCardForDetails . mediaFiles . length ) % selectedCardForDetails . mediaFiles . length )
}
}
const handleCreateSupply = async ( ) = > {
try {
const supplyInput = {
@ -356,7 +630,7 @@ export function WBProductCards({ onBack, onComplete }: WBProductCardsProps) {
< Card key = { sc . card . nmID } className = "bg-white/10 backdrop-blur border-white/20 p-4" >
< div className = "flex space-x-4" >
< img
src = { sc . card . mediaFiles [ 0 ] || '/api/placeholder/120/120' }
src = { sc . card . mediaFiles ? . [ 0 ] || '/api/placeholder/120/120' }
alt = { sc . card . title }
className = "w-20 h-20 rounded-lg object-cover"
/ >
@ -504,8 +778,26 @@ export function WBProductCards({ onBack, onComplete }: WBProductCardsProps) {
< / div >
< / Card >
{ /* Состояние загрузки */ }
{ loading && (
< div className = "grid grid-cols-1 lg:grid-cols-2 xl:grid-cols-3 gap-6" >
{ [ . . . Array ( 6 ) ] . map ( ( _ , i ) = > (
< Card key = { i } className = "bg-white/10 backdrop-blur border-white/20 p-4 animate-pulse" >
< div className = "space-y-4" >
< div className = "bg-white/20 rounded-lg h-48 w-full" > < / div >
< div className = "space-y-2" >
< div className = "bg-white/20 rounded h-4 w-3/4" > < / div >
< div className = "bg-white/20 rounded h-4 w-1/2" > < / div >
< / div >
< div className = "bg-white/20 rounded h-10 w-full" > < / div >
< / div >
< / Card >
) ) }
< / div >
) }
{ /* Карточки товаров */ }
{ wbCards . length > 0 && (
{ ! loading && wbCards . length > 0 && (
< div className = "grid grid-cols-1 lg:grid-cols-2 xl:grid-cols-3 gap-6" >
{ wbCards . map ( ( card ) = > {
const selectedQuantity = getSelectedQuantity ( card )
@ -520,13 +812,28 @@ export function WBProductCards({ onBack, onComplete }: WBProductCardsProps) {
< div className = "p-4 space-y-4" >
{ /* Изображение и основная информация */ }
< div className = "space-y-3" >
< img
src = { card . mediaFiles [ 0 ] || '/api/placeholder/300/200' }
alt = { card . title }
className = "w-full h-48 rounded-lg object-cover"
/ >
< div className = "relative group" >
< img
src = { card . mediaFiles ? . [ 0 ] || '/api/placeholder/300/200' }
alt = { card . title }
className = "w-full h-48 rounded-lg object-cover cursor-pointer"
onClick = { ( ) = > handleCardClick ( card ) }
/ >
{ /* Кнопка "Подробнее" при наведении */ }
< div className = "absolute inset-0 bg-black/40 opacity-0 group-hover:opacity-100 transition-opacity duration-300 flex items-center justify-center rounded-lg" >
< Button
size = "sm"
variant = "outline"
onClick = { ( ) = > handleCardClick ( card ) }
className = "bg-white/20 backdrop-blur text-white border-white/30 hover:bg-white/30"
>
< Eye className = "h-4 w-4 mr-2" / >
П о д р о б н е е
< / Button >
< / div >
< / div >
< div >
< h3 className = "text-white font-medium text-lg mb-1" > { card . title } < / h3 >
< h3 className = "text-white font-medium text-lg mb-1 cursor-pointer hover:text-purple-300 transition-colors" onClick = { ( ) = > handleCardClick ( card ) } > { card . title } < / h3 >
< p className = "text-white/60 text-sm mb-2" > { card . vendorCode } < / p >
< div className = "flex items-center justify-between" >
< span className = "text-white font-bold text-xl" > { formatCurrency ( price ) } < / span >
@ -718,13 +1025,183 @@ export function WBProductCards({ onBack, onComplete }: WBProductCardsProps) {
< Card className = "bg-white/10 backdrop-blur border-white/20 p-12" >
< div className = "text-center" >
< Package className = "h-16 w-16 text-white/20 mx-auto mb-4" / >
< h3 className = "text-xl font-semibold text-white mb-2" > П о и с к т о в а р о в < / h3 >
< p className = "text-white/60 mb-4" >
В в е д и т е з а п р о с в п о л е п о и с к а , ч т о б ы н а й т и т о в а р ы в в а ш е м к а т а л о г е Wildberries
< / p >
< h3 className = "text-xl font-semibold text-white mb-2" > К а р т о ч к и т о в а р о в Wildberries < / h3 >
{ user ? . organization ? . apiKeys ? . find ( key = > key . marketplace === 'WILDBERRIES' ) ? . isActive ? (
< >
< p className = "text-white/60 mb-6" >
В в е д и т е з а п р о с в п о л е п о и с к а , ч т о б ы н а й т и т о в а р ы в в а ш е м к а т а л о г е Wildberries , и л и з а г р у з и т е в с е д о с т у п н ы е к а р т о ч к и
< / p >
< Button
onClick = { loadAllCards }
className = "bg-gradient-to-r from-blue-500 to-cyan-500 hover:from-blue-600 hover:to-cyan-600 text-white"
>
< Package className = "h-4 w-4 mr-2" / >
З а г р у з и т ь к а р т о ч к и и з WB API
< / Button >
< / >
) : (
< >
< p className = "text-white/60 mb-4" >
Д л я р а б о т ы с р е а л ь н ы м и к а р т о ч к а м и т о в а р о в н е о б х о д и м о н а с т р о и т ь API к л ю ч Wildberries в н а с т р о й к а х о р г а н и з а ц и и
< / p >
< p className = "text-white/40 text-sm mb-6" >
С е й ч а с п о к а з а н ы д е м о н с т р а ц и о н н ы е т о в а р ы . Д л я т е с т и р о в а н и я и с п о л ь з у й т е п о и с к и л и з а г р у з и т е в с е .
< / p >
< Button
onClick = { loadAllCards }
className = "bg-gradient-to-r from-blue-500 to-cyan-500 hover:from-blue-600 hover:to-cyan-600 text-white"
>
< Package className = "h-4 w-4 mr-2" / >
П о к а з а т ь д е м о т о в а р ы
< / Button >
< / >
) }
< / div >
< / Card >
) }
{ /* Модальное окно с детальной информацией о товаре */ }
< Dialog open = { ! ! selectedCardForDetails } onOpenChange = { ( open ) = > ! open && closeDetailsModal ( ) } >
< DialogContent className = "max-w-4xl max-h-[90vh] bg-black/90 backdrop-blur-xl border border-white/20 p-0" >
< DialogHeader className = "sr-only" >
< DialogTitle > Д е т а л ь н а я и н ф о р м а ц и я о т о в а р е < / DialogTitle >
< / DialogHeader >
{ selectedCardForDetails && (
< div className = "grid grid-cols-1 md:grid-cols-2 gap-6 p-6" >
{ /* Изображения */ }
< div className = "space-y-4" >
< div className = "relative" >
< img
src = { selectedCardForDetails . mediaFiles ? . [ currentImageIndex ] || '/api/placeholder/400/400' }
alt = { selectedCardForDetails . title }
className = "w-full aspect-square rounded-lg object-cover"
/ >
{ /* Навигация по изображениям */ }
{ selectedCardForDetails . mediaFiles ? . length > 1 && (
< >
< button
onClick = { prevImage }
className = "absolute left-4 top-1/2 -translate-y-1/2 bg-black/50 hover:bg-black/70 text-white p-3 rounded-full"
>
< ChevronLeft className = "h-6 w-6" / >
< / button >
< button
onClick = { nextImage }
className = "absolute right-4 top-1/2 -translate-y-1/2 bg-black/50 hover:bg-black/70 text-white p-3 rounded-full"
>
< ChevronRight className = "h-6 w-6" / >
< / button >
< div className = "absolute bottom-4 left-1/2 -translate-x-1/2 bg-black/50 px-3 py-1 rounded-full text-white text-sm" >
{ currentImageIndex + 1 } и з { selectedCardForDetails . mediaFiles ? . length || 0 }
< / div >
< / >
) }
< / div >
{ /* Миниатюры изображений */ }
{ selectedCardForDetails . mediaFiles ? . length > 1 && (
< div className = "flex space-x-2 overflow-x-auto" >
{ selectedCardForDetails . mediaFiles ? . map ( ( image , index ) = > (
< img
key = { index }
src = { image }
alt = { ` ${ selectedCardForDetails . title } ${ index + 1 } ` }
className = { ` w-16 h-16 rounded-lg object-cover cursor-pointer flex-shrink-0 ${
index === currentImageIndex ? 'ring-2 ring-purple-500' : 'opacity-60 hover:opacity-100'
} ` }
onClick = { ( ) = > setCurrentImageIndex ( index ) }
/ >
) ) }
< / div >
) }
< / div >
{ /* Информация о товаре */ }
< div className = "space-y-6" >
< div >
< h2 className = "text-2xl font-bold text-white mb-2" > { selectedCardForDetails . title } < / h2 >
< p className = "text-white/60 mb-4" > А р т и к у л : { selectedCardForDetails . vendorCode } < / p >
< div className = "grid grid-cols-2 gap-4 text-sm" >
< div >
< span className = "text-white/60" > Б р е н д : < / span >
< span className = "text-white ml-2" > { selectedCardForDetails . brand } < / span >
< / div >
< div >
< span className = "text-white/60" > К а т е г о р и я : < / span >
< span className = "text-white ml-2" > { selectedCardForDetails . object } < / span >
< / div >
< div >
< span className = "text-white/60" > Р о д и т е л ь с к а я к а т е г о р и я : < / span >
< span className = "text-white ml-2" > { selectedCardForDetails . parent } < / span >
< / div >
< div >
< span className = "text-white/60" > С т р а н а п р о и з в о д с т в а : < / span >
< span className = "text-white ml-2" > { selectedCardForDetails . countryProduction } < / span >
< / div >
< / div >
< / div >
{ selectedCardForDetails . description && (
< div >
< h3 className = "text-white font-semibold mb-2" > О п и с а н и е < / h3 >
< p className = "text-white/80 text-sm leading-relaxed" > { selectedCardForDetails . description } < / p >
< / div >
) }
{ /* Размеры и цены */ }
< div >
< h3 className = "text-white font-semibold mb-3" > Д о с т у п н ы е в а р и а н т ы < / h3 >
< div className = "space-y-2" >
{ selectedCardForDetails . sizes . map ( ( size ) = > (
< div key = { size . chrtID } className = "bg-white/5 rounded-lg p-3" >
< div className = "flex justify-between items-center mb-2" >
< span className = "text-white font-medium" > { size . wbSize } < / span >
< Badge className = { ` ${ size . quantity > 10 ? 'bg-green-500/20 text-green-300' : size . quantity > 0 ? 'bg-yellow-500/20 text-yellow-300' : 'bg-red-500/20 text-red-300' } ` } >
{ size . quantity } ш т .
< / Badge >
< / div >
< div className = "flex justify-between items-center text-sm" >
< span className = "text-white/60" > Р а з м е р : { size . techSize } < / span >
< div className = "text-right" >
< div className = "text-white font-bold" > { formatCurrency ( size . discountedPrice || size . price ) } < / div >
{ size . discountedPrice && size . discountedPrice < size . price && (
< div className = "text-white/40 line-through text-xs" > { formatCurrency ( size . price ) } < / div >
) }
< / div >
< / div >
< / div >
) ) }
< / div >
< / div >
{ /* Кнопки действий в модальном окне */ }
< div className = "flex gap-3 pt-4 border-t border-white/20" >
< Button
className = "flex-1 bg-gradient-to-r from-purple-500 to-pink-500 hover:from-purple-600 hover:to-pink-600 text-white"
onClick = { ( ) = > {
const currentQuantity = getSelectedQuantity ( selectedCardForDetails )
updateCardSelection ( selectedCardForDetails , 'selectedQuantity' , currentQuantity + 1 )
} }
>
< Plus className = "h-4 w-4 mr-2" / >
Д о б а в и т ь в к о р з и н у
< / Button >
< Button
variant = "outline"
className = "border-white/20 text-white hover:bg-white/10"
onClick = { closeDetailsModal }
>
З а к р ы т ь
< / Button >
< / div >
< / div >
< / div >
) }
< / DialogContent >
< / Dialog >
< / div >
)
}