- integrate spatie/icalendar-generator for candidate & panelist .ics invitations (RSVP & 15m alerts)
- add StoreInterviewRequest, ScheduleInterviewDto, and invitation/reminder queued actions
- add migration for job_title, round, description, and reminder_sent_at on interviews table
- style datetime-local picker indicators to pure white on dark input backgrounds
- support button loading spinners with :showLoader="true" and loadingText
- auto-open schedule drawer on validation failure with inline @error alerts and old() bindings
67 lines
2.3 KiB
PHP
67 lines
2.3 KiB
PHP
<?php
|
|
|
|
namespace App\Actions\Interview;
|
|
|
|
use App\Mail\InterviewReminderMail;
|
|
use App\Models\Interview;
|
|
use Illuminate\Support\Facades\Log;
|
|
use Illuminate\Support\Facades\Mail;
|
|
|
|
class SendUpcomingInterviewRemindersAction
|
|
{
|
|
/**
|
|
* Scan and dispatch 15-minute advance email reminders to candidates and panelists.
|
|
*
|
|
* @return int Count of reminded interview sessions
|
|
*/
|
|
public function execute(int $windowMinutes = 20): int
|
|
{
|
|
$now = now();
|
|
$targetWindow = $now->copy()->addMinutes($windowMinutes);
|
|
|
|
$interviews = Interview::where('status', 'scheduled')
|
|
->whereNull('reminder_sent_at')
|
|
->whereNotNull('scheduled_at')
|
|
->where('scheduled_at', '>=', $now->subMinutes(5)) // Within 5m grace
|
|
->where('scheduled_at', '<=', $targetWindow)
|
|
->get();
|
|
|
|
$processedCount = 0;
|
|
|
|
foreach ($interviews as $interview) {
|
|
// 1. Candidate reminder email
|
|
if (! empty($interview->candidate_email)) {
|
|
try {
|
|
Mail::to($interview->candidate_email)
|
|
->queue(new InterviewReminderMail($interview, 'candidate', $interview->candidate_name));
|
|
} catch (\Throwable $e) {
|
|
Log::error('Failed to send 15m reminder to candidate: '.$e->getMessage(), [
|
|
'interview_id' => $interview->id,
|
|
]);
|
|
}
|
|
}
|
|
|
|
// 2. Panelists reminder emails
|
|
$assignedUsers = $interview->assignedUsers();
|
|
foreach ($assignedUsers as $interviewer) {
|
|
if (! empty($interviewer->email)) {
|
|
try {
|
|
Mail::to($interviewer->email)
|
|
->queue(new InterviewReminderMail($interview, 'interviewer', $interviewer->name));
|
|
} catch (\Throwable $e) {
|
|
Log::error('Failed to send 15m reminder to interviewer: '.$e->getMessage(), [
|
|
'interview_id' => $interview->id,
|
|
'interviewer_id' => $interviewer->id,
|
|
]);
|
|
}
|
|
}
|
|
}
|
|
|
|
$interview->update(['reminder_sent_at' => now()]);
|
|
$processedCount++;
|
|
}
|
|
|
|
return $processedCount;
|
|
}
|
|
}
|