From c49027492cdeb384928a67c897be46ef78839ef3 Mon Sep 17 00:00:00 2001 From: Yoga Pangestu Date: Fri, 14 Aug 2026 05:10:29 +0700 Subject: [PATCH] feat: add SendAttendanceReminderJob to notify users for attendance reminders --- app/Jobs/SendAttendanceReminderJob.php | 72 +++++++++++++++++++++++ app/Notifications/WebPushNotification.php | 9 ++- routes/console.php | 5 ++ 3 files changed, 85 insertions(+), 1 deletion(-) create mode 100644 app/Jobs/SendAttendanceReminderJob.php diff --git a/app/Jobs/SendAttendanceReminderJob.php b/app/Jobs/SendAttendanceReminderJob.php new file mode 100644 index 0000000..9db831f --- /dev/null +++ b/app/Jobs/SendAttendanceReminderJob.php @@ -0,0 +1,72 @@ +toDateString(); + + $users = User::query() + ->select(['id', 'name']) + ->active() + ->whereHas('roles', function ($q) { + $q->whereHas('permissions', fn($pq) => $pq->where('name', Permission::ATTENDANCES_CREATE->value)); + }) + ->whereHas('employee') + ->get(); + + foreach ($users as $user) { + $employee = $user->employee; + + if (! $employee) { + continue; + } + + $hasAttendedToday = Attendance::where('employee_id', $employee->id) + ->where('attendance_date', $today) + ->exists(); + + if ($hasAttendedToday) { + continue; + } + + $isOnLeave = LeaveRequest::approved() + ->where('employee_id', $employee->id) + ->where('start_date', '<=', $today) + ->where('end_date', '>=', $today) + ->exists(); + + if ($isOnLeave) { + continue; + } + + try { + $user->notify(new WebPushNotification( + title: 'Reminder Presensi', + body: 'Selamat pagi! Jangan lupa untuk melakukan presensi masuk hari ini.', + url: route('admin.hr.attendances.index'), + )); + } catch (\Exception $e) { + Log::error("Gagal mengirim reminder presensi ke user {$user->id}: {$e->getMessage()}"); + } + } + } +} diff --git a/app/Notifications/WebPushNotification.php b/app/Notifications/WebPushNotification.php index 098fe63..f670933 100644 --- a/app/Notifications/WebPushNotification.php +++ b/app/Notifications/WebPushNotification.php @@ -16,6 +16,7 @@ public function __construct( public string $title, public string $body, public string $icon = '/icon-192x192.png', + public ?string $url = null, ) {} public function via(object $notifiable): array @@ -25,9 +26,15 @@ public function via(object $notifiable): array public function toWebPush(object $notifiable, mixed $notification): WebPushMessage { - return (new WebPushMessage) + $webPushMessage = (new WebPushMessage) ->title($this->title) ->icon($this->icon) ->body($this->body); + + if ($this->url) { + $webPushMessage->data(['url' => $this->url]); + } + + return $webPushMessage; } } diff --git a/routes/console.php b/routes/console.php index 03f29fd..7dfc353 100644 --- a/routes/console.php +++ b/routes/console.php @@ -3,11 +3,16 @@ use App\Console\Commands\GeneratePayrollCommand; use App\Jobs\CheckAttendancePenaltiesJob; use App\Jobs\CleanupOrphanedMediaJob; +use App\Jobs\SendAttendanceReminderJob; +use Carbon\Carbon; use Illuminate\Support\Facades\Schedule; Schedule::command(GeneratePayrollCommand::class) ->daily() ->when(fn () => now()->day === 5) ->at('00:00'); +Schedule::job(new SendAttendanceReminderJob) + ->dailyAt('07:00') + ->when(fn () => now()->dayOfWeek !== Carbon::SUNDAY); Schedule::job(new CheckAttendancePenaltiesJob)->dailyAt('12:00'); Schedule::job(new CleanupOrphanedMediaJob)->monthlyOn(1, '01:00');