sls/resources/views/components/interview/schedule-modal.blade.php
kushal.saha e4fabef1d3 feat(interview): add icalendar invites, white date pickers, and drawer validation feedback
- 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
2026-08-27 10:16:46 +00:00

179 lines
9.5 KiB
PHP

@props([
'interviewers' => [],
])
@php
$errors = $errors ?? new \Illuminate\Support\ViewErrorBag;
$hasInterviewErrors = $errors->any() && (
$errors->has('candidate_name') ||
$errors->has('candidate_email') ||
$errors->has('candidate_phone') ||
$errors->has('job_title') ||
$errors->has('round') ||
$errors->has('scheduled_at') ||
$errors->has('valid_hours') ||
$errors->has('language') ||
$errors->has('assigned_interviewers') ||
$errors->has('assigned_interviewers.*') ||
$errors->has('description')
);
@endphp
<x-modal id="create-interview-modal" title="Schedule Candidate Assessment" maxWidth="lg" class="{{ $hasInterviewErrors ? 'active' : '' }}">
<form action="{{ route('interview.store') }}" method="POST">
@csrf
@if($hasInterviewErrors)
<div class="mb-4 bg-rose-500/10 border border-rose-500/30 rounded-xl p-3.5 text-xs text-rose-300 flex items-start gap-2.5">
<i class="fa-solid fa-triangle-exclamation text-rose-400 mt-0.5 shrink-0 text-sm"></i>
<div class="flex-1">
<strong class="block font-semibold text-rose-200">Please correct the following errors:</strong>
<ul class="list-disc list-inside mt-1 space-y-0.5 text-[11px] text-rose-300">
@foreach($errors->all() as $errorMsg)
<li>{{ $errorMsg }}</li>
@endforeach
</ul>
</div>
</div>
@endif
<div class="grid grid-cols-1 md:grid-cols-2 gap-3.5 mb-3.5">
<div>
<x-label required>Candidate Name</x-label>
<x-input type="text" name="candidate_name" value="{{ old('candidate_name') }}" required placeholder="e.g. Soumyadeep Mondal" />
@error('candidate_name')
<p class="text-[11px] text-rose-400 mt-1 font-medium flex items-center gap-1">
<i class="fa-solid fa-circle-exclamation text-[10px]"></i> {{ $message }}
</p>
@enderror
</div>
<div>
<x-label required>Candidate Email</x-label>
<x-input type="email" name="candidate_email" value="{{ old('candidate_email') }}" required placeholder="candidate@example.com" />
@error('candidate_email')
<p class="text-[11px] text-rose-400 mt-1 font-medium flex items-center gap-1">
<i class="fa-solid fa-circle-exclamation text-[10px]"></i> {{ $message }}
</p>
@enderror
</div>
</div>
<div class="grid grid-cols-1 md:grid-cols-2 gap-3.5 mb-3.5">
<div>
<x-label required>Position / Job Title</x-label>
<x-input type="text" name="job_title" value="{{ old('job_title') }}" required placeholder="e.g. AIML Engineer" />
@error('job_title')
<p class="text-[11px] text-rose-400 mt-1 font-medium flex items-center gap-1">
<i class="fa-solid fa-circle-exclamation text-[10px]"></i> {{ $message }}
</p>
@enderror
</div>
<div>
<x-label>Assessment Round</x-label>
<x-input type="text" name="round" value="{{ old('round', 'R1') }}" placeholder="e.g. R1, Technical" />
@error('round')
<p class="text-[11px] text-rose-400 mt-1 font-medium flex items-center gap-1">
<i class="fa-solid fa-circle-exclamation text-[10px]"></i> {{ $message }}
</p>
@enderror
</div>
</div>
<div class="grid grid-cols-1 md:grid-cols-3 gap-3 mb-3.5">
<div>
<x-label required>Phone Number</x-label>
<x-input type="text" name="candidate_phone" value="{{ old('candidate_phone') }}" required placeholder="9876543210" />
@error('candidate_phone')
<p class="text-[11px] text-rose-400 mt-1 font-medium flex items-center gap-1">
<i class="fa-solid fa-circle-exclamation text-[10px]"></i> {{ $message }}
</p>
@enderror
</div>
<div>
<x-label>Start Time (Timeline)</x-label>
<x-input type="datetime-local" name="scheduled_at" value="{{ old('scheduled_at', now()->format('Y-m-d\TH:i')) }}" class="bg-slate-900" />
@error('scheduled_at')
<p class="text-[11px] text-rose-400 mt-1 font-medium flex items-center gap-1">
<i class="fa-solid fa-circle-exclamation text-[10px]"></i> {{ $message }}
</p>
@enderror
</div>
<div>
<x-label required>Access Duration</x-label>
<x-select name="valid_hours" required>
<option value="1" @selected(old('valid_hours') === '1')>1 Hour</option>
<option value="2" @selected(old('valid_hours', '2') === '2')>2 Hours</option>
<option value="4" @selected(old('valid_hours') === '4')>4 Hours</option>
<option value="12" @selected(old('valid_hours') === '12')>12 Hours</option>
<option value="24" @selected(old('valid_hours') === '24')>24 Hours</option>
</x-select>
@error('valid_hours')
<p class="text-[11px] text-rose-400 mt-1 font-medium flex items-center gap-1">
<i class="fa-solid fa-circle-exclamation text-[10px]"></i> {{ $message }}
</p>
@enderror
</div>
</div>
<div class="mb-3.5">
<x-label required>Coding Language</x-label>
<x-select name="language" required>
<option value="python" @selected(old('language', 'python') === 'python')>Python 3</option>
<option value="cpp" @selected(old('language') === 'cpp')>C++ (GCC)</option>
<option value="c" @selected(old('language') === 'c')>C (GCC)</option>
<option value="java" @selected(old('language') === 'java')>Java 15</option>
<option value="php" @selected(old('language') === 'php')>PHP 8.2 / Laravel</option>
<option value="javascript" @selected(old('language') === 'javascript')>JavaScript (Node.js)</option>
</x-select>
@error('language')
<p class="text-[11px] text-rose-400 mt-1 font-medium flex items-center gap-1">
<i class="fa-solid fa-circle-exclamation text-[10px]"></i> {{ $message }}
</p>
@enderror
</div>
<div class="mb-3.5">
<x-label>Instructions / Notes (Optional)</x-label>
<textarea name="description" rows="2" class="w-full bg-slate-900 border @error('description') border-rose-500/80 focus:border-rose-500 @else border-slate-700/60 focus:border-indigo-500 @enderror rounded-xl p-3 text-xs text-white placeholder-slate-500 focus:outline-none transition-colors" placeholder="Special requirements, interview topics, or instructions...">{{ old('description') }}</textarea>
@error('description')
<p class="text-[11px] text-rose-400 mt-1 font-medium flex items-center gap-1">
<i class="fa-solid fa-circle-exclamation text-[10px]"></i> {{ $message }}
</p>
@enderror
</div>
<div class="mb-4">
<div class="flex justify-between items-center mb-1.5">
<x-label>Assign Internal Interviewers / Panelists</x-label>
</div>
<div class="grid grid-cols-1 sm:grid-cols-2 gap-2 max-h-[120px] overflow-y-auto bg-black/30 p-2.5 rounded-lg border @error('assigned_interviewers') border-rose-500/80 @else border-slate-700/60 @enderror">
@foreach($interviewers as $usr)
<label class="flex items-center gap-2 text-xs text-white cursor-pointer hover:text-indigo-300 transition-colors">
<input type="checkbox" name="assigned_interviewers[]" value="{{ $usr->id }}" @checked(in_array($usr->id, (array) old('assigned_interviewers', []))) class="accent-indigo-500 rounded">
{{ $usr->name }} ({{ $usr->email }})
</label>
@endforeach
</div>
@error('assigned_interviewers')
<p class="text-[11px] text-rose-400 mt-1 font-medium flex items-center gap-1">
<i class="fa-solid fa-circle-exclamation text-[10px]"></i> {{ $message }}
</p>
@enderror
@error('assigned_interviewers.*')
<p class="text-[11px] text-rose-400 mt-1 font-medium flex items-center gap-1">
<i class="fa-solid fa-circle-exclamation text-[10px]"></i> {{ $message }}
</p>
@enderror
</div>
<div class="flex justify-end gap-2.5 mt-5">
<x-button type="button" onclick="document.getElementById('create-interview-modal').classList.remove('active')" variant="secondary">
Cancel
</x-button>
<x-button type="submit" variant="primary" icon="fa-solid fa-paper-plane" :showLoader="true" loadingText="Scheduling...">
Schedule & Send Invites
</x-button>
</div>
</form>
</x-modal>