sls/resources/views/components/interview/session-row.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

131 lines
6.1 KiB
PHP

@props([
'interview',
])
@php
$metrics = $interview->proctor_metrics;
if ($interview->isExpired() && $interview->call_status === 'active') {
$interview->update(['call_status' => 'ended', 'active_peers' => []]);
$interview->call_status = 'ended';
}
$loginUrl = route('interview.candidate.login');
$credentialsText = implode("\n", array_filter([
"Candidate Assessment Credentials",
"--------------------------------",
"Candidate: " . $interview->candidate_name . (!empty($interview->round) ? " ({$interview->round})" : ""),
!empty($interview->job_title) ? "Position: {$interview->job_title}" : null,
"Email: {$interview->candidate_email}",
"Phone: {$interview->candidate_phone}",
"Login URL: {$loginUrl}",
"Temp Password: {$interview->temp_password}",
"Timeline: {$interview->formatted_timeline}",
]));
@endphp
<tr class="hover:bg-white/[0.02] transition-colors">
<!-- Candidate Info & Temp Pass -->
<td class="py-3.5 px-4">
<div>
<div class="flex items-start gap-2">
<div class="min-w-0">
<div class="flex items-center gap-2">
<strong class="text-white text-sm block truncate">{{ $interview->candidate_name }}</strong>
@if(!empty($interview->round))
<span class="bg-indigo-500/20 text-indigo-300 text-[10px] font-semibold px-1.5 py-0.5 rounded border border-indigo-500/30 font-mono shrink-0">{{ $interview->round }}</span>
@endif
</div>
@if(!empty($interview->job_title))
<div class="text-[11px] text-slate-300 font-medium mt-0.5 truncate">{{ $interview->job_title }}</div>
@endif
</div>
<!-- Copy Credentials Button -->
<button
type="button"
onclick="copyInterviewCredentials(this)"
data-credentials="{{ $credentialsText }}"
title="Copy candidate & meeting credentials"
class="copy-cred-btn inline-flex items-center gap-1.5 px-2 py-1 text-[11px] font-medium rounded-md bg-white/5 border border-slate-700/60 text-slate-300 hover:text-white hover:bg-indigo-500/20 hover:border-indigo-500/40 transition-all cursor-pointer shrink-0 group select-none"
>
<i class="fa-regular fa-copy text-[11px] group-hover:scale-110 transition-transform"></i>
</button>
</div>
<div class="text-xs text-slate-400 mt-1">{{ $interview->candidate_email }} &bull; {{ $interview->candidate_phone }}</div>
<div class="mt-1.5 flex flex-col gap-1">
<div class="text-[0.7rem] text-emerald-300 bg-emerald-500/10 px-2 py-0.5 rounded inline-block w-fit">
<i class="fa-solid fa-key text-[0.65rem] mr-1"></i> Temp Pass: <strong>{{ $interview->temp_password }}</strong>
</div>
<div class="text-[0.7rem] text-sky-400">
<i class="fa-solid fa-globe text-[0.65rem] mr-1"></i> Login URL: <a href="{{ route('interview.candidate.login') }}" target="_blank" class="text-sky-400 underline">{{ route('interview.candidate.login') }}</a>
</div>
</div>
</div>
</td>
<!-- Unique Submission ID -->
<td class="py-3.5 px-4">
<code class="bg-white/5 px-2 py-1 rounded text-sky-400 text-xs font-mono">
{{ $interview->submission_unique_id }}
</code>
</td>
<!-- Status & Validity -->
<td class="py-3.5 px-4">
@if($interview->isExpired())
<x-pill variant="danger" size="sm">EXPIRED</x-pill>
@elseif($interview->status === 'completed')
<x-pill variant="success" size="sm">COMPLETED</x-pill>
@elseif(!$interview->isExpired() && $interview->call_status === 'active')
<x-pill variant="success" size="sm" icon="fa-solid fa-phone" class="animate-pulse font-extrabold">CALL IN PROGRESS</x-pill>
@elseif($interview->isActiveTimeline())
<x-pill variant="info" size="sm" icon="fa-solid fa-bolt">TIMELINE LIVE</x-pill>
@else
<x-pill variant="warning" size="sm">SCHEDULED</x-pill>
@endif
<div class="text-[0.7rem] text-slate-400 mt-1">
Timeline: {{ $interview->formatted_timeline }}
</div>
</td>
<!-- Language -->
<td class="py-3.5 px-4">
<x-pill variant="ghost" size="sm" class="uppercase font-bold">
{{ $interview->language }}
</x-pill>
</td>
<!-- Proctoring Alerts -->
<td class="py-3.5 px-4">
<x-interview.proctor-badges :metrics="$metrics" />
</td>
<!-- Review & Actions -->
<td class="py-3.5 px-4 text-right whitespace-nowrap">
<div class="inline-flex items-center gap-2 justify-end">
<!-- Button 1: Report Page -->
<a href="{{ route('interview.report', $interview->id) }}" target="_blank">
<x-button variant="success" size="sm" icon="fa-solid fa-file-pdf" class="bg-emerald-500/15 border-emerald-500/30 text-emerald-300 hover:bg-emerald-500/25">
PDF Report
</x-button>
</a>
<!-- Button 2: Join Call -->
@if(!$interview->isExpired() && $interview->call_status === 'active')
<a href="{{ route('interview.show', $interview->id) }}?join_call=1">
<x-button variant="success" size="sm" icon="fa-solid fa-phone-volume" class="animate-pulse font-extrabold shadow-md shadow-emerald-500/50">
Join Active Call
</x-button>
</a>
@else
<a href="{{ route('interview.show', $interview->id) }}">
<x-button variant="primary" size="sm" icon="fa-solid fa-video" class="bg-indigo-500/15 border-indigo-500/30 text-indigo-300 hover:bg-indigo-500/25">
Join Call
</x-button>
</a>
@endif
</div>
</td>
</tr>