289 lines
15 KiB
HTML
289 lines
15 KiB
HTML
{% extends "base.html" %}
|
|
{% load static i18n %}
|
|
|
|
{% block title %}{{ title }} - {{ block.super }}{% endblock %}
|
|
|
|
{% block content %}
|
|
<div class="max-w-4xl mx-auto py-6 px-4">
|
|
|
|
<!-- Breadcrumb -->
|
|
<nav class="mb-6" aria-label="breadcrumb">
|
|
<ol class="flex items-center gap-2 text-sm flex-wrap">
|
|
<li><a href="{% url 'agency_assignment_list' %}" class="text-gray-500 hover:underline transition flex items-center gap-1">
|
|
<i data-lucide="briefcase" class="w-4 h-4"></i> {% trans "Assignments" %}
|
|
</a></li>
|
|
<li class="text-gray-400">/</li>
|
|
<li class="font-semibold" style="color: #9d2235;">{{ title }}</li>
|
|
</ol>
|
|
</nav>
|
|
|
|
<!-- Header -->
|
|
<div class="flex flex-col sm:flex-row justify-between items-start sm:items-center gap-4 mb-6">
|
|
<h1 class="text-2xl sm:text-3xl font-bold flex items-center gap-3">
|
|
<div class="w-12 h-12 rounded-xl flex items-center justify-center" style="background-color: rgba(157, 34, 53, 0.1);">
|
|
<i data-lucide="briefcase" class="w-6 h-6" style="color: #9d2235;"></i>
|
|
</div>
|
|
{{ title }}
|
|
</h1>
|
|
<a href="{% url 'agency_assignment_list' %}"
|
|
class="inline-flex items-center gap-2 px-6 py-3 rounded-lg font-medium border-2 border-gray-200 text-gray-700 hover:bg-gray-50 transition-all duration-200">
|
|
<i data-lucide="arrow-left" class="w-4 h-4"></i>
|
|
<span class="hidden sm:inline">{% trans "Back to List" %}</span>
|
|
</a>
|
|
</div>
|
|
|
|
<!-- Info Card -->
|
|
<div class="bg-white rounded-xl shadow-sm border border-gray-200 mb-6 p-6">
|
|
<div class="rounded-lg p-4 border-l-4" style="background-color: #eff6ff; border-color: #9d2235;">
|
|
<h6 class="font-bold mb-2 flex items-center gap-2" style="color: #9d2235;">
|
|
<i data-lucide="info" class="w-4 h-4"></i>
|
|
{% trans "About Assignments" %}
|
|
</h6>
|
|
<p class="text-gray-600 text-sm">
|
|
{% trans "Assign a job to an external hiring agency. The agency will be able to submit candidates for this position until the deadline." %}
|
|
</p>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Form Card -->
|
|
<div class="bg-white rounded-xl shadow-sm border border-gray-200">
|
|
<div class="px-6 py-4 border-b border-gray-100" style="background-color: #f8f9fa;">
|
|
<h2 class="text-lg font-bold flex items-center gap-2">
|
|
<i data-lucide="briefcase" class="w-5 h-5" style="color: #9d2235;"></i>
|
|
{% trans "Assignment Details" %}
|
|
</h2>
|
|
</div>
|
|
|
|
<div class="p-6">
|
|
{% if form.non_field_errors %}
|
|
<div class="mb-6 p-4 rounded-lg bg-red-50 border border-red-200" role="alert">
|
|
<div class="flex items-start gap-3">
|
|
<i data-lucide="alert-triangle" class="w-5 h-5 text-red-600 flex-shrink-0 mt-0.5"></i>
|
|
<div>
|
|
<h5 class="font-semibold text-red-800 mb-1">{% trans "Error" %}</h5>
|
|
{% for error in form.non_field_errors %}
|
|
<p class="text-red-700 mb-0">{{ error }}</p>
|
|
{% endfor %}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{% endif %}
|
|
|
|
<form method="post" novalidate id="assignment-form" class="space-y-6">
|
|
{% csrf_token %}
|
|
|
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
|
<!-- Agency -->
|
|
<div>
|
|
<label for="{{ form.agency.id_for_label }}" class="block text-sm font-semibold text-gray-700 mb-2">
|
|
{{ form.agency.label }} <span class="text-red-500">*</span>
|
|
</label>
|
|
<select name="agency" id="{{ form.agency.id_for_label }}"
|
|
class="w-full px-4 py-3 border border-gray-200 rounded-xl text-sm focus:ring-2 focus:ring-temple-red/20 focus:border-temple-red outline-none transition bg-white">
|
|
<option value="">{% trans "Select Agency" %}</option>
|
|
{% for choice in form.agency.field.queryset %}
|
|
<option value="{{ choice.pk }}" {% if form.agency.value == choice.pk|stringformat:"s" %}selected{% endif %}>
|
|
{{ choice.name }}
|
|
</option>
|
|
{% endfor %}
|
|
</select>
|
|
{% if form.agency.errors %}
|
|
<div class="text-red-600 text-sm mt-1">{{ form.agency.errors.0 }}</div>
|
|
{% endif %}
|
|
{% if form.agency.help_text %}
|
|
<p class="text-xs text-gray-500 mt-1">{{ form.agency.help_text }}</p>
|
|
{% endif %}
|
|
</div>
|
|
|
|
<!-- Job -->
|
|
<div>
|
|
<label for="{{ form.job.id_for_label }}" class="block text-sm font-semibold text-gray-700 mb-2">
|
|
{{ form.job.label }} <span class="text-red-500">*</span>
|
|
</label>
|
|
<select name="job" id="{{ form.job.id_for_label }}"
|
|
class="w-full px-4 py-3 border border-gray-200 rounded-xl text-sm focus:ring-2 focus:ring-temple-red/20 focus:border-temple-red outline-none transition bg-white">
|
|
<option value="">{% trans "Select Job" %}</option>
|
|
{% for choice in form.job.field.queryset %}
|
|
<option value="{{ choice.pk }}" {% if form.job.value == choice.pk|stringformat:"s" %}selected{% endif %}>
|
|
{{ choice.title }}
|
|
</option>
|
|
{% endfor %}
|
|
</select>
|
|
{% if form.job.errors %}
|
|
<div class="text-red-600 text-sm mt-1">{{ form.job.errors.0 }}</div>
|
|
{% endif %}
|
|
{% if form.job.help_text %}
|
|
<p class="text-xs text-gray-500 mt-1">{{ form.job.help_text }}</p>
|
|
{% endif %}
|
|
</div>
|
|
|
|
<!-- Max Candidates -->
|
|
<div>
|
|
<label for="{{ form.max_candidates.id_for_label }}" class="block text-sm font-semibold text-gray-700 mb-2">
|
|
{{ form.max_candidates.label }} <span class="text-red-500">*</span>
|
|
</label>
|
|
<input type="number"
|
|
name="max_candidates"
|
|
id="{{ form.max_candidates.id_for_label }}"
|
|
value="{{ form.max_candidates.value|default:'' }}"
|
|
min="1"
|
|
class="w-full px-4 py-3 border border-gray-200 rounded-xl text-sm focus:ring-2 focus:ring-temple-red/20 focus:border-temple-red outline-none transition"
|
|
placeholder="{% trans 'Maximum candidates' %}">
|
|
{% if form.max_candidates.errors %}
|
|
<div class="text-red-600 text-sm mt-1">{{ form.max_candidates.errors.0 }}</div>
|
|
{% endif %}
|
|
{% if form.max_candidates.help_text %}
|
|
<p class="text-xs text-gray-500 mt-1">{{ form.max_candidates.help_text }}</p>
|
|
{% else %}
|
|
<p class="text-xs text-gray-500 mt-1">{% trans "Maximum number of candidates agency can submit" %}</p>
|
|
{% endif %}
|
|
</div>
|
|
|
|
<!-- Deadline Date -->
|
|
<div>
|
|
<label for="{{ form.deadline_date.id_for_label }}" class="block text-sm font-semibold text-gray-700 mb-2">
|
|
{{ form.deadline_date.label }} <span class="text-red-500">*</span>
|
|
</label>
|
|
<input type="datetime-local"
|
|
name="deadline_date"
|
|
id="{{ form.deadline_date.id_for_label }}"
|
|
value="{{ form.deadline_date.value|default:'' }}"
|
|
class="w-full px-4 py-3 border border-gray-200 rounded-xl text-sm focus:ring-2 focus:ring-temple-red/20 focus:border-temple-red outline-none transition">
|
|
{% if form.deadline_date.errors %}
|
|
<div class="text-red-600 text-sm mt-1">{{ form.deadline_date.errors.0 }}</div>
|
|
{% endif %}
|
|
{% if form.deadline_date.help_text %}
|
|
<p class="text-xs text-gray-500 mt-1">{{ form.deadline_date.help_text }}</p>
|
|
{% else %}
|
|
<p class="text-xs text-gray-500 mt-1">{% trans "Date and time when submission period ends" %}</p>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Admin Notes (Full Width) -->
|
|
<div>
|
|
<label for="{{ form.admin_notes.id_for_label }}" class="block text-sm font-semibold text-gray-700 mb-2">
|
|
{{ form.admin_notes.label }}
|
|
</label>
|
|
<textarea
|
|
name="admin_notes"
|
|
id="{{ form.admin_notes.id_for_label }}"
|
|
rows="4"
|
|
class="w-full px-4 py-3 border border-gray-200 rounded-xl text-sm focus:ring-2 focus:ring-temple-red/20 focus:border-temple-red outline-none transition resize-vertical"
|
|
placeholder="{% trans 'Internal notes about this assignment (not visible to agency)' %}">{{ form.admin_notes.value|default:'' }}</textarea>
|
|
{% if form.admin_notes.errors %}
|
|
<div class="text-red-600 text-sm mt-1">{{ form.admin_notes.errors.0 }}</div>
|
|
{% endif %}
|
|
{% if form.admin_notes.help_text %}
|
|
<p class="text-xs text-gray-500 mt-1">{{ form.admin_notes.help_text }}</p>
|
|
{% endif %}
|
|
</div>
|
|
|
|
<!-- Form Actions -->
|
|
<div class="flex flex-col sm:flex-row justify-between items-center gap-4 pt-4 border-t border-gray-200">
|
|
<a href="{% url 'agency_assignment_list' %}"
|
|
class="inline-flex items-center gap-2 px-6 py-3 rounded-lg font-medium border-2 border-gray-200 text-gray-700 hover:bg-gray-50 transition-all duration-200">
|
|
<i data-lucide="x" class="w-4 h-4"></i>
|
|
{% trans "Cancel" %}
|
|
</a>
|
|
<button type="submit"
|
|
class="inline-flex items-center gap-2 px-8 py-3 rounded-lg font-medium text-white transition-all duration-200"
|
|
style="background-color: #9d2235;"
|
|
onmouseover="this.style.backgroundColor='#7a1a29'"
|
|
onmouseout="this.style.backgroundColor='#9d2235'">
|
|
<i data-lucide="save" class="w-4 h-4"></i>
|
|
{{ button_text|default:"{% trans 'Create Assignment' %}" }}
|
|
</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
|
|
</div>
|
|
|
|
<script>
|
|
document.addEventListener('DOMContentLoaded', function() {
|
|
// Initialize Lucide icons
|
|
if (typeof lucide !== 'undefined') {
|
|
lucide.createIcons();
|
|
}
|
|
|
|
// Form Validation
|
|
const form = document.getElementById('assignment-form');
|
|
if (form) {
|
|
form.addEventListener('submit', function(e) {
|
|
const agencySelect = document.getElementById('{{ form.agency.id_for_label }}');
|
|
const jobSelect = document.getElementById('{{ form.job.id_for_label }}');
|
|
const maxCandidatesInput = document.getElementById('{{ form.max_candidates.id_for_label }}');
|
|
const deadlineInput = document.getElementById('{{ form.deadline_date.id_for_label }}');
|
|
|
|
// Agency validation
|
|
if (agencySelect && !agencySelect.value) {
|
|
e.preventDefault();
|
|
alert("{% trans 'Please select an agency.' %}");
|
|
agencySelect.focus();
|
|
return false;
|
|
}
|
|
|
|
// Job validation
|
|
if (jobSelect && !jobSelect.value) {
|
|
e.preventDefault();
|
|
alert("{% trans 'Please select a job.' %}");
|
|
jobSelect.focus();
|
|
return false;
|
|
}
|
|
|
|
// Max candidates validation
|
|
if (maxCandidatesInput && (!maxCandidatesInput.value || parseInt(maxCandidatesInput.value) < 1)) {
|
|
e.preventDefault();
|
|
alert("{% trans 'Please enter a valid number of candidates (minimum 1).' %}");
|
|
maxCandidatesInput.focus();
|
|
return false;
|
|
}
|
|
|
|
// Deadline validation
|
|
if (deadlineInput && !deadlineInput.value) {
|
|
e.preventDefault();
|
|
alert("{% trans 'Please select a deadline date and time.' %}");
|
|
deadlineInput.focus();
|
|
return false;
|
|
}
|
|
|
|
// Check deadline is in future
|
|
if (deadlineInput && deadlineInput.value) {
|
|
const deadline = new Date(deadlineInput.value);
|
|
const now = new Date();
|
|
if (deadline <= now) {
|
|
e.preventDefault();
|
|
alert("{% trans 'Deadline must be in the future.' %}");
|
|
deadlineInput.focus();
|
|
return false;
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
// Set minimum datetime for deadline to current time
|
|
const deadlineInput = document.getElementById('{{ form.deadline_date.id_for_label }}');
|
|
if (deadlineInput) {
|
|
const now = new Date();
|
|
const localDateTime = new Date(now.getTime() - now.getTimezoneOffset() * 60000)
|
|
.toISOString()
|
|
.slice(0, 16);
|
|
deadlineInput.min = localDateTime;
|
|
}
|
|
|
|
// Auto-populate or log job selection
|
|
const jobSelect = document.getElementById('{{ form.job.id_for_label }}');
|
|
const agencySelect = document.getElementById('{{ form.agency.id_for_label }}');
|
|
|
|
if (jobSelect && agencySelect) {
|
|
jobSelect.addEventListener('change', function() {
|
|
// You could add logic here to filter agencies based on job requirements
|
|
// For now, just log the selection
|
|
console.log('Job selected:', this.value);
|
|
});
|
|
}
|
|
});
|
|
</script>
|
|
{% endblock %} |