feat: enhance raw material filtering with name selection and add distinct name retrieval

This commit is contained in:
Yoga Pangestu 2026-08-12 21:01:03 +07:00
parent d0482c03db
commit ef987360f4
3 changed files with 58 additions and 5 deletions

View File

@ -22,9 +22,10 @@ public function index(PaginatedRequest $request): Response
return Inertia::render('admin/master/raw-material/index', [
'rawMaterials' => $this->service->paginated(
...$request->validatedWithDefaults(),
filters: $request->only(['is_active', 'stock']),
filters: $request->only(['is_active', 'stock', 'name']),
),
'filters' => $request->only(['is_active', 'stock']),
'rawMaterialNames' => $this->service->getNames(),
'filters' => $request->only(['is_active', 'stock', 'name']),
]);
}

View File

@ -7,6 +7,7 @@
use App\Services\Concerns\RegistersMedia;
use App\Services\S3PresignedService;
use Illuminate\Contracts\Pagination\LengthAwarePaginator;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\DB;
class RawMaterialService
@ -17,6 +18,13 @@ public function __construct(
private S3PresignedService $s3Service,
) {}
public function getNames(): Collection
{
return RawMaterial::select('name')
->distinct()
->pluck('name');
}
public function paginated(int $perPage = 25, string $search = '', string $sort = 'created_at', string $direction = 'desc', array $filters = []): LengthAwarePaginator
{
$paginator = RawMaterial::query()
@ -25,6 +33,7 @@ public function paginated(int $perPage = 25, string $search = '', string $sort =
'rawMaterialPrices:id,raw_material_id,variant,price,stock',
])
->when($search, fn ($q) => $q->where('name', 'like', "%{$search}%"))
->when($filters['name'] ?? null, fn ($q, $name) => $q->where('name', 'like', "%{$name}%"))
->when(($filters['is_active'] ?? null) !== null && ($filters['is_active'] ?? null) !== '', function ($q) use ($filters) {
$q->where('is_active', $filters['is_active'] === 'true');
})

View File

@ -1,12 +1,20 @@
import { Head, Link, router } from '@inertiajs/react';
import { Plus } from 'lucide-react';
import { useState } from 'react';
import { useMemo, useState } from 'react';
import { CardTable } from '@/components/data-display';
import { DeleteConfirmDialog } 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,
@ -38,13 +46,15 @@ type Props = {
per_page: number;
total: number;
};
rawMaterialNames: string[];
filters: {
is_active?: string;
name?: string;
stock?: string;
};
};
export default function RawMaterialIndex({ rawMaterials, filters }: Props) {
export default function RawMaterialIndex({ rawMaterials, rawMaterialNames, filters }: Props) {
const { can } = useCan();
const [deleting, setDeleting] = useState<RawMaterial | null>(null);
const [deletingVariant, setDeletingVariant] = useState<{
@ -76,6 +86,11 @@ export default function RawMaterialIndex({ rawMaterials, filters }: Props) {
filterWithParams: false,
});
const sortedRawMaterialNames = useMemo(
() => [...rawMaterialNames].sort(),
[rawMaterialNames],
);
function handleDelete() {
if (!deleting) {
return;
@ -107,9 +122,37 @@ export default function RawMaterialIndex({ rawMaterials, filters }: Props) {
open={filterOpen}
onOpenChange={setFilterOpen}
filters={filters}
hasActiveFilters={Boolean(filters.is_active || filters.stock)}
hasActiveFilters={Boolean(filters.is_active || filters.name || filters.stock)}
onClear={clearFilters}
>
<div className="flex flex-col gap-2">
<label className="text-xs text-muted-foreground">
Nama Bahan Baku
</label>
<Combobox
items={sortedRawMaterialNames}
value={filters.name ?? ''}
onValueChange={(value) =>
applyFilter('name', (value as string) ?? '')
}
>
<ComboboxInput
placeholder="Pilih bahan baku..."
className="w-full"
/>
<ComboboxContent>
<ComboboxEmpty>
Tidak ada bahan baku ditemukan.
</ComboboxEmpty>
<ComboboxList>
{(name) => (
<ComboboxItem key={name} value={name}>{name}</ComboboxItem>
)}
</ComboboxList>
</ComboboxContent>
</Combobox>
</div>
<div className="flex flex-col gap-2">
<label className="text-xs text-muted-foreground">Status</label>
<Select