import { Link, router } from '@inertiajs/react'; import { Head } from '@inertiajs/react'; import { Plus } from 'lucide-react'; import { useCallback, useMemo, useState } from 'react'; import { CardTable } from '@/components/data-display'; import { DeleteConfirmDialog } from '@/components/dialogs'; import type { ImagePreviewItem } from '@/components/dialogs'; import { FilterPopover } from '@/components/data-display'; import { useCardTableExpand } from '@/components/hooks/use-card-table-expand'; import { PageHeader } from '@/components/layout'; import { Button } from '@/components/ui/button'; import { Combobox, ComboboxContent, ComboboxEmpty, ComboboxInput, ComboboxItem, ComboboxList, } from '@/components/ui/combobox'; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from '@/components/ui/select'; import { useCan } from '@/hooks/use-can'; import { useServerTable } from '@/hooks/use-server-table'; import { destroy, create as productCreate, edit as productEdit, index as productIndex, toggleStatus, toggleFeatured as productToggleFeatured, approve as productApprove, reject as productReject, resubmit as productResubmit, variants as productVariants, } from '@/routes/admin/master/products'; import { destroy as variantDestroy, edit as variantEdit, } from '@/routes/admin/master/products/variants'; import type { Product, ProductVariant } from './columns'; import { ProductCardRow } from './product-card'; import { RejectDialog } from './reject-dialog'; import { VariantSubRow } from './variant/sub-row'; type Props = { products: { data: Product[]; current_page: number; last_page: number; per_page: number; total: number; }; categories: { id: number; name: string; }[]; productNames: string[]; filters: { status?: string; name?: string; stock?: string; category?: string; featured?: string; }; highlight?: number; }; export default function ProductIndex({ products, categories, productNames, filters, highlight }: Props) { const { can } = useCan(); const [deleting, setDeleting] = useState(null); const [deletingVariant, setDeletingVariant] = useState<{ product: Product; variant: ProductVariant; } | null>(null); const [rejecting, setRejecting] = useState(null); const [loadedVariants, setLoadedVariants] = useState>({}); const [loadingVariants, setLoadingVariants] = useState>({}); const expand = useCardTableExpand(false); const pagination = { current_page: products.current_page, last_page: products.last_page, per_page: products.per_page, total: products.total, }; const { search, filterOpen, setFilterOpen, handlePageChange, handlePerPageChange, handleSearchChange, applyFilter, clearFilters, } = useServerTable({ route: () => productIndex.url(), pagination, filters, filterWithParams: false, }); const sortedProductNames = useMemo( () => [...productNames].sort(), [productNames], ); const fetchVariants = useCallback((product: Product) => { if (loadedVariants[product.id] || loadingVariants[product.id]) { return; } setLoadingVariants((prev) => ({ ...prev, [product.id]: true })); fetch(productVariants.url(product.id)) .then((res) => res.json()) .then((data) => { setLoadedVariants((prev) => ({ ...prev, [product.id]: data.variants ?? [], })); }) .catch(() => { setLoadedVariants((prev) => ({ ...prev, [product.id]: [] })); }) .finally(() => { setLoadingVariants((prev) => ({ ...prev, [product.id]: false })); }); }, [loadedVariants, loadingVariants]); const allPreviewItems: ImagePreviewItem[] = useMemo( () => Object.entries(loadedVariants).flatMap(([productId, variants]) => variants .filter((v) => v.photo_urls?.length > 0) .map((v) => ({ id: `${productId}-${v.id}`, src: v.photo_urls[0], sources: v.photo_urls, title: v.name, description: `Stok Bagus: ${v.formatted_stock} | Stok Reject: ${v.formatted_reject_stock} | Stok Ecer: ${v.formatted_retail_stock}`, })) ), [loadedVariants], ); const selectedCategory = useMemo( () => categories.find((c) => String(c.id) === filters.category) ?? null, [categories, filters.category], ); function handleDelete() { if (!deleting) { return; } router.delete(destroy.url(deleting.id), { onSuccess: () => setDeleting(null), }); } function handleDeleteVariant() { if (!deletingVariant) { return; } router.delete( variantDestroy.url({ product: deletingVariant.product.id, variant: deletingVariant.variant.id, }), { onSuccess: () => { setDeletingVariant(null); setLoadedVariants((prev) => ({ ...prev, [deletingVariant.product.id]: (prev[deletingVariant.product.id] ?? []).filter( (v) => v.id !== deletingVariant.variant.id, ), })); }, }, ); } const filterToolbar = (
applyFilter('name', (value as string) ?? '') } > Tidak ada produk ditemukan. {(name) => ( {name} )}
Berdasarkan stok bagus
cat.name} getOptionAsValue={(cat) => String(cat.id)} value={selectedCategory} onValueChange={(value) => applyFilter('category', value ? String(value.id) : '') } > Tidak ada kategori ditemukan. {(cat) => ( {cat.name} )}
); return ( <>
Menampilkan produk dari notifikasi.

) } actions={ can('products.create') ? ( ) : undefined } /> p.id} expandedKeys={expand.expandedKeys} onToggleExpand={(key) => { const p = products.data.find((r) => r.id === key); const isCurrentlyExpanded = expand.expandedKeys === 'all' || expand.expandedKeys.has(key); if (p && !isCurrentlyExpanded) { fetchVariants(p); } expand.toggleExpand(key); }} searchValue={search} onSearchChange={handleSearchChange} pagination={pagination} onPageChange={handlePageChange} onPerPageChange={handlePerPageChange} toolbar={filterToolbar} renderCard={({ item, index, isExpanded, onToggleExpand, }) => ( { router.visit(productEdit.url(p.id)); }} onDelete={(p) => setDeleting(p)} onReject={(p) => setRejecting(p)} toggleStatusUrl={(id) => toggleStatus.url(id)} toggleFeaturedUrl={(id) => productToggleFeatured.url(id)} approveUrl={(id) => productApprove.url(id)} resubmitUrl={(id) => productResubmit.url(id)} /> )} renderSubContent={(product) => ( { router.visit( variantEdit.url({ product: p.id, variant: v.id, }), ); }} onDeleteVariantClick={(p, v) => setDeletingVariant({ product: p, variant: v }) } /> )} /> { if (!open) { setDeleting(null); } }} title="Hapus Produk" description={(product) => `Apakah Anda yakin ingin menghapus produk "${product.name}"? Tindakan ini tidak dapat dibatalkan.` } onConfirm={handleDelete} /> { if (!open) { setDeletingVariant(null); } }} title="Hapus Varian" description={(target) => `Apakah Anda yakin ingin menghapus varian "${target.variant.name}" dari produk "${target.product.name}"? Tindakan ini tidak dapat dibatalkan.` } onConfirm={handleDeleteVariant} /> { if (!open) { setRejecting(null); } }} rejectUrl={(id) => productReject.url(id)} />
); }