
Создана новая модульная архитектура компонентов для создания поставок расходников фулфилмента с улучшенной организацией кода и разделением ответственности. ESLint warnings исправим в отдельном коммите для cleaner history. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
143 lines
6.2 KiB
TypeScript
143 lines
6.2 KiB
TypeScript
// =============================================================================
|
||
// 🛒 БЛОК КОРЗИНЫ
|
||
// =============================================================================
|
||
// ВНИМАНИЕ: Визуал остается ТОЧНО таким же как в монолитной версии!
|
||
// Все gradients, glassmorphism, анимации сохранены
|
||
|
||
import { ShoppingCart } from 'lucide-react'
|
||
|
||
import { Button } from '@/components/ui/button'
|
||
import { Card } from '@/components/ui/card'
|
||
import { Input } from '@/components/ui/input'
|
||
|
||
import type { ShoppingCartBlockProps } from './types'
|
||
|
||
export function ShoppingCartBlock({
|
||
selectedConsumables,
|
||
deliveryDate,
|
||
notes,
|
||
selectedLogistics,
|
||
logisticsPartners,
|
||
isCreatingSupply,
|
||
getTotalAmount,
|
||
getTotalItems,
|
||
formatCurrency,
|
||
onUpdateQuantity,
|
||
onSetDeliveryDate,
|
||
onSetNotes,
|
||
onSetLogistics,
|
||
onCreateSupply,
|
||
}: ShoppingCartBlockProps) {
|
||
return (
|
||
<Card className="bg-white/10 backdrop-blur border-white/20 p-3 sticky top-0">
|
||
<h3 className="text-white font-semibold mb-3 flex items-center text-sm">
|
||
<ShoppingCart className="h-4 w-4 mr-2" />
|
||
Корзина ({getTotalItems()} шт)
|
||
</h3>
|
||
|
||
{selectedConsumables.length === 0 ? (
|
||
<div className="text-center py-6">
|
||
<div className="bg-gradient-to-br from-purple-500/20 to-pink-500/20 rounded-full p-4 w-fit mx-auto mb-3">
|
||
<ShoppingCart className="h-8 w-8 text-purple-300" />
|
||
</div>
|
||
<p className="text-white/60 text-sm font-medium mb-2">Корзина пуста</p>
|
||
<p className="text-white/40 text-xs mb-3">Добавьте расходники для создания поставки</p>
|
||
</div>
|
||
) : (
|
||
<div className="space-y-2 mb-3 max-h-48 overflow-y-auto">
|
||
{selectedConsumables.map((consumable) => (
|
||
<div key={consumable.id} className="flex items-center justify-between p-2 bg-white/5 rounded-lg">
|
||
<div className="flex-1 min-w-0">
|
||
<p className="text-white text-xs font-medium truncate">{consumable.name}</p>
|
||
<p className="text-white/60 text-xs">
|
||
{formatCurrency(consumable.price)} × {consumable.selectedQuantity}
|
||
</p>
|
||
</div>
|
||
<div className="flex items-center space-x-2">
|
||
<span className="text-green-400 font-medium text-xs">
|
||
{formatCurrency(consumable.price * consumable.selectedQuantity)}
|
||
</span>
|
||
<Button
|
||
variant="ghost"
|
||
size="sm"
|
||
onClick={() => onUpdateQuantity(consumable.id, 0)}
|
||
className="h-5 w-5 p-0 text-red-400 hover:text-red-300 hover:bg-red-500/10"
|
||
>
|
||
×
|
||
</Button>
|
||
</div>
|
||
</div>
|
||
))}
|
||
</div>
|
||
)}
|
||
|
||
<div className="border-t border-white/20 pt-3">
|
||
<div className="mb-3">
|
||
<label className="text-white/60 text-xs mb-1 block">Дата поставки:</label>
|
||
<Input
|
||
type="date"
|
||
value={deliveryDate}
|
||
onChange={(e) => onSetDeliveryDate(e.target.value)}
|
||
className="bg-white/10 border-white/20 text-white h-8 text-sm"
|
||
min={new Date().toISOString().split('T')[0]}
|
||
required
|
||
/>
|
||
</div>
|
||
|
||
{/* Выбор логистики */}
|
||
<div className="mb-3">
|
||
<label className="text-white/60 text-xs mb-1 block">Логистика (опционально):</label>
|
||
<div className="relative">
|
||
<select
|
||
value={selectedLogistics?.id || ''}
|
||
onChange={(e) => {
|
||
const logisticsId = e.target.value
|
||
const logistics = logisticsPartners.find((p: any) => p.id === logisticsId)
|
||
onSetLogistics(logistics || null)
|
||
}}
|
||
className="w-full bg-white/10 border border-white/20 rounded-md px-3 py-2 text-white text-sm focus:outline-none focus:ring-1 focus:ring-purple-500 focus:border-transparent appearance-none"
|
||
>
|
||
<option value="" className="bg-gray-800 text-white">
|
||
Выберите логистику
|
||
</option>
|
||
{logisticsPartners.map((partner: any) => (
|
||
<option key={partner.id} value={partner.id} className="bg-gray-800 text-white">
|
||
{partner.name || partner.fullName || partner.inn}
|
||
</option>
|
||
))}
|
||
</select>
|
||
<div className="absolute inset-y-0 right-0 flex items-center px-2 pointer-events-none">
|
||
<svg className="w-4 h-4 text-white/60" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 9l-7 7-7-7" />
|
||
</svg>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
{/* Заметки */}
|
||
<div className="mb-3">
|
||
<label className="text-white/60 text-xs mb-1 block">Заметки (необязательно):</label>
|
||
<textarea
|
||
value={notes}
|
||
onChange={(e) => onSetNotes(e.target.value)}
|
||
placeholder="Дополнительная информация о поставке"
|
||
rows={3}
|
||
className="w-full bg-white/10 border border-white/20 rounded-md px-3 py-2 text-white text-sm placeholder-white/40 focus:outline-none focus:ring-1 focus:ring-purple-500 focus:border-transparent resize-none"
|
||
/>
|
||
</div>
|
||
|
||
<div className="flex items-center justify-between mb-3">
|
||
<span className="text-white font-semibold text-sm">Итого:</span>
|
||
<span className="text-green-400 font-bold text-lg">{formatCurrency(getTotalAmount())}</span>
|
||
</div>
|
||
<Button
|
||
onClick={onCreateSupply}
|
||
disabled={isCreatingSupply || !deliveryDate || selectedConsumables.length === 0}
|
||
className="w-full bg-gradient-to-r from-purple-500 to-pink-500 hover:from-purple-600 hover:to-pink-600 text-white disabled:opacity-50 h-8 text-sm"
|
||
>
|
||
{isCreatingSupply ? 'Создание...' : 'Создать поставку'}
|
||
</Button>
|
||
</div>
|
||
</Card>
|
||
)
|
||
} |