253 lines
9.4 KiB
HTML
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 %} |