279 lines
14 KiB
HTML
279 lines
14 KiB
HTML
{% extends "base.html" %}
|
|
{% load static i18n crispy_forms_tags %}
|
|
|
|
{% block title %}{% trans "Create Application" %} - {{ block.super }}{% endblock %}
|
|
|
|
{% block content %}
|
|
<div class="px-4 py-6">
|
|
<!-- Header Card -->
|
|
<div class="bg-gradient-to-br from-temple-red to-red-800 rounded-xl shadow-xl p-6 mb-6 text-white">
|
|
<div class="flex flex-col md:flex-row md:justify-between md:items-start gap-4">
|
|
<div class="flex-1">
|
|
<h1 class="text-3xl font-bold mb-2 flex items-center gap-2">
|
|
<i data-lucide="user-plus" class="w-8 h-8"></i>
|
|
{% trans "Create New Application" %}
|
|
</h1>
|
|
<p class="text-red-100 text-lg">{% trans "Enter details to create a new application record." %}</p>
|
|
</div>
|
|
<div class="flex gap-2">
|
|
<button type="button" class="modal-trigger bg-white/20 hover:bg-white/30 backdrop-blur-sm text-white px-4 py-2 rounded-lg text-sm font-medium transition flex items-center gap-2" data-modal="personModal">
|
|
<i data-lucide="user-plus" class="w-4 h-4"></i>
|
|
<span class="hidden sm:inline">{% trans "Create New Applicant" %}</span>
|
|
</button>
|
|
<a href="{% url 'application_list' %}" class="bg-white/20 hover:bg-white/30 backdrop-blur-sm text-white px-4 py-2 rounded-lg text-sm font-medium transition flex items-center gap-2" title="{% trans 'Back to List' %}">
|
|
<i data-lucide="arrow-left" class="w-4 h-4"></i>
|
|
<span class="hidden sm:inline">{% trans "Back to List" %}</span>
|
|
</a>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Form Card -->
|
|
<div class="bg-white rounded-xl shadow-md overflow-hidden border border-gray-200">
|
|
<div class="px-6 py-4 border-b border-gray-200 bg-gray-50">
|
|
<h2 class="text-xl font-semibold text-temple-dark flex items-center gap-2">
|
|
<i data-lucide="file-text" class="w-5 h-5"></i>
|
|
{% trans "Application Information" %}
|
|
</h2>
|
|
</div>
|
|
<div class="p-6">
|
|
<form method="post" enctype="multipart/form-data">
|
|
{% csrf_token %}
|
|
|
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
|
|
{% for field in form %}
|
|
<div>
|
|
<label for="{{ field.id_for_label }}" class="block text-sm font-semibold text-gray-700 mb-2">
|
|
{{ field.label }}
|
|
{% if field.field.required %}<span class="text-red-500">*</span>{% endif %}
|
|
</label>
|
|
{{ field }}
|
|
{% if field.help_text %}
|
|
<p class="text-sm text-gray-500 mt-1">{{ field.help_text }}</p>
|
|
{% endif %}
|
|
{% for error in field.errors %}
|
|
<p class="text-sm text-red-500 mt-1">{{ error }}</p>
|
|
{% endfor %}
|
|
</div>
|
|
{% endfor %}
|
|
</div>
|
|
|
|
<div class="border-t border-gray-200 mt-6 pt-6">
|
|
<button 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 "Create Application" %}
|
|
</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Modal -->
|
|
<div class="hidden fixed inset-0 z-50 overflow-y-auto" id="personModal" role="dialog" aria-labelledby="personModalLabel">
|
|
<div class="flex items-center justify-center min-h-screen px-4 pt-4 pb-20 text-center sm:block sm:p-0">
|
|
<div class="fixed inset-0 bg-black/50 transition-opacity" aria-hidden="true"></div>
|
|
<span class="hidden sm:inline-block sm:align-middle sm:h-screen sm:align-middle" aria-hidden="true">​</span>
|
|
<div class="inline-block align-bottom bg-white rounded-2xl text-left overflow-hidden shadow-xl transform transition-all sm:my-8 sm:align-middle sm:max-w-3xl sm:w-full">
|
|
<div class="bg-white px-4 pt-5 pb-4 sm:p-6 border-b border-gray-200 flex justify-between items-center">
|
|
<h3 class="text-lg font-semibold text-gray-900 flex items-center gap-2" id="personModalLabel">
|
|
<i data-lucide="help-circle" class="w-5 h-5 text-temple-red"></i>
|
|
{% trans "Create New Applicant" %}
|
|
</h3>
|
|
<button type="button" class="modal-close-btn text-gray-400 hover:text-gray-600 transition">
|
|
<i data-lucide="x" class="w-5 h-5"></i>
|
|
</button>
|
|
</div>
|
|
<div class="px-4 pt-5 pb-4 sm:p-6">
|
|
<form id="person_form" hx-post="{% url 'person_create' %}" hx-vals='{"view":"job"}' hx-target="#div_id_person" hx-select="#div_id_person" hx-swap="outerHTML">
|
|
{% csrf_token %}
|
|
<div class="grid grid-cols-1 md:grid-cols-3 gap-4">
|
|
<div>
|
|
<label for="{{ person_form.first_name.id_for_label }}" class="block text-sm font-semibold text-gray-700 mb-2">
|
|
{{ person_form.first_name.label }}
|
|
</label>
|
|
{{ person_form.first_name }}
|
|
{% for error in person_form.first_name.errors %}
|
|
<p class="text-sm text-red-500 mt-1">{{ error }}</p>
|
|
{% endfor %}
|
|
</div>
|
|
<div>
|
|
<label for="{{ person_form.middle_name.id_for_label }}" class="block text-sm font-semibold text-gray-700 mb-2">
|
|
{{ person_form.middle_name.label }}
|
|
</label>
|
|
{{ person_form.middle_name }}
|
|
{% for error in person_form.middle_name.errors %}
|
|
<p class="text-sm text-red-500 mt-1">{{ error }}</p>
|
|
{% endfor %}
|
|
</div>
|
|
<div>
|
|
<label for="{{ person_form.last_name.id_for_label }}" class="block text-sm font-semibold text-gray-700 mb-2">
|
|
{{ person_form.last_name.label }}
|
|
</label>
|
|
{{ person_form.last_name }}
|
|
{% for error in person_form.last_name.errors %}
|
|
<p class="text-sm text-red-500 mt-1">{{ error }}</p>
|
|
{% endfor %}
|
|
</div>
|
|
</div>
|
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-4 mt-4">
|
|
<div>
|
|
<label for="{{ person_form.email.id_for_label }}" class="block text-sm font-semibold text-gray-700 mb-2">
|
|
{{ person_form.email.label }}
|
|
</label>
|
|
{{ person_form.email }}
|
|
{% for error in person_form.email.errors %}
|
|
<p class="text-sm text-red-500 mt-1">{{ error }}</p>
|
|
{% endfor %}
|
|
</div>
|
|
<div>
|
|
<label for="{{ person_form.phone.id_for_label }}" class="block text-sm font-semibold text-gray-700 mb-2">
|
|
{{ person_form.phone.label }}
|
|
</label>
|
|
{{ person_form.phone }}
|
|
{% for error in person_form.phone.errors %}
|
|
<p class="text-sm text-red-500 mt-1">{{ error }}</p>
|
|
{% endfor %}
|
|
</div>
|
|
</div>
|
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-4 mt-4">
|
|
<div>
|
|
<label for="{{ person_form.gpa.id_for_label }}" class="block text-sm font-semibold text-gray-700 mb-2">
|
|
{{ person_form.gpa.label }}
|
|
</label>
|
|
{{ person_form.gpa }}
|
|
{% for error in person_form.gpa.errors %}
|
|
<p class="text-sm text-red-500 mt-1">{{ error }}</p>
|
|
{% endfor %}
|
|
</div>
|
|
<div>
|
|
<label for="{{ person_form.national_id.id_for_label }}" class="block text-sm font-semibold text-gray-700 mb-2">
|
|
{{ person_form.national_id.label }}
|
|
</label>
|
|
{{ person_form.national_id }}
|
|
{% for error in person_form.national_id.errors %}
|
|
<p class="text-sm text-red-500 mt-1">{{ error }}</p>
|
|
{% endfor %}
|
|
</div>
|
|
</div>
|
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-4 mt-4">
|
|
<div>
|
|
<label for="{{ person_form.date_of_birth.id_for_label }}" class="block text-sm font-semibold text-gray-700 mb-2">
|
|
{{ person_form.date_of_birth.label }}
|
|
</label>
|
|
{{ person_form.date_of_birth }}
|
|
{% for error in person_form.date_of_birth.errors %}
|
|
<p class="text-sm text-red-500 mt-1">{{ error }}</p>
|
|
{% endfor %}
|
|
</div>
|
|
<div>
|
|
<label for="{{ person_form.nationality.id_for_label }}" class="block text-sm font-semibold text-gray-700 mb-2">
|
|
{{ person_form.nationality.label }}
|
|
</label>
|
|
{{ person_form.nationality }}
|
|
{% for error in person_form.nationality.errors %}
|
|
<p class="text-sm text-red-500 mt-1">{{ error }}</p>
|
|
{% endfor %}
|
|
</div>
|
|
</div>
|
|
<div class="mt-4">
|
|
<label for="{{ person_form.address.id_for_label }}" class="block text-sm font-semibold text-gray-700 mb-2">
|
|
{{ person_form.address.label }}
|
|
</label>
|
|
{{ person_form.address }}
|
|
{% for error in person_form.address.errors %}
|
|
<p class="text-sm text-red-500 mt-1">{{ error }}</p>
|
|
{% endfor %}
|
|
</div>
|
|
</form>
|
|
</div>
|
|
<div class="bg-gray-50 px-4 py-3 sm:px-6 sm:flex sm:flex-row-reverse">
|
|
<button type="submit" class="modal-save-btn w-full inline-flex justify-center rounded-xl border border-transparent shadow-sm px-4 py-2 bg-temple-red text-base font-medium text-white hover:bg-red-800 focus:outline-none sm:ml-3 sm:w-auto sm:text-sm">
|
|
<i data-lucide="save" class="w-4 h-4 mr-2"></i>{% trans "Save" %}
|
|
</button>
|
|
<button type="button" class="modal-close-btn mt-3 w-full inline-flex justify-center rounded-xl border border-gray-300 shadow-sm px-4 py-2 bg-white text-base font-medium text-gray-700 hover:bg-gray-50 focus:outline-none sm:mt-0 sm:ml-3 sm:w-auto sm:text-sm">
|
|
<i data-lucide="x" class="w-4 h-4 mr-2"></i>{% trans "Close" %}
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{% endblock %}
|
|
|
|
{% block customJS %}
|
|
<script>
|
|
document.addEventListener('DOMContentLoaded', function() {
|
|
// Initialize Lucide icons
|
|
lucide.createIcons();
|
|
|
|
// Modal functionality
|
|
const modal = document.getElementById('personModal');
|
|
let isModalOpen = false;
|
|
|
|
// Open modal buttons
|
|
const openModalBtns = document.querySelectorAll('.modal-trigger');
|
|
openModalBtns.forEach(btn => {
|
|
btn.addEventListener('click', function(e) {
|
|
e.preventDefault();
|
|
e.stopPropagation();
|
|
if (modal) {
|
|
modal.classList.remove('hidden');
|
|
document.body.style.overflow = 'hidden';
|
|
isModalOpen = true;
|
|
setTimeout(() => lucide.createIcons(), 100);
|
|
}
|
|
});
|
|
});
|
|
|
|
// Close modal buttons
|
|
document.querySelectorAll('.modal-close-btn').forEach(btn => {
|
|
btn.addEventListener('click', function(e) {
|
|
e.preventDefault();
|
|
if (modal && isModalOpen) {
|
|
modal.classList.add('hidden');
|
|
document.body.style.overflow = '';
|
|
isModalOpen = false;
|
|
}
|
|
});
|
|
});
|
|
|
|
// Close modal when clicking outside
|
|
if (modal) {
|
|
modal.addEventListener('click', function(e) {
|
|
if (e.target === modal && isModalOpen) {
|
|
modal.classList.add('hidden');
|
|
document.body.style.overflow = '';
|
|
isModalOpen = false;
|
|
}
|
|
});
|
|
}
|
|
|
|
// Close modal on escape key
|
|
document.addEventListener('keydown', function(e) {
|
|
if (e.key === 'Escape' && modal && isModalOpen) {
|
|
modal.classList.add('hidden');
|
|
document.body.style.overflow = '';
|
|
isModalOpen = false;
|
|
}
|
|
});
|
|
|
|
// Add form styling
|
|
const formInputs = document.querySelectorAll('input, 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');
|
|
});
|
|
|
|
// Reinitialize Lucide icons after HTMX updates
|
|
document.body.addEventListener('htmx:afterSwap', function(evt) {
|
|
lucide.createIcons();
|
|
|
|
// Re-apply form styling to new elements
|
|
const newFormInputs = evt.detail.xhr.response.querySelectorAll ?
|
|
evt.detail.xhr.response.querySelectorAll('input, select, textarea') : [];
|
|
newFormInputs.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');
|
|
});
|
|
});
|
|
});
|
|
</script>
|
|
{% endblock %} |