Remove EmployeeStatus enum and related references from the codebase. Update Employee and User models to eliminate employee status handling. Refactor EmployeeController and related components to remove employee status filtering and display. Adjust EmployeeRequest and migration files accordingly.

This commit is contained in:
Yoga Pangestu 2026-06-10 01:31:50 +07:00
parent 926da48a8e
commit 184b012fd9
12 changed files with 63 additions and 170 deletions

View File

@ -1,23 +0,0 @@
<?php
namespace App\Enums;
use App\Traits\ProvidesEnumOptions;
enum EmployeeStatus: string
{
use ProvidesEnumOptions;
case ACTIVE = 'active';
case INACTIVE = 'inactive';
case RESIGNED = 'resigned';
public function label(): string
{
return match ($this) {
self::ACTIVE => 'Aktif',
self::INACTIVE => 'Tidak Aktif',
self::RESIGNED => 'Resign',
};
}
}

View File

@ -2,7 +2,6 @@
namespace App\Http\Controllers\Admin\Hr;
use App\Enums\EmployeeStatus;
use App\Enums\EmploymentStatus;
use App\Enums\Gender;
use App\Http\Controllers\Concerns\ParsesDataTableQuery;
@ -31,7 +30,6 @@ public function index(Request $request): Response
$direction = $tableQuery['direction'];
$employmentStatus = $request->string('employment_status')->toString();
$employeeStatus = $request->string('employee_status')->toString();
$query = User::query()
->with(['profile', 'employee'])
@ -43,19 +41,12 @@ public function index(Request $request): Response
->orWhereHas('profile', function ($query) use ($search): void {
$query->where('full_name', 'like', "%{$search}%")
->orWhere('phone_number', 'like', "%{$search}%");
})
->orWhereHas('employee', function ($query) use ($search): void {
$query->where('employee_code', 'like', "%{$search}%");
});
});
})
->when(
$employmentStatus !== '',
fn ($query) => $query->whereHas('employee', fn ($query) => $query->where('employment_status', $employmentStatus))
)
->when(
$employeeStatus !== '',
fn ($query) => $query->whereHas('employee', fn ($query) => $query->where('employee_status', $employeeStatus))
);
$this->applySorting($query, $sort, $direction);
@ -69,10 +60,8 @@ public function index(Request $request): Response
'employees' => $employees,
'filters' => $this->dataTableFilters($tableQuery, [
'employment_status' => $employmentStatus,
'employee_status' => $employeeStatus,
]),
'employmentStatuses' => EmploymentStatus::selectOptions(),
'employeeStatuses' => EmployeeStatus::selectOptions(),
]);
}
@ -124,7 +113,7 @@ public function edit(User $user): Response
return Inertia::render('admin/hr/employees/Edit', [
'genders' => Gender::selectOptions(),
'employmentStatuses' => EmploymentStatus::selectOptions(),
'employee' => $this->transformEmployee($user),
'employee' => $this->transformEmployeeForForm($user),
]);
}
@ -203,11 +192,9 @@ public function destroy(User $user): RedirectResponse
private function applySorting(Builder $query, string $sort, string $direction): void
{
$employeeSorts = [
'employee_code',
'join_date',
'base_salary',
'employment_status',
'employee_status',
];
if (in_array($sort, $employeeSorts, true)) {
@ -241,6 +228,29 @@ private function applySorting(Builder $query, string $sort, string $direction):
$query->latest();
}
/**
* @return array<string, mixed>
*/
private function transformEmployeeForForm(User $user): array
{
$employee = $user->employee;
$profile = $user->profile;
return [
'id' => $user->id,
'email' => $user->email,
'username' => $user->username,
'full_name' => $profile->full_name,
'phone_number' => $profile->phone_number,
'gender' => $profile->gender?->value,
'birth_date' => $profile->birth_date?->format('Y-m-d'),
'address' => $profile->address,
'join_date' => $employee->join_date?->format('Y-m-d'),
'employment_status' => $employee->employment_status?->value,
'base_salary' => $employee->base_salary,
];
}
/**
* @return array<string, mixed>
*/
@ -251,13 +261,10 @@ private function transformEmployee(User $user): array
return [
'id' => $user->id,
'employee_code' => $employee->employee_code,
'join_date' => $employee->join_date_formatted,
'resign_date' => $employee->resign_date_formatted,
'employment_status' => $employee->employment_status?->value,
'employment_status_label' => $employee->employment_status?->label(),
'employee_status' => $employee->employee_status?->value,
'employee_status_label' => $employee->employee_status?->label(),
'base_salary' => $employee->base_salary,
'base_salary_formatted' => $employee->base_salary_formatted,
'email' => $user->email,

View File

@ -14,13 +14,6 @@ public function authorize(): bool
return auth()->check();
}
protected function prepareForValidation(): void
{
$this->merge([
'base_salary' => $this->integer('base_salary'),
]);
}
/**
* @return array<string, mixed>
*/

View File

@ -2,13 +2,10 @@
namespace App\Models;
use App\Enums\EmployeeStatus;
use App\Enums\EmploymentStatus;
use App\Observers\EmployeeObserver;
use Carbon\Carbon;
use Illuminate\Database\Eloquent\Attributes\Appends;
use Illuminate\Database\Eloquent\Attributes\Guarded;
use Illuminate\Database\Eloquent\Attributes\ObservedBy;
use Illuminate\Database\Eloquent\Attributes\Scope;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Casts\Attribute;
@ -18,7 +15,6 @@
#[Guarded(['id'])]
#[Appends(['base_salary_formatted', 'join_date_formatted', 'resign_date_formatted'])]
#[ObservedBy([EmployeeObserver::class])]
class Employee extends Model
{
use SoftDeletes;
@ -29,29 +25,10 @@ protected function casts(): array
'join_date' => 'date',
'resign_date' => 'date',
'employment_status' => EmploymentStatus::class,
'employee_status' => EmployeeStatus::class,
'base_salary' => 'integer',
];
}
#[Scope]
public function active(Builder $query): void
{
$query->where('employee_status', EmployeeStatus::ACTIVE->value);
}
#[Scope]
public function inactive(Builder $query): void
{
$query->where('employee_status', EmployeeStatus::INACTIVE->value);
}
#[Scope]
public function resigned(Builder $query): void
{
$query->where('employee_status', EmployeeStatus::RESIGNED->value);
}
public function fullTime(Builder $query): void
{
$query->where('employment_status', EmploymentStatus::FULL_TIME->value);
@ -60,25 +37,25 @@ public function fullTime(Builder $query): void
#[Scope]
public function partTime(Builder $query): void
{
$query->where('employee_status', EmploymentStatus::PART_TIME->value);
$query->where('employment_status', EmploymentStatus::PART_TIME->value);
}
#[Scope]
public function contract(Builder $query): void
{
$query->where('employee_status', EmploymentStatus::CONTRACT->value);
$query->where('employment_status', EmploymentStatus::CONTRACT->value);
}
#[Scope]
public function temporary(Builder $query): void
{
$query->where('employee_status', EmploymentStatus::TEMPORARY->value);
$query->where('employment_status', EmploymentStatus::TEMPORARY->value);
}
public function baseSalaryFormatted(): Attribute
{
return Attribute::make(
get: fn () => number_format($this->base_salary, 0, ',', '.'),
get: fn () => 'Rp '.number_format($this->base_salary, 0, ',', '.'),
);
}

View File

@ -4,6 +4,8 @@
use Illuminate\Database\Eloquent\Attributes\Guarded;
use Illuminate\Database\Eloquent\Attributes\Hidden;
use Illuminate\Database\Eloquent\Attributes\Scope;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Relations\HasOne;
use Illuminate\Database\Eloquent\SoftDeletes;
@ -25,6 +27,18 @@ protected function casts(): array
];
}
#[Scope]
public function active(Builder $query): void
{
$query->where('is_active', true);
}
#[Scope]
public function inactive(Builder $query): void
{
$query->where('is_active', false);
}
public function profile(): HasOne
{
return $this->hasOne(UserProfile::class);

View File

@ -1,35 +0,0 @@
<?php
namespace App\Observers;
use App\Models\Employee;
use Illuminate\Support\Facades\DB;
class EmployeeObserver
{
public function creating(Employee $employee): void
{
if (filled($employee->employee_code)) {
return;
}
$year = now()->format('Y');
$prefix = "EMP-{$year}-";
$employee->employee_code = DB::transaction(function () use ($prefix): string {
$latestCode = Employee::withTrashed()
->where('employee_code', 'like', $prefix.'%')
->lockForUpdate()
->orderByDesc('employee_code')
->value('employee_code');
$sequence = 1;
if ($latestCode !== null) {
$sequence = (int) substr($latestCode, strlen($prefix)) + 1;
}
return $prefix.str_pad((string) $sequence, 4, '0', STR_PAD_LEFT);
});
}
}

View File

@ -1,6 +1,5 @@
<?php
use App\Enums\EmployeeStatus;
use App\Enums\EmploymentStatus;
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
@ -18,11 +17,9 @@ public function up(): void
$table->foreignId('user_id')->constrained()->cascadeOnDelete()->unique();
$table->string('employee_code', 20)->unique();
$table->date('join_date');
$table->date('resign_date')->nullable();
$table->enum('employment_status', array_column(EmploymentStatus::cases(), 'value'))->default(EmploymentStatus::FULL_TIME->value);
$table->enum('employee_status', array_column(EmployeeStatus::cases(), 'value'))->default(EmployeeStatus::ACTIVE->value);
$table->unsignedInteger('base_salary');
$table->timestamp('created_at')->useCurrent();

View File

@ -3,21 +3,8 @@ import { h } from 'vue';
import { DataTableColumnHeader } from '@/components/data-table';
import DataTableActions from '@/components/hr/employees/data-table-actions.vue';
import EmployeeStatusToggle from '@/components/hr/employees/employee-status-toggle.vue';
import { Badge } from '@/components/ui/badge';
import type { EmployeeListItem } from '@/types/employee';
function employeeStatusVariant(status: string): 'default' | 'secondary' | 'destructive' {
if (status === 'active') {
return 'default';
}
if (status === 'resigned') {
return 'destructive';
}
return 'secondary';
}
function formatDate(value: string | null): string {
if (!value) {
return '-';
@ -37,12 +24,6 @@ function formatDate(value: string | null): string {
}
export const columns: ColumnDef<EmployeeListItem>[] = [
{
accessorKey: 'employee_code',
enableSorting: true,
header: () => h(DataTableColumnHeader, { title: 'Kode', column: 'employee_code' }),
cell: ({ row }) => h('span', { class: 'font-medium' }, row.getValue('employee_code')),
},
{
accessorKey: 'full_name',
enableSorting: true,
@ -107,29 +88,14 @@ export const columns: ColumnDef<EmployeeListItem>[] = [
enableSorting: true,
header: () => h(DataTableColumnHeader, { title: 'Status Kepegawaian', column: 'employment_status' }),
},
{
accessorKey: 'employee_status_label',
enableSorting: true,
header: () => h(DataTableColumnHeader, { title: 'Status Pegawai', column: 'employee_status' }),
cell: ({ row }) => h(
Badge,
{ variant: employeeStatusVariant(row.original.employee_status) },
() => row.original.employee_status_label,
),
},
{
accessorKey: 'base_salary_formatted',
enableSorting: true,
header: () => h(DataTableColumnHeader, {
title: 'Gaji Pokok',
column: 'base_salary',
class: 'justify-end',
}),
cell: ({ row }) => h(
'div',
{ class: 'text-right' },
`Rp ${row.original.base_salary_formatted}`,
),
cell: ({ row }) => row.original.base_salary_formatted,
},
{
id: 'status_toggle',

View File

@ -42,11 +42,7 @@ function toggleStatus(checked: boolean) {
<template>
<div class="flex items-center gap-2">
<Switch
:model-value="isActive"
:disabled="processing || employee.employee_status === 'resigned'"
@update:model-value="toggleStatus"
/>
<Switch :model-value="isActive" :disabled="processing" @update:model-value="toggleStatus" />
<Badge :variant="isActive ? 'default' : 'secondary'">
{{ isActive ? 'Aktif' : 'Nonaktif' }}
</Badge>

View File

@ -5,10 +5,10 @@ import { computed } from 'vue';
import EmployeeForm from '@/components/hr/EmployeeForm.vue';
import { Button } from '@/components/ui/button';
import AdminLayout from '@/layouts/AdminLayout.vue';
import type { EmployeeListItem, EnumOption } from '@/types/employee';
import type { EmployeeEditItem, EnumOption } from '@/types/employee';
const props = defineProps<{
employee: EmployeeListItem;
employee: EmployeeEditItem;
genders: EnumOption[];
employmentStatuses: EnumOption[];
}>();
@ -22,8 +22,8 @@ const initialData = computed(() => ({
birth_date: props.employee.birth_date ?? '',
address: props.employee.address ?? '',
join_date: props.employee.join_date ?? '',
employment_status: props.employee.employment_status,
base_salary: String(props.employee.base_salary),
employment_status: props.employee.employment_status ?? 'full_time',
base_salary: props.employee.base_salary != null ? String(props.employee.base_salary) : '',
}));
</script>

View File

@ -18,10 +18,8 @@ const props = defineProps<{
sort?: string;
direction?: 'asc' | 'desc';
employment_status?: string;
employee_status?: string;
};
employmentStatuses: EnumOption[];
employeeStatuses: EnumOption[];
}>();
const search = ref(props.filters.search ?? '');
@ -29,7 +27,7 @@ const search = ref(props.filters.search ?? '');
const { query, setSearch, setSort, setFilter, resetFilters, syncFromServer } = useDataTableQuery({
url: '/admin/hr/employees',
initial: { ...props.filters },
filterKeys: ['employment_status', 'employee_status'],
filterKeys: ['employment_status'],
});
useDataTableQuerySync(() => props.filters, syncFromServer);
@ -41,17 +39,10 @@ const filterDefs = computed<DataTableFilterDef[]>(() => [
type: 'select',
options: props.employmentStatuses,
},
{
key: 'employee_status',
label: 'Status Pegawai',
type: 'select',
options: props.employeeStatuses,
},
]);
const filterValues = computed(() => ({
employment_status: query.value.employment_status ?? '',
employee_status: query.value.employee_status ?? '',
}));
const currentSort = computed<DataTableSort | null>(() => {

View File

@ -3,15 +3,26 @@ export type EnumOption = {
label: string;
};
export type EmployeeEditItem = {
id: number;
email: string | null;
username: string | null;
full_name: string | null;
phone_number: string | null;
gender: string | null;
birth_date: string | null;
address: string | null;
join_date: string | null;
employment_status: string | null;
base_salary: number | null;
};
export type EmployeeListItem = {
id: number;
employee_code: string;
join_date: string | null;
resign_date: string | null;
employment_status: string;
employment_status_label: string;
employee_status: string;
employee_status_label: string;
base_salary: number;
base_salary_formatted: string;
email: string | null;
@ -43,7 +54,6 @@ export type EmployeeFilters = {
sort?: string;
direction?: 'asc' | 'desc' | null;
employment_status?: string;
employee_status?: string;
};
export type PaginatedEmployees = {