From c140192453a5ddde72daf4a74bd64493033f094d Mon Sep 17 00:00:00 2001 From: Yoga Pangestu Date: Sun, 14 Jun 2026 14:58:25 +0700 Subject: [PATCH] feat: prevent duplicate pending leave requests and restrict submission UI when a pending request exists --- .../Admin/Hr/LeaveRequestController.php | 10 ++++++++++ app/Services/Hr/LeaveRequestService.php | 17 ++++++++++++++--- .../js/pages/admin/hr/leave-requests/Index.vue | 2 +- resources/js/types/leave-request.ts | 1 + 4 files changed, 26 insertions(+), 4 deletions(-) diff --git a/app/Http/Controllers/Admin/Hr/LeaveRequestController.php b/app/Http/Controllers/Admin/Hr/LeaveRequestController.php index 5ebd1a7..9482c0e 100644 --- a/app/Http/Controllers/Admin/Hr/LeaveRequestController.php +++ b/app/Http/Controllers/Admin/Hr/LeaveRequestController.php @@ -2,6 +2,7 @@ namespace App\Http\Controllers\Admin\Hr; +use App\Enums\LeaveRequestStatus; use App\Enums\Permission; use App\Http\Controllers\Concerns\FlashesEntityMessage; use App\Http\Controllers\Concerns\ParsesDataTableQuery; @@ -29,12 +30,21 @@ public function index(Request $request): Response $tableQuery = $this->parseDataTableQuery($request); $user = $request->user(); + $hasPending = false; + if ($user?->employee !== null) { + $hasPending = LeaveRequest::query() + ->where('employee_id', $user->employee->id) + ->where('status', LeaveRequestStatus::PENDING) + ->exists(); + } + return Inertia::render('admin/hr/leave-requests/Index', [ 'leaveRequests' => $this->leaveRequestService->paginateForIndex($tableQuery), 'authEmployeeId' => $user?->employee?->id, 'canSubmit' => $user?->can(Permission::LEAVE_REQUESTS_CREATE->value) && ! $user->can(Permission::LEAVE_REQUESTS_VERIFY->value) && $user->employee !== null, + 'hasPending' => $hasPending, 'filters' => $this->dataTableFilters($tableQuery), ]); } diff --git a/app/Services/Hr/LeaveRequestService.php b/app/Services/Hr/LeaveRequestService.php index 9cf21ed..62b1f70 100644 --- a/app/Services/Hr/LeaveRequestService.php +++ b/app/Services/Hr/LeaveRequestService.php @@ -51,6 +51,17 @@ public function create(array $validated, User $user): void { $employee = $this->resolveAuthEmployee($user); + $hasPending = LeaveRequest::query() + ->where('employee_id', $employee->id) + ->where('status', LeaveRequestStatus::PENDING) + ->exists(); + + if ($hasPending) { + throw ValidationException::withMessages([ + 'start_date' => 'Anda masih memiliki pengajuan cuti yang aktif (menunggu persetujuan).', + ]); + } + $startDate = Carbon::parse($validated['start_date'])->startOfDay(); $endDate = Carbon::parse($validated['end_date'])->startOfDay(); @@ -107,7 +118,7 @@ public function approve(LeaveRequest $leaveRequest, User $user): void DB::transaction(function () use ($leaveRequest, $user): void { $leaveRequest->status = LeaveRequestStatus::APPROVED; - $leaveRequest->verified_at = now(); + $leaveRequest->verified_at = Carbon::now(); $leaveRequest->verified_by_id = $user->id; $leaveRequest->save(); }); @@ -129,7 +140,7 @@ public function reject(LeaveRequest $leaveRequest, string $reason, User $user): DB::transaction(function () use ($leaveRequest, $user, $reason): void { $leaveRequest->status = LeaveRequestStatus::REJECTED; - $leaveRequest->verified_at = now(); + $leaveRequest->verified_at = Carbon::now(); $leaveRequest->verified_by_id = $user->id; $leaveRequest->save(); @@ -157,7 +168,7 @@ private function calculateTotalDays(Carbon $startDate, Carbon $endDate): int private function ensureMinimumLeadTime(Carbon $startDate): void { - $minimumStartDate = now()->startOfDay()->addDay(); + $minimumStartDate = Carbon::now()->startOfDay()->addDay(); if ($startDate->lt($minimumStartDate)) { throw ValidationException::withMessages([ diff --git a/resources/js/pages/admin/hr/leave-requests/Index.vue b/resources/js/pages/admin/hr/leave-requests/Index.vue index 43c92b4..f239c09 100644 --- a/resources/js/pages/admin/hr/leave-requests/Index.vue +++ b/resources/js/pages/admin/hr/leave-requests/Index.vue @@ -93,7 +93,7 @@ watch( - diff --git a/resources/js/types/leave-request.ts b/resources/js/types/leave-request.ts index 17591d6..cd5b52c 100644 --- a/resources/js/types/leave-request.ts +++ b/resources/js/types/leave-request.ts @@ -37,6 +37,7 @@ export type LeaveRequestPageProps = { leaveRequests: PaginatedLeaveRequests; authEmployeeId: number | null; canSubmit: boolean; + hasPending: boolean; filters: { search: string; sort?: string;