refactor: restrict role-based permissions and update employee management flow accordingly

This commit is contained in:
Yoga Pangestu 2026-06-14 02:30:34 +07:00
parent 57c311eaa1
commit cffea55157
4 changed files with 80 additions and 6 deletions

View File

@ -28,13 +28,27 @@ public function __construct(
public function index(Request $request): Response
{
$tableQuery = $this->parseDataTableQuery($request);
$role = $request->string('role')->toString();
$gender = $request->string('gender')->toString();
$employmentStatus = $request->string('employment_status')->toString();
$isActive = $request->string('is_active')->toString();
return Inertia::render('admin/hr/employees/Index', [
'employees' => $this->employeeService->paginateForIndex($tableQuery, $employmentStatus),
'employees' => $this->employeeService->paginateForIndex(
$tableQuery,
$role,
$gender,
$employmentStatus,
$isActive
),
'filters' => $this->dataTableFilters($tableQuery, [
'role' => $role,
'gender' => $gender,
'employment_status' => $employmentStatus,
'is_active' => $isActive,
]),
'roles' => Role::assignableSelectOptions(),
'genders' => Gender::selectOptions(),
'employmentStatuses' => EmploymentStatus::selectOptions(),
]);
}

View File

@ -15,8 +15,13 @@ class EmployeeService
/**
* @param array{search: string, sort: string, direction: 'asc'|'desc'} $tableQuery
*/
public function paginateForIndex(array $tableQuery, string $employmentStatus): LengthAwarePaginator
{
public function paginateForIndex(
array $tableQuery,
string $role = '',
string $gender = '',
string $employmentStatus = '',
string $isActive = '',
): LengthAwarePaginator {
$query = User::query()
->with(['profile', 'employee', 'roles'])
->whereHas('employee')
@ -31,9 +36,21 @@ public function paginateForIndex(array $tableQuery, string $employmentStatus): L
});
});
})
->when(
$role !== '',
fn ($query) => $query->whereHas('roles', fn ($query) => $query->where('name', $role))
)
->when(
$gender !== '',
fn ($query) => $query->whereHas('profile', fn ($query) => $query->where('gender', $gender))
)
->when(
$employmentStatus !== '',
fn ($query) => $query->whereHas('employee', fn ($query) => $query->where('employment_status', $employmentStatus))
)
->when(
$isActive !== '',
fn ($query) => $query->where('is_active', $isActive === '1')
);
$this->applySorting($query, $tableQuery['sort'], $tableQuery['direction']);

View File

@ -1,4 +1,5 @@
<script setup lang="ts">
import { useMediaQuery } from '@vueuse/core';
import { ListFilter, Search } from '@lucide/vue';
import { computed } from 'vue';
import { Badge } from '@/components/ui/badge';
@ -55,6 +56,10 @@ function onFilterChange(key: string, value: string): void {
function resetFilters(): void {
emit('filters-reset');
}
const isMobile = useMediaQuery('(max-width: 640px)');
const selectSide = computed(() => (isMobile.value ? 'bottom' : 'left'));
const selectSideOffset = computed(() => (isMobile.value ? 4 : 12));
</script>
<template>
@ -83,7 +88,16 @@ function resetFilters(): void {
</Badge>
</Button>
</PopoverTrigger>
<PopoverContent align="end" class="w-80 space-y-4 p-4">
<PopoverContent
align="end"
class="w-80 space-y-4 p-4"
@pointer-down-outside="(event) => {
const target = event.target as HTMLElement;
if (target.closest('[data-slot=select-content]') || target.closest('[data-slot=select-trigger]')) {
event.preventDefault();
}
}"
>
<div class="space-y-1">
<h4 class="text-sm font-medium">
Filter
@ -116,7 +130,7 @@ function resetFilters(): void {
<SelectTrigger :id="`filter-${filter.key}`" class="w-full">
<SelectValue :placeholder="filter.placeholder ?? 'Semua'" />
</SelectTrigger>
<SelectContent>
<SelectContent class="z-[300]" :side="selectSide" :side-offset="selectSideOffset">
<SelectItem value="all">
Semua
</SelectItem>

View File

@ -18,8 +18,13 @@ const props = defineProps<{
search: string;
sort?: string;
direction?: 'asc' | 'desc';
role?: string;
gender?: string;
employment_status?: string;
is_active?: string;
};
roles: EnumOption[];
genders: EnumOption[];
employmentStatuses: EnumOption[];
}>();
@ -29,22 +34,46 @@ const search = ref(props.filters.search ?? '');
const { query, setSearch, setSort, setFilter, resetFilters, syncFromServer } = useDataTableQuery({
url: '/admin/hr/employees',
initial: { ...props.filters },
filterKeys: ['employment_status'],
filterKeys: ['role', 'gender', 'employment_status', 'is_active'],
});
useDataTableQuerySync(() => props.filters, syncFromServer);
const filterDefs = computed<DataTableFilterDef[]>(() => [
{
key: 'role',
label: 'Role',
type: 'select',
options: props.roles,
},
{
key: 'gender',
label: 'Jenis Kelamin',
type: 'select',
options: props.genders,
},
{
key: 'employment_status',
label: 'Status Kepegawaian',
type: 'select',
options: props.employmentStatuses,
},
{
key: 'is_active',
label: 'Status Akun',
type: 'select',
options: [
{ value: '1', label: 'Aktif' },
{ value: '0', label: 'Nonaktif' },
],
},
]);
const filterValues = computed(() => ({
role: query.value.role ?? '',
gender: query.value.gender ?? '',
employment_status: query.value.employment_status ?? '',
is_active: query.value.is_active ?? '',
}));
const currentSort = computed<DataTableSort | null>(() => {