import { Head, router } from '@inertiajs/react'; import type { Row } from '@tanstack/react-table'; import { Filter, Plus, X } from 'lucide-react'; import { useCallback, useMemo, useState } from 'react'; import { ConfirmDialog } from '@/components/confirm-dialog'; import type { PaginationState, SortState } from '@/components/data-table'; import { DataTable } from '@/components/data-table'; import { ImagePreviewModal } from '@/components/image-preview-modal'; import { Button } from '@/components/ui/button'; import { Combobox, ComboboxContent, ComboboxEmpty, ComboboxInput, ComboboxItem, ComboboxList, } from '@/components/ui/combobox'; import { Popover, PopoverContent, PopoverTrigger, } from '@/components/ui/popover'; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from '@/components/ui/select'; import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow, } from '@/components/ui/table'; import { destroy, create as productCreate, index as productIndex, edit as productEdit, toggleStatus, } from '@/routes/admin/master/products'; import type { Product } from './columns'; import { createProductColumns } from './columns'; type Props = { products: { data: Product[]; current_page: number; last_page: number; per_page: number; total: number; }; filters: { status?: string; name?: string; }; }; function formatCurrency(amount: number): string { return new Intl.NumberFormat('id-ID', { style: 'currency', currency: 'IDR', minimumFractionDigits: 0, }).format(amount); } function formatNumber(num: number): string { return new Intl.NumberFormat('id-ID').format(num); } function VariantPhotoPreview({ url, title }: { url: string; title: string }) { const [open, setOpen] = useState(false); return ( <> ); } function VariantSubRow({ row, searchValue, }: { row: Row; searchValue?: string; }) { const allVariants = row.original.product_variants ?? []; const query = (searchValue ?? '').toLowerCase().trim(); const variants = query ? allVariants.filter((v) => v.name.toLowerCase().includes(query)) : allVariants; return ( Foto Nama Varian Stok Bagus Stok Reject Stok Ecer Harga {variants.length === 0 ? ( Tidak ada varian. ) : ( variants.map((variant) => ( {variant.photo_url ? ( ) : (
N/A
)}
{variant.name} {formatNumber(variant.stock)} {formatNumber(variant.reject_stock)} {formatNumber(variant.retail_stock)} {variant.product_prices?.length > 0 ? (
{variant.product_prices.map((p) => ( {p.type_label}: {' '} {formatCurrency(p.price)} ))}
) : ( '-' )}
)) )}
); } export default function ProductIndex({ products, filters }: Props) { const [deleting, setDeleting] = useState(null); const [filterOpen, setFilterOpen] = useState(false); const [search, setSearch] = useState(''); const [sort, setSort] = useState({ column: 'created_at', direction: 'desc', }); const hasActiveFilters = filters.status || filters.name; const pagination: PaginationState = { current_page: products.current_page, last_page: products.last_page, per_page: products.per_page, total: products.total, }; const productNames = useMemo(() => { const names = products.data.map((p) => p.name); return [...new Set(names)].sort(); }, [products.data]); function applyFilter(key: string, value: string) { const newFilters = { ...filters }; if (value === '' || value === 'all') { delete newFilters[key as keyof typeof newFilters]; } else { newFilters[key as keyof typeof newFilters] = value; } router.get(productIndex(), newFilters, { preserveState: true, replace: true, }); } function clearFilters() { router.get( productIndex(), {}, { preserveState: true, replace: true, }, ); setFilterOpen(false); } function handlePageChange(page: number) { router.get( productIndex.url(), { page, per_page: pagination.per_page, search, sort: sort.column, direction: sort.direction, ...filters, }, { preserveState: true, replace: true }, ); } function handlePerPageChange(perPage: number) { router.get( productIndex.url(), { page: 1, per_page: perPage, search, sort: sort.column, direction: sort.direction, ...filters, }, { preserveState: true, replace: true }, ); } const handleSearchChange = useCallback( (value: string) => { setSearch(value); router.get( productIndex.url(), { page: 1, per_page: pagination.per_page, search: value, sort: sort.column, direction: sort.direction, ...filters, }, { preserveState: true, replace: true }, ); }, [pagination.per_page, sort, filters], ); function handleSortChange(column: string, direction: 'asc' | 'desc') { setSort({ column, direction }); router.get( productIndex.url(), { page: 1, per_page: pagination.per_page, search, sort: column, direction, ...filters, }, { preserveState: true, replace: true }, ); } function handleDelete() { if (!deleting) { return; } router.delete(destroy.url(deleting.id), { onSuccess: () => setDeleting(null), }); } const columns = createProductColumns({ handleEdit: (product) => { window.location.href = productEdit.url(product.id); }, handleDeleteClick: (product) => setDeleting(product), toggleStatusUrl: (id) => toggleStatus.url(id), }); const filterToolbar = (
Filter {hasActiveFilters && ( )}
applyFilter('name', value as string) } > Tidak ada produk ditemukan. {productNames.map((name) => ( {name} ))}
); return ( <>

Produk

( )} defaultExpanded toolbar={filterToolbar} /> { if (!open) { setDeleting(null); } }} title="Hapus Produk" description={`Apakah Anda yakin ingin menghapus produk "${deleting?.name}"? Tindakan ini tidak dapat dibatalkan.`} confirmLabel="Hapus" onConfirm={handleDelete} />
); } ProductIndex.layout = { breadcrumbs: [ { title: 'Master', href: productIndex.url(), }, { title: 'Produk', href: productIndex.url(), }, ], };