@ -1,351 +1,486 @@
"use client"
"use client" ;
import React , { useState , useEffect } from ' react'
import { Card } from ' @/components/ui/card'
import { Button } from ' @/components/ui/button'
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 { Popover , PopoverContent , PopoverTrigger } from '@/components/ui/popover'
import DatePicker from "react-datepicker"
import "react-datepicker/dist/react-datepicker.css"
import {
Search ,
Plus ,
import React , { useState , useEffect } from " react" ;
import { Card } from " @/components/ui/card" ;
import { Button } from " @/components/ui/button" ;
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 {
Popover ,
PopoverContent ,
PopoverTrigger ,
} from "@/components/ui/popover" ;
import DatePicker from "react-datepicker" ;
import "react-datepicker/dist/react-datepicker.css" ;
import {
Search ,
Plus ,
Calendar as CalendarIcon ,
Package ,
Check ,
X ,
User ,
Phone ,
MapPin
} from 'lucide-react'
import { WildberriesService } from '@/services/wildberries-service'
import { useAuth } from '@/hooks/useAuth'
import { useQuery , useMutation } from '@apollo/client'
import { apolloClient } from '@/lib/apollo-client'
import { GET_MY_COUNTERPARTIES , GET_COUNTERPARTY_SERVICES , GET_COUNTERPARTY_SUPPLIES } from '@/graphql/queries'
import { CREATE_WILDBERRIES_SUPPLY } from '@/graphql/mutations'
import { toast } from 'sonner'
import { format } from 'date-fns'
import { ru } from 'date-fns/locale'
import { WildberriesCard } from '@/types/supplies'
MapPin ,
Building ,
Truck ,
} from "lucide-react" ;
import { WildberriesService } from "@/services/wildberries-service" ;
import { useAuth } from "@/hooks/useAuth" ;
import { useQuery , useMutation } from "@apollo/client" ;
import { apolloClient } from "@/lib/apollo-client" ;
import {
GET_MY_COUNTERPARTIES ,
GET_COUNTERPARTY_SERVICES ,
GET_COUNTERPARTY_SUPPLIES ,
} from "@/graphql/queries" ;
import { CREATE_WILDBERRIES_SUPPLY } from "@/graphql/mutations" ;
import { toast } from "sonner" ;
import { format } from "date-fns" ;
import { ru } from "date-fns/locale" ;
import { WildberriesCard } from "@/types/supplies" ;
// Добавляем CSS стили для line-clamp
const lineClampStyles = `
.line-clamp-2 {
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
overflow: hidden;
}
` ;
interface SupplyItem {
card : WildberriesCard
quantity : number
pricePerUnit : number
totalPrice : number
supplierId : string
card : WildberriesCard ;
quantity : number ;
pricePerUnit : number ;
totalPrice : number ;
supplierId : string ;
}
interface Organization {
id : string
name? : string
fullName? : string
type : string
id : string ;
name? : string ;
fullName? : string ;
type : string ;
}
interface FulfillmentService {
id : string
name : string
description? : string
price : number
id : string ;
name : string ;
description? : string ;
price : number ;
}
interface Supplier {
id : string
name : string
contactName : string
phone : string
market : string
address : string
place : string
telegram : string
id : string ;
name : string ;
contactName : string ;
phone : string ;
market : string ;
address : string ;
place : string ;
telegram : string ;
}
interface DirectSupplyCreationProps {
onComplete : ( ) = > void
onCreateSupply : ( ) = > void
canCreateSupply : boolean
isCreatingSupply : boolean
onCanCreateSupplyChange ? : ( canCreate : boolean ) = > void
onComplete : ( ) = > void ;
onCreateSupply : ( ) = > void ;
canCreateSupply : boolean ;
isCreatingSupply : boolean ;
onCanCreateSupplyChange ? : ( canCreate : boolean ) = > void ;
}
export function DirectSupplyCreation ( { onComplete , onCreateSupply , canCreateSupply , isCreatingSupply , onCanCreateSupplyChange } : DirectSupplyCreationProps ) {
const { user } = useAuth ( )
// Состояние для товаров
const [ searchTerm , setSearchTerm ] = useState ( '' )
const [ loading , setLoading ] = useState ( fals e)
const [ wbCards , setWbCards ] = useState < WildberriesCard [ ] > ( [ ] )
const [ supplyItems , setSupplyItems ] = useState < SupplyItem [ ] > ( [ ] )
// Общие настрой ки
const [ deliveryDate , setDeliveryDate ] = useState < Date | undefined > ( undefined )
const [ selectedFulfillmentOrg , setSelectedFulfillmentOrg ] = useState < string > ( '' )
const [ selectedServices , setSelectedServices ] = useState < string [ ] > ( [ ] )
const [ selectedConsumables , setSelectedConsumables ] = useState < string [ ] > ( [ ] )
export function DirectSupplyCreation ( {
onComplete ,
onCreateSupply ,
canCreateSupply ,
isCreatingSupply ,
onCanCreateSupplyChang e,
} : DirectSupplyCreationProps ) {
const { user } = useAuth ( ) ;
// Новые состояния для блока создания постав ки
const [ deliveryDate , setDeliveryDate ] = useState < string > ( "" ) ;
const [ selectedFulfillment , setSelectedFulfillment ] = useState < string > ( "" ) ;
const [ goodsQuantity , setGoodsQuantity ] = useState < number > ( 1200 ) ;
const [ goodsVolume , setGoodsVolume ] = useState < number > ( 0 ) ;
const [ cargoPlaces , setCargoPlaces ] = useState < number > ( 0 ) ;
const [ goodsPrice , setGoodsPrice ] = useState < number > ( 0 ) ;
const [ fulfillmentServicesPrice , setFulfillmentServicesPrice ] =
useState < number > ( 0 ) ;
const [ logisticsPrice , setLogisticsPrice ] = useState < number > ( 0 ) ;
// Оригинальные состояния для товаров
const [ searchTerm , setSearchTerm ] = useState ( "" ) ;
const [ loading , setLoading ] = useState ( false ) ;
const [ wbCards , setWbCards ] = useState < WildberriesCard [ ] > ( [ ] ) ;
const [ supplyItems , setSupplyItems ] = useState < SupplyItem [ ] > ( [ ] ) ;
// Общие настройки (оригинальные)
const [ deliveryDateOriginal , setDeliveryDateOriginal ] = useState <
Date | undefined
> ( undefined ) ;
const [ selectedFulfillmentOrg , setSelectedFulfillmentOrg ] =
useState < string > ( "" ) ;
const [ selectedServices , setSelectedServices ] = useState < string [ ] > ( [ ] ) ;
const [ selectedConsumables , setSelectedConsumables ] = useState < string [ ] > ( [ ] ) ;
// Поставщики
const [ suppliers , setSuppliers ] = useState < Supplier [ ] > ( [ ] )
const [ showSupplierModal , setShowSupplierModal ] = useState ( false )
const [ suppliers , setSuppliers ] = useState < Supplier [ ] > ( [ ] ) ;
const [ showSupplierModal , setShowSupplierModal ] = useState ( false ) ;
const [ newSupplier , setNewSupplier ] = useState ( {
name : '' ,
contactName : '' ,
phone : '' ,
market : '' ,
address : '' ,
place : '' ,
telegram : ''
} )
name : "" ,
contactName : "" ,
phone : "" ,
market : "" ,
address : "" ,
place : "" ,
telegram : "" ,
} ) ;
// Данные для фулфилмента
const [ organizationServices , setOrganizationServices ] = useState < { [ orgId : string ] : FulfillmentService [ ] } > ( { } )
const [ organizationSupplies , setOrganizationSupplies ] = useState < { [ orgId : string ] : FulfillmentService [ ] } > ( { } )
const [ organizationServices , setOrganizationServices ] = useState < {
[ orgId : string ] : FulfillmentService [ ] ;
} > ( { } ) ;
const [ organizationSupplies , setOrganizationSupplies ] = useState < {
[ orgId : string ] : FulfillmentService [ ] ;
} > ( { } ) ;
// Загружаем контрагентов-фулфилментов
const { data : counterpartiesData } = useQuery ( GET_MY_COUNTERPARTIES )
const { data : counterpartiesData } = useQuery ( GET_MY_COUNTERPARTIES ) ;
// Мутация для создания поставки
const [ createSupply , { loading : creatingSupply } ] = useMutation ( CREATE_WILDBERRIES_SUPPLY , {
onCompleted : ( data ) = > {
if ( data . createWildberriesSupply . success ) {
toast . success ( data . createWildberriesSupply . message )
onComplete ( )
} else {
toast . error ( data . createWildberriesSupply . message )
}
} ,
onError : ( error ) = > {
toast . error ( 'Ошибка при создании поставки' )
c onsole . error ( 'Error creating supply:' , error )
const [ createSupply , { loading : creatingSupply } ] = useMutation (
CREATE_WILDBERRIES_SUPPLY ,
{
onCompleted : ( data ) = > {
if ( data . createWildberriesSupply . success ) {
toast . success ( data . createWildberriesSupply . message ) ;
onComplete ( ) ;
} else {
toast . error ( data . createWildberriesSupply . message ) ;
}
} ,
onError : ( error ) = > {
toast . error ( "Ошибка при создании поставки" ) ;
console . error ( "Error creating supply:" , error ) ;
} ,
}
} )
) ;
// Моковые данные товаров для демонстрации
const getMockCards = ( ) : WildberriesCard [ ] = > [
{
nmID : 123456789 ,
vendorCode : ' SKU001' ,
title : ' Платье летнее розовое' ,
description : ' Легкое летнее платье из натурального хлопка' ,
brand : ' Fashion' ,
object : ' Платья' ,
parent : ' Одежда' ,
countryProduction : ' Россия' ,
supplierVendorCode : ' SUPPLIER-001' ,
mediaFiles : [ ' /api/placeholder/400/400' ] ,
sizes : [ { chrtID : 123456 , techSize : 'M' , wbSize : 'M Розовый' , price : 2500 , discountedPrice : 2000 , quantity : 50 } ]
vendorCode : " SKU001" ,
title : " Платье летнее розовое" ,
description : " Легкое летнее платье из натурального хлопка" ,
brand : " Fashion" ,
object : " Платья" ,
parent : " Одежда" ,
countryProduction : " Россия" ,
supplierVendorCode : " SUPPLIER-001" ,
mediaFiles : [ " /api/placeholder/400/400" ] ,
sizes : [
{
chrtID : 123456 ,
techSize : "M" ,
wbSize : "M Розовый" ,
price : 2500 ,
discountedPrice : 2000 ,
quantity : 50 ,
} ,
] ,
} ,
{
nmID : 987654321 ,
vendorCode : ' SKU002' ,
title : ' Платье черное вечернее' ,
description : ' Элегантное вечернее платье для особых случаев' ,
brand : ' Fashion' ,
object : ' Платья' ,
parent : ' Одежда' ,
countryProduction : ' Россия' ,
supplierVendorCode : ' SUPPLIER-002' ,
mediaFiles : [ ' /api/placeholder/400/403' ] ,
sizes : [ { chrtID : 987654 , techSize : 'M' , wbSize : 'M Черный' , price : 3500 , discountedPrice : 3000 , quantity : 30 } ]
vendorCode : " SKU002" ,
title : " Платье черное вечернее" ,
description : " Элегантное вечернее платье для особых случаев" ,
brand : " Fashion" ,
object : " Платья" ,
parent : " Одежда" ,
countryProduction : " Россия" ,
supplierVendorCode : " SUPPLIER-002" ,
mediaFiles : [ " /api/placeholder/400/403" ] ,
sizes : [
{
chrtID : 987654 ,
techSize : "M" ,
wbSize : "M Черный" ,
price : 3500 ,
discountedPrice : 3000 ,
quantity : 30 ,
} ,
] ,
} ,
{
nmID : 555666777 ,
vendorCode : ' SKU003' ,
title : ' Блузка белая офисная' ,
description : ' Классическая белая блузка для офиса' ,
brand : ' Office' ,
object : ' Блузки' ,
parent : ' Одежда' ,
countryProduction : ' Турция' ,
supplierVendorCode : ' SUPPLIER-003' ,
mediaFiles : [ ' /api/placeholder/400/405' ] ,
sizes : [ { chrtID : 555666 , techSize : 'L' , wbSize : 'L Белый' , price : 1800 , discountedPrice : 1500 , quantity : 40 } ]
vendorCode : " SKU003" ,
title : " Блузка белая офисная" ,
description : " Классическая белая блузка для офиса" ,
brand : " Office" ,
object : " Блузки" ,
parent : " Одежда" ,
countryProduction : " Турция" ,
supplierVendorCode : " SUPPLIER-003" ,
mediaFiles : [ " /api/placeholder/400/405" ] ,
sizes : [
{
chrtID : 555666 ,
techSize : "L" ,
wbSize : "L Белый" ,
price : 1800 ,
discountedPrice : 1500 ,
quantity : 40 ,
} ,
] ,
} ,
{
nmID : 444333222 ,
vendorCode : ' SKU004' ,
title : ' Джинсы женские синие' ,
description : ' Классические женские джинсы прямого кроя' ,
brand : ' Denim' ,
object : ' Джинсы' ,
parent : ' Одежда' ,
countryProduction : ' Бангладеш' ,
supplierVendorCode : ' SUPPLIER-004' ,
mediaFiles : [ ' /api/placeholder/400/408' ] ,
sizes : [ { chrtID : 444333 , techSize : '30' , wbSize : '30 Синий' , price : 2800 , discountedPrice : 2300 , quantity : 25 } ]
vendorCode : " SKU004" ,
title : " Джинсы женские синие" ,
description : " Классические женские джинсы прямого кроя" ,
brand : " Denim" ,
object : " Джинсы" ,
parent : " Одежда" ,
countryProduction : " Бангладеш" ,
supplierVendorCode : " SUPPLIER-004" ,
mediaFiles : [ " /api/placeholder/400/408" ] ,
sizes : [
{
chrtID : 444333 ,
techSize : "30" ,
wbSize : "30 Синий" ,
price : 2800 ,
discountedPrice : 2300 ,
quantity : 25 ,
} ,
] ,
} ,
{
nmID : 111222333 ,
vendorCode : ' SKU005' ,
title : ' Кроссовки женские белые' ,
description : ' Удобные женские кроссовки для повседневной носки' ,
brand : ' Sport' ,
object : ' Кроссовки' ,
parent : ' Обувь' ,
countryProduction : ' Вьетнам' ,
supplierVendorCode : ' SUPPLIER-005' ,
mediaFiles : [ ' /api/placeholder/400/410' ] ,
sizes : [ { chrtID : 111222 , techSize : '37' , wbSize : '37 Белый' , price : 3200 , discountedPrice : 2800 , quantity : 35 } ]
vendorCode : " SKU005" ,
title : " Кроссовки женские белые" ,
description : " Удобные женские кроссовки для повседневной носки" ,
brand : " Sport" ,
object : " Кроссовки" ,
parent : " Обувь" ,
countryProduction : " Вьетнам" ,
supplierVendorCode : " SUPPLIER-005" ,
mediaFiles : [ " /api/placeholder/400/410" ] ,
sizes : [
{
chrtID : 111222 ,
techSize : "37" ,
wbSize : "37 Белый" ,
price : 3200 ,
discountedPrice : 2800 ,
quantity : 35 ,
} ,
] ,
} ,
{
nmID : 777888999 ,
vendorCode : ' SKU006' ,
title : ' Сумка женская черная' ,
description : ' Стильная женская сумка из экокожи' ,
brand : ' Accessories' ,
object : ' Сумки' ,
parent : ' Аксессуары' ,
countryProduction : ' Китай' ,
supplierVendorCode : ' SUPPLIER-006' ,
mediaFiles : [ ' /api/placeholder/400/411' ] ,
sizes : [ { chrtID : 777888 , techSize : 'Универсальный' , wbSize : 'Черный' , price : 1500 , discountedPrice : 1200 , quantity : 60 } ]
}
]
vendorCode : " SKU006" ,
title : " Сумка женская черная" ,
description : " Стильная женская сумка из экокожи" ,
brand : " Accessories" ,
object : " Сумки" ,
parent : " Аксессуары" ,
countryProduction : " Китай" ,
supplierVendorCode : " SUPPLIER-006" ,
mediaFiles : [ " /api/placeholder/400/411" ] ,
sizes : [
{
chrtID : 777888 ,
techSize : "Универсальный" ,
wbSize : "Черный" ,
price : 1500 ,
discountedPrice : 1200 ,
quantity : 60 ,
} ,
] ,
} ,
] ;
// Загружаем товары при инициализации
useEffect ( ( ) = > {
loadCards ( )
} , [ user ] )
loadCards ( ) ;
} , [ user ] ) ;
const loadCards = async ( ) = > {
setLoading ( true )
setLoading ( true ) ;
try {
const wbApiKey = user ? . organization ? . apiKeys ? . find ( key = > key . marketplace === 'WILDBERRIES' )
const wbApiKey = user ? . organization ? . apiKeys ? . find (
( key ) = > key . marketplace === "WILDBERRIES"
) ;
if ( wbApiKey ? . isActive ) {
const validationData = wbApiKey . validationData as Record <string , string >
const apiToken = validationData ? . token ||
validationData ? . apiKey ||
validationData ? . key ||
( wbApiKey as { apiKey? : string } ) . apiKey
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 , 20 )
setWbCards ( cards )
console . log ( ' Загружено карточек из WB API:' , cards . length )
return
console . log ( " Загружаем карточки из WB API..." ) ;
const cards = await WildberriesService . getAllCards ( apiToken , 20 ) ;
setWbCards ( cards ) ;
console . log ( " Загружено карточек из WB API:" , cards . length ) ;
return ;
}
}
// Если API ключ не настроен, показываем моковые данные
console . log ( ' API ключ WB не настроен, показываем моковые данные' )
setWbCards ( getMockCards ( ) )
console . log ( " API ключ WB не настроен, показываем моковые данные" ) ;
setWbCards ( getMockCards ( ) ) ;
} catch ( error ) {
console . error ( ' Ошибка загрузки карточек WB:' , error )
console . error ( " Ошибка загрузки карточек WB:" , error ) ;
// При ошибке API показываем моковые данные
setWbCards ( getMockCards ( ) )
setWbCards ( getMockCards ( ) ) ;
} finally {
setLoading ( false )
setLoading ( false ) ;
}
}
} ;
const searchCards = async ( ) = > {
if ( ! searchTerm . trim ( ) ) {
loadCards ( )
return
loadCards ( ) ;
return ;
}
setLoading ( true )
setLoading ( true ) ;
try {
const wbApiKey = user ? . organization ? . apiKeys ? . find ( key = > key . marketplace === 'WILDBERRIES' )
const wbApiKey = user ? . organization ? . apiKeys ? . find (
( key ) = > key . marketplace === "WILDBERRIES"
) ;
if ( wbApiKey ? . isActive ) {
const validationData = wbApiKey . validationData as Record <string , string >
const apiToken = validationData ? . token ||
validationData ? . apiKey ||
validationData ? . key ||
( wbApiKey as { apiKey? : string } ) . apiKey
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 , 20 )
setWbCards ( cards )
console . log ( 'Найдено карточек в WB API:' , cards . length )
return
console . log ( " Поиск в WB API:" , searchTerm ) ;
const cards = await WildberriesService . searchCards (
apiToken ,
searchTerm ,
20
) ;
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 . nmID . toString ( ) . includes ( searchTerm . toLowerCase ( ) ) ||
card . object ? . toLowerCase ( ) . includes ( searchTerm . toLowerCase ( ) )
)
setWbCards ( filteredCards )
console . log ( 'Найдено моковых товаров:' , filteredCards . length )
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 . nmID . toString ( ) . includes ( searchTerm . toLowerCase ( ) ) ||
card . object ? . toLowerCase ( ) . includes ( searchTerm . toLowerCase ( ) )
) ;
setWbCards ( filteredCards ) ;
console . log ( "Найдено моковых товаров:" , filteredCards . length ) ;
} catch ( error ) {
console . error ( ' Ошибка поиска карточек WB:' , 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 . nmID . toString ( ) . includes ( searchTerm . toLowerCase ( ) ) ||
card . object ? . toLowerCase ( ) . includes ( searchTerm . toLowerCase ( ) )
)
setWbCards ( filteredCards )
console . log ( 'Найдено моковых товаров (fallback):' , filteredCards . length )
const mockCards = getMockCards ( ) ;
const filteredCards = mockCards . filter (
( card ) = >
card . title . toLowerCase ( ) . includes ( searchTerm . toLowerCase ( ) ) ||
card . brand . toLowerCase ( ) . includes ( searchTerm . toLowerCase ( ) ) ||
card . nmID . toString ( ) . includes ( searchTerm . toLowerCase ( ) ) ||
card . object ? . toLowerCase ( ) . includes ( searchTerm . toLowerCase ( ) )
) ;
setWbCards ( filteredCards ) ;
console . log ( "Найдено моковых товаров (fallback):" , filteredCards . length ) ;
} finally {
setLoading ( false )
setLoading ( false ) ;
}
}
} ;
// Функции для работы с услугами и расходниками
const loadOrganizationServices = async ( organizationId : string ) = > {
if ( organizationServices [ organizationId ] ) return
if ( organizationServices [ organizationId ] ) return ;
try {
const response = await apolloClient . query ( {
query : GET_COUNTERPARTY_SERVICES ,
variables : { organizationId }
} )
variables : { organizationId } ,
} ) ;
if ( response . data ? . counterpartyServices ) {
setOrganizationServices ( prev = > ( {
setOrganizationServices ( ( prev ) = > ( {
. . . prev ,
[ organizationId ] : response . data . counterpartyServices
} ) )
[ organizationId ] : response . data . counterpartyServices ,
} ) ) ;
}
} catch ( error ) {
console . error ( ' Ошибка загрузки услуг организации:' , error )
console . error ( " Ошибка загрузки услуг организации:" , error ) ;
}
}
} ;
const loadOrganizationSupplies = async ( organizationId : string ) = > {
if ( organizationSupplies [ organizationId ] ) return
if ( organizationSupplies [ organizationId ] ) return ;
try {
const response = await apolloClient . query ( {
query : GET_COUNTERPARTY_SUPPLIES ,
variables : { organizationId }
} )
variables : { organizationId } ,
} ) ;
if ( response . data ? . counterpartySupplies ) {
setOrganizationSupplies ( prev = > ( {
setOrganizationSupplies ( ( prev ) = > ( {
. . . prev ,
[ organizationId ] : response . data . counterpartySupplies
} ) )
[ organizationId ] : response . data . counterpartySupplies ,
} ) ) ;
}
} catch ( error ) {
console . error ( ' Ошибка загрузки расходников организации:' , error )
console . error ( " Ошибка загрузки расходников организации:" , error ) ;
}
}
} ;
// Работа с товарами поставки
const addToSupply = ( card : WildberriesCard ) = > {
const existingItem = supplyItems . find ( item = > item . card . nmID === card . nmID )
const existingItem = supplyItems . find (
( item ) = > item . card . nmID === card . nmID
) ;
if ( existingItem ) {
toast . info ( ' Товар уже добавлен в поставку' )
return
toast . info ( " Товар уже добавлен в поставку" ) ;
return ;
}
const newItem : SupplyItem = {
@ -353,114 +488,132 @@ export function DirectSupplyCreation({ onComplete, onCreateSupply, canCreateSupp
quantity : 1200 ,
pricePerUnit : 0 ,
totalPrice : 0 ,
supplierId : ''
}
supplierId : "" ,
} ;
setSupplyItems ( prev = > [ . . . prev , newItem ] )
toast . success ( ' Товар добавлен в поставку' )
}
setSupplyItems ( ( prev ) = > [ . . . prev , newItem ] ) ;
toast . success ( " Товар добавлен в поставку" ) ;
} ;
const removeFromSupply = ( nmID : number ) = > {
setSupplyItems ( prev = > prev . filter ( item = > item . card . nmID !== nmID ) )
}
setSupplyItems ( ( prev ) = > prev . filter ( ( item ) = > item . card . nmID !== nmID ) ) ;
} ;
const updateSupplyItem = ( nmID : number , field : keyof SupplyItem , value : string | number ) = > {
setSupplyItems ( prev = > prev . map ( item = > {
if ( item . card . nmID === nmID ) {
const updatedItem = { . . . item , [ field ] : value }
if ( field === 'quantity' || field === 'pricePerUnit' ) {
updated Item . totalPrice = updatedItem . quantity * updatedItem . pricePerUnit
const updateSupplyItem = (
nmID : number ,
field : keyof SupplyItem ,
value : string | number
) = > {
setSupply Items ( ( prev ) = >
prev . map ( ( item ) = > {
if ( item . card . nmID === nmID ) {
const updatedItem = { . . . item , [ field ] : value } ;
if ( field === "quantity" || field === "pricePerUnit" ) {
updatedItem . totalPrice =
updatedItem . quantity * updatedItem . pricePerUnit ;
}
return updatedItem ;
}
return updatedI tem
}
return item
} ) )
}
return i tem;
} )
) ;
} ;
// Работа с поставщиками
const handleCreateSupplier = ( ) = > {
if ( ! newSupplier . name || ! newSupplier . contactName || ! newSupplier . phone ) {
toast . error ( ' Заполните обязательные поля' )
return
toast . error ( " Заполните обязательные поля" ) ;
return ;
}
const supplier : Supplier = {
id : Date.now ( ) . toString ( ) ,
. . . newSupplier
}
. . . newSupplier ,
} ;
setSuppliers ( prev = > [ . . . prev , supplier ] )
setSuppliers ( ( prev ) = > [ . . . prev , supplier ] ) ;
setNewSupplier ( {
name : '' ,
contactName : '' ,
phone : '' ,
market : '' ,
address : '' ,
place : '' ,
telegram : ''
} )
setShowSupplierModal ( false )
toast . success ( ' Поставщик создан' )
}
name : "" ,
contactName : "" ,
phone : "" ,
market : "" ,
address : "" ,
place : "" ,
telegram : "" ,
} ) ;
setShowSupplierModal ( false ) ;
toast . success ( " Поставщик создан" ) ;
} ;
// Расчеты
// Расчеты для нового блока
const getTotalSum = ( ) = > {
return goodsPrice + fulfillmentServicesPrice + logisticsPrice ;
} ;
// Оригинальные расчеты
const getTotalQuantity = ( ) = > {
return supplyItems . reduce ( ( sum , item ) = > sum + item . quantity , 0 )
}
return supplyItems . reduce ( ( sum , item ) = > sum + item . quantity , 0 ) ;
} ;
const getTotalItemsCost = ( ) = > {
return supplyItems . reduce ( ( sum , item ) = > sum + item . totalPrice , 0 )
}
return supplyItems . reduce ( ( sum , item ) = > sum + item . totalPrice , 0 ) ;
} ;
const getServicesCost = ( ) = > {
if ( ! selectedFulfillmentOrg || selectedServices . length === 0 ) return 0
const services = organizationServices [ selectedFulfillmentOrg ] || [ ]
return selectedServices . reduce ( ( sum , serviceId ) = > {
const service = services . find ( s = > s . id === serviceId )
return sum + ( service ? service.price : 0 )
} , 0 ) * getTotalQuantity ( )
}
if ( ! selectedFulfillmentOrg || selectedServices . length === 0 ) return 0 ;
const services = organizationServices [ selectedFulfillmentOrg ] || [ ] ;
return (
selectedServices . reduce ( ( sum , serviceId ) = > {
const service = services . find ( ( s ) = > s . id === serviceId ) ;
return sum + ( service ? service.price : 0 ) ;
} , 0 ) * getTotalQuantity ( )
) ;
} ;
const getConsumablesCost = ( ) = > {
if ( ! selectedFulfillmentOrg || selectedConsumables . length === 0 ) return 0
const supplies = organizationSupplies [ selectedFulfillmentOrg ] || [ ]
return selectedConsumables . reduce ( ( sum , supplyId ) = > {
const supply = supplies . find ( s = > s . id === supplyId )
return sum + ( supply ? supply.price : 0 )
} , 0 ) * getTotalQuantity ( )
}
if ( ! selectedFulfillmentOrg || selectedConsumables . length === 0 ) return 0 ;
const supplies = organizationSupplies [ selectedFulfillmentOrg ] || [ ] ;
return (
selectedConsumables . reduce ( ( sum , supplyId ) = > {
const supply = supplies . find ( ( s ) = > s . id === supplyId ) ;
return sum + ( supply ? supply.price : 0 ) ;
} , 0 ) * getTotalQuantity ( )
) ;
} ;
const formatCurrency = ( amount : number ) = > {
return new Intl . NumberFormat ( ' ru-RU' , {
style : ' currency' ,
currency : ' RUB' ,
minimumFractionDigits : 0
} ) . format ( amount )
}
return new Intl . NumberFormat ( " ru-RU" , {
style : " currency" ,
currency : " RUB" ,
minimumFractionDigits : 0 ,
} ) . format ( amount ) ;
} ;
// Создание поставки
const handleCreateSupplyInternal = async ( ) = > {
if ( supplyItems . length === 0 ) {
toast . error ( ' Добавьте товары в поставку' )
return
toast . error ( " Добавьте товары в поставку" ) ;
return ;
}
if ( ! deliveryDate ) {
toast . error ( ' Выберите дату поставки' )
return
if ( ! deliveryDateOriginal ) {
toast . error ( " Выберите дату поставки" ) ;
return ;
}
if ( supplyItems . some ( item = > item . quantity <= 0 || item . pricePerUnit <= 0 ) ) {
toast . error ( 'Укажите количество и цену для всех товаров' )
return
if (
supplyItems . some ( ( item ) = > item . quantity <= 0 || item . pricePerUnit <= 0 )
) {
toast . error ( "Укажите количество и цену для всех товаров" ) ;
return ;
}
try {
const supplyInput = {
deliveryDate : deliveryDate.toISOString ( ) . split ( 'T' ) [ 0 ] ,
cards : supplyItems.map ( item = > ( {
deliveryDate : deliveryDateOriginal .toISOString ( ) . split ( "T" ) [ 0 ] ,
cards : supplyItems.map ( ( item ) = > ( {
nmId : item.card.nmID.toString ( ) ,
vendorCode : item.card.vendorCode ,
title : item.card.title ,
@ -471,426 +624,846 @@ export function DirectSupplyCreation({ onComplete, onCreateSupply, canCreateSupp
selectedFulfillmentServices : selectedServices ,
selectedConsumableOrg : selectedFulfillmentOrg ,
selectedConsumableServices : selectedConsumables ,
deliveryDate : deliveryDate.toISOString ( ) . split ( 'T' ) [ 0 ] ,
mediaFiles : item.card.mediaFiles
} ) )
}
deliveryDate : deliveryDateOriginal .toISOString ( ) . split ( "T" ) [ 0 ] ,
mediaFiles : item.card.mediaFiles ,
} ) ) ,
} ;
await createSupply ( { variables : { input : supplyInput } } )
toast . success ( ' Поставка успешно создана!' )
onComplete ( )
await createSupply ( { variables : { input : supplyInput } } ) ;
toast . success ( " Поставка успешно создана!" ) ;
onComplete ( ) ;
} catch ( error ) {
console . error ( ' Error creating supply:' , error )
toast . error ( ' Ошибка при создании поставки' )
console . error ( " Error creating supply:" , error ) ;
toast . error ( " Ошибка при создании поставки" ) ;
}
}
} ;
// Обработка внешнего вызова создания поставки
React . useEffect ( ( ) = > {
if ( isCreatingSupply ) {
handleCreateSupplyInternal ( )
handleCreateSupplyInternal ( ) ;
}
} , [ isCreatingSupply ] )
} , [ isCreatingSupply ] ) ;
// Обновление статуса возможности создания поставки
React . useEffect ( ( ) = > {
const canCreate = supplyItems . length > 0 &&
deliveryDate !== null &&
supplyItems . every ( item = > item . quantity > 0 && item . pricePerUnit > 0 )
if ( onCanCreateSupplyChange ) {
onCanCreateSupplyChange ( canCreate )
}
} , [ supplyItems , deliveryDate , onCanCreateSupplyChange ] )
const canCreate =
supplyItems . length > 0 &&
deliveryDateOriginal !== null &&
supplyItems . every ( ( item ) = > item . quantity > 0 && item . pricePerUnit > 0 ) ;
const fulfillmentOrgs = ( counterpartiesData ? . myCounterparties || [ ] ) . filter ( ( org : Organization ) = > org . type === 'FULFILLMENT' )
if ( onCanCreateSupplyChange ) {
onCanCreateSupplyChange ( canCreate ) ;
}
} , [ supplyItems , deliveryDateOriginal , onCanCreateSupplyChange ] ) ;
const fulfillmentOrgs = ( counterpartiesData ? . myCounterparties || [ ] ) . filter (
( org : Organization ) = > org . type === "FULFILLMENT"
) ;
const markets = [
{ value : ' sadovod' , label : ' Садовод' } ,
{ value : ' tyak-moscow' , label : ' ТЯК Москва' }
]
{ value : " sadovod" , label : " Садовод" } ,
{ value : " tyak-moscow" , label : " ТЯК Москва" } ,
] ;
return (
< div className = "space-y-3" >
{ /* Основные настройки */ }
< Card className = "bg-gradient-to-r from-purple-500 to-pink-500 p-3 " >
< div className = "grid grid-cols-2 md:grid-cols-6 gap-3 text-xs " >
{ /* Дата поставки */ }
< div >
< Popover >
< PopoverTrigger asChild >
< button className = "w-full bg-white/20 rounded px-2 py-1 text-white hover:bg-white/30 transition-colors text-xs" >
< CalendarIcon className = "h-3 w-3 inline mr-1" / >
{ deliveryDate ? format ( deliveryDate , "dd.MM" ) : "Дата" }
< / button >
< / PopoverTrigger >
< PopoverContent className = "w-auto p-0" >
< DatePicker
selected = { deliveryDate }
onChange = { ( date : Date | null ) = > setDeliveryDate ( date || undefined ) }
minDate = { new Date ( ) }
inline
locale = "ru"
/ >
< / PopoverContent >
< / Popover >
< / div >
{ /* Фулфилмент */ }
< div className = "md:col-span-2" >
< Select
value = { selectedFulfillmentOrg }
onValueChange = { ( value ) = > {
setSelectedFulfillmentOrg ( value )
setSelectedServices ( [ ] )
setSelectedConsumables ( [ ] )
if ( value ) {
loadOrganizationServices ( value )
loadOrganizationSupplies ( value )
}
} }
>
< SelectTrigger className = "bg-white/20 border-0 text-white h-7 text-xs" >
< SelectValue placeholder = "Фулфилмент" / >
< / SelectTrigger >
< SelectContent >
{ fulfillmentOrgs . map ( ( org : Organization ) = > (
< SelectItem key = { org . id } value = { org . id } >
{ org . name || org . fullName }
< / SelectItem >
) ) }
< / SelectContent >
< / Select >
< / div >
{ /* Показатели */ }
< div className = "text-center" >
< div className = "text-white/80" > Т о в а р о в < / div >
< div className = "font-bold text-white" > { getTotalQuantity ( ) } < / div >
< / div >
< div className = "text-center" >
< div className = "text-white/80" > С т о и м о с т ь < / div >
< div className = "font-bold text-white" > { formatCurrency ( getTotalItemsCost ( ) ) . replace ( ' ₽' , '₽' ) } < / div >
< / div >
< div className = "text-center" >
< div className = "text-white/80" > У с л у г и Ф Ф < / div >
< div className = "font-bold text-white" > { formatCurrency ( getServicesCost ( ) + getConsumablesCost ( ) ) . replace ( ' ₽' , '₽' ) } < / div >
< / div >
< / div >
< / Card >
{ /* Поиск и карточки */ }
< Card className = "bg-white/10 backdrop-blur border-white/20 p-2" >
< div className = "flex items-center space-x-2 mb-2" >
< Input
placeholder = "Поиск товаров..."
value = { searchTerm }
onChange = { ( e ) = > setSearchTerm ( e . target . value ) }
className = "bg-white/5 border-white/20 text-white placeholder-white/50 h-7 text-xs flex-1"
onKeyPress = { ( e ) = > e . key === 'Enter' && searchCards ( ) }
/ >
< Button onClick = { searchCards } disabled = { loading } variant = "secondary" size = "sm" className = "h-7 px-2 text-xs" >
< Search className = "h-3 w-3" / >
< / Button >
< / div >
< div className = "flex space-x-2 overflow-x-auto pb-1" >
{ loading ? (
[ . . . Array ( 6 ) ] . map ( ( _ , i ) = > (
< div key = { i } className = "flex-shrink-0 w-16 h-20 bg-white/5 rounded animate-pulse" > < / div >
) )
) : (
wbCards . map ( ( card ) = > {
const isInSupply = supplyItems . some ( item = > item . card . nmID === card . nmID )
return (
< div
key = { card . nmID }
className = { ` flex-shrink-0 w-16 cursor-pointer transition-all hover:scale-105 relative ${ isInSupply ? 'ring-1 ring-purple-400' : '' } ` }
onClick = { ( ) = > addToSupply ( card ) }
>
< img
src = { WildberriesService . getCardImage ( card , 'c246x328' ) || '/api/placeholder/64/80' }
alt = { card . title }
className = "w-16 h-20 rounded object-cover"
/ >
{ isInSupply && (
< div className = "absolute -top-1 -right-1 bg-purple-500 text-white rounded-full w-3 h-3 flex items-center justify-center text-[8px]" >
✓
< / div >
) }
< / div >
)
} )
) }
< / div >
< / Card >
{ /* Услуги и расходники в одной строке */ }
{ selectedFulfillmentOrg && (
< Card className = "bg-white/10 backdrop-blur border-white/20 p-2" >
< div className = "grid grid-cols-1 md:grid-cols-2 gap-4 text-xs" >
< >
< style > { lineClampStyles } < / style >
< div className = "space-y-3 w-full " >
{ /* НОВЫЙ БЛОК СОЗДАНИЯ ПОСТАВКИ */ }
< Card className = "bg-white/10 backdrop-blur-xl border border-white/20 p-3 " >
{ /* Первая строка */ }
< div className = "grid grid-cols-1 md:grid-cols-4 gap-3 items-end mb-2" >
{ /* 1. Модуль выбора даты */ }
< div >
< div className = "text-white/80 mb-1" > У с л у г и ф у л ф и л м е н т а : < / div >
< div className = "flex flex-wrap gap-1" >
{ organizationServices [ selectedFulfillmentOrg ] ? (
organizationServices [ selectedFulfillmentOrg ] . map ( ( service ) = > (
< label key = { service . id } className = "flex items-center space-x-1 cursor-pointer bg-white/5 rounded px-2 py-1 hover:bg-white/10 " >
< input
type = "checkbox "
checked = { s electedServices . includes ( service . id ) }
onChange = { ( e ) = > {
if ( e . target . checked ) {
setSelectedServices ( prev = > [ . . . prev , service . id ] )
} else {
setSelectedServices ( prev = > prev . filter ( id = > id !== service . id ) )
}
} }
className = "w-3 h-3"
/ >
< span className = "text-white text-xs" > { service . name } ( { service . price } ₽ ) < / span >
< / label >
) )
) : (
< span className = "text-white/60" > З а г р у з к а . . . < / span >
) }
< Label className = "text-white/80 text-xs mb-1 block flex items-center gap-1" >
< CalendarIcon className = "h-3 w-3" / >
Д а т а
< / Label >
< div className = "relative ">
< input
type = "date "
value = { d eliveryDate }
onChange = { ( e ) = > setDeliveryDate ( e . target . value ) }
className = "w-full h-8 rounded-lg border-0 bg-white/20 backdrop-blur px-2 py-1 text-white placeholder:text-white/50 focus:bg-white/30 focus:outline-none focus:ring-1 focus:ring-white/20 text-xs font-medium"
min = { new Date ( ) . toISOString ( ) . split ( "T" ) [ 0 ] }
/ >
< / div >
< / div >
{ /* 2. Модуль выбора фулфилмента */ }
< div >
< div className = "text-white/80 mb-1" > Р а с х о д н ы е м а т е р и а л ы : < / div >
< div className = "flex flex-wrap gap-1" >
{ organizationSupplies [ selectedFulfillmentOrg ] ? (
organizationSupplies [ selectedFulfillmentOrg ] . map ( ( supply ) = > (
< label key = { supply . id } className = "flex items-center space-x-1 cursor-pointer bg-white/5 rounded px-2 py-1 hover:bg-white/10" >
< input
type = "checkbox"
checked = { selectedConsumables . includes ( supply . id ) }
onChange = { ( e ) = > {
if ( e . target . checked ) {
setSelectedConsumables ( prev = > [ . . . prev , supply . id ] )
} else {
setSelectedConsumable s ( prev = > prev . filter ( id = > id !== supply . id ) )
}
} }
className = "w-3 h-3"
/ >
< span className = "text-white text-xs" > { supply . name } ( { supply . price } ₽ ) < / span >
< / label >
) )
) : (
< span className = "text-white/60" > З а г р у з к а . . . < / span >
) }
< Label className = "text-white/80 text-xs mb-1 block flex items-center gap-1" >
< Building className = "h-3 w-3" / >
Ф у л ф и л м е н т
< / Label >
< Select
value = { selectedFulfillment }
onValueChange = { setSelectedFulfillment }
>
< SelectTrigger className = "h-8 bg-white/20 border-0 text-white focus:bg-white/30 focus:ring-1 focus:ring-white/20 text-xs" >
< SelectValue placeholder = "ФУЛФИЛМЕНТ ИВАНОВО" / >
< / SelectTrigger >
< SelectContent >
{ fulfillmentOrg s. map ( ( org : Organization ) = > (
< SelectItem key = { org . id } value = { org . id } >
{ org . name || org . fullName }
< / SelectItem >
) ) }
< / SelectContent >
< / Select >
< / div >
{ /* 3. Объём товаров */ }
< div >
< Label className = "text-white/80 text-xs mb-1 block" >
О б ъ ё м т о в а р о в
< / Label >
< Input
type = "number"
value = { goodsVolume || "" }
onChange = { ( e ) = >
setGoodsVolume ( parseFloat ( e . target . value ) || 0 )
}
placeholder = "м³"
className = "h-8 bg-white/20 border-0 text-white placeholder:text-white/50 focus:bg-white/30 focus:ring-1 focus:ring-white/20 text-xs"
/ >
< / div >
{ /* 4. Грузовые места */ }
< div >
< Label className = "text-white/80 text-xs mb-1 block" >
Г р у з о в ы е м е с т а
< / Label >
< Input
type = "number"
value = { cargoPlaces || "" }
onChange = { ( e ) = > setCargoPlaces ( parseInt ( e . target . value ) || 0 ) }
placeholder = "шт"
className = "h-8 bg-white/20 border-0 text-white placeholder:text-white/50 focus:bg-white/30 focus:ring-1 focus:ring-white/20 text-xs"
/ >
< / div >
< / div >
{ /* Вторая строка */ }
< div className = "grid grid-cols-1 md:grid-cols-4 gap-3 items-end" >
{ /* 5. Цена товаров */ }
< div >
< Label className = "text-white/80 text-xs mb-1 block" >
Ц е н а т о в а р о в
< / Label >
< Input
type = "number"
value = { goodsPrice || "" }
onChange = { ( e ) = > setGoodsPrice ( parseFloat ( e . target . value ) || 0 ) }
placeholder = "₽"
className = "h-8 bg-white/20 border-0 text-white placeholder:text-white/50 focus:bg-white/30 focus:ring-1 focus:ring-white/20 text-xs"
/ >
< / div >
{ /* 6. Цена услуг фулфилмента */ }
< div >
< Label className = "text-white/80 text-xs mb-1 block" >
Ц е н а у с л у г ф у л ф и л м е н т
< / Label >
< Input
type = "number"
value = { fulfillmentServicesPrice || "" }
onChange = { ( e ) = >
setFulfillmentServicesPrice ( parseFloat ( e . target . value ) || 0 )
}
placeholder = "₽"
className = "h-8 bg-white/20 border-0 text-white placeholder:text-white/50 focus:bg-white/30 focus:ring-1 focus:ring-white/20 text-xs"
/ >
< / div >
{ /* 7. Цена логистики */ }
< div >
< Label className = "text-white/80 text-xs mb-1 block" >
Л о г и с т и к а д о ф у л ф и л м е н т а
< / Label >
< Input
type = "number"
value = { logisticsPrice || "" }
onChange = { ( e ) = >
setLogisticsPrice ( parseFloat ( e . target . value ) || 0 )
}
placeholder = "₽"
className = "h-8 bg-white/20 border-0 text-white placeholder:text-white/50 focus:bg-white/30 focus:ring-1 focus:ring-white/20 text-xs"
/ >
< / div >
{ /* 8. Итоговая сумма */ }
< div >
< Label className = "text-white/80 text-xs mb-1 block" > И т о г о < / Label >
< div className = "h-8 bg-white/10 rounded-lg flex items-center justify-center" >
< span className = "text-white font-bold text-sm" >
{ formatCurrency ( getTotalSum ( ) ) . replace ( " ₽" , " ₽" ) }
< / span >
< / div >
< / div >
< / div >
< / Card >
) }
{ /* Компактная таблица товаров */ }
< Card className = "bg-white/10 backdrop-blur border-white/20 p-2 " >
< div className = "flex items-center justify-between mb-2 " >
< span className = "text-white font-medium text-sm" > Т о в а р ы в п о с т а в к е < / span >
< Button
onClick = { ( ) = > setShowSupplierModal ( true ) }
variant = "outline"
size = "sm"
className = "bg-white/5 border-white/20 text-white hover:bg-white/10 h-6 px-2 text-xs"
>
< Plus className = "h-3 w-3 mr-1" / >
П о с т а в щ и к
< / Button >
< / div >
{ supplyItems . length === 0 ? (
< div className = "text-center py-4" >
< Package className = "h-6 w-6 text-white/20 mx-auto mb-1" / >
< p className = "text -white/6 0 text-xs" > Д о б а в ь т е т о в а р ы и з к а р т о ч е к в ы ш е < / p >
{ /* Блок поиска товаров - оптимизированное расположение */ }
< Card className = "bg-white/10 backdrop-blur-xl border border-white/20 p-3 " >
< div className = "mb-1 " >
< Label className = "text-white/80 text-xs mb-2 block flex items-center gap-1" >
< Search className = "h-3 w-3" / >
П о и с к т о в а р о в Wildberries
< / Label >
< div className = "flex items-center space-x-2" >
< Input
placeholder = "Введите название товара, артикул или бренд..."
value = { searchTerm }
onChange = { ( e ) = > setSearchTerm ( e . target . value ) }
className = "bg-white/20 border-0 text-white placeholder-white/60 h-8 text-xs flex-1 focus:bg-white/30 focus:ring-1 focus:ring-white/20"
onKeyPress = { ( e ) = > e . key === "Enter" && searchCards ( ) }
/ >
< Button
onClick = { searchCards }
disabled = { loading }
className = "h-8 px-4 bg-white/20 hover:bg -white/3 0 border-0 text-white text-xs font-medium backdrop-blur"
>
{ loading ? (
< div className = "animate-spin rounded-full h-3 w-3 border-b-2 border-white" > < / div >
) : (
< >
< Search className = "h-3 w-3 mr-1" / >
П о и с к
< / >
) }
< / Button >
< / div >
< / div >
) : (
{ /* Карточки товаров - увеличенный размер и единообразность */ }
< div className = "space-y-1" >
{ supplyItems . map ( ( item ) = > (
< div key = { item . card . nmID } className = "grid grid-cols-12 gap-2 items-center bg-white/5 rounded p-2 text-xs ">
{ /* Товар */ }
< div className = "col-span-4 flex items-center space-x-2" >
< img
src = { WildberriesService . getCardImage ( item . card , 'c246x328' ) || '/api/placeholder/24/24' }
alt = { item . card . title }
className = "w-6 h-6 rounded object-cover "
/ >
< div className = "min-w-0" >
< div className = "text-white font-medium truncate text-xs" > { item . card . title } < / div >
< div className = "text-white/60 text-[10px]" > А р т : { item . card . vendorCode } < / div >
< div className = "flex items-center justify-between" >
< span className = "text-white/80 text-xs font-medium ">
Н а й д е н о т о в а р о в : { wbCards . length }
< / span >
{ supplyItems . length > 0 && (
< Badge
variant = "secondary"
className = "bg-purple-500/20 text-purple-200 text-xs "
>
В п о с т а в к е : { supplyItems . length }
< / Badge >
) }
< / div >
< div className = "grid grid-cols-3 sm:grid-cols-4 md:grid-cols-6 lg:grid-cols-8 xl:grid-cols-10 gap-2" >
{ loading
? [ . . . Array ( 12 ) ] . map ( ( _ , i ) = > (
< div
key = { i }
className = "aspect-[3/4] bg-white/5 rounded-lg animate-pulse"
> < / div >
) )
: wbCards . map ( ( card ) = > {
const isInSupply = supplyItems . some (
( item ) = > item . card . nmID === card . nmID
) ;
return (
< div
key = { card . nmID }
className = { ` group relative cursor-pointer transition-all duration-200 hover:scale-105 hover:z-10 ${
isInSupply
? "ring-2 ring-purple-400 ring-offset-1 ring-offset-transparent"
: ""
} ` }
onClick = { ( ) = > addToSupply ( card ) }
>
< div className = "aspect-[3/4] bg-white/10 rounded-lg overflow-hidden backdrop-blur-sm border border-white/20 hover:border-white/40 transition-all" >
< img
src = {
WildberriesService . getCardImage (
card ,
"c516x688"
) || "/api/placeholder/120/160"
}
alt = { card . title }
className = "w-full h-full object-cover transition-transform group-hover:scale-110"
loading = "lazy"
/ >
{ /* Оверлей с информацией */ }
< div className = "absolute inset-0 bg-gradient-to-t from-black/80 via-transparent to-transparent opacity-0 group-hover:opacity-100 transition-opacity" >
< div className = "absolute bottom-2 left-2 right-2" >
< div className = "text-white text-xs font-medium line-clamp-2 mb-1" >
{ card . title }
< / div >
< div className = "text-white/70 text-[10px]" >
А р т : { card . vendorCode }
< / div >
{ card . sizes && card . sizes [ 0 ] && (
< div className = "text-purple-300 text-[10px] font-medium" >
о т { " " }
{ card . sizes [ 0 ] . discountedPrice ||
card . sizes [ 0 ] . price } { " " }
₽
< / div >
) }
< / div >
< / div >
{ /* Индикатор добавления в поставку */ }
{ isInSupply && (
< div className = "absolute top-2 right-2 bg-purple-500 text-white rounded-full w-6 h-6 flex items-center justify-center text-xs font-bold shadow-lg" >
✓
< / div >
) }
{ /* Индикатор при наведении */ }
< div className = "absolute top-2 left-2 bg-white/20 backdrop-blur text-white rounded-full w-6 h-6 flex items-center justify-center opacity-0 group-hover:opacity-100 transition-opacity" >
< Plus className = "h-3 w-3" / >
< / div >
< / div >
< / div >
) ;
} ) }
< / div >
{ ! loading && wbCards . length === 0 && (
< div className = "text-center py-4" >
< Package className = "h-8 w-8 text-white/20 mx-auto mb-2" / >
< p className = "text-white/60 text-xs" >
{ searchTerm
? "Товары не найдены"
: "Введите запрос для поиска товаров" }
< / p >
{ searchTerm && (
< p className = "text-white/40 text-[10px] mt-1" >
П о п р о б у й т е и з м е н и т ь у с л о в и я п о и с к а
< / p >
) }
< / div >
) }
< / div >
< / Card >
{ /* Услуги и расходники в одной строке */ }
{ selectedFulfillmentOrg && (
< Card className = "bg-white/10 backdrop-blur border-white/20 p-2" >
< div className = "grid grid-cols-1 md:grid-cols-2 gap-4 text-xs" >
< div >
< div className = "text-white/80 mb-1" > У с л у г и ф у л ф и л м е н т а : < / div >
< div className = "flex flex-wrap gap-1" >
{ organizationServices [ selectedFulfillmentOrg ] ? (
organizationServices [ selectedFulfillmentOrg ] . map (
( service ) = > (
< label
key = { service . id }
className = "flex items-center space-x-1 cursor-pointer bg-white/5 rounded px-2 py-1 hover:bg-white/10"
>
< input
type = "checkbox"
checked = { selectedServices . includes ( service . id ) }
onChange = { ( e ) = > {
if ( e . target . checked ) {
setSelectedServices ( ( prev ) = > [
. . . prev ,
service . id ,
] ) ;
} else {
setSelectedServices ( ( prev ) = >
prev . filter ( ( id ) = > id !== service . id )
) ;
}
} }
className = "w-3 h-3"
/ >
< span className = "text-white text-xs" >
{ service . name } ( { service . price } ₽ )
< / span >
< / label >
)
)
) : (
< span className = "text-white/60" > З а г р у з к а . . . < / span >
) }
< / div >
< / div >
< div >
< div className = "text-white/80 mb-1" > Р а с х о д н ы е м а т е р и а л ы : < / div >
< div className = "flex flex-wrap gap-1" >
{ organizationSupplies [ selectedFulfillmentOrg ] ? (
organizationSupplies [ selectedFulfillmentOrg ] . map (
( supply ) = > (
< label
key = { supply . id }
className = "flex items-center space-x-1 cursor-pointer bg-white/5 rounded px-2 py-1 hover:bg-white/10"
>
< input
type = "checkbox"
checked = { selectedConsumables . includes ( supply . id ) }
onChange = { ( e ) = > {
if ( e . target . checked ) {
setSelectedConsumables ( ( prev ) = > [
. . . prev ,
supply . id ,
] ) ;
} else {
setSelectedConsumables ( ( prev ) = >
prev . filter ( ( id ) = > id !== supply . id )
) ;
}
} }
className = "w-3 h-3"
/ >
< span className = "text-white text-xs" >
{ supply . name } ( { supply . price } ₽ )
< / span >
< / label >
)
)
) : (
< span className = "text-white/60" > З а г р у з к а . . . < / span >
) }
< / div >
< / div >
< / div >
< / Card >
) }
{ /* Модуль товаров в поставке - новый дизайн */ }
< Card className = "bg-white/10 backdrop-blur border-white/20 p-3" >
< div className = "flex items-center justify-between mb-3" >
< span className = "text-white font-medium text-sm" >
Т о в а р ы в п о с т а в к е
< / span >
< Button
onClick = { ( ) = > setShowSupplierModal ( true ) }
variant = "outline"
size = "sm"
className = "bg-white/5 border-white/20 text-white hover:bg-white/10 h-7 px-3 text-xs"
>
< Plus className = "h-3 w-3 mr-1" / >
П о с т а в щ и к
< / Button >
< / div >
{ supplyItems . length === 0 ? (
< div className = "text-center py-6" >
< Package className = "h-8 w-8 text-white/20 mx-auto mb-2" / >
< p className = "text-white/60 text-xs" >
Д о б а в ь т е т о в а р ы и з к а р т о ч е к в ы ш е
< / p >
< / div >
) : (
< div className = "space-y-4" >
{ supplyItems . map ( ( item ) = > (
< Card
key = { item . card . nmID }
className = "bg-white/5 border-white/10 p-3"
>
{ /* Заголовок товара с кнопкой удаления */ }
< div className = "flex items-center justify-between mb-3" >
< div className = "flex items-center space-x-2" >
< div className = "text-white font-medium text-sm line-clamp-1" >
{ item . card . title }
< / div >
< div className = "text-white/60 text-xs" >
А р т : { item . card . vendorCode }
< / div >
< / div >
< Button
onClick = { ( ) = > removeFromSupply ( item . card . nmID ) }
size = "sm"
variant = "ghost"
className = "h-6 w-6 p-0 text-white/60 hover:text-red-400"
>
< X className = "h-4 w-4" / >
< / Button >
< / div >
< / div >
{ /* Количество */ }
< div className = "col-span-2" >
< Input
typ e = "number"
value = { item . quantity }
onChange = { ( e ) = > updateSupplyItem ( item . card . nmID , 'quantity' , parseInt ( e . target . value ) || 0 ) }
className = "bg-purple-500 border-0 text-white text-center h-6 text-xs font-bold"
min = "1"
/ >
< / div >
{ /* Цена */ }
< div className = "col-span-2" >
< Input
type = "number"
value = { item . pricePerUnit || '' }
onChange = { ( e ) = > updateSupplyItem ( item . card . nmID , 'pricePerUnit' , parseFloat ( e . target . value ) || 0 ) }
className = "bg-white/10 border-white/20 text-white text-center h-6 text-xs"
placeholder = "Цена"
/ >
< / div >
{ /* Поставщик */ }
< div className = "col-span-3" >
< Select
value = { item . supplierId }
onValueChange = { ( value ) = > updateSupplyItem ( item . card . nmID , 'supplierId' , value ) }
{ /* Названия блоков */ }
< div
className = "grid grid-cols-8 gap-2"
s tyl e= { { marginBottom : "4px" } }
>
< SelectTrigger className = "bg-white/5 border -white/2 0 text-white h-6 text-xs " >
< SelectValue placeholder = "Поставщик" / >
< div className = "text -white/8 0 text-xs font-medium text-center " >
Т о в а р
< / div >
< div className = "text-white/80 text-xs font-medium text-center" >
П а р а м е т р ы
< / div >
< div className = "text-white/80 text-xs font-medium text-center" >
З а к а з а т ь
< / div >
< div className = "text-white/80 text-xs font-medium text-center" >
Ц е н а
< / div >
< div className = "text-white/80 text-xs font-medium text-center" >
У с л у г и ф у л ф и л м е н т а
< / div >
< div className = "text-white/80 text-xs font-medium text-center" >
П о с т а в щ и к
< / div >
< div className = "text-white/80 text-xs font-medium text-center" >
Р а с х о д н и к и ф у л ф и л м е н т а
< / div >
< div className = "text-white/80 text-xs font-medium text-center" >
Р а с х о д н и к и с е л л е р а
< / div >
< / div >
{ /* Оптимизированная сетка для 13" - все блоки в одну строку */ }
< div className = "grid grid-cols-8 gap-2" >
{ /* Блок 1: Картинка товара */ }
< div className = "bg-white/10 rounded-lg overflow-hidden relative" >
< img
src = {
WildberriesService . getCardImage (
item . card ,
"c246x328"
) || "/api/placeholder/60/60"
}
alt = { item . card . title }
className = "w-full h-full object-cover"
/ >
< / div >
{ /* Блок 2: Параметры */ }
< div className = "bg-white/10 rounded-lg p-2 flex flex-col justify-center" >
< div className = "space-y-1" >
< div className = "text-white/70 text-[9px] text-center" >
{ item . card . object }
< / div >
< div className = "text-white/70 text-[9px] text-center" >
{ item . card . countryProduction }
< / div >
{ item . card . sizes && item . card . sizes [ 0 ] && (
< div className = "text-white/70 text-[9px] text-center" >
{ item . card . sizes [ 0 ] . techSize }
< / div >
) }
< / div >
< / div >
{ /* Блок 3: Заказать */ }
< div className = "bg-white/10 rounded-lg p-2 flex flex-col justify-center" >
< div className = "text-white/60 text-[10px] mb-1 text-center" >
К о л и ч е с т в о
< / div >
< Input
type = "number"
value = { item . quantity }
onChange = { ( e ) = >
updateSupplyItem (
item . card . nmID ,
"quantity" ,
parseInt ( e . target . value ) || 0
)
}
className = "bg-purple-500/20 border-purple-400/30 text-white text-center h-7 text-xs font-bold"
min = "1"
/ >
< / div >
{ /* Блок 4: Цена */ }
< div className = "bg-white/10 rounded-lg p-2 flex flex-col justify-center" >
< div className = "text-white/60 text-[10px] mb-1 text-center" >
З а е д и н и ц у
< / div >
< Input
type = "number"
value = { item . pricePerUnit || "" }
onChange = { ( e ) = >
updateSupplyItem (
item . card . nmID ,
"pricePerUnit" ,
parseFloat ( e . target . value ) || 0
)
}
className = "bg-white/20 border-white/20 text-white text-center h-7 text-xs"
placeholder = "₽"
/ >
< div className = "text-white/80 text-[9px] font-medium text-center mt-1" >
{ formatCurrency ( item . totalPrice ) . replace ( " ₽" , "₽" ) }
< / div >
< / div >
{ /* Блок 5: Услуги фулфилмента */ }
< div className = "bg-white/10 rounded-lg p-2 flex flex-col justify-center" >
< div className = "space-y-1 max-h-16 overflow-y-auto" >
{ selectedFulfillmentOrg &&
organizationServices [ selectedFulfillmentOrg ] ? (
organizationServices [ selectedFulfillmentOrg ]
. slice ( 0 , 2 )
. map ( ( service ) = > (
< label
key = { service . id }
className = "flex items-center space-x-1 cursor-pointer"
>
< input
type = "checkbox"
checked = { selectedServices . includes (
service . id
) }
onChange = { ( e ) = > {
if ( e . target . checked ) {
setSelectedServices ( ( prev ) = > [
. . . prev ,
service . id ,
] ) ;
} else {
setSelectedServices ( ( prev ) = >
prev . filter ( ( id ) = > id !== service . id )
) ;
}
} }
className = "w-2 h-2"
/ >
< span className = "text-white text-[9px]" >
{ service . name . substring ( 0 , 8 ) } . . .
< / span >
< / label >
) )
) : (
< span className = "text-white/60 text-[9px] text-center" >
В ы б е р и т е ф у л ф и л м е н т
< / span >
) }
< / div >
< / div >
{ /* Блок 6: Поставщик */ }
< div className = "bg-white/10 rounded-lg p-2 flex flex-col justify-center" >
< Select
value = { item . supplierId }
onValueChange = { ( value ) = >
updateSupplyItem ( item . card . nmID , "supplierId" , value )
}
>
< SelectTrigger className = "bg-white/20 border-white/20 text-white h-7 text-[10px]" >
< SelectValue placeholder = "Выбрать" / >
< / SelectTrigger >
< SelectContent >
{ suppliers . map ( ( supplier ) = > (
< SelectItem key = { supplier . id } value = { supplier . id } >
{ supplier . name }
< / SelectItem >
) ) }
< / SelectContent >
< / Select >
< / div >
{ /* Блок 7: Расходники фулфилмента */ }
< div className = "bg-white/10 rounded-lg p-2 flex flex-col justify-center" >
< div className = "space-y-1 max-h-16 overflow-y-auto" >
{ selectedFulfillmentOrg &&
organizationSupplies [ selectedFulfillmentOrg ] ? (
organizationSupplies [ selectedFulfillmentOrg ]
. slice ( 0 , 2 )
. map ( ( supply ) = > (
< label
key = { supply . id }
className = "flex items-center space-x-1 cursor-pointer"
>
< input
type = "checkbox"
checked = { selectedConsumables . includes (
supply . id
) }
onChange = { ( e ) = > {
if ( e . target . checked ) {
setSelectedConsumables ( ( prev ) = > [
. . . prev ,
supply . id ,
] ) ;
} else {
setSelectedConsumables ( ( prev ) = >
prev . filter ( ( id ) = > id !== supply . id )
) ;
}
} }
className = "w-2 h-2"
/ >
< span className = "text-white text-[9px]" >
{ supply . name . substring ( 0 , 6 ) } . . .
< / span >
< / label >
) )
) : (
< span className = "text-white/60 text-[9px] text-center" >
В ы б е р и т е ф у л ф и л м е н т
< / span >
) }
< / div >
< / div >
{ /* Блок 8: Расходники селлера */ }
< div className = "bg-white/10 rounded-lg p-2 flex flex-col justify-center" >
< div className = "space-y-1" >
< label className = "flex items-center space-x-1 cursor-pointer" >
< input type = "checkbox" className = "w-2 h-2" / >
< span className = "text-white text-[9px]" >
У п а к о в к а
< / span >
< / label >
< label className = "flex items-center space-x-1 cursor-pointer" >
< input type = "checkbox" className = "w-2 h-2" / >
< span className = "text-white text-[9px]" >
Э т и к е т к и
< / span >
< / label >
< label className = "flex items-center space-x-1 cursor-pointer" >
< input type = "checkbox" className = "w-2 h-2" / >
< span className = "text-white text-[9px]" > П а к е т ы < / span >
< / label >
< / div >
< / div >
< / div >
< / Card >
) ) }
< / div >
) }
< / Card >
{ /* Модальное окно создания поставщика */ }
< Dialog open = { showSupplierModal } onOpenChange = { setShowSupplierModal } >
< DialogContent className = "glass-card border-white/10 max-w-md" >
< DialogHeader >
< DialogTitle className = "text-white" >
С о з д а т ь п о с т а в щ и к а
< / DialogTitle >
< / DialogHeader >
< div className = "space-y-3" >
< div className = "grid grid-cols-2 gap-3" >
< div >
< Label className = "text-white/60 text-xs" > Н а з в а н и е * < / Label >
< Input
value = { newSupplier . name }
onChange = { ( e ) = >
setNewSupplier ( ( prev ) = > ( {
. . . prev ,
name : e.target.value ,
} ) )
}
className = "bg-white/10 border-white/20 text-white h-8 text-xs"
placeholder = "Название"
/ >
< / div >
< div >
< Label className = "text-white/60 text-xs" > И м я * < / Label >
< Input
value = { newSupplier . contactName }
onChange = { ( e ) = >
setNewSupplier ( ( prev ) = > ( {
. . . prev ,
contactName : e.target.value ,
} ) )
}
className = "bg-white/10 border-white/20 text-white h-8 text-xs"
placeholder = "Имя"
/ >
< / div >
< / div >
< div className = "grid grid-cols-2 gap-3" >
< div >
< Label className = "text-white/60 text-xs" > Т е л е ф о н * < / Label >
< Input
value = { newSupplier . phone }
onChange = { ( e ) = >
setNewSupplier ( ( prev ) = > ( {
. . . prev ,
phone : e.target.value ,
} ) )
}
className = "bg-white/10 border-white/20 text-white h-8 text-xs"
placeholder = "+7 999 123-45-67"
/ >
< / div >
< div >
< Label className = "text-white/60 text-xs" > Р ы н о к < / Label >
< Select
value = { newSupplier . market }
onValueChange = { ( value ) = >
setNewSupplier ( ( prev ) = > ( { . . . prev , market : value } ) )
}
>
< SelectTrigger className = "bg-white/10 border-white/20 text-white h-8 text-xs" >
< SelectValue placeholder = "Рынок" / >
< / SelectTrigger >
< SelectContent >
{ supplier s. map ( ( supplier ) = > (
< SelectItem key = { supplier . id } value = { supplier . id } >
{ supplier . name }
{ market s. map ( ( market ) = > (
< SelectItem key = { market . value } value = { market . value } >
{ market . label }
< / SelectItem >
) ) }
< / SelectContent >
< / Select >
< / div >
< / div >
{ /* Сумма и удаление */ }
< div className = "col-span-1 flex items-center justify-between" >
< span className = "text-white font-bold text-xs" >
{ formatCurrency ( item . totalPrice ) . replace ( ' ₽' , '₽' ) }
< / span >
< Button
onClick = { ( ) = > removeFrom Supply ( item . card . nmID ) }
size = "sm"
variant = "ghost"
className = "h-4 w-4 p-0 text-white/60 hover:text-red-400"
>
< X className = "h-3 w-3" / >
< / Button >
< div className = "grid grid-cols-2 gap-3" >
< div >
< Label className = "text-white/60 text-xs" > А д р е с < / Label >
< Input
value = { newSupplier . address }
onChange = { ( e ) = >
setNew Supplier ( ( prev ) = > ( {
. . . prev ,
address : e.target.value ,
} ) )
}
className = "bg-white/10 border-white/20 text-white h-8 text-xs"
placeholder = "Адрес"
/ >
< / div >
< div >
< Label className = "text-white/60 text-xs" > М е с т о < / Label >
< Input
value = { newSupplier . place }
onChange = { ( e ) = >
setNewSupplier ( ( prev ) = > ( {
. . . prev ,
place : e.target.value ,
} ) )
}
className = "bg-white/10 border-white/20 text-white h-8 text-xs"
placeholder = "Павильон/место"
/ >
< / div >
< / div >
) ) }
< / div >
) }
< / Card >
{ /* Модальное окно создания поставщика */ }
< Dialog open = { showSupplierModal } onOpenChange = { setShowSupplierModal } >
< DialogContent className = "glass-card border-white/10 max-w-md" >
< DialogHeader >
< DialogTitle className = "text-white" > С о з д а т ь п о с т а в щ и к а < / DialogTitle >
< / DialogHeader >
< div className = "space-y-3" >
< div className = "grid grid-cols-2 gap-3" >
< div >
< Label className = "text-white/60 text-xs" > Н а з в а н и е * < / Label >
< Label className = "text-white/60 text-xs" > Т е л е г р а м < / Label >
< Input
value = { newSupplier . n ame }
onChange = { ( e ) = > setNewSupplier ( prev = > ( { . . . prev , name : e.target.value } ) ) }
value = { newSupplier . telegr am}
onChange = { ( e ) = >
setNewSupplier ( ( prev ) = > ( {
. . . prev ,
telegram : e.target.value ,
} ) )
}
className = "bg-white/10 border-white/20 text-white h-8 text-xs"
placeholder = "Название "
placeholder = "@username "
/ >
< / div >
< div >
< Label className = "text-white/60 text-xs" > И м я * < / Label >
< Input
value = { newSupplier . contactName }
onChange = { ( e ) = > setNewSupplier ( prev = > ( { . . . prev , contactName : e.target.value } ) ) }
className = "bg-white/10 border-white/20 text-white h-8 text-xs"
placeholder = "Имя"
/ >
< / div >
< / div >
< div className = "grid grid-cols-2 gap-3 " >
< div >
< Label className = "text-white/60 text-xs" > Т е л е ф о н * < / Label >
< Input
value = { newSupplier . phone }
onChange = { ( e ) = > setNewSupplier ( prev = > ( { . . . prev , phone : e.target.value } ) ) }
className = "bg-white/10 border-white/20 text-white h-8 text-xs"
placeholder = "+7 999 123-45-67"
/ >
< / div >
< div >
< Label className = "text-white/60 text-xs" > Р ы н о к < / Label >
< Select
value = { newSupplier . market }
onValueChange = { ( value ) = > setNewSupplier ( prev = > ( { . . . prev , market : value } ) ) }
< div className = "flex space-x-2 " >
< Button
onClick = { ( ) = > setShowSupplierModal ( false ) }
variant = "outline"
className = "flex-1 bg-white/5 border-white/20 text-white hover:bg-white/10 h-8 text-xs"
>
< SelectTrigger className = "bg-white/10 border-white/20 text-white h-8 text-xs" >
< SelectValue placeholder = "Рынок" / >
< / SelectTrigger >
< SelectContent >
{ markets . map ( ( market ) = > (
< SelectItem key = { market . value } value = { market . value } >
{ market . label }
< / SelectItem >
) ) }
< / SelectContent >
< / Select >
О т м е н а
< / Button >
< Button
onClick = { handleCreateSupplier }
className = "flex-1 bg-gradient-to-r from-purple-500 to-pink-500 hover:from-purple-600 hover:to-pink-600 h-8 text-xs"
>
С о з д а т ь
< / Button >
< / div >
< / div >
< div className = "grid grid-cols-2 gap-3" >
< div >
< Label className = "text-white/60 text-xs" > А д р е с < / Label >
< Input
value = { newSupplier . address }
onChange = { ( e ) = > setNewSupplier ( prev = > ( { . . . prev , address : e.target.value } ) ) }
className = "bg-white/10 border-white/20 text-white h-8 text-xs"
placeholder = "Адрес"
/ >
< / div >
< div >
< Label className = "text-white/60 text-xs" > М е с т о < / Label >
< Input
value = { newSupplier . place }
onChange = { ( e ) = > setNewSupplier ( prev = > ( { . . . prev , place : e.target.value } ) ) }
className = "bg-white/10 border-white/20 text-white h-8 text-xs"
placeholder = "Павильон/место"
/ >
< / div >
< / div >
< div >
< Label className = "text-white/60 text-xs" > Т е л е г р а м < / Label >
< Input
value = { newSupplier . telegram }
onChange = { ( e ) = > setNewSupplier ( prev = > ( { . . . prev , telegram : e.target.value } ) ) }
className = "bg-white/10 border-white/20 text-white h-8 text-xs"
placeholder = "@username"
/ >
< / div >
< div className = "flex space-x-2" >
< Button
onClick = { ( ) = > setShowSupplierModal ( false ) }
variant = "outline"
className = "flex-1 bg-white/5 border-white/20 text-white hover:bg-white/10 h-8 text-xs"
>
О т м е н а
< / Button >
< Button
onClick = { handleCreateSupplier }
className = "flex-1 bg-gradient-to-r from-purple-500 to-pink-500 hover:from-purple-600 hover:to-pink-600 h-8 text-xs"
>
С о з д а т ь
< / Button >
< / div >
< / div >
< / DialogContent >
< / Dialog >
< / div >
)
}
< / DialogContent >
< / Dialog >
< / div >
< / >
) ;
}