ATS/templates/recruitment/interview_calendar.html
2026-02-01 13:38:06 +03:00

253 lines
9.4 KiB
HTML

{% extends "base.html" %}
{% load static i18n %}
{% block title %} {% trans "Interview Calendar" %} {% endblock %}
{% block customCSS %}
<link href="https://cdn.jsdelivr.net/npm/fullcalendar@5.10.1/main.min.css" rel="stylesheet">
<style>
:root {
--calendar-color: #9d2235;
--calendar-light: rgba(157, 34, 53, 0.1);
}
.calendar-container {
background-color: white;
border-radius: 1rem;
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1);
padding: 1.5rem;
border: 1px solid #e5e7eb;
}
.fc-toolbar-title {
color: #9d2235 !important;
font-weight: 700 !important;
font-size: 1.5rem !important;
}
.fc-button-primary {
background-color: #9d2235 !important;
border-color: #9d2235 !important;
text-transform: capitalize;
font-weight: 600 !important;
padding: 0.5rem 1rem !important;
}
.fc-button-primary:hover {
background-color: #7a1a29 !important;
border-color: #7a1a29 !important;
transform: translateY(-1px);
box-shadow: 0 4px 8px rgba(157, 34, 53, 0.3) !important;
}
.fc-button-primary:not(:disabled):active,
.fc-button-primary.fc-button-active {
background-color: #5e1320 !important;
border-color: #5e1320 !important;
}
.fc-daygrid-day.fc-day-today {
background-color: rgba(157, 34, 53, 0.1) !important;
}
.fc-daygrid-day-number {
font-weight: 600;
color: #374151;
}
.fc-col-header-cell-cushion {
font-weight: 600;
color: #374151;
padding: 0.75rem;
}
.fc-event {
cursor: pointer;
padding: 2px 5px;
border-radius: 4px;
}
.status-badge {
font-size: 0.75rem;
padding: 0.25rem 0.6rem;
border-radius: 1rem;
font-weight: 600;
display: inline-block;
}
.status-scheduled { background-color: #e3f2fd; color: #0d47a1; }
.status-confirmed { background-color: #e8f5e9; color: #1b5e20; }
.status-cancelled { background-color: #ffebee; color: #b71c1c; }
.status-completed { background-color: #f5f5f5; color: #424242; }
.calendar-legend {
display: flex;
flex-wrap: wrap;
gap: 1.5rem;
margin-top: 1.5rem;
padding: 1.25rem;
background-color: #f9fafb;
border-radius: 0.75rem;
border: 1px solid #e5e7eb;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.05);
}
.legend-item {
display: flex;
align-items: center;
gap: 0.75rem;
font-size: 0.875rem;
color: #4b5563;
font-weight: 500;
}
.legend-color {
width: 14px;
height: 14px;
border-radius: 50%;
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);
}
</style>
{% endblock %}
{% block content %}
<div class="container mx-auto px-4 py-8">
<!-- Header -->
<div class="flex flex-col sm:flex-row sm:items-center sm:justify-between gap-4 mb-6">
<div>
<h1 class="text-3xl font-bold text-gray-900 mb-2 flex items-center gap-3">
<div class="bg-temple-red/10 p-3 rounded-xl">
<i data-lucide="calendar" class="w-8 h-8 text-temple-red"></i>
</div>
{% trans "Interview Calendar" %}
</h1>
<p class="text-gray-600">{{ job.title|default:"Global Schedule" }}</p>
</div>
</div>
<!-- Calendar -->
<div class="calendar-container mb-6">
<div id="calendar"></div>
<div class="calendar-legend">
<div class="legend-item">
<div class="legend-color" style="background-color: #00636e; box-shadow: 0 0 8px rgba(0, 99, 110, 0.4);"></div>
<span>{% trans "Scheduled" %}</span>
</div>
<div class="legend-item">
<div class="legend-color" style="background-color: #00a86b; box-shadow: 0 0 8px rgba(0, 168, 107, 0.4);"></div>
<span>{% trans "Confirmed" %}</span>
</div>
<div class="legend-item">
<div class="legend-color" style="background-color: #e74c3c; box-shadow: 0 0 8px rgba(231, 76, 60, 0.4);"></div>
<span>{% trans "Cancelled" %}</span>
</div>
<div class="legend-item">
<div class="legend-color" style="background-color: #95a5a6; box-shadow: 0 0 8px rgba(149, 165, 166, 0.4);"></div>
<span>{% trans "Completed" %}</span>
</div>
</div>
</div>
<!-- Interview Details -->
<div class="interview-details hidden" id="interview-details">
<div class="bg-white rounded-2xl shadow-sm border border-gray-200 overflow-hidden">
<div class="p-6 border-b border-gray-200 flex justify-between items-center bg-white">
<h5 class="text-xl font-bold text-temple-red flex items-center gap-2">
<i data-lucide="info" class="w-5 h-5"></i>
{% trans "Interview Details" %}
</h5>
<button type="button" id="close-details" class="inline-flex items-center justify-center w-8 h-8 rounded-lg border border-gray-300 hover:border-temple-red hover:text-temple-red text-gray-600 transition">
<i data-lucide="x" class="w-4 h-4"></i>
</button>
</div>
<div class="p-6" id="interview-info">
</div>
</div>
</div>
</div>
{{ events|json_script:"calendar-events-data" }}
<script src="https://cdn.jsdelivr.net/npm/fullcalendar@5.10.1/main.min.js"></script>
<script>
document.addEventListener('DOMContentLoaded', function() {
var calendarEl = document.getElementById('calendar');
const eventsData = JSON.parse(document.getElementById('calendar-events-data').textContent);
var calendar = new FullCalendar.Calendar(calendarEl, {
initialView: 'dayGridMonth',
headerToolbar: {
left: 'prev,next today',
center: 'title',
right: 'dayGridMonth,timeGridWeek,timeGridDay'
},
events: eventsData,
eventClick: function(info) {
info.jsEvent.preventDefault();
showInterviewDetails(info.event);
},
height: 'auto',
eventDisplay: 'block',
displayEventTime: true,
});
calendar.render();
function showInterviewDetails(event) {
const detailsContainer = document.getElementById('interview-details');
const infoContainer = document.getElementById('interview-info');
const status = event.extendedProps.status || 'scheduled';
const statusClass = `status-${status}`;
const statusText = status.charAt(0).toUpperCase() + status.slice(1);
let meetingInfo = '';
if (event.extendedProps.meeting_id) {
meetingInfo = `
<div class="mt-3 p-3 bg-gray-50 rounded-lg border border-gray-200">
<h6 class="text-temple-red font-bold mb-2 flex items-center gap-2">
<i data-lucide="video" class="w-4 h-4"></i>
{% trans "Meeting Information" %}
</h6>
<p class="mb-1 text-sm"><strong>{% trans "Meeting ID:" %}</strong> ${event.extendedProps.meeting_id}</p>
<p class="mb-0 text-sm"><strong>{% trans "Join URL:" %}</strong> <a href="${event.extendedProps.join_url}" target="_blank" class="text-temple-red hover:underline break-all">${event.extendedProps.join_url}</a></p>
</div>
`;
}
infoContainer.innerHTML = `
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
<div class="border-r border-gray-200 pr-0 md:pr-6">
<h6 class="text-gray-500 text-xs font-bold uppercase mb-3">{% trans "Candidate" %}</h6>
<p class="mb-2 text-sm"><strong>{% trans "Name:" %}</strong> ${event.extendedProps.candidate}</p>
<p class="text-sm"><strong>{% trans "Email:" %}</strong> ${event.extendedProps.email}</p>
</div>
<div>
<h6 class="text-gray-500 text-xs font-bold uppercase mb-3">{% trans "Schedule" %}</h6>
<p class="mb-2 text-sm"><strong>{% trans "Date:" %}</strong> ${event.start.toLocaleDateString()}</p>
<p class="mb-2 text-sm"><strong>{% trans "Time:" %}</strong> ${event.start.toLocaleTimeString([], {hour: '2-digit', minute:'2-digit'})}</p>
<p class="text-sm"><strong>{% trans "Status:" %}</strong> <span class="status-badge ${statusClass}">${statusText}</span></p>
</div>
</div>
${meetingInfo}
<div class="mt-4 pt-3 border-t border-gray-200">
<a href="${event.url}" class="inline-flex items-center gap-2 bg-temple-red hover:bg-[#7a1a29] text-white font-semibold px-4 py-2 rounded-xl transition shadow-sm hover:shadow-md">
<i data-lucide="external-link" class="w-4 h-4"></i>
{% trans "View Full Application" %}
</a>
</div>
`;
detailsContainer.classList.remove('hidden');
detailsContainer.scrollIntoView({ behavior: 'smooth', block: 'start' });
lucide.createIcons();
}
document.getElementById('close-details').addEventListener('click', function() {
document.getElementById('interview-details').classList.add('hidden');
});
});
</script>
{% endblock %}