634 lines
35 KiB
HTML
634 lines
35 KiB
HTML
{% extends 'base.html' %}
|
|
{% load static i18n %}
|
|
|
|
{% block title %}{% blocktrans %}Interview Stage - {{ job.title }} - ATS{% endblocktrans %}{% endblock %}
|
|
|
|
{% block content %}
|
|
|
|
<div class="p-3 sm:p-4 lg:p-8">
|
|
<!-- Page Header -->
|
|
<div class="flex flex-col sm:flex-row justify-between items-start sm:items-center mb-6 gap-4">
|
|
<div>
|
|
<h1 class="text-2xl sm:text-3xl font-bold text-temple-dark mb-2 flex items-center gap-2">
|
|
<i data-lucide="calendar-alt" class="w-6 h-6 sm:w-7 sm:h-7"></i>
|
|
{% trans "Interview Management" %}
|
|
</h1>
|
|
<p class="text-gray-500 text-sm sm:text-base">
|
|
{% trans "Job:" %} {{ job.title }}
|
|
<span class="inline-block ml-2 px-2 py-0.5 bg-gray-200 text-gray-700 rounded-full text-xs font-normal">
|
|
{{ job.internal_job_id }}
|
|
</span>
|
|
</p>
|
|
</div>
|
|
<div class="flex gap-2 w-full sm:w-auto">
|
|
<a href="{% url 'export_applications_csv' job.slug 'interview' %}"
|
|
class="flex items-center gap-2 px-4 py-2 border-2 border-temple-red text-temple-red rounded-lg hover:bg-temple-red hover:text-white transition text-sm font-medium"
|
|
title="{% trans 'Export interview applications to CSV' %}">
|
|
<i data-lucide="download" class="w-4 h-4"></i>
|
|
<span class="hidden sm:inline">{% trans "Export CSV" %}</span>
|
|
</a>
|
|
<a href="{% url 'job_detail' job.slug %}"
|
|
class="flex items-center gap-2 px-4 py-2 border-2 border-temple-red text-temple-red rounded-lg hover:bg-temple-red hover:text-white transition text-sm font-medium">
|
|
<i data-lucide="arrow-left" class="w-4 h-4"></i>
|
|
<span class="hidden sm:inline">{% trans "Back to Job" %}</span>
|
|
</a>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Applicant Tracking Timeline -->
|
|
<div class="mb-6">
|
|
{% include 'jobs/partials/applicant_tracking.html' %}
|
|
</div>
|
|
|
|
<!-- Application List Header -->
|
|
<div class="flex flex-col sm:flex-row justify-between items-start sm:items-center mb-4 gap-3">
|
|
<h2 class="text-xl font-bold text-temple-dark flex items-center gap-2">
|
|
<i data-lucide="users" class="w-5 h-5"></i>
|
|
{% trans "Application List" %}
|
|
<span class="ml-2 px-2 py-1 bg-temple-red text-white text-xs rounded-full">
|
|
{{ applications|length }}
|
|
</span>
|
|
</h2>
|
|
</div>
|
|
|
|
<!-- Main Card -->
|
|
<div class="bg-white border border-gray-200 rounded-xl shadow-sm overflow-hidden">
|
|
<!-- Bulk Action Bar -->
|
|
{% if applications %}
|
|
<div class="p-3 sm:p-4 bg-gray-50 border-b border-gray-200">
|
|
<div class="flex flex-col sm:flex-row gap-3 sm:gap-4 items-end">
|
|
<!-- Stage Change Form -->
|
|
<form hx-boost="true" hx-include="#application-form"
|
|
action="{% url 'application_update_status' job.slug %}"
|
|
method="post"
|
|
class="flex items-end gap-2 flex-1 w-full sm:w-auto">
|
|
{% csrf_token %}
|
|
|
|
<div class="flex-1 w-full sm:w-auto">
|
|
<select name="mark_as" id="update_status"
|
|
class="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-temple-red focus:border-transparent text-sm">
|
|
<option selected>----------</option>
|
|
<option value="Document Review">{% trans "To Document Review" %}</option>
|
|
<option value="Exam">{% trans "To Exam" %}</option>
|
|
</select>
|
|
</div>
|
|
|
|
<button id="changeStage" type="submit"
|
|
class="px-4 py-2 bg-temple-red text-white rounded-lg hover:bg-temple-red/90 transition text-sm font-medium flex items-center justify-center gap-2">
|
|
<i data-lucide="arrow-right" class="w-4 h-4"></i>
|
|
{% trans "Change Stage" %}
|
|
</button>
|
|
</form>
|
|
|
|
<div class="hidden sm:block w-px h-7 bg-gray-300"></div>
|
|
|
|
<!-- Bulk Schedule Form -->
|
|
<form hx-boost="true" hx-include="#application-form"
|
|
action="{% url 'schedule_interviews' job.slug %}"
|
|
method="get"
|
|
class="flex-1 w-full sm:w-auto">
|
|
<button id="scheduleInterview" type="submit"
|
|
class="w-full sm:w-auto px-4 py-2 bg-temple-red text-white rounded-lg hover:bg-temple-red/90 transition text-sm font-medium flex items-center justify-center gap-2">
|
|
<i data-lucide="calendar-plus" class="w-4 h-4"></i>
|
|
{% trans "Bulk Schedule Interviews" %}
|
|
</button>
|
|
</form>
|
|
|
|
<div class="hidden sm:block w-px h-7 bg-gray-300"></div>
|
|
|
|
<!-- Email Button -->
|
|
<button id="emailBotton" type="button"
|
|
class="flex-1 w-full sm:w-auto px-4 py-2 border-2 border-temple-red text-temple-red rounded-lg hover:bg-temple-red hover:text-white transition text-sm font-medium flex items-center justify-center gap-2"
|
|
onclick="openEmailModal()"
|
|
title="{% trans 'Email Participants' %}">
|
|
<i data-lucide="mail" class="w-4 h-4"></i>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
{% endif %}
|
|
|
|
<!-- Table -->
|
|
<div class="overflow-x-auto">
|
|
<form id="application-form" action="{% url 'application_update_status' job.slug %}" method="post">
|
|
{% csrf_token %}
|
|
<table class="w-full border-collapse">
|
|
<thead class="bg-gray-50">
|
|
<tr>
|
|
<th class="px-4 py-3 text-left text-xs font-semibold text-temple-dark border-b-2 border-temple-red w-[2%]">
|
|
{% if applications %}
|
|
<div class="flex items-center">
|
|
<input type="checkbox" class="h-4 w-4 text-temple-red rounded border-gray-300 focus:ring-temple-red" id="selectAllCheckbox">
|
|
</div>
|
|
{% endif %}
|
|
</th>
|
|
<th class="px-4 py-3 text-left text-xs font-semibold text-temple-dark border-b-2 border-temple-red w-[13%]">
|
|
<i data-lucide="user" class="w-3 h-3 inline mr-1"></i> {% trans "Name" %}
|
|
</th>
|
|
<th class="px-4 py-3 text-left text-xs font-semibold text-temple-dark border-b-2 border-temple-red w-[15%]">
|
|
<i data-lucide="phone" class="w-3 h-3 inline mr-1"></i> {% trans "Contact Info" %}
|
|
</th>
|
|
<th class="px-4 py-3 text-center text-xs font-semibold text-temple-dark border-b-2 border-temple-red w-[10%]">
|
|
<i data-lucide="video" class="w-3 h-3 inline mr-1"></i> {% trans "Interviews" %}
|
|
</th>
|
|
<th class="px-4 py-3 text-center text-xs font-semibold text-temple-dark border-b-2 border-temple-red w-[10%]">
|
|
<i data-lucide="sticky-note" class="w-3 h-3 inline mr-1"></i> {% trans "Notes" %}
|
|
</th>
|
|
<th class="px-4 py-3 text-center text-xs font-semibold text-temple-dark border-b-2 border-temple-red w-[10%]">
|
|
<i data-lucide="check-circle" class="w-3 h-3 inline mr-1"></i> {% trans "Result" %}
|
|
</th>
|
|
<th class="px-4 py-3 text-center text-xs font-semibold text-temple-dark border-b-2 border-temple-red w-[40%]">
|
|
<i data-lucide="settings" class="w-3 h-3 inline mr-1"></i> {% trans "Actions" %}
|
|
</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{% for application in applications %}
|
|
<tr class="hover:bg-gray-50 transition-colors">
|
|
<td class="px-4 py-3 border-b border-gray-200">
|
|
<div class="flex items-center">
|
|
<input name="candidate_ids" value="{{ application.id }}"
|
|
type="checkbox"
|
|
class="h-4 w-4 text-temple-red rounded border-gray-300 focus:ring-temple-red rowCheckbox"
|
|
id="application-{{ application.id }}">
|
|
</div>
|
|
</td>
|
|
<td class="px-4 py-3 border-b border-gray-200">
|
|
<button type="button"
|
|
class="font-semibold text-temple-red hover:text-temple-red/80 transition"
|
|
onclick="openCandidateModal('{% url 'application_criteria_view_htmx' application.pk %}')"
|
|
title="{% trans 'View Profile' %}">
|
|
{{ application.name }}
|
|
</button>
|
|
</td>
|
|
<td class="px-4 py-3 border-b border-gray-200">
|
|
<div class="text-xs text-gray-500">
|
|
<i data-lucide="mail" class="w-3 h-3 inline mr-1"></i> {{ application.email }}<br>
|
|
<i data-lucide="phone" class="w-3 h-3 inline mr-1"></i> {{ application.phone }}
|
|
</div>
|
|
</td>
|
|
<td class="px-4 py-3 text-center border-b border-gray-200">
|
|
<button type="button"
|
|
class="px-3 py-1.5 border-2 border-temple-red text-temple-red rounded-lg hover:bg-temple-red hover:text-white transition text-xs font-medium"
|
|
onclick="openCandidateModal('{% url 'get_interview_list' application.slug %}')"
|
|
title="{% trans 'View Interviews' %}">
|
|
<i data-lucide="list" class="w-3 h-3 inline mr-1"></i>
|
|
View
|
|
</button>
|
|
</td>
|
|
<td class="px-4 py-3 border-b border-gray-200 text-center">
|
|
<button type="button"
|
|
class="px-3 py-1.5 border-2 border-temple-red text-temple-red rounded-lg hover:bg-temple-red hover:text-white transition text-xs font-medium"
|
|
onclick="openNoteModal('{% url 'application_add_note' application.slug %}')"
|
|
title="{% trans 'Add Note' %}">
|
|
<i data-lucide="plus-circle" class="w-3 h-3"></i>
|
|
</button>
|
|
</td>
|
|
<td class="px-4 py-3 text-center border-b border-gray-200" id="interview-result-{{ application.pk }}">
|
|
{% if not application.interview_status %}
|
|
<button type="button"
|
|
class="px-3 py-1.5 bg-yellow-400 hover:bg-yellow-500 text-yellow-900 rounded-lg transition text-xs font-medium"
|
|
onclick="openCandidateModal('{% url 'update_application_status' job.slug application.slug 'interview' 'passed' %}')"
|
|
title="{% trans 'Add Result' %}">
|
|
<i data-lucide="plus" class="w-3 h-3"></i>
|
|
</button>
|
|
{% else %}
|
|
{% if application.interview_status %}
|
|
<button type="button"
|
|
class="px-3 py-1.5 {% if application.interview_status == 'Passed' %}bg-green-500 hover:bg-green-600 text-white{% else %}bg-red-500 hover:bg-red-600 text-white{% endif %} rounded-lg transition text-xs font-medium"
|
|
onclick="openCandidateModal('{% url 'update_application_status' job.slug application.slug 'interview' 'passed' %}')"
|
|
title="{% trans 'Update Result' %}">
|
|
{{ application.interview_status }}
|
|
</button>
|
|
{% endif %}
|
|
{% endif %}
|
|
</td>
|
|
<td class="px-4 py-3 border-b border-gray-200 text-center">
|
|
<div class="flex items-center justify-center gap-1">
|
|
{% if application.get_latest_meeting %}
|
|
{% if application.get_latest_meeting.location_type == 'Remote' %}
|
|
<button type="button"
|
|
class="px-2 py-1.5 border-2 border-gray-300 text-gray-600 rounded-lg hover:border-temple-red hover:text-temple-red transition text-xs font-medium"
|
|
onclick="openCandidateModal('{% url 'reschedule_meeting_for_application' job.slug application.pk application.get_latest_meeting.pk %}')"
|
|
title="{% trans 'Reschedule' %}">
|
|
<i data-lucide="refresh-ccw" class="w-3 h-3"></i>
|
|
</button>
|
|
<button type="button"
|
|
class="px-2 py-1.5 border-2 border-red-300 text-red-600 rounded-lg hover:bg-red-500 hover:text-white transition text-xs font-medium"
|
|
onclick="openCandidateModal('{% url 'schedule_meeting_for_application' job.slug application.pk application.get_latest_meeting.pk %}')"
|
|
title="{% trans 'Delete Meeting' %}">
|
|
<i data-lucide="trash-2" class="w-3 h-3"></i>
|
|
</button>
|
|
{% else %}
|
|
<button type="button"
|
|
class="px-2 py-1.5 border-2 border-gray-300 text-gray-600 rounded-lg hover:border-temple-red hover:text-temple-red transition text-xs font-medium"
|
|
onclick="openCandidateModal('{% url 'reschedule_onsite_meeting' job.slug application.pk application.get_latest_meeting.pk %}')"
|
|
title="{% trans 'Reschedule' %}">
|
|
<i data-lucide="refresh-ccw" class="w-3 h-3"></i>
|
|
</button>
|
|
<button type="button"
|
|
class="px-2 py-1.5 border-2 border-red-300 text-red-600 rounded-lg hover:bg-red-500 hover:text-white transition text-xs font-medium"
|
|
onclick="openCandidateModal('{% url 'delete_onsite_meeting_for_application' job.slug application.pk application.get_latest_meeting.pk %}')"
|
|
title="{% trans 'Delete Meeting' %}">
|
|
<i data-lucide="trash-2" class="w-3 h-3"></i>
|
|
</button>
|
|
{% endif %}
|
|
{% else %}
|
|
<button type="button"
|
|
class="px-3 py-1.5 border-2 border-temple-red text-temple-red rounded-lg hover:bg-temple-red hover:text-white transition text-xs font-medium"
|
|
onclick="openCandidateModal('{% url 'interview_create_type_selection' application.slug %}')"
|
|
title="{% trans 'Schedule Interview' %}">
|
|
<i data-lucide="calendar-plus" class="w-3 h-3 inline mr-1"></i>
|
|
{% trans "Schedule Interview" %}
|
|
</button>
|
|
{% endif %}
|
|
</div>
|
|
</td>
|
|
</tr>
|
|
{% endfor %}
|
|
</tbody>
|
|
</table>
|
|
|
|
{% if not applications %}
|
|
<div class="p-8 text-center bg-blue-50 border border-blue-200 rounded-lg m-4">
|
|
<i data-lucide="info" class="w-8 h-8 text-blue-500 mx-auto mb-2"></i>
|
|
<p class="text-blue-700 text-sm">{% trans "No applications are currently in the Interview stage for this job." %}</p>
|
|
</div>
|
|
{% endif %}
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Candidate View Modal -->
|
|
<div id="candidateviewModal" class="fixed inset-0 z-50 hidden" aria-labelledby="candidateviewModalLabel" role="dialog" aria-modal="true">
|
|
<!-- Backdrop -->
|
|
<div class="absolute inset-0 bg-black/50 backdrop-blur-sm" onclick="closeCandidateModal()"></div>
|
|
|
|
<!-- Modal Content -->
|
|
<div class="relative z-10 flex min-h-screen items-center justify-center p-4">
|
|
<div class="bg-white border border-gray-200 rounded-xl shadow-lg w-full max-w-4xl max-h-[90vh] overflow-hidden flex flex-col">
|
|
<div class="flex justify-between items-center px-6 py-4 border-b border-gray-200">
|
|
<h5 class="text-lg font-bold text-temple-dark" id="candidateviewModalLabel">
|
|
{% trans "Application Details" %}
|
|
</h5>
|
|
<button type="button" class="text-gray-400 hover:text-gray-600 transition p-1" onclick="closeCandidateModal()" aria-label="Close">
|
|
<i data-lucide="x" class="w-5 h-5"></i>
|
|
</button>
|
|
</div>
|
|
<div id="candidateviewModalBody" class="flex-1 overflow-y-auto p-6">
|
|
<div class="text-center py-10 text-gray-500">
|
|
<i data-lucide="loader-2" class="w-10 h-10 animate-spin mx-auto mb-3 text-temple-red"></i>
|
|
<p>{% trans "Loading application data..." %}</p>
|
|
</div>
|
|
</div>
|
|
<div class="px-6 py-4 border-t border-gray-200 bg-gray-50">
|
|
<button type="button" class="w-full px-4 py-2 border-2 border-gray-300 text-gray-600 rounded-lg hover:border-temple-red hover:text-temple-red transition text-sm font-medium" onclick="closeCandidateModal()">
|
|
{% trans "Close" %}
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Email Modal -->
|
|
<div id="emailModal" class="fixed inset-0 z-50 hidden" aria-labelledby="emailModalLabel" role="dialog" aria-modal="true">
|
|
<!-- Backdrop -->
|
|
<div class="absolute inset-0 bg-black/50 backdrop-blur-sm" onclick="closeEmailModal()"></div>
|
|
|
|
<!-- Modal Content -->
|
|
<div class="relative z-10 flex min-h-screen items-center justify-center p-4">
|
|
<div class="bg-white border border-gray-200 rounded-xl shadow-lg w-full max-w-4xl max-h-[90vh] overflow-hidden flex flex-col">
|
|
<div class="flex justify-between items-center px-6 py-4 border-b border-gray-200">
|
|
<h5 class="text-lg font-bold text-temple-dark" id="emailModalLabel">
|
|
<i data-lucide="mail" class="w-5 h-5 inline mr-2"></i>{% trans "Compose Email" %}
|
|
</h5>
|
|
<button type="button" class="text-gray-400 hover:text-gray-600 transition p-1" onclick="closeEmailModal()" aria-label="Close">
|
|
<i data-lucide="x" class="w-5 h-5"></i>
|
|
</button>
|
|
</div>
|
|
<div id="emailModalBody" class="flex-1 overflow-y-auto p-6">
|
|
<div class="text-center py-10 text-gray-500">
|
|
<i data-lucide="loader-2" class="w-10 h-10 animate-spin mx-auto mb-3 text-temple-red"></i>
|
|
<p>{% trans "Loading email form..." %}</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Note Modal -->
|
|
<div id="noteModal" class="fixed inset-0 z-50 hidden" aria-labelledby="noteModalLabel" role="dialog" aria-modal="true">
|
|
<!-- Backdrop -->
|
|
<div class="absolute inset-0 bg-black/50 backdrop-blur-sm" onclick="closeNoteModal()"></div>
|
|
|
|
<!-- Modal Content -->
|
|
<div class="relative z-10 flex min-h-screen items-center justify-center p-4">
|
|
<div class="bg-white border border-gray-200 rounded-xl shadow-lg w-full max-w-4xl max-h-[90vh] overflow-hidden flex flex-col">
|
|
<div class="flex justify-between items-center px-6 py-4 border-b border-gray-200">
|
|
<h5 class="text-lg font-bold text-temple-dark" id="noteModalLabel">
|
|
<i data-lucide="sticky-note" class="w-5 h-5 inline mr-2"></i>{% trans "Add Note" %}
|
|
</h5>
|
|
<button type="button" class="text-gray-400 hover:text-gray-600 transition p-1" onclick="closeNoteModal()" aria-label="Close">
|
|
<i data-lucide="x" class="w-5 h-5"></i>
|
|
</button>
|
|
</div>
|
|
<div id="noteModalBody" class="flex-1 overflow-y-auto p-6">
|
|
<div class="text-center py-10 text-gray-500">
|
|
<i data-lucide="loader-2" class="w-10 h-10 animate-spin mx-auto mb-3 text-temple-red"></i>
|
|
<p>{% trans "Loading note form..." %}</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Stage Confirmation Modal -->
|
|
<div id="stageConfirmationModal" class="fixed inset-0 z-50 hidden" aria-labelledby="stageConfirmationModalLabel" role="dialog" aria-modal="true">
|
|
<!-- Backdrop -->
|
|
<div class="absolute inset-0 bg-black/50 backdrop-blur-sm" onclick="closeStageConfirmationModal()"></div>
|
|
|
|
<!-- Modal Content -->
|
|
<div class="relative z-10 flex min-h-screen items-center justify-center p-4">
|
|
<div class="bg-white border border-gray-200 rounded-xl shadow-lg w-full max-w-lg">
|
|
<div class="flex justify-between items-center px-6 py-4 border-b border-gray-200">
|
|
<h5 class="text-lg font-bold text-temple-dark" id="stageConfirmationModalLabel">
|
|
<i data-lucide="info" class="w-5 h-5 inline mr-2"></i>{% trans "Confirm Stage Change" %}
|
|
</h5>
|
|
<button type="button" class="text-gray-400 hover:text-gray-600 transition p-1" onclick="closeStageConfirmationModal()" aria-label="Close">
|
|
<i data-lucide="x" class="w-5 h-5"></i>
|
|
</button>
|
|
</div>
|
|
<div class="p-6">
|
|
<div class="flex items-center justify-center py-3 mb-3">
|
|
<i data-lucide="arrow-right-left" class="w-16 h-16 text-temple-red"></i>
|
|
</div>
|
|
<p class="text-center mb-2 text-base text-gray-800">
|
|
<span id="stageConfirmationMessage">{% trans "Are you sure you want to change to this stage?" %}</span>
|
|
</p>
|
|
<div class="bg-blue-50 border border-blue-200 text-blue-800 px-4 py-3 rounded-lg text-center" role="alert">
|
|
<i data-lucide="user-check" class="w-4 h-4 inline mr-2"></i>
|
|
<span class="font-semibold">{% trans "Selected Stage:" %}</span>
|
|
<span id="targetStageName" class="font-bold">{% trans "--" %}</span>
|
|
</div>
|
|
</div>
|
|
<div class="px-6 py-4 border-t border-gray-200 flex justify-end gap-2">
|
|
<button type="button" class="px-4 py-2 border-2 border-gray-300 text-gray-600 rounded-lg hover:border-temple-red hover:text-temple-red transition text-sm font-medium" onclick="closeStageConfirmationModal()">
|
|
<i data-lucide="x" class="w-4 h-4 inline mr-1"></i>{% trans "Cancel" %}
|
|
</button>
|
|
<button type="button" class="px-4 py-2 bg-temple-red text-white rounded-lg hover:bg-temple-red/90 transition text-sm font-medium" id="confirmStageChangeButton">
|
|
<i data-lucide="check" class="w-4 h-4 inline mr-1"></i>{% trans "Confirm" %}
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{% endblock %}
|
|
|
|
{% block customJS %}
|
|
<script>
|
|
// Reinitialize Lucide icons after content loads
|
|
document.addEventListener('DOMContentLoaded', function () {
|
|
if (typeof lucide !== 'undefined') {
|
|
lucide.createIcons();
|
|
}
|
|
});
|
|
|
|
// ========================================
|
|
// Modal Functions
|
|
// ========================================
|
|
|
|
function openCandidateModal(url) {
|
|
const modal = document.getElementById('candidateviewModal');
|
|
const modalBody = document.getElementById('candidateviewModalBody');
|
|
|
|
// Reset content
|
|
modalBody.innerHTML = `
|
|
<div class="text-center py-10 text-gray-500">
|
|
<i data-lucide="loader-2" class="w-10 h-10 animate-spin mx-auto mb-3 text-temple-red"></i>
|
|
<p>{% trans "Loading application data..." %}</p>
|
|
</div>
|
|
`;
|
|
|
|
// Show modal
|
|
modal.classList.remove('hidden');
|
|
document.body.style.overflow = 'hidden';
|
|
|
|
// Load content via HTMX
|
|
if (url && typeof htmx !== 'undefined') {
|
|
htmx.ajax('GET', url, {target: '#candidateviewModalBody', swap: 'innerHTML'});
|
|
}
|
|
|
|
// Reinitialize icons
|
|
setTimeout(() => {
|
|
if (typeof lucide !== 'undefined') lucide.createIcons();
|
|
}, 100);
|
|
}
|
|
|
|
function closeCandidateModal() {
|
|
const modal = document.getElementById('candidateviewModal');
|
|
modal.classList.add('hidden');
|
|
document.body.style.overflow = '';
|
|
}
|
|
|
|
function openEmailModal() {
|
|
const modal = document.getElementById('emailModal');
|
|
const modalBody = document.getElementById('emailModalBody');
|
|
const applicationForm = document.getElementById('application-form');
|
|
const url = '{% url "compose_application_email" job.slug %}';
|
|
|
|
// Reset content
|
|
modalBody.innerHTML = `
|
|
<div class="text-center py-10 text-gray-500">
|
|
<i data-lucide="loader-2" class="w-10 h-10 animate-spin mx-auto mb-3 text-temple-red"></i>
|
|
<p>{% trans "Loading email form..." %}</p>
|
|
</div>
|
|
`;
|
|
|
|
// Show modal
|
|
modal.classList.remove('hidden');
|
|
document.body.style.overflow = 'hidden';
|
|
|
|
// Load content via HTMX with form data
|
|
if (url && typeof htmx !== 'undefined' && applicationForm) {
|
|
htmx.ajax('GET', url, {
|
|
target: '#emailModalBody',
|
|
swap: 'innerHTML',
|
|
values: htmx.values(applicationForm)
|
|
});
|
|
}
|
|
|
|
// Reinitialize icons
|
|
setTimeout(() => {
|
|
if (typeof lucide !== 'undefined') lucide.createIcons();
|
|
}, 100);
|
|
}
|
|
|
|
function closeEmailModal() {
|
|
const modal = document.getElementById('emailModal');
|
|
modal.classList.add('hidden');
|
|
document.body.style.overflow = '';
|
|
}
|
|
|
|
function openNoteModal(url) {
|
|
const modal = document.getElementById('noteModal');
|
|
const modalBody = document.getElementById('noteModalBody');
|
|
|
|
// Reset content
|
|
modalBody.innerHTML = `
|
|
<div class="text-center py-10 text-gray-500">
|
|
<i data-lucide="loader-2" class="w-10 h-10 animate-spin mx-auto mb-3 text-temple-red"></i>
|
|
<p>{% trans "Loading note form..." %}</p>
|
|
</div>
|
|
`;
|
|
|
|
// Show modal
|
|
modal.classList.remove('hidden');
|
|
document.body.style.overflow = 'hidden';
|
|
|
|
// Load content via HTMX
|
|
if (url && typeof htmx !== 'undefined') {
|
|
htmx.ajax('GET', url, {target: '#noteModalBody', swap: 'innerHTML'});
|
|
}
|
|
|
|
// Reinitialize icons
|
|
setTimeout(() => {
|
|
if (typeof lucide !== 'undefined') lucide.createIcons();
|
|
}, 100);
|
|
}
|
|
|
|
function closeNoteModal() {
|
|
const modal = document.getElementById('noteModal');
|
|
modal.classList.add('hidden');
|
|
document.body.style.overflow = '';
|
|
}
|
|
|
|
function openStageConfirmationModal(selectedStage) {
|
|
const modal = document.getElementById('stageConfirmationModal');
|
|
const messageElement = document.getElementById('stageConfirmationMessage');
|
|
const targetStageElement = document.getElementById('targetStageName');
|
|
|
|
// Update confirmation message
|
|
if (messageElement && targetStageElement) {
|
|
const checkedCount = Array.from(document.querySelectorAll('.rowCheckbox:checked')).length;
|
|
if (checkedCount > 0) {
|
|
messageElement.textContent = `{% trans "Are you sure you want to move" %} ${checkedCount} {% trans "candidate(s) to this stage?" %}`;
|
|
targetStageElement.textContent = selectedStage;
|
|
} else {
|
|
messageElement.textContent = '{% trans "Please select at least one candidate." %}';
|
|
targetStageElement.textContent = '--';
|
|
}
|
|
}
|
|
|
|
// Show modal
|
|
modal.classList.remove('hidden');
|
|
document.body.style.overflow = 'hidden';
|
|
}
|
|
|
|
function closeStageConfirmationModal() {
|
|
const modal = document.getElementById('stageConfirmationModal');
|
|
modal.classList.add('hidden');
|
|
document.body.style.overflow = '';
|
|
}
|
|
|
|
// ========================================
|
|
// Checkbox and Form Logic
|
|
// ========================================
|
|
document.addEventListener('DOMContentLoaded', function () {
|
|
const selectAllCheckbox = document.getElementById('selectAllCheckbox');
|
|
const rowCheckboxes = document.querySelectorAll('.rowCheckbox');
|
|
const changeStageButton = document.getElementById('changeStage');
|
|
const emailButton = document.getElementById('emailBotton');
|
|
const updateStatus = document.getElementById('update_status');
|
|
const scheduleInterviewButton = document.getElementById('scheduleInterview');
|
|
const confirmStageChangeButton = document.getElementById('confirmStageChangeButton');
|
|
let isConfirmed = false;
|
|
|
|
if (selectAllCheckbox) {
|
|
// Function to safely update header checkbox state
|
|
function updateSelectAllState() {
|
|
const checkedCount = Array.from(rowCheckboxes).filter(cb => cb.checked).length;
|
|
const totalCount = rowCheckboxes.length;
|
|
|
|
if (checkedCount === 0) {
|
|
selectAllCheckbox.checked = false;
|
|
selectAllCheckbox.indeterminate = false;
|
|
if (changeStageButton) changeStageButton.disabled = true;
|
|
if (emailButton) emailButton.disabled = true;
|
|
if (updateStatus) updateStatus.disabled = true;
|
|
if (scheduleInterviewButton) scheduleInterviewButton.disabled = true;
|
|
} else if (checkedCount === totalCount) {
|
|
selectAllCheckbox.checked = true;
|
|
selectAllCheckbox.indeterminate = false;
|
|
if (changeStageButton) changeStageButton.disabled = false;
|
|
if (emailButton) emailButton.disabled = false;
|
|
if (updateStatus) updateStatus.disabled = false;
|
|
if (scheduleInterviewButton) scheduleInterviewButton.disabled = false;
|
|
} else {
|
|
selectAllCheckbox.checked = false;
|
|
selectAllCheckbox.indeterminate = true;
|
|
if (changeStageButton) changeStageButton.disabled = false;
|
|
if (emailButton) emailButton.disabled = false;
|
|
if (updateStatus) updateStatus.disabled = false;
|
|
if (scheduleInterviewButton) scheduleInterviewButton.disabled = false;
|
|
}
|
|
}
|
|
|
|
// Logic for 'Select All' checkbox
|
|
selectAllCheckbox.addEventListener('change', function () {
|
|
const isChecked = selectAllCheckbox.checked;
|
|
|
|
rowCheckboxes.forEach(checkbox => checkbox.removeEventListener('change', updateSelectAllState));
|
|
|
|
rowCheckboxes.forEach(function (checkbox) {
|
|
checkbox.checked = isChecked;
|
|
checkbox.dispatchEvent(new Event('change', { bubbles: true }));
|
|
});
|
|
|
|
rowCheckboxes.forEach(checkbox => checkbox.addEventListener('change', updateSelectAllState));
|
|
updateSelectAllState();
|
|
});
|
|
|
|
rowCheckboxes.forEach(function (checkbox) {
|
|
checkbox.addEventListener('change', updateSelectAllState);
|
|
});
|
|
|
|
updateSelectAllState();
|
|
}
|
|
|
|
// Stage Confirmation Logic
|
|
if (changeStageButton) {
|
|
changeStageButton.addEventListener('click', function(event) {
|
|
const selectedStage = updateStatus.value;
|
|
|
|
if (selectedStage && selectedStage.trim() !== '') {
|
|
if (!isConfirmed) {
|
|
event.preventDefault();
|
|
openStageConfirmationModal(selectedStage);
|
|
return false;
|
|
}
|
|
isConfirmed = false;
|
|
}
|
|
});
|
|
|
|
if (confirmStageChangeButton) {
|
|
confirmStageChangeButton.addEventListener('click', function() {
|
|
closeStageConfirmationModal();
|
|
isConfirmed = true;
|
|
changeStageButton.click();
|
|
});
|
|
}
|
|
}
|
|
|
|
// Close modals on escape key
|
|
document.addEventListener('keydown', function(e) {
|
|
if (e.key === 'Escape') {
|
|
closeCandidateModal();
|
|
closeEmailModal();
|
|
closeNoteModal();
|
|
closeStageConfirmationModal();
|
|
}
|
|
});
|
|
});
|
|
</script>
|
|
{% endblock %} |