feat: enhance Purchase management with supplier filtering and pagination adjustments
This commit is contained in:
parent
b4cb3f4d48
commit
bcf5842c4a
@ -7,6 +7,7 @@
|
||||
use App\Http\Requests\PaginatedRequest;
|
||||
use App\Models\Purchase;
|
||||
use App\Services\Admin\Manage\PurchaseService;
|
||||
use App\Services\Admin\Master\SupplierService;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
use Inertia\Inertia;
|
||||
use Inertia\Response;
|
||||
@ -14,7 +15,8 @@
|
||||
class PurchaseController extends Controller
|
||||
{
|
||||
public function __construct(
|
||||
private PurchaseService $service,
|
||||
private readonly PurchaseService $service,
|
||||
private readonly SupplierService $supplierService,
|
||||
) {}
|
||||
|
||||
public function index(PaginatedRequest $request): Response
|
||||
@ -22,7 +24,10 @@ public function index(PaginatedRequest $request): Response
|
||||
return Inertia::render('admin/manage/purchase/index', [
|
||||
'purchases' => $this->service->paginated(
|
||||
...$request->validatedWithDefaults(),
|
||||
filters: $request->only(['supplier_id']),
|
||||
),
|
||||
'suppliers' => $this->supplierService->getAll(),
|
||||
'filters' => $request->only(['supplier_id']),
|
||||
]);
|
||||
}
|
||||
|
||||
|
||||
@ -21,7 +21,7 @@ public function __construct(
|
||||
private S3PresignedService $s3Service,
|
||||
) {}
|
||||
|
||||
public function paginated(int $perPage = 25, string $search = '', string $sort = 'created_at', string $direction = 'desc'): LengthAwarePaginator
|
||||
public function paginated(int $perPage = 25, string $search = '', string $sort = 'created_at', string $direction = 'desc', array $filters = []): LengthAwarePaginator
|
||||
{
|
||||
$paginator = Purchase::query()
|
||||
->select(['id', 'supplier_id', 'created_by_id', 'subtotal', 'discount', 'shipping_cost', 'total', 'notes', 'created_at'])
|
||||
@ -39,6 +39,7 @@ public function paginated(int $perPage = 25, string $search = '', string $sort =
|
||||
$q->whereHas('supplier', fn ($sq) => $sq->where('name', 'like', "%{$search}%"))
|
||||
->orWhere('notes', 'like', "%{$search}%");
|
||||
})
|
||||
->when($filters['supplier_id'] ?? null, fn ($q, $supplierId) => $q->where('supplier_id', $supplierId))
|
||||
->orderBy($sort, $direction)
|
||||
->paginate($perPage);
|
||||
|
||||
|
||||
@ -1,19 +1,28 @@
|
||||
import { Head, Link, router } from '@inertiajs/react';
|
||||
import { Plus } from 'lucide-react';
|
||||
import { useState } from 'react';
|
||||
import { CardTable } from '@/components/card-table';
|
||||
import { DeleteConfirmDialog } from '@/components/delete-confirm-dialog';
|
||||
import { FilterPopover } from '@/components/filter-popover';
|
||||
import { useCardTableExpand } from '@/components/hooks/use-card-table-expand';
|
||||
import { PageHeader } from '@/components/page-header';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import {
|
||||
Combobox,
|
||||
ComboboxContent,
|
||||
ComboboxEmpty,
|
||||
ComboboxInput,
|
||||
ComboboxItem,
|
||||
ComboboxList,
|
||||
} from '@/components/ui/combobox';
|
||||
import { useCan } from '@/hooks/use-can';
|
||||
import { useServerTable } from '@/hooks/use-server-table';
|
||||
import {
|
||||
destroy,
|
||||
create as purchaseCreate,
|
||||
index as purchaseIndex,
|
||||
edit as purchaseEdit,
|
||||
index as purchaseIndex,
|
||||
} from '@/routes/admin/manage/purchases';
|
||||
import { Head, Link, router } from '@inertiajs/react';
|
||||
import { Plus } from 'lucide-react';
|
||||
import { useMemo, useState } from 'react';
|
||||
import type { Purchase } from './columns';
|
||||
import { PurchaseCardRow } from './purchase-card';
|
||||
import { PurchaseItemSubRow } from './purchase-sub-row';
|
||||
@ -26,9 +35,20 @@ type Props = {
|
||||
per_page: number;
|
||||
total: number;
|
||||
};
|
||||
suppliers: {
|
||||
id: number;
|
||||
name: string;
|
||||
}[];
|
||||
filters: {
|
||||
supplier_id?: string;
|
||||
};
|
||||
};
|
||||
|
||||
export default function PurchaseIndex({ purchases }: Props) {
|
||||
export default function PurchaseIndex({
|
||||
purchases,
|
||||
filters,
|
||||
suppliers,
|
||||
}: Props) {
|
||||
const { can } = useCan();
|
||||
const [deleting, setDeleting] = useState<Purchase | null>(null);
|
||||
const expand = useCardTableExpand(true);
|
||||
@ -42,14 +62,29 @@ export default function PurchaseIndex({ purchases }: Props) {
|
||||
|
||||
const {
|
||||
search,
|
||||
filterOpen,
|
||||
setFilterOpen,
|
||||
handlePageChange,
|
||||
handlePerPageChange,
|
||||
handleSearchChange,
|
||||
applyFilter,
|
||||
clearFilters,
|
||||
} = useServerTable({
|
||||
route: () => purchaseIndex.url(),
|
||||
pagination,
|
||||
filters,
|
||||
filterWithParams: false,
|
||||
reloadOnly: ['purchases', 'filters'],
|
||||
});
|
||||
|
||||
const selectedSupplier = useMemo(
|
||||
() =>
|
||||
suppliers.find(
|
||||
(s) => String(s.id) === filters.supplier_id,
|
||||
) ?? null,
|
||||
[suppliers, filters.supplier_id],
|
||||
);
|
||||
|
||||
function handleDelete() {
|
||||
if (!deleting) {
|
||||
return;
|
||||
@ -60,6 +95,50 @@ export default function PurchaseIndex({ purchases }: Props) {
|
||||
});
|
||||
}
|
||||
|
||||
const filterToolbar = (
|
||||
<FilterPopover
|
||||
open={filterOpen}
|
||||
onOpenChange={setFilterOpen}
|
||||
filters={filters}
|
||||
hasActiveFilters={Boolean(filters.supplier_id)}
|
||||
onClear={clearFilters}
|
||||
>
|
||||
<div className="flex flex-col gap-2">
|
||||
<label className="text-xs text-muted-foreground">
|
||||
Supplier
|
||||
</label>
|
||||
<Combobox
|
||||
items={suppliers}
|
||||
itemToStringLabel={(supplier) => supplier.name}
|
||||
value={selectedSupplier}
|
||||
onValueChange={(value) =>
|
||||
applyFilter(
|
||||
'supplier_id',
|
||||
value ? String(value.id) : '',
|
||||
)
|
||||
}
|
||||
>
|
||||
<ComboboxInput
|
||||
placeholder="Pilih supplier..."
|
||||
className="w-full"
|
||||
/>
|
||||
<ComboboxContent>
|
||||
<ComboboxEmpty>
|
||||
Tidak ada supplier ditemukan.
|
||||
</ComboboxEmpty>
|
||||
<ComboboxList>
|
||||
{(supplier) => (
|
||||
<ComboboxItem value={supplier}>
|
||||
{supplier.name}
|
||||
</ComboboxItem>
|
||||
)}
|
||||
</ComboboxList>
|
||||
</ComboboxContent>
|
||||
</Combobox>
|
||||
</div>
|
||||
</FilterPopover>
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Head title="Belanja" />
|
||||
@ -87,6 +166,7 @@ export default function PurchaseIndex({ purchases }: Props) {
|
||||
searchValue={search}
|
||||
onSearchChange={handleSearchChange}
|
||||
searchPlaceholder="Cari berdasarkan supplier..."
|
||||
toolbar={filterToolbar}
|
||||
pagination={pagination}
|
||||
onPageChange={handlePageChange}
|
||||
onPerPageChange={handlePerPageChange}
|
||||
@ -100,7 +180,7 @@ export default function PurchaseIndex({ purchases }: Props) {
|
||||
purchase={item}
|
||||
index={
|
||||
(pagination.current_page - 1) *
|
||||
pagination.per_page +
|
||||
pagination.per_page +
|
||||
index +
|
||||
1
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user