From 67439af903a372c6a4ce38c909d5c463552f389f Mon Sep 17 00:00:00 2001 From: Yoga Pangestu Date: Sun, 14 Jun 2026 02:54:06 +0700 Subject: [PATCH] feat: add category filter to product index page --- .../Controllers/Admin/Master/ProductController.php | 5 ++++- app/Services/Master/ProductService.php | 6 +++++- resources/js/pages/admin/master/products/Index.vue | 12 +++++++++++- 3 files changed, 20 insertions(+), 3 deletions(-) diff --git a/app/Http/Controllers/Admin/Master/ProductController.php b/app/Http/Controllers/Admin/Master/ProductController.php index 5870bd3..c988571 100644 --- a/app/Http/Controllers/Admin/Master/ProductController.php +++ b/app/Http/Controllers/Admin/Master/ProductController.php @@ -29,13 +29,16 @@ public function index(Request $request): Response { $tableQuery = $this->parseDataTableQuery($request); $isActive = $request->string('is_active')->toString(); + $categoryId = $request->string('category_id')->toString(); return Inertia::render('admin/master/products/Index', [ - 'products' => $this->productService->paginateForIndex($tableQuery, $isActive), + 'products' => $this->productService->paginateForIndex($tableQuery, $isActive, $categoryId), 'outOfStockGroups' => $this->productService->outOfStockGroups(), 'stockWarningGroups' => $this->productService->stockWarningGroups(), + 'categories' => $this->productService->categoryOptions(), 'filters' => $this->dataTableFilters($tableQuery, [ 'is_active' => $isActive, + 'category_id' => $categoryId, ]), ]); } diff --git a/app/Services/Master/ProductService.php b/app/Services/Master/ProductService.php index 13b42b7..360dd77 100644 --- a/app/Services/Master/ProductService.php +++ b/app/Services/Master/ProductService.php @@ -113,7 +113,7 @@ private function inventoryAlertGroups(callable $filter): array /** * @param array{search: string, sort: string, direction: 'asc'|'desc'} $tableQuery */ - public function paginateForIndex(array $tableQuery, string $isActive): LengthAwarePaginator + public function paginateForIndex(array $tableQuery, string $isActive, string $categoryId = ''): LengthAwarePaginator { $query = Product::query() ->with([ @@ -135,6 +135,10 @@ public function paginateForIndex(array $tableQuery, string $isActive): LengthAwa ->when( $isActive !== '', fn (Builder $query) => $query->where('is_active', $isActive === '1') + ) + ->when( + $categoryId !== '', + fn (Builder $query) => $query->whereHas('categories', fn (Builder $query) => $query->where('categories.id', $categoryId)) ); $this->applySorting($query, $tableQuery['sort'], $tableQuery['direction']); diff --git a/resources/js/pages/admin/master/products/Index.vue b/resources/js/pages/admin/master/products/Index.vue index be66032..1f1e52b 100644 --- a/resources/js/pages/admin/master/products/Index.vue +++ b/resources/js/pages/admin/master/products/Index.vue @@ -14,6 +14,7 @@ import { import AdminLayout from '@/layouts/AdminLayout.vue'; import type { DataTableFilterDef } from '@/types/data-table'; import type { + CategoryOption, PaginatedProducts, ProductOutOfStockGroup, ProductStockWarningGroup, @@ -23,11 +24,13 @@ const props = defineProps<{ products: PaginatedProducts; outOfStockGroups: ProductOutOfStockGroup[]; stockWarningGroups: ProductStockWarningGroup[]; + categories: CategoryOption[]; filters: { search: string; sort?: string; direction?: 'asc' | 'desc'; is_active?: string; + category_id?: string; }; }>(); @@ -38,12 +41,18 @@ const { query, setSearch, setFilter, resetFilters, syncFromServer } = useDataTableQuery({ url: '/admin/master/products', initial: { ...props.filters }, - filterKeys: ['is_active'], + filterKeys: ['is_active', 'category_id'], }); useDataTableQuerySync(() => props.filters, syncFromServer); const filterDefs = computed(() => [ + { + key: 'category_id', + label: 'Kategori', + type: 'select', + options: props.categories.map((c) => ({ value: String(c.value), label: c.label })), + }, { key: 'is_active', label: 'Status', @@ -56,6 +65,7 @@ const filterDefs = computed(() => [ ]); const filterValues = computed(() => ({ + category_id: query.value.category_id ?? '', is_active: query.value.is_active ?? '', }));