import { Head, Link, useForm, router } from '@inertiajs/react'; import { format } from 'date-fns'; import { Plus, Trash2, CalendarIcon, Minus, ShoppingCart, Search, ImagePlus, X, Loader, Save, ArrowLeft } from 'lucide-react'; import React, { useMemo, useState } from 'react'; import { toast } from 'sonner'; import { Badge } from '@/components/ui/badge'; import { Button } from '@/components/ui/button'; import { Calendar } from '@/components/ui/calendar'; import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogFooter, } from "@/components/ui/dialog"; import { Empty, EmptyDescription, EmptyHeader, EmptyTitle } from '@/components/ui/empty'; import { Field, FieldError } from "@/components/ui/field"; import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover'; import { ScrollArea } from '@/components/ui/scroll-area'; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from "@/components/ui/select"; import { Sheet, SheetContent, SheetTrigger, SheetClose, } from "@/components/ui/sheet"; import { cn } from '@/lib/utils'; import { formatCurrency, formatNumber } from '@/lib/formatters'; import purchaseRoutes from '@/routes/purchase'; import type { Product, ProductPrice } from '@/types'; type CartItem = { product_id: number; quantity: number; unit_price: number; product: Product }; function getPurchasePrice(prices: ProductPrice[] | undefined): number { return prices?.find(p => p.price_type === 'purchase')?.price ?? 0; } function getPurchasePriceLabel(prices: ProductPrice[] | undefined): string { return prices?.find(p => p.price_type === 'purchase')?.price_formatted ?? 'Rp 0'; } export default function PurchaseCreate({ products, cartItems }: { products: Product[], cartItems: (CartItem & { id: number })[] }) { const [isCalendarOpen, setIsCalendarOpen] = useState(false); const [search, setSearch] = useState(''); const [selectedCategory, setSelectedCategory] = useState('all'); const [qtyDialogIndex, setQtyDialogIndex] = useState(null); const [qtyInputValue, setQtyInputValue] = useState(''); const categories = useMemo(() => { const map = new Map(); products.forEach(p => p.categories?.forEach(c => map.set(c.id, c.name))); return Array.from(map.entries()).map(([id, name]) => ({ id, name })); }, [products]); const { data, setData, post, processing, errors, transform } = useForm({ purchase_date: format(new Date(), 'yyyy-MM-dd'), note: '', items: [] as any[], }); const filteredProducts = products.filter(p => { const matchesSearch = p.name.toLowerCase().includes(search.toLowerCase()); const matchesCategory = selectedCategory === 'all' || p.categories?.some(c => String(c.id) === selectedCategory); return matchesSearch && matchesCategory; }); const getCartItem = (productId: number) => cartItems.find(item => item.product_id === productId); const addToCart = (product: Product) => { const unitPrice = getPurchasePrice(product.prices); router.post(purchaseRoutes.addToCart().url, { product_id: product.id, quantity: 1, unit_price: unitPrice }, { preserveScroll: true, }); }; const decreaseQuantity = (product: Product, e: React.MouseEvent) => { e.stopPropagation(); const item = getCartItem(product.id); if (!item) { return; } router.post(purchaseRoutes.addToCart().url, { product_id: product.id, quantity: -1, unit_price: item.unit_price }, { preserveScroll: true, }); }; const increaseQuantity = (product: Product, e: React.MouseEvent) => { e.stopPropagation(); addToCart(product); }; const removeFromCart = (itemId: number) => { router.delete(purchaseRoutes.removeFromCart(itemId).url, { preserveScroll: true, }); }; const updateCartQuantity = (itemId: number, quantity: number) => { if (quantity < 1) { return; } router.patch(purchaseRoutes.updateCartItem(itemId).url, { quantity }, { preserveScroll: true, }); }; const openQtyDialog = (item: CartItem & { id: number }, e: React.MouseEvent) => { e.stopPropagation(); setQtyInputValue(String(item.quantity)); setQtyDialogIndex(item.id); }; const confirmQty = () => { const val = parseInt(qtyInputValue); if (!isNaN(val) && val >= 1 && qtyDialogIndex !== null) { updateCartQuantity(qtyDialogIndex, val); } setQtyDialogIndex(null); }; const onSubmit = (e: React.FormEvent) => { e.preventDefault(); if (cartItems.length === 0) { toast.error('Pilih minimal satu produk'); return; } transform((data) => ({ ...data, items: cartItems.map(item => ({ product_id: item.product_id, quantity: item.quantity, unit_price: item.unit_price })) })); post(purchaseRoutes.store().url, { onSuccess: (response: any) => { toast.success(response.props.flash.success); }, }); }; const total = useMemo(() => { return cartItems.reduce((acc, item) => acc + (item.quantity * item.unit_price), 0); }, [cartItems]); const cartTotalItems = cartItems.reduce((a, i) => a + i.quantity, 0); const CartFormContent = () => (
Keranjang {cartTotalItems} pcs {cartItems.length === 0 ? (

Keranjang masih kosong

Klik produk untuk menambahkan

) : (
{cartItems.map((item) => (
{/* Product info row */}
{item.product.thumbnail_url ? ( {item.product.name} ) : (
)}

{item.product.name}

{formatCurrency(item.quantity * item.unit_price)}

{/* Qty + price text */}
{item.quantity} pcs
× {formatCurrency(item.unit_price)}
))}
)}
{/* Footer */}
{ if (selectedDate) { setData('purchase_date', format(selectedDate, 'yyyy-MM-dd')); } else { setData('purchase_date', ''); } setIsCalendarOpen(false); }} /> setData('note', e.target.value)} placeholder="...." />

Total Belanja

{formatCurrency(total)}

{cartItems.length} produk

{cartTotalItems} pcs

); return (

Tambah Belanja

{/* ── Product Grid (Scrollable) ── */}
{/* Search + Category Filter */}
setSearch(e.target.value)} />
{/* Scrollable Area for Product Cards */}
{filteredProducts.map((product) => { const cartItem = getCartItem(product.id); const displayPrice = getPurchasePriceLabel(product.prices); return ( addToCart(product)} > {/* Full Image */}
{product.thumbnail_url ? ( {product.name} ) : (
)}
{/* Categories + Name */}
{product.categories?.map(cat => ( {cat.name} ))}

{product.name}

{/* Price + Stepper */}
e.stopPropagation()} >
{displayPrice}
); })} {filteredProducts.length === 0 && (
Ooops... Tidak ada data yang ditemukan.
)}
{/* ── Cart Sidebar (Fixed on Desktop) ── */}
Keranjang {cartTotalItems} pcs {cartItems.length === 0 ? (

Keranjang masih kosong

Klik produk untuk menambahkan

) : (
{cartItems.map((item) => (
{item.product.thumbnail_url ? ( {item.product.name} ) : (
)}

{item.product.name}

{formatCurrency(item.quantity * item.unit_price)}

{item.quantity} pcs
× {formatCurrency(item.unit_price)}
))}
)}
{ if (selectedDate) { setData('purchase_date', format(selectedDate, 'yyyy-MM-dd')); } else { setData('purchase_date', ''); } setIsCalendarOpen(false); }} /> setData('note', e.target.value)} autoComplete='off' placeholder="...." />

Total Belanja

{formatCurrency(total)}

{cartItems.length} produk

{cartTotalItems} pcs

{/* ── Floating Mobile Cart Trigger ── */}
{/* Header */}
Keranjang
{cartTotalItems} pcs
{/* Scrollable cart items — min-h-0 wajib agar flex-1 bisa scroll */}
{cartItems.length === 0 ? (

Keranjang masih kosong

Klik produk untuk menambahkan

) : (
{cartItems.map((item) => (
{item.product.thumbnail_url ? ( {item.product.name} ) : (
)}

{item.product.name}

{formatCurrency(item.quantity * item.unit_price)}

{item.quantity} pcs
× {formatCurrency(item.unit_price)}
))}
)}
{/* Footer */}
{ if (selectedDate) { setData('purchase_date', format(selectedDate, 'yyyy-MM-dd')); } else { setData('purchase_date', ''); } }} /> setData('note', e.target.value)} placeholder="...." />

Total Belanja

{formatCurrency(total)}

{cartItems.length} produk

{cartTotalItems} pcs

{/* Quantity Input Dialog */} { if (!open) { setQtyDialogIndex(null); } }}> Ubah Jumlah
setQtyInputValue(e.target.value)} onKeyDown={e => { if (e.key === 'Enter') { confirmQty(); } }} className="text-lg font-bold" autoFocus />
); } PurchaseCreate.layout = { breadcrumbs: [{ title: 'Kelola' }], };