'use no memo'; import { Form, Head, Link } from '@inertiajs/react'; import { ArrowLeft, Minus, Plus, ShoppingCart, Trash2 } from 'lucide-react'; import { useCallback, useEffect, useMemo, useRef, useState } from 'react'; import { toast } from 'sonner'; import { ConfirmDialog } from '@/components/dialogs'; import { FileUpload } from '@/components/inputs'; import { ImagePreviewModal } from '@/components/dialogs'; import { InputError } from '@/components/ui'; import { NumberInput } from '@/components/inputs'; import { Button } from '@/components/ui/button'; import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; import { Label } from '@/components/ui/label'; import { RadioGroup, RadioGroupItem } from '@/components/ui/radio-group'; import { Sheet, SheetContent, SheetFooter, SheetHeader, SheetTitle, } from '@/components/ui/sheet'; import { Textarea } from '@/components/ui/textarea'; import { formatNumber } from '@/lib/format'; import { getTemporaryUrl } from '@/lib/upload'; import { formatCurrency } from '@/lib/utils'; import { index as restockIndex, update } from '@/routes/admin/manage/restocks'; import type { RestockCreateData, RestockForEdit } from './columns'; type CartLine = { key: string; photoUrl: string | null; title: string; subtitle: string; price: number; quantity: number; onAdjust: (delta: number) => void; onSet: (value: number) => void; onRemove: () => void; }; type Props = { restock: RestockForEdit; products: RestockCreateData['products']; }; export default function RestockEdit({ restock, products }: Props) { const [stockType, setStockType] = useState<'good' | 'reject'>( restock.stock_type === 'reject' ? 'reject' : 'good', ); const selectedProductId = useMemo(() => { const items = restock.items ?? []; const product = products.find((p) => p.product_variants.some((v) => items.some((i) => i.product_variant_id === v.id), ), ); return product ? String(product.id) : ''; }, [products, restock.items]); const [quantities, setQuantities] = useState>(() => Object.fromEntries( (restock.items ?? []).map((item) => [ item.product_variant_id, item.quantity, ]), ), ); const [notes, setNotes] = useState(restock.notes ?? ''); const [photo, setPhoto] = useState(restock.photo_key); const [photoUrl, setPhotoUrl] = useState(restock.photo_url); const [uploading, setUploading] = useState(false); const [cartOpen, setCartOpen] = useState(false); const [previewKey, setPreviewKey] = useState(null); const [cartRemoveKey, setCartRemoveKey] = useState(null); const quantitiesRef = useRef(quantities); useEffect(() => { quantitiesRef.current = quantities; }, [quantities]); const selectedProduct = useMemo( () => products.find((p) => String(p.id) === selectedProductId) ?? null, [products, selectedProductId], ); const variantById = useMemo( () => new Map( products.flatMap((p) => p.product_variants.map((v) => [v.id, v]), ), ), [products], ); const getUnitPrice = useCallback( (variantId: number) => { const variant = variantById.get(variantId); if (!variant) { return 0; } return stockType === 'reject' ? variant.reject_price : variant.capital_price; }, [variantById, stockType], ); const subtotal = Object.entries(quantities).reduce( (sum, [variantId, quantity]) => { const unitPrice = getUnitPrice(Number(variantId)); return sum + unitPrice * quantity; }, 0, ); const updateQuantity = useCallback((variantId: number, value: number) => { setQuantities((prev) => ({ ...prev, [variantId]: Math.max(0, value), })); }, []); const incrementQuantity = useCallback( (variantId: number, amount: number) => { setQuantities((prev) => ({ ...prev, [variantId]: Math.max(0, (prev[variantId] ?? 0) + amount), })); }, [], ); const cartItems: CartLine[] = (() => { const lines: CartLine[] = []; for (const [variantId, quantity] of Object.entries(quantities)) { if (quantity <= 0) { continue; } const id = Number(variantId); const variant = variantById.get(id); if (variant) { const unitPrice = stockType === 'reject' ? variant.reject_price : variant.capital_price; lines.push({ key: `variant-${id}`, photoUrl: variant.photo_url, title: variant.name, subtitle: `${formatCurrency(unitPrice)} / pcs`, price: unitPrice, quantity, onAdjust: (delta) => incrementQuantity(id, delta), onSet: (value) => updateQuantity(id, value), onRemove: () => updateQuantity(id, 0), }); } } return lines.sort((a, b) => a.title.localeCompare(b.title)); })(); function formatQuantity(value: number): string { return formatNumber(value, { maximumFractionDigits: 4 }); } function getPayload() { return { stock_type: stockType, items: Object.entries(quantitiesRef.current) .map(([variantId, quantity]) => ({ product_variant_id: Number(variantId), quantity: Number(quantity), })) .filter((item) => item.quantity > 0), notes: notes || null, photo_key: photo, }; } return ( <>

Edit Restock

({ ...formData, ...getPayload(), })} onError={() => { toast.error('Ada data yang belum sesuai, silakan periksa kembali input Anda.'); }} > {({ errors, processing }) => (
Item Restock {selectedProduct ? (
{selectedProduct.product_variants.map( (variant) => { const currentStock = stockType === 'good' ? variant.stock : variant.reject_stock; return (
0 ? 'flex items-center justify-between gap-3 rounded-lg border border-primary p-3' : 'flex items-center justify-between gap-3 rounded-lg border p-3' } >
{variant.photo_url ? ( { ) : (
N/A
)}

{ variant.name }

Stok:{' '} {formatQuantity( Number( currentStock, ), )}{' '} pcs ยท{' '} {formatCurrency( stockType === 'reject' ? variant.reject_price : variant.capital_price, )}

updateQuantity( variant.id, val, ) } />
); }, )}
) : (

Tidak ada item.

)}
Ringkasan
setStockType( value as 'good' | 'reject', ) } className="flex flex-wrap gap-4" >
Subtotal {formatCurrency(subtotal)}
Total {formatCurrency( subtotal, )}