Files
sfera-new/src/components/fulfillment-supplies/create-fulfillment-consumables-supply-v2-modular/blocks/ShoppingCartBlock.tsx
Veronika Smirnova 57f8f762c9 feat(components): добавить модульную V2 систему создания поставок расходников
Создана новая модульная архитектура компонентов для создания поставок
расходников фулфилмента с улучшенной организацией кода и разделением
ответственности.

ESLint warnings исправим в отдельном коммите для cleaner history.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-25 22:39:14 +03:00

143 lines
6.2 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.

// =============================================================================
// 🛒 БЛОК КОРЗИНЫ
// =============================================================================
// ВНИМАНИЕ: Визуал остается ТОЧНО таким же как в монолитной версии!
// Все 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>
)
}