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

402 lines
21 KiB
HTML

{% extends 'base.html' %}
{% load static i18n %}
{% block title %}{% blocktrans %}Hired Stage - {{ job.title }} - University 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="trophy" class="w-6 h-6 sm:w-7 sm:h-7"></i>
{% trans "Hired Applications" %} - {{ job.title }}
</h1>
<h2 class="text-sm sm:text-base text-gray-500 mb-0">
{% trans "Successfully Hired:" %} <span class="font-bold">{{ applications|length }}</span>
</h2>
</div>
<div class="flex gap-2 w-full sm:w-auto">
<a href="{% url 'export_applications_csv' job.slug 'hired' %}"
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 hired 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>
<!-- Success Header -->
<div class="bg-gradient-to-br from-green-500 to-teal-500 text-white p-6 rounded-xl mb-6 text-center shadow-lg">
<i data-lucide="check-circle" class="w-12 h-12 mb-3 mx-auto"></i>
<h3 class="text-xl font-bold mb-2">{% trans "Congratulations!" %}</h3>
<p class="mb-0 opacity-90">{% trans "These applications have successfully completed the hiring process and joined your team." %}</p>
</div>
<!-- ERP Sync Status -->
{% if job.source %}
<div class="bg-white border border-gray-200 rounded-xl shadow-sm p-4 sm:p-6 mb-6">
<div class="flex flex-col lg:flex-row justify-between items-start lg:items-center gap-4">
<div>
<h5 class="text-lg font-bold text-temple-dark mb-4 flex items-center gap-2">
<i data-lucide="database" class="w-5 h-5"></i> {% trans "ERP Sync Status" %}
</h5>
<div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-3">
<div>
<small class="text-gray-500 block text-xs">{% trans "Source:" %}</small>
<strong class="text-sm">{{ job.source.name }}</strong>
</div>
<div>
<small class="text-gray-500 block text-xs">{% trans "Sync Status:" %}</small>
<span class="px-2 py-1 rounded-full text-xs font-bold
{% if job.source.sync_status == 'SUCCESS' %}bg-green-500 text-white
{% elif job.source.sync_status == 'SYNCING' %}bg-yellow-400 text-yellow-900
{% elif job.source.sync_status == 'ERROR' %}bg-red-500 text-white
{% else %}bg-gray-400 text-white{% endif %}">
{{ job.source.get_sync_status_display }}
</span>
</div>
<div>
<small class="text-gray-500 block text-xs">{% trans "Last Sync:" %}</small>
<strong class="text-sm">
{% if job.source.last_sync_at %}
{{ job.source.last_sync_at|date:"M d, Y H:i" }}
{% else %}
<span class="text-gray-400">{% trans "Never" %}</span>
{% endif %}
</strong>
</div>
<div>
<small class="text-gray-500 block text-xs">{% trans "Hired Candidates:" %}</small>
<strong class="text-sm">{{ applications|length }}</strong>
</div>
</div>
</div>
</div>
<div class="bg-blue-50 border border-blue-200 text-blue-800 px-4 py-3 rounded-lg mt-4 text-sm" role="alert">
<i data-lucide="info" class="w-4 h-4 inline mr-2"></i>
{% trans "ERP sync is automatically triggered when candidates are moved to 'Hired' stage." %}
</div>
</div>
{% else %}
<div class="bg-yellow-50 border border-yellow-200 text-yellow-800 px-4 py-3 rounded-lg mb-6 text-sm" role="alert">
<i data-lucide="alert-triangle" class="w-4 h-4 inline mr-2"></i>
{% trans "No ERP source configured for this job. Automatic sync is disabled." %}
</div>
{% endif %}
<!-- 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 "Hired Applications" %}
<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">
<!-- Table -->
<div class="overflow-x-auto">
<form id="application-form" action="{% url 'application_update_status' job.slug %}" method="get">
{% 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-[15%]">
<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-left text-xs font-semibold text-temple-dark border-b-2 border-temple-red w-[15%]">
<i data-lucide="briefcase" class="w-3 h-3 inline mr-1"></i> {% trans "Applied Position" %}
</th>
<th class="px-4 py-3 text-center text-xs font-semibold text-temple-dark border-b-2 border-temple-red w-[15%]">
<i data-lucide="calendar-check" class="w-3 h-3 inline mr-1"></i> {% trans "Hired Date" %}
</th>
<th class="px-4 py-3 text-center text-xs font-semibold text-temple-dark border-b-2 border-temple-red w-[15%]">
<i data-lucide="check-circle" class="w-3 h-3 inline mr-1"></i> {% trans "Status" %}
</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="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="font-semibold text-temple-dark">
{{ application.name }}
</div>
</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 border-b border-gray-200">
<div class="text-xs text-gray-500">
<strong>{{ job.title }}</strong><br>
<small>{{ job.department }}</small>
</div>
</td>
<td class="px-4 py-3 text-center border-b border-gray-200">
<div class="text-xs text-gray-500">
{% if application.offer_date %}
<i data-lucide="calendar" class="w-3 h-3 inline mr-1"></i>
{{ application.offer_date|date:"M d, Y" }}
{% else %}
<span class="text-gray-400">--</span>
{% endif %}
</div>
</td>
<td class="px-4 py-3 text-center border-b border-gray-200">
<div class="px-3 py-1.5 bg-green-500 text-white rounded-lg text-xs font-bold inline-flex items-center gap-1">
<i data-lucide="check-circle" class="w-3 h-3"></i>
{% trans "Hired" %}
</div>
</td>
<td class="px-4 py-3 border-b border-gray-200 text-center">
<div class="flex items-center justify-center gap-1">
<button type="button"
class="px-3 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 'application_criteria_view_htmx' application.pk %}')"
title="{% trans 'View Profile' %}">
<i data-lucide="eye" class="w-3 h-3"></i>
</button>
<a href="{% url 'application_resume_template' application.slug %}"
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"
title="{% trans 'View Resume Template' %}">
<i data-lucide="file-alt" class="w-3 h-3"></i>
</a>
</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 have been hired for this position yet." %}</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 "Hired 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>
<!-- Sync Results Modal -->
<div id="syncResultsModal" class="fixed inset-0 z-50 hidden" aria-labelledby="syncResultsModalLabel" role="dialog" aria-modal="true">
<!-- Backdrop -->
<div class="absolute inset-0 bg-black/50 backdrop-blur-sm" onclick="closeSyncResultsModal()"></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="syncResultsModalLabel">
<i data-lucide="refresh-ccw" class="w-5 h-5 inline mr-2"></i>{% trans "Sync Results" %}
</h5>
<button type="button" class="text-gray-400 hover:text-gray-600 transition p-1" onclick="closeSyncResultsModal()" aria-label="Close">
<i data-lucide="x" class="w-5 h-5"></i>
</button>
</div>
<div id="syncResultsModalBody" 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 "Syncing applications..." %}</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="closeSyncResultsModal()">
{% trans "Close" %}
</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 openSyncResultsModal() {
const modal = document.getElementById('syncResultsModal');
modal.classList.remove('hidden');
document.body.style.overflow = 'hidden';
}
function closeSyncResultsModal() {
const modal = document.getElementById('syncResultsModal');
modal.classList.add('hidden');
document.body.style.overflow = '';
}
// Close modals on escape key
document.addEventListener('keydown', function(e) {
if (e.key === 'Escape') {
closeCandidateModal();
closeEmailModal();
closeSyncResultsModal();
}
});
</script>
{% endblock %}