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

231 lines
10 KiB
HTML

{% extends "base.html" %}
{% load static i18n crispy_forms_tags %}
{% block title %}{% trans "Update" %} {{ object.name }} - {{ block.super }}{% endblock %}
{% block content %}
<div class="px-4 py-6 max-w-4xl mx-auto">
<!-- Breadcrumb Navigation -->
<nav aria-label="breadcrumb" class="mb-6">
<ol class="flex items-center space-x-2 text-sm">
<li>
<a href="{% url 'application_list' %}" class="text-gray-500 hover:text-temple-red transition">
<i data-lucide="users" class="w-4 h-4 inline mr-1"></i> {% trans "Applications" %}
</a>
</li>
<li class="text-gray-400">/</li>
<li>
<a href="{% url 'application_detail' object.slug %}" class="text-gray-500 hover:text-temple-red transition">
{{ object.name }}
</a>
</li>
<li class="text-temple-red font-semibold" aria-current="page">{% trans "Update" %}</li>
</ol>
</nav>
<!-- Header -->
<div class="flex flex-col sm:flex-row sm:justify-between sm:items-center gap-4 mb-6">
<h3 class="text-2xl font-bold text-temple-dark flex items-center gap-2">
<i data-lucide="user-edit" class="w-6 h-6"></i>
{% trans "Update Application" %}
</h3>
<div class="flex gap-2">
<a href="{% url 'application_detail' object.slug %}" class="inline-flex items-center gap-2 border border-gray-300 text-gray-700 hover:bg-gray-50 px-4 py-2 rounded-lg text-sm font-medium transition">
<i data-lucide="eye" class="w-4 h-4"></i>
{% trans "View Details" %}
</a>
<a href="{% url 'application_delete' object.slug %}" class="inline-flex items-center gap-2 bg-red-500 hover:bg-red-600 text-white px-4 py-2 rounded-lg text-sm font-medium transition">
<i data-lucide="trash-2" class="w-4 h-4"></i>
{% trans "Delete" %}
</a>
<a href="{% url 'application_list' %}" class="inline-flex items-center gap-2 border border-gray-300 text-gray-700 hover:bg-gray-50 px-4 py-2 rounded-lg text-sm font-medium transition">
<i data-lucide="arrow-left" class="w-4 h-4"></i>
{% trans "Back to List" %}
</a>
</div>
</div>
<!-- Current Profile Info -->
<div class="bg-white rounded-xl shadow-md border border-gray-200 mb-6">
<div class="p-6">
<div class="bg-gray-50 rounded-lg p-4">
<h6 class="text-sm font-semibold text-temple-dark mb-3 flex items-center gap-2">
<i data-lucide="info" class="w-4 h-4"></i>
{% trans "Currently Editing" %}
</h6>
<div class="flex items-center gap-4">
{% if object.profile_image %}
<img src="{{ object.profile_image.url }}" alt="{{ object.name }}"
class="w-24 h-24 rounded-full border-2 border-temple-red object-cover">
{% else %}
<div class="w-24 h-24 rounded-full border-2 border-gray-300 bg-gray-100 flex items-center justify-center">
<i data-lucide="user" class="w-8 h-8 text-gray-400"></i>
</div>
{% endif %}
<div>
<h5 class="text-lg font-semibold text-gray-900 mb-1">{{ object.name }}</h5>
{% if object.email %}
<p class="text-gray-600 text-sm mb-1">{{ object.email }}</p>
{% endif %}
<p class="text-gray-500 text-xs">
{% trans "Created" %}: {{ object.created_at|date:"d M Y" }} •
{% trans "Last Updated" %}: {{ object.updated_at|date:"d M Y" }}
</p>
</div>
</div>
</div>
</div>
</div>
<!-- Form Card -->
<div class="bg-white rounded-xl shadow-md border border-gray-200">
<div class="p-6">
{% if form.non_field_errors %}
<div class="bg-red-50 border border-red-200 rounded-lg p-4 mb-4" role="alert">
<h5 class="font-semibold text-red-800 flex items-center gap-2 mb-2">
<i data-lucide="alert-triangle" class="w-5 h-5"></i>
{% trans "Error" %}
</h5>
{% for error in form.non_field_errors %}
<p class="text-red-700 text-sm">{{ error }}</p>
{% endfor %}
</div>
{% endif %}
{% if messages %}
{% for message in messages %}
<div class="rounded-lg p-4 mb-4 {% if message.tags == 'success' %}bg-green-50 border border-green-200 text-green-800{% elif message.tags == 'error' %}bg-red-50 border border-red-200 text-red-800{% else %}bg-blue-50 border border-blue-200 text-blue-800{% endif %}" role="alert">
{{ message }}
</div>
{% endfor %}
{% endif %}
<form method="post" action="{% url 'application_update' object.slug %}" enctype="multipart/form-data" id="candidate-form">
{% csrf_token %}
{{ form|crispy }}
</form>
<div class="flex gap-2 mt-6">
<button form="candidate-form" type="submit" class="bg-temple-red hover:bg-red-800 text-white font-semibold px-8 py-3 rounded-xl transition shadow-md hover:shadow-lg flex items-center gap-2">
<i data-lucide="save" class="w-5 h-5"></i>
{% trans "Update" %}
</button>
</div>
</div>
</div>
</div>
{% endblock %}
{% block customJS %}
<script>
document.addEventListener('DOMContentLoaded', function() {
// Initialize Lucide icons
lucide.createIcons();
// Add form styling
const formInputs = document.querySelectorAll('input:not([type="hidden"]), select, textarea');
formInputs.forEach(input => {
input.classList.add('w-full', 'px-3', 'py-2.5', 'border', 'border-gray-300', 'rounded-lg', 'focus:ring-2', 'focus:ring-temple-red', 'focus:border-transparent', 'transition');
});
// Style form labels and containers
const formGroups = document.querySelectorAll('.form-group');
formGroups.forEach(group => {
group.classList.add('mb-6');
});
const formLabels = document.querySelectorAll('label');
formLabels.forEach(label => {
label.classList.add('block', 'text-sm', 'font-semibold', 'text-gray-700', 'mb-2');
});
// Profile Image Preview
const profileImageInput = document.getElementById('id_profile_image');
const imagePreviewContainer = document.getElementById('image-preview-container');
const originalImage = imagePreviewContainer ? imagePreviewContainer.innerHTML : '';
if (profileImageInput) {
profileImageInput.addEventListener('change', function(e) {
const file = e.target.files[0];
if (file && file.type.startsWith('image/')) {
const reader = new FileReader();
reader.onload = function(e) {
if (imagePreviewContainer) {
imagePreviewContainer.innerHTML = `
<img src="${e.target.result}" alt="Profile Preview" class="w-32 h-32 rounded-full border-3 border-temple-red object-cover mx-auto mb-3">
<h5 class="text-gray-600 text-sm font-medium">${file.name}</h5>
<p class="text-gray-500 text-xs">{% trans "New photo selected" %}</p>
`;
}
};
reader.readAsDataURL(file);
} else if (!file && imagePreviewContainer) {
// Reset to original if no file selected
imagePreviewContainer.innerHTML = originalImage;
}
});
}
// Form Validation
const form = document.getElementById('candidate-form');
if (form) {
form.addEventListener('submit', function(e) {
const submitBtn = form.querySelector('button[type="submit"]');
// Add loading state
submitBtn.disabled = true;
submitBtn.innerHTML = `<i data-lucide="loader-2" class="w-5 h-5 mr-2 animate-spin"></i> {% trans "Saving..." %}`;
// Basic validation
const name = document.getElementById('id_name');
if (name && !name.value.trim()) {
e.preventDefault();
submitBtn.disabled = false;
submitBtn.innerHTML = `<i data-lucide="save" class="w-5 h-5 mr-2"></i> {% trans "Update" %}`;
alert('{% trans "Name is required." %}');
return;
}
const email = document.getElementById('id_email');
if (email && email.value.trim() && !isValidEmail(email.value.trim())) {
e.preventDefault();
submitBtn.disabled = false;
submitBtn.innerHTML = `<i data-lucide="save" class="w-5 h-5 mr-2"></i> {% trans "Update" %}`;
alert('{% trans "Please enter a valid email address." %}');
return;
}
});
}
// Email validation helper
function isValidEmail(email) {
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
return emailRegex.test(email);
}
// Warn before leaving if changes are made
let formChanged = false;
const formInputsTrack = form ? form.querySelectorAll('input, select, textarea') : [];
formInputsTrack.forEach(input => {
input.addEventListener('change', function() {
formChanged = true;
});
});
window.addEventListener('beforeunload', function(e) {
if (formChanged) {
e.preventDefault();
e.returnValue = '{% trans "You have unsaved changes. Are you sure you want to leave?" %}';
return e.returnValue;
}
});
if (form) {
form.addEventListener('submit', function() {
formChanged = false;
});
}
});
</script>
{% endblock %}