Изменен механизм выбора вкладок в компоненте CreateSupplyPage: заменено состояние selectedVariant на activeTab для улучшения читаемости кода. Обновлены условия отображения корзины и логика обработки товаров. Оптимизирован рендеринг карточек и оптовиков. В компоненте WBProductCards изменен заголовок на "Нет товаров" при отсутствии выбранных карточек.

This commit is contained in:
Bivekich
2025-07-23 14:19:59 +03:00
parent ac3578aa7f
commit ff873482a5
2 changed files with 350 additions and 432 deletions

View File

@ -255,7 +255,7 @@ const mockProducts: WholesalerProduct[] = [
export function CreateSupplyPage() { export function CreateSupplyPage() {
const router = useRouter() const router = useRouter()
const { getSidebarMargin } = useSidebar() const { getSidebarMargin } = useSidebar()
const [selectedVariant, setSelectedVariant] = useState<'cards' | 'wholesaler' | null>(null) const [activeTab, setActiveTab] = useState<'cards' | 'wholesaler'>('cards')
const [selectedWholesaler, setSelectedWholesaler] = useState<WholesalerForCreation | null>(null) const [selectedWholesaler, setSelectedWholesaler] = useState<WholesalerForCreation | null>(null)
const [selectedProducts, setSelectedProducts] = useState<SelectedProduct[]>([]) const [selectedProducts, setSelectedProducts] = useState<SelectedProduct[]>([])
const [selectedCards, setSelectedCards] = useState<SelectedCard[]>([]) const [selectedCards, setSelectedCards] = useState<SelectedCard[]>([])
@ -290,10 +290,10 @@ export function CreateSupplyPage() {
// Автоматически показываем корзину если в ней есть товары и мы на этапе выбора оптовиков // Автоматически показываем корзину если в ней есть товары и мы на этапе выбора оптовиков
useEffect(() => { useEffect(() => {
if (selectedVariant === 'wholesaler' && !selectedWholesaler && selectedProducts.length > 0) { if (activeTab === 'wholesaler' && !selectedWholesaler && selectedProducts.length > 0) {
setShowSummary(true) setShowSummary(true)
} }
}, [selectedVariant, selectedWholesaler, selectedProducts.length]) }, [activeTab, selectedWholesaler, selectedProducts.length])
const formatCurrency = (amount: number) => { const formatCurrency = (amount: number) => {
return new Intl.NumberFormat('ru-RU', { return new Intl.NumberFormat('ru-RU', {
@ -367,7 +367,7 @@ export function CreateSupplyPage() {
} }
const handleCreateSupply = () => { const handleCreateSupply = () => {
if (selectedVariant === 'cards') { if (activeTab === 'cards') {
console.log('Создание поставки с карточками Wildberries') console.log('Создание поставки с карточками Wildberries')
// TODO: Здесь будет создание поставки с данными карточек // TODO: Здесь будет создание поставки с данными карточек
} else { } else {
@ -382,17 +382,13 @@ export function CreateSupplyPage() {
setSelectedWholesaler(null) setSelectedWholesaler(null)
// НЕ очищаем корзину! setSelectedProducts([]) // НЕ очищаем корзину! setSelectedProducts([])
setShowSummary(false) setShowSummary(false)
} else if (selectedVariant) {
setSelectedVariant(null)
setSelectedProducts([]) // Очищаем корзину только при полном выходе
setShowSummary(false)
} else { } else {
router.push('/supplies') router.push('/supplies')
} }
} }
// Рендер товаров оптовика // Рендер товаров оптовика
if (selectedWholesaler && selectedVariant === 'wholesaler') { if (selectedWholesaler && activeTab === 'wholesaler') {
return ( return (
<div className="h-screen flex overflow-hidden"> <div className="h-screen flex overflow-hidden">
<Sidebar /> <Sidebar />
@ -743,69 +739,93 @@ export function CreateSupplyPage() {
) )
} }
// Рендер карточек Wildberries
if (selectedVariant === 'cards') {
return (
<WBProductCards
onBack={() => setSelectedVariant(null)}
onComplete={handleCardsComplete}
/>
)
}
// Рендер выбора оптовиков
if (selectedVariant === 'wholesaler') { // Главная страница с табами
return ( return (
<div className="h-screen flex overflow-hidden"> <div className="h-screen flex overflow-hidden">
<Sidebar /> <Sidebar />
<main className={`flex-1 ${getSidebarMargin()} px-6 py-4 overflow-hidden transition-all duration-300`}> <main className={`flex-1 ${getSidebarMargin()} px-4 py-3 overflow-hidden transition-all duration-300`}>
<div className="p-8"> <div className="p-4">
<div className="flex items-center justify-between mb-8"> {/* Верхняя строка: Назад + Заголовок + Табы */}
<div className="flex items-center justify-between mb-4">
<div className="flex items-center space-x-4"> <div className="flex items-center space-x-4">
<Button <Button
variant="ghost" variant="ghost"
size="sm" size="sm"
onClick={handleGoBack} onClick={() => router.push('/supplies')}
className="text-white/60 hover:text-white hover:bg-white/10" className="text-white/60 hover:text-white hover:bg-white/10"
> >
<ArrowLeft className="h-4 w-4 mr-2" /> <ArrowLeft className="h-4 w-4 mr-2" />
Назад Назад
</Button> </Button>
<h1 className="text-2xl font-bold text-white">Создание поставки</h1>
</div>
<div> <div>
<h1 className="text-3xl font-bold text-white mb-2">Выбор оптовика</h1> <div className="grid grid-cols-2 bg-white/10 backdrop-blur border border-white/20 rounded-lg p-1">
<p className="text-white/60"> <button
{selectedProducts.length > 0 onClick={() => setActiveTab('cards')}
? `Выберите еще оптовика или перейдите к оформлению (${selectedProducts.length} товаров в корзине)` className={`px-4 py-2 text-sm rounded transition-all ${
: "Выберите оптовика для создания поставки" activeTab === 'cards'
} ? 'bg-white/20 text-white'
</p> : 'text-white/60 hover:text-white hover:bg-white/10'
</div> }`}
</div>
{selectedProducts.length > 0 && (
<Button
variant="ghost"
size="sm"
onClick={() => setShowSummary(!showSummary)}
className="text-white/60 hover:text-white hover:bg-white/10"
> >
<ShoppingCart className="h-4 w-4 mr-2" /> <ShoppingCart className="h-4 w-4 mr-1 inline" />
Корзина ({selectedProducts.length}) Карточки
</Button> </button>
<button
onClick={() => setActiveTab('wholesaler')}
className={`px-4 py-2 text-sm rounded transition-all ${
activeTab === 'wholesaler'
? 'bg-white/20 text-white'
: 'text-white/60 hover:text-white hover:bg-white/10'
}`}
>
<Users className="h-4 w-4 mr-1 inline" />
Оптовики
</button>
</div>
</div>
</div>
{/* Контент карточек */}
{activeTab === 'cards' && (
<WBProductCards
onBack={() => router.push('/supplies')}
onComplete={handleCardsComplete}
/>
)} )}
{/* Контент оптовиков */}
{activeTab === 'wholesaler' && (
<div>
{/* Поиск */}
<div className="mb-4">
<div className="relative max-w-md">
<Search className="absolute left-3 top-3 h-4 w-4 text-white/40" />
<Input
placeholder="Поиск оптовиков..."
value={searchQuery}
onChange={(e) => setSearchQuery(e.target.value)}
className="pl-10 glass-input text-white placeholder:text-white/40 h-10"
/>
</div>
</div> </div>
{/* Корзина */} {/* Корзина */}
{showSummary && selectedProducts.length > 0 && ( {showSummary && selectedProducts.length > 0 && (
<Card className="bg-gradient-to-br from-purple-500/10 to-pink-500/10 backdrop-blur-xl border border-purple-500/20 mb-8 shadow-2xl"> <Card className="bg-gradient-to-br from-purple-500/10 to-pink-500/10 backdrop-blur-xl border border-purple-500/20 mb-6 shadow-2xl">
<div className="p-6"> <div className="p-4">
<div className="flex items-center justify-between mb-6"> <div className="flex items-center justify-between mb-4">
<div className="flex items-center space-x-3"> <div className="flex items-center space-x-3">
<div className="p-3 bg-gradient-to-r from-purple-500 to-pink-500 rounded-xl"> <div className="p-2 bg-gradient-to-r from-purple-500 to-pink-500 rounded-lg">
<ShoppingCart className="h-6 w-6 text-white" /> <ShoppingCart className="h-5 w-5 text-white" />
</div> </div>
<div> <div>
<h3 className="text-white font-bold text-xl">Корзина</h3> <h3 className="text-white font-bold text-lg">Корзина</h3>
<p className="text-purple-200 text-sm">{selectedProducts.length} товаров от {Object.keys(selectedProducts.reduce((acc, p) => ({ ...acc, [p.wholesalerId]: true }), {})).length} поставщиков</p> <p className="text-purple-200 text-xs">{selectedProducts.length} товаров от {Object.keys(selectedProducts.reduce((acc, p) => ({ ...acc, [p.wholesalerId]: true }), {})).length} поставщиков</p>
</div> </div>
</div> </div>
<Button <Button
@ -831,8 +851,8 @@ export function CreateSupplyPage() {
return acc return acc
}, {} as Record<string, { wholesaler: string; products: SelectedProduct[] }>) }, {} as Record<string, { wholesaler: string; products: SelectedProduct[] }>)
).map(([wholesalerId, group]) => ( ).map(([wholesalerId, group]) => (
<div key={wholesalerId} className="mb-6 last:mb-0"> <div key={wholesalerId} className="mb-4 last:mb-0">
<div className="flex items-center mb-3 pb-2 border-b border-white/10"> <div className="flex items-center mb-2 pb-1 border-b border-white/10">
<Building2 className="h-4 w-4 text-blue-400 mr-2" /> <Building2 className="h-4 w-4 text-blue-400 mr-2" />
<span className="text-white font-medium">{group.wholesaler}</span> <span className="text-white font-medium">{group.wholesaler}</span>
<Badge className="ml-2 bg-blue-500/20 text-blue-300 border-blue-500/30 text-xs"> <Badge className="ml-2 bg-blue-500/20 text-blue-300 border-blue-500/30 text-xs">
@ -840,7 +860,7 @@ export function CreateSupplyPage() {
</Badge> </Badge>
</div> </div>
<div className="space-y-3"> <div className="space-y-2">
{group.products.map((product) => { {group.products.map((product) => {
const discountedPrice = product.discount const discountedPrice = product.discount
? product.price * (1 - product.discount / 100) ? product.price * (1 - product.discount / 100)
@ -848,16 +868,16 @@ export function CreateSupplyPage() {
const totalPrice = discountedPrice * product.selectedQuantity const totalPrice = discountedPrice * product.selectedQuantity
return ( return (
<div key={`${product.wholesalerId}-${product.id}`} className="flex items-center space-x-4 bg-white/5 rounded-lg p-4"> <div key={`${product.wholesalerId}-${product.id}`} className="flex items-center space-x-3 bg-white/5 rounded-lg p-3">
<img <img
src={product.mainImage || '/api/placeholder/60/60'} src={product.mainImage || '/api/placeholder/50/50'}
alt={product.name} alt={product.name}
className="w-16 h-16 rounded-lg object-cover" className="w-12 h-12 rounded-lg object-cover"
/> />
<div className="flex-1 min-w-0"> <div className="flex-1 min-w-0">
<h4 className="text-white font-medium text-sm mb-1 truncate">{product.name}</h4> <h4 className="text-white font-medium text-xs mb-1 truncate">{product.name}</h4>
<p className="text-white/60 text-xs mb-2">{product.article}</p> <p className="text-white/60 text-xs mb-1">{product.article}</p>
<div className="flex items-center space-x-3"> <div className="flex items-center space-x-2">
<div className="flex items-center space-x-1"> <div className="flex items-center space-x-1">
<Button <Button
variant="ghost" variant="ghost"
@ -878,11 +898,11 @@ export function CreateSupplyPage() {
) )
} }
}} }}
className="h-7 w-7 p-0 text-white/60 hover:text-white hover:bg-white/10" className="h-6 w-6 p-0 text-white/60 hover:text-white hover:bg-white/10"
> >
<Minus className="h-3 w-3" /> <Minus className="h-3 w-3" />
</Button> </Button>
<span className="text-white text-sm w-8 text-center">{product.selectedQuantity}</span> <span className="text-white text-xs w-6 text-center">{product.selectedQuantity}</span>
<Button <Button
variant="ghost" variant="ghost"
size="sm" size="sm"
@ -896,13 +916,13 @@ export function CreateSupplyPage() {
) )
}} }}
disabled={product.selectedQuantity >= product.quantity} disabled={product.selectedQuantity >= product.quantity}
className="h-7 w-7 p-0 text-white/60 hover:text-white hover:bg-white/10" className="h-6 w-6 p-0 text-white/60 hover:text-white hover:bg-white/10"
> >
<Plus className="h-3 w-3" /> <Plus className="h-3 w-3" />
</Button> </Button>
</div> </div>
<div className="text-right"> <div className="text-right">
<div className="text-white font-semibold text-sm">{formatCurrency(totalPrice)}</div> <div className="text-white font-semibold text-xs">{formatCurrency(totalPrice)}</div>
{product.discount && ( {product.discount && (
<div className="text-white/40 text-xs line-through"> <div className="text-white/40 text-xs line-through">
{formatCurrency(product.price * product.selectedQuantity)} {formatCurrency(product.price * product.selectedQuantity)}
@ -931,16 +951,16 @@ export function CreateSupplyPage() {
))} ))}
{/* Итого */} {/* Итого */}
<div className="border-t border-white/20 pt-4 mt-6"> <div className="border-t border-white/20 pt-3 mt-4">
<div className="flex justify-between items-center"> <div className="flex justify-between items-center">
<span className="text-white font-semibold text-lg"> <span className="text-white font-semibold text-sm">
Итого: {getTotalItems()} товаров Итого: {getTotalItems()} товаров
</span> </span>
<span className="text-white font-bold text-2xl"> <span className="text-white font-bold text-lg">
{formatCurrency(getTotalAmount())} {formatCurrency(getTotalAmount())}
</span> </span>
</div> </div>
<div className="flex space-x-3 mt-4"> <div className="flex space-x-2 mt-3">
<Button <Button
variant="outline" variant="outline"
className="flex-1 border-purple-300/30 text-white hover:bg-white/10" className="flex-1 border-purple-300/30 text-white hover:bg-white/10"
@ -962,19 +982,6 @@ export function CreateSupplyPage() {
</Card> </Card>
)} )}
{/* Поиск */}
<div className="mb-6">
<div className="relative">
<Search className="absolute left-3 top-3 h-4 w-4 text-white/40" />
<Input
placeholder="Поиск оптовиков по названию или ИНН..."
value={searchQuery}
onChange={(e) => setSearchQuery(e.target.value)}
className="pl-10 glass-input text-white placeholder:text-white/40"
/>
</div>
</div>
{counterpartiesLoading ? ( {counterpartiesLoading ? (
<div className="flex items-center justify-center p-8"> <div className="flex items-center justify-center p-8">
<div className="text-center"> <div className="text-center">
@ -993,7 +1000,7 @@ export function CreateSupplyPage() {
</p> </p>
</div> </div>
) : ( ) : (
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6"> <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-4">
{filteredWholesalers.map((wholesaler: { {filteredWholesalers.map((wholesaler: {
id: string; id: string;
name?: string; name?: string;
@ -1005,7 +1012,7 @@ export function CreateSupplyPage() {
}) => ( }) => (
<Card <Card
key={wholesaler.id} key={wholesaler.id}
className="bg-white/10 backdrop-blur border-white/20 p-6 cursor-pointer transition-all hover:bg-white/15 hover:border-white/30 hover:scale-105" className="bg-white/10 backdrop-blur border-white/20 p-4 cursor-pointer transition-all hover:bg-white/15 hover:border-white/30 hover:scale-[1.02]"
onClick={() => { onClick={() => {
// Адаптируем данные под существующий интерфейс // Адаптируем данные под существующий интерфейс
const adaptedWholesaler = { const adaptedWholesaler = {
@ -1023,16 +1030,16 @@ export function CreateSupplyPage() {
setSelectedWholesaler(adaptedWholesaler) setSelectedWholesaler(adaptedWholesaler)
}} }}
> >
<div className="space-y-4"> <div className="space-y-3">
<div className="flex items-start space-x-3"> <div className="flex items-start space-x-2">
<div className="p-3 bg-blue-500/20 rounded-lg"> <div className="p-2 bg-blue-500/20 rounded-lg">
<Building2 className="h-6 w-6 text-blue-400" /> <Building2 className="h-4 w-4 text-blue-400" />
</div> </div>
<div className="flex-1 min-w-0"> <div className="flex-1 min-w-0">
<h3 className="text-white font-semibold text-lg mb-1 truncate"> <h3 className="text-white font-semibold text-sm mb-1 truncate">
{wholesaler.name || 'Неизвестная организация'} {wholesaler.name || 'Неизвестная организация'}
</h3> </h3>
<p className="text-white/60 text-xs mb-2 truncate"> <p className="text-white/60 text-xs mb-1 truncate">
{wholesaler.fullName || wholesaler.name} {wholesaler.fullName || wholesaler.name}
</p> </p>
{wholesaler.inn && ( {wholesaler.inn && (
@ -1043,31 +1050,31 @@ export function CreateSupplyPage() {
</div> </div>
</div> </div>
<div className="space-y-2"> <div className="space-y-1">
{wholesaler.address && ( {wholesaler.address && (
<div className="flex items-center space-x-2"> <div className="flex items-center space-x-1">
<MapPin className="h-4 w-4 text-gray-400" /> <MapPin className="h-3 w-3 text-gray-400" />
<span className="text-white/80 text-sm truncate">{wholesaler.address}</span> <span className="text-white/80 text-xs truncate">{wholesaler.address}</span>
</div> </div>
)} )}
{wholesaler.phones?.[0]?.value && ( {wholesaler.phones?.[0]?.value && (
<div className="flex items-center space-x-2"> <div className="flex items-center space-x-1">
<Phone className="h-4 w-4 text-gray-400" /> <Phone className="h-3 w-3 text-gray-400" />
<span className="text-white/80 text-sm">{wholesaler.phones[0].value}</span> <span className="text-white/80 text-xs">{wholesaler.phones[0].value}</span>
</div> </div>
)} )}
{wholesaler.emails?.[0]?.value && ( {wholesaler.emails?.[0]?.value && (
<div className="flex items-center space-x-2"> <div className="flex items-center space-x-1">
<Mail className="h-4 w-4 text-gray-400" /> <Mail className="h-3 w-3 text-gray-400" />
<span className="text-white/80 text-sm truncate">{wholesaler.emails[0].value}</span> <span className="text-white/80 text-xs truncate">{wholesaler.emails[0].value}</span>
</div> </div>
)} )}
</div> </div>
<div className="mt-4"> <div className="mt-2">
<Badge className="bg-green-500/20 text-green-300 border-green-500/30"> <Badge className="bg-green-500/20 text-green-300 border-green-500/30 text-xs px-2 py-1">
Контрагент Контрагент
</Badge> </Badge>
</div> </div>
@ -1095,76 +1102,7 @@ export function CreateSupplyPage() {
</div> </div>
)} )}
</div> </div>
</main> )}
</div>
)
}
// Главная страница выбора варианта
return (
<div className="h-screen flex overflow-hidden">
<Sidebar />
<main className={`flex-1 ${getSidebarMargin()} px-6 py-4 overflow-hidden transition-all duration-300`}>
<div className="p-8">
<div className="flex items-center justify-between mb-8">
<div className="flex items-center space-x-4">
<Button
variant="ghost"
size="sm"
onClick={() => router.push('/supplies')}
className="text-white/60 hover:text-white hover:bg-white/10"
>
<ArrowLeft className="h-4 w-4 mr-2" />
Назад к поставкам
</Button>
<div>
<h1 className="text-3xl font-bold text-white mb-2">Создание поставки</h1>
<p className="text-white/60">Выберите способ создания поставки</p>
</div>
</div>
</div>
<div className="grid grid-cols-1 md:grid-cols-2 gap-8">
<Card
className="bg-white/10 backdrop-blur border-white/20 p-8 cursor-pointer transition-all hover:bg-white/15 hover:border-white/30 hover:scale-105"
onClick={() => setSelectedVariant('cards')}
>
<div className="text-center space-y-6">
<div className="p-6 bg-blue-500/20 rounded-2xl w-fit mx-auto">
<ShoppingCart className="h-12 w-12 text-blue-400" />
</div>
<div>
<h3 className="text-2xl font-semibold text-white mb-3">Карточки</h3>
<p className="text-white/60">
Создание поставки через выбор товаров по карточкам Wildberries
</p>
</div>
<Badge className="bg-green-500/20 text-green-300 border-green-500/30 text-lg px-4 py-2">
Доступно
</Badge>
</div>
</Card>
<Card
className="bg-white/10 backdrop-blur border-white/20 p-8 cursor-pointer transition-all hover:bg-white/15 hover:border-white/30 hover:scale-105"
onClick={() => setSelectedVariant('wholesaler')}
>
<div className="text-center space-y-6">
<div className="p-6 bg-green-500/20 rounded-2xl w-fit mx-auto">
<Users className="h-12 w-12 text-green-400" />
</div>
<div>
<h3 className="text-2xl font-semibold text-white mb-3">Оптовик</h3>
<p className="text-white/60">
Создание поставки через выбор товаров у оптовиков
</p>
</div>
<Badge className="bg-green-500/20 text-green-300 border-green-500/30 text-lg px-4 py-2">
Доступно
</Badge>
</div>
</Card>
</div>
</div> </div>
</main> </main>
</div> </div>

View File

@ -997,26 +997,8 @@ export function WBProductCards({ onBack, onComplete }: WBProductCardsProps) {
} }
return ( return (
<div className="h-screen flex overflow-hidden"> <div className="space-y-4">
<Sidebar /> <div className="flex items-center justify-end">
<main className={`flex-1 ${getSidebarMargin()} overflow-auto transition-all duration-300`}>
<div className="p-6 space-y-6">
<div className="flex items-center justify-between">
<div className="flex items-center space-x-3">
<Button
variant="ghost"
size="sm"
onClick={onBack}
className="text-white/60 hover:text-white hover:bg-white/10"
>
<ArrowLeft className="h-4 w-4 mr-2" />
Назад
</Button>
<div>
<h2 className="text-2xl font-bold text-white mb-1">Карточки товаров Wildberries</h2>
<p className="text-white/60">Найдите и выберите товары для поставки</p>
</div>
</div>
{selectedCards.length > 0 && ( {selectedCards.length > 0 && (
<Button <Button
@ -1324,7 +1306,7 @@ export function WBProductCards({ onBack, onComplete }: WBProductCardsProps) {
<Card className="bg-white/10 backdrop-blur border-white/20 p-8"> <Card className="bg-white/10 backdrop-blur border-white/20 p-8">
<div className="text-center max-w-md mx-auto"> <div className="text-center max-w-md mx-auto">
<Package className="h-12 w-12 text-white/20 mx-auto mb-4" /> <Package className="h-12 w-12 text-white/20 mx-auto mb-4" />
<h3 className="text-lg font-semibold text-white mb-2">Карточки товаров Wildberries</h3> <h3 className="text-lg font-semibold text-white mb-2">Нет товаров</h3>
{user?.organization?.apiKeys?.find(key => key.marketplace === 'WILDBERRIES')?.isActive ? ( {user?.organization?.apiKeys?.find(key => key.marketplace === 'WILDBERRIES')?.isActive ? (
<> <>
<p className="text-white/60 mb-4 text-sm"> <p className="text-white/60 mb-4 text-sm">
@ -1447,7 +1429,5 @@ export function WBProductCards({ onBack, onComplete }: WBProductCardsProps) {
</DialogContent> </DialogContent>
</Dialog> </Dialog>
</div> </div>
</main>
</div>
) )
} }