dstpabuaran.com/resources/js/pages/admin/manage/stock-mutation/index.tsx

241 lines
7.9 KiB
TypeScript

import { Head } from '@inertiajs/react';
import { format } from 'date-fns';
import { useMemo } from 'react';
import { DataTable, FilterPopover } from '@/components/data-display';
import { DatePicker } from '@/components/inputs';
import { PageHeader } from '@/components/layout';
import {
Combobox,
ComboboxContent,
ComboboxEmpty,
ComboboxInput,
ComboboxItem,
ComboboxList,
} from '@/components/ui/combobox';
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from '@/components/ui/select';
import { useServerTable } from '@/hooks/use-server-table';
import { index as stockMutationsIndex } from '@/routes/admin/manage/stock-mutations';
import { columns } from './columns';
import type { StockMutation } from './columns';
type FilterOption = {
id: number;
name: string;
};
type SelectOption = {
value: string;
label: string;
};
type Props = {
mutations: {
data: StockMutation[];
current_page: number;
last_page: number;
per_page: number;
total: number;
};
filters: {
product_id?: string;
type?: string;
stock_quality?: string;
date_from?: string;
date_to?: string;
};
filterOptions: {
products: FilterOption[];
typeOptions: SelectOption[];
stockQualityOptions: SelectOption[];
};
};
export default function StockMutationIndex({
mutations,
filters,
filterOptions,
}: Props) {
const pagination = {
current_page: mutations.current_page,
last_page: mutations.last_page,
per_page: mutations.per_page,
total: mutations.total,
};
const {
search,
filterOpen,
setFilterOpen,
handlePageChange,
handlePerPageChange,
handleSearchChange,
applyFilter,
clearFilters,
} = useServerTable({
route: () => stockMutationsIndex.url(),
pagination,
filters,
});
const selectedProduct = useMemo(
() =>
filterOptions.products.find(
(p) => String(p.id) === filters.product_id,
) ?? null,
[filterOptions.products, filters.product_id],
);
const filterToolbar = (
<FilterPopover
open={filterOpen}
onOpenChange={setFilterOpen}
filters={filters}
hasActiveFilters={
Boolean(filters.product_id) ||
Boolean(filters.type) ||
Boolean(filters.stock_quality) ||
Boolean(filters.date_from) ||
Boolean(filters.date_to)
}
onClear={clearFilters}
>
<div className="flex flex-col gap-2">
<label className="text-xs text-muted-foreground">Produk</label>
<Combobox
items={filterOptions.products}
itemToStringLabel={(p) => p.name}
value={selectedProduct}
onValueChange={(value) =>
applyFilter('product_id', value ? String(value.id) : '')
}
>
<ComboboxInput
placeholder="Pilih produk..."
className="w-full"
/>
<ComboboxContent>
<ComboboxEmpty>
Tidak ada produk ditemukan.
</ComboboxEmpty>
<ComboboxList>
{(product) => (
<ComboboxItem value={product}>
{product.name}
</ComboboxItem>
)}
</ComboboxList>
</ComboboxContent>
</Combobox>
</div>
<div className="flex flex-col gap-2">
<label className="text-xs text-muted-foreground">Tipe</label>
<Select
value={filters.type ?? 'all'}
onValueChange={(value) => applyFilter('type', value)}
>
<SelectTrigger className="w-full">
<SelectValue placeholder="Semua Tipe" />
</SelectTrigger>
<SelectContent>
<SelectItem value="all">Semua Tipe</SelectItem>
{filterOptions.typeOptions.map((opt) => (
<SelectItem key={opt.value} value={opt.value}>
{opt.label}
</SelectItem>
))}
</SelectContent>
</Select>
</div>
<div className="flex flex-col gap-2">
<label className="text-xs text-muted-foreground">
Kualitas
</label>
<Select
value={filters.stock_quality ?? 'all'}
onValueChange={(value) =>
applyFilter('stock_quality', value)
}
>
<SelectTrigger className="w-full">
<SelectValue placeholder="Semua Kualitas" />
</SelectTrigger>
<SelectContent>
<SelectItem value="all">Semua Kualitas</SelectItem>
{filterOptions.stockQualityOptions.map((opt) => (
<SelectItem key={opt.value} value={opt.value}>
{opt.label}
</SelectItem>
))}
</SelectContent>
</Select>
</div>
<div className="flex flex-col gap-2">
<label className="text-xs text-muted-foreground">
Dari Tanggal
</label>
<DatePicker
value={filters.date_from ?? null}
onChange={(date) =>
applyFilter(
'date_from',
date ? format(date, 'yyyy-MM-dd') : '',
)
}
placeholder="Pilih tanggal mulai"
/>
</div>
<div className="flex flex-col gap-2">
<label className="text-xs text-muted-foreground">
Sampai Tanggal
</label>
<DatePicker
value={filters.date_to ?? null}
onChange={(date) =>
applyFilter(
'date_to',
date ? format(date, 'yyyy-MM-dd') : '',
)
}
placeholder="Pilih tanggal akhir"
/>
</div>
</FilterPopover>
);
return (
<>
<Head title="Riwayat Stok" />
<div className="flex h-full flex-1 flex-col gap-6 overflow-x-auto p-4 md:p-6">
<PageHeader
title="Riwayat Stok"
description={
<p className="mt-1 text-sm text-muted-foreground">
Catatan seluruh pergerakan stok produk: restock, penjualan, stok opname, transfer kualitas, dan penyesuaian varian.
</p>
}
/>
<DataTable
columns={columns}
data={mutations.data}
searchKey="description"
emptyText="Belum ada riwayat mutasi stok."
pagination={pagination}
onPageChange={handlePageChange}
onPerPageChange={handlePerPageChange}
onSearchChange={handleSearchChange}
searchValue={search}
toolbar={filterToolbar}
/>
</div>
</>
);
}