Files
sfera/src/components/supplies/direct-supply-creation.tsx

896 lines
34 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"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,
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'
interface SupplyItem {
card: WildberriesCard
quantity: number
pricePerUnit: number
totalPrice: number
supplierId: string
}
interface Organization {
id: string
name?: string
fullName?: string
type: string
}
interface FulfillmentService {
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
}
interface DirectSupplyCreationProps {
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(false)
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[]>([])
// Поставщики
const [suppliers, setSuppliers] = useState<Supplier[]>([])
const [showSupplierModal, setShowSupplierModal] = useState(false)
const [newSupplier, setNewSupplier] = useState({
name: '',
contactName: '',
phone: '',
market: '',
address: '',
place: '',
telegram: ''
})
// Данные для фулфилмента
const [organizationServices, setOrganizationServices] = useState<{[orgId: string]: FulfillmentService[]}>({})
const [organizationSupplies, setOrganizationSupplies] = useState<{[orgId: string]: FulfillmentService[]}>({})
// Загружаем контрагентов-фулфилментов
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('Ошибка при создании поставки')
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 }]
},
{
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 }]
},
{
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 }]
},
{
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 }]
},
{
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 }]
},
{
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 }]
}
]
// Загружаем товары при инициализации
useEffect(() => {
loadCards()
}, [user])
const loadCards = async () => {
setLoading(true)
try {
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
if (apiToken) {
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())
} catch (error) {
console.error('Ошибка загрузки карточек WB:', error)
// При ошибке API показываем моковые данные
setWbCards(getMockCards())
} finally {
setLoading(false)
}
}
const searchCards = async () => {
if (!searchTerm.trim()) {
loadCards()
return
}
setLoading(true)
try {
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
if (apiToken) {
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)
} 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.nmID.toString().includes(searchTerm.toLowerCase()) ||
card.object?.toLowerCase().includes(searchTerm.toLowerCase())
)
setWbCards(filteredCards)
console.log('Найдено моковых товаров (fallback):', filteredCards.length)
} finally {
setLoading(false)
}
}
// Функции для работы с услугами и расходниками
const loadOrganizationServices = async (organizationId: string) => {
if (organizationServices[organizationId]) return
try {
const response = await apolloClient.query({
query: GET_COUNTERPARTY_SERVICES,
variables: { organizationId }
})
if (response.data?.counterpartyServices) {
setOrganizationServices(prev => ({
...prev,
[organizationId]: response.data.counterpartyServices
}))
}
} catch (error) {
console.error('Ошибка загрузки услуг организации:', error)
}
}
const loadOrganizationSupplies = async (organizationId: string) => {
if (organizationSupplies[organizationId]) return
try {
const response = await apolloClient.query({
query: GET_COUNTERPARTY_SUPPLIES,
variables: { organizationId }
})
if (response.data?.counterpartySupplies) {
setOrganizationSupplies(prev => ({
...prev,
[organizationId]: response.data.counterpartySupplies
}))
}
} catch (error) {
console.error('Ошибка загрузки расходников организации:', error)
}
}
// Работа с товарами поставки
const addToSupply = (card: WildberriesCard) => {
const existingItem = supplyItems.find(item => item.card.nmID === card.nmID)
if (existingItem) {
toast.info('Товар уже добавлен в поставку')
return
}
const newItem: SupplyItem = {
card,
quantity: 1200,
pricePerUnit: 0,
totalPrice: 0,
supplierId: ''
}
setSupplyItems(prev => [...prev, newItem])
toast.success('Товар добавлен в поставку')
}
const removeFromSupply = (nmID: number) => {
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') {
updatedItem.totalPrice = updatedItem.quantity * updatedItem.pricePerUnit
}
return updatedItem
}
return item
}))
}
// Работа с поставщиками
const handleCreateSupplier = () => {
if (!newSupplier.name || !newSupplier.contactName || !newSupplier.phone) {
toast.error('Заполните обязательные поля')
return
}
const supplier: Supplier = {
id: Date.now().toString(),
...newSupplier
}
setSuppliers(prev => [...prev, supplier])
setNewSupplier({
name: '',
contactName: '',
phone: '',
market: '',
address: '',
place: '',
telegram: ''
})
setShowSupplierModal(false)
toast.success('Поставщик создан')
}
// Расчеты
const getTotalQuantity = () => {
return supplyItems.reduce((sum, item) => sum + item.quantity, 0)
}
const getTotalItemsCost = () => {
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()
}
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()
}
const formatCurrency = (amount: number) => {
return new Intl.NumberFormat('ru-RU', {
style: 'currency',
currency: 'RUB',
minimumFractionDigits: 0
}).format(amount)
}
// Создание поставки
const handleCreateSupplyInternal = async () => {
if (supplyItems.length === 0) {
toast.error('Добавьте товары в поставку')
return
}
if (!deliveryDate) {
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 => ({
nmId: item.card.nmID.toString(),
vendorCode: item.card.vendorCode,
title: item.card.title,
brand: item.card.brand,
selectedQuantity: item.quantity,
customPrice: item.totalPrice,
selectedFulfillmentOrg: selectedFulfillmentOrg,
selectedFulfillmentServices: selectedServices,
selectedConsumableOrg: selectedFulfillmentOrg,
selectedConsumableServices: selectedConsumables,
deliveryDate: deliveryDate.toISOString().split('T')[0],
mediaFiles: item.card.mediaFiles
}))
}
await createSupply({ variables: { input: supplyInput } })
toast.success('Поставка успешно создана!')
onComplete()
} catch (error) {
console.error('Error creating supply:', error)
toast.error('Ошибка при создании поставки')
}
}
// Обработка внешнего вызова создания поставки
React.useEffect(() => {
if (isCreatingSupply) {
handleCreateSupplyInternal()
}
}, [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 fulfillmentOrgs = (counterpartiesData?.myCounterparties || []).filter((org: Organization) => org.type === 'FULFILLMENT')
const markets = [
{ 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">
<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-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/60 text-xs">Добавьте товары из карточек выше</p>
</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>
</div>
{/* Количество */}
<div className="col-span-2">
<Input
type="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)}
>
<SelectTrigger className="bg-white/5 border-white/20 text-white h-6 text-xs">
<SelectValue placeholder="Поставщик" />
</SelectTrigger>
<SelectContent>
{suppliers.map((supplier) => (
<SelectItem key={supplier.id} value={supplier.id}>
{supplier.name}
</SelectItem>
))}
</SelectContent>
</Select>
</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={() => removeFromSupply(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>
</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>
<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>
{markets.map((market) => (
<SelectItem key={market.value} value={market.value}>
{market.label}
</SelectItem>
))}
</SelectContent>
</Select>
</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>
)
}