ui and error fixes
This commit is contained in:
parent
ca71e8353e
commit
2211c5f3b2
@ -2157,6 +2157,8 @@ class MessageForm(forms.ModelForm):
|
||||
hiring_agency__user=self.user,
|
||||
status="ACTIVE"
|
||||
).order_by("-created_at")
|
||||
print(self.user)
|
||||
print("Agency user job queryset:", self.fields["job"].queryset)
|
||||
elif self.user.user_type == "candidate":
|
||||
# Candidates can only see jobs they applied for
|
||||
self.fields["job"].queryset = JobPosting.objects.filter(
|
||||
@ -2179,6 +2181,7 @@ class MessageForm(forms.ModelForm):
|
||||
self.fields["recipient"].queryset = User.objects.filter(
|
||||
user_type="staff"
|
||||
).distinct().order_by("username")
|
||||
|
||||
elif self.user.user_type == "candidate":
|
||||
# Candidates can only message staff
|
||||
self.fields["recipient"].queryset = User.objects.filter(
|
||||
|
||||
@ -468,6 +468,10 @@ class JobPosting(Base):
|
||||
vacancy_fill_rate = 0.0
|
||||
|
||||
return vacancy_fill_rate
|
||||
|
||||
def has_already_applied_to_this_job(self, person):
|
||||
"""Check if a given person has already applied to this job."""
|
||||
return self.applications.filter(person=person).exists()
|
||||
|
||||
|
||||
class JobPostingImage(models.Model):
|
||||
|
||||
@ -1069,9 +1069,11 @@ def send_bulk_email_task(subject, message, recipient_list,attachments=None,sende
|
||||
# Since the async caller sends one task per recipient, total_recipients should be 1.
|
||||
for recipient in recipient_list:
|
||||
# The 'message' is the custom message specific to this recipient.
|
||||
if _task_send_individual_email(subject, message, recipient, attachments,sender,job):
|
||||
r=_task_send_individual_email(subject, message, recipient, attachments,sender,job)
|
||||
print(f"Email send result for {recipient}: {r}")
|
||||
if r:
|
||||
successful_sends += 1
|
||||
|
||||
print(f"successful_sends: {successful_sends} out of {total_recipients}")
|
||||
if successful_sends > 0:
|
||||
logger.info(f"Bulk email task completed successfully. Sent to {successful_sends}/{total_recipients} recipients.")
|
||||
return {
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -279,7 +279,7 @@ def application_detail(request, slug):
|
||||
@login_required
|
||||
@staff_user_required
|
||||
def application_resume_template_view(request, slug):
|
||||
"""Display formatted resume template for a candidate"""
|
||||
"""Display formatted resume template for a application"""
|
||||
application = get_object_or_404(models.Application, slug=slug)
|
||||
|
||||
if not request.user.is_staff:
|
||||
@ -398,7 +398,7 @@ def dashboard_view(request):
|
||||
|
||||
# --- 2. TIME SERIES: GLOBAL DAILY APPLICANTS ---
|
||||
|
||||
# Group ALL candidates by creation date
|
||||
# Group ALL applications by creation date
|
||||
global_daily_applications_qs = all_applications_queryset.annotate(
|
||||
date=TruncDate('created_at')
|
||||
).values('date').annotate(
|
||||
@ -482,7 +482,7 @@ def dashboard_view(request):
|
||||
# )
|
||||
|
||||
|
||||
# candidates_with_score_query= candidate_queryset.filter(is_resume_parsed=True).annotate(
|
||||
# applications_with_score_query= application_queryset.filter(is_resume_parsed=True).annotate(
|
||||
# # The Coalesce handles NULL values (from missing data, non-numeric data, or NullIf) and sets them to 0.
|
||||
# annotated_match_score=Coalesce(safe_match_score_cast, Value(0))
|
||||
# )
|
||||
@ -637,10 +637,10 @@ def dashboard_view(request):
|
||||
@login_required
|
||||
@staff_user_required
|
||||
def applications_offer_view(request, slug):
|
||||
"""View for candidates in the Offer stage"""
|
||||
"""View for applications in the Offer stage"""
|
||||
job = get_object_or_404(models.JobPosting, slug=slug)
|
||||
|
||||
# Filter candidates for this specific job and stage
|
||||
# Filter applications for this specific job and stage
|
||||
applications = job.offer_applications
|
||||
|
||||
# Handle search
|
||||
|
||||
@ -18,7 +18,7 @@
|
||||
|
||||
<div class="card-body">
|
||||
<form hx-boost="true" method="post" id="email-compose-form" action="{% url 'compose_application_email' job.slug %}"
|
||||
hx-include="#candidate-form"
|
||||
hx-include="#application-form"
|
||||
hx-target="#messageContent"
|
||||
hx-select="#messageContent"
|
||||
hx-push-url="false"
|
||||
@ -388,7 +388,7 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
subject: subject.value,
|
||||
message: message.value,
|
||||
recipients: Array.from(form.querySelectorAll('input[name="{{ form.recipients.name }}"]:checked')).map(cb => cb.value),
|
||||
include_candidate_info: form.querySelector('#{{ form.include_candidate_info.id_for_label }}').checked,
|
||||
include_application_info: form.querySelector('#{{ form.include_application_info.id_for_label }}').checked,
|
||||
include_meeting_details: form.querySelector('#{{ form.include_meeting_details.id_for_label }}').checked
|
||||
};
|
||||
|
||||
@ -428,8 +428,8 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
}
|
||||
|
||||
// Restore checkboxes
|
||||
if (draft.include_candidate_info) {
|
||||
form.querySelector('#{{ form.include_candidate_info.id_for_label }}').checked = draft.include_candidate_info;
|
||||
if (draft.include_application_info) {
|
||||
form.querySelector('#{{ form.include_application_info.id_for_label }}').checked = draft.include_application_info;
|
||||
}
|
||||
if (draft.include_meeting_details) {
|
||||
form.querySelector('#{{ form.include_meeting_details.id_for_label }}').checked = draft.include_meeting_details;
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
{% extends "base.html" %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block title %}Create Onsite Interview{% endblock %}
|
||||
{% block title %}{% trans "Create Onsite Interview" %}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="container mt-4">
|
||||
@ -10,18 +11,18 @@
|
||||
<div class="card-header">
|
||||
<h4 class="mb-0">
|
||||
<i class="fas fa-building me-2"></i>
|
||||
Create Onsite Interview for {{ candidate.name }}
|
||||
{% blocktrans %}Create Onsite Interview for {{ application.name }}{% endblocktrans %}
|
||||
</h4>
|
||||
<a href="{% url 'interview_create_type_selection' candidate.slug %}"
|
||||
<a href="{% url 'interview_create_type_selection' application.slug %}"
|
||||
class="btn btn-outline-primary">
|
||||
<i class="fas fa-arrow-left me-2"></i>
|
||||
Back to Candidate List
|
||||
{% trans "Back to application List" %}
|
||||
</a>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<p class="text-muted mb-3">
|
||||
Schedule an onsite interview for <strong>{{ candidate.name }}</strong>
|
||||
for the position of <strong>{{ job.title }}</strong>.
|
||||
{% blocktrans %}Schedule an onsite interview for <strong>{{ application.name }}</strong>
|
||||
for the position of <strong>{{ job.title }}</strong>.{% endblocktrans %}
|
||||
</p>
|
||||
|
||||
{% if messages %}
|
||||
@ -33,7 +34,7 @@
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
|
||||
<form method="post" action="{% url 'interview_create_onsite' candidate_slug=candidate.slug %}">
|
||||
<form method="post" action="{% url 'interview_create_onsite' application_slug=application.slug %}">
|
||||
{% csrf_token %}
|
||||
|
||||
<div class="row">
|
||||
@ -41,7 +42,7 @@
|
||||
<div class="mb-3">
|
||||
<label for="{{ form.interview_date.id_for_label }}" class="form-label">
|
||||
<i class="fas fa-calendar me-1"></i>
|
||||
Topic
|
||||
{% trans "Topic" %}
|
||||
</label>
|
||||
{{ form.topic }}
|
||||
{% if form.topic.errors %}
|
||||
@ -57,7 +58,7 @@
|
||||
<div class="mb-3">
|
||||
<label for="{{ form.interview_date.id_for_label }}" class="form-label">
|
||||
<i class="fas fa-calendar me-1"></i>
|
||||
Interview Date
|
||||
{% trans "Interview Date" %}
|
||||
</label>
|
||||
{{ form.interview_date }}
|
||||
{% if form.interview_date.errors %}
|
||||
@ -72,7 +73,7 @@
|
||||
<div class="mb-3">
|
||||
<label for="{{ form.interview_time.id_for_label }}" class="form-label">
|
||||
<i class="fas fa-clock me-1"></i>
|
||||
Interview Time
|
||||
{% trans "Interview Time" %}
|
||||
</label>
|
||||
{{ form.interview_time }}
|
||||
{% if form.interview_time.errors %}
|
||||
@ -89,7 +90,7 @@
|
||||
<div class="mb-3">
|
||||
<label for="{{ form.duration.id_for_label }}" class="form-label">
|
||||
<i class="fas fa-hourglass-half me-1"></i>
|
||||
Duration (minutes)
|
||||
{% trans "Duration (minutes)" %}
|
||||
</label>
|
||||
{{ form.duration }}
|
||||
{% if form.duration.errors %}
|
||||
@ -132,7 +133,7 @@
|
||||
<div class="mb-3">
|
||||
<label for="{{ form.physical_address.id_for_label }}" class="form-label">
|
||||
<i class="fas fa-map-marker-alt me-1"></i>
|
||||
Physical Address
|
||||
{% trans "Physical Address" %}
|
||||
</label>
|
||||
{{ form.physical_address }}
|
||||
{% if form.physical_address.errors %}
|
||||
@ -147,7 +148,7 @@
|
||||
<div class="mb-3">
|
||||
<label for="{{ form.room_number.id_for_label }}" class="form-label">
|
||||
<i class="fas fa-door-open me-1"></i>
|
||||
Room Number
|
||||
{% trans "Room Number" %}
|
||||
</label>
|
||||
{{ form.room_number }}
|
||||
{% if form.room_number.errors %}
|
||||
@ -203,7 +204,7 @@
|
||||
<div class="d-flex justify-content-between">
|
||||
<button type="submit" class="btn btn-main-action">
|
||||
<i class="fas fa-save me-2"></i>
|
||||
Schedule Onsite Interview
|
||||
{% trans "Schedule Onsite Interview" %}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
@ -226,7 +227,7 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
|
||||
dateInput.addEventListener('change', function() {
|
||||
if (this.value < today) {
|
||||
this.setCustomValidity('Interview date must be in the future');
|
||||
this.setCustomValidity('{% trans "Interview date must be in the future" %}');
|
||||
} else {
|
||||
this.setCustomValidity('');
|
||||
}
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
{% extends "base.html" %}
|
||||
{% load i18n crispy_forms_tags %}
|
||||
|
||||
{% block title %}Create Remote Interview{% endblock %}
|
||||
{% block title %}{% trans "Create Remote Interview" %}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="container mt-4">
|
||||
@ -11,18 +11,18 @@
|
||||
<div class="card-header">
|
||||
<h4 class="mb-0">
|
||||
<i class="fas fa-video me-2"></i>
|
||||
Create Remote Interview for {{ candidate.name }}
|
||||
{% blocktrans %}Create Remote Interview for {{ application.name }}{% endblocktrans %}
|
||||
</h4>
|
||||
<a href="{% url 'interview_create_type_selection' candidate.slug %}"
|
||||
<a href="{% url 'interview_create_type_selection' application.slug %}"
|
||||
class="btn btn-outline-primary">
|
||||
<i class="fas fa-arrow-left me-2"></i>
|
||||
Back to Candidate List
|
||||
{% trans "Back to application List" %}
|
||||
</a>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<p class="text-muted mb-3">
|
||||
Schedule a remote interview for <strong>{{ candidate.name }}</strong>
|
||||
for the position of <strong>{{ job.title }}</strong>.
|
||||
{% blocktrans %}Schedule a remote interview for <strong>{{ application.name }}</strong>
|
||||
for the position of <strong>{{ job.title }}</strong>.{% endblocktrans %}
|
||||
</p>
|
||||
|
||||
{% if messages %}
|
||||
@ -34,13 +34,13 @@
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
|
||||
<form method="post" action="{% url 'interview_create_remote' candidate_slug=candidate.slug %}">
|
||||
<form method="post" action="{% url 'interview_create_remote' application_slug=application.slug %}">
|
||||
{% csrf_token %}
|
||||
{{form|crispy}}
|
||||
<div class="d-flex justify-content-between">
|
||||
<button type="submit" class="btn btn-main-action">
|
||||
<i class="fas fa-save me-2"></i>
|
||||
Schedule Remote Interview
|
||||
{% trans "Schedule Remote Interview" %}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
@ -63,7 +63,7 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
|
||||
dateInput.addEventListener('change', function() {
|
||||
if (this.value < today) {
|
||||
this.setCustomValidity('Interview date must be in the future');
|
||||
this.setCustomValidity('{% trans "Interview date must be in the future" %}');
|
||||
} else {
|
||||
this.setCustomValidity('');
|
||||
}
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
{% extends "base.html" %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block title %}Create Interview - Select Type{% endblock %}
|
||||
{% block title %}{% trans "Create Interview - Select Type" %}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="container mt-4">
|
||||
@ -10,41 +11,41 @@
|
||||
<div class="card-header">
|
||||
<h4 class="mb-0">
|
||||
<i class="fas fa-calendar-plus me-2"></i>
|
||||
Create Interview for {{ candidate.name }}
|
||||
{% blocktrans %}Create Interview for {{ application.name }}{% endblocktrans %}
|
||||
</h4>
|
||||
</div>
|
||||
<div class="card-body" hx-boost="true" hx-push-url="false" hx-select=".card-body" hx-swap="innerHTML" hx-target="#candidateviewModalBody">
|
||||
<p class="text-muted mb-3">
|
||||
Select the type of interview you want to schedule for <strong>{{ candidate.name }}</strong>
|
||||
for the position of <strong>{{ job.title }}</strong>.
|
||||
{% blocktrans %}Select the type of interview you want to schedule for <strong>{{ application.name }}</strong>
|
||||
for the position of <strong>{{ job.title }}</strong>.{% endblocktrans %}
|
||||
</p>
|
||||
|
||||
<div class="d-grid gap-3" style="grid-template-columns: 1fr 1fr;">
|
||||
<a href="{% url 'interview_create_remote' candidate_slug=candidate.slug %}"
|
||||
<a href="{% url 'interview_create_remote' application_slug=application.slug %}"
|
||||
class="btn btn-outline-primary btn-lg h-100 p-3 text-decoration-none">
|
||||
<div class="text-center">
|
||||
<i class="fas fa-video me-2"></i>
|
||||
<div class="mt-2">Remote Interview</div>
|
||||
<small class="d-block">Via Zoom/Video Conference</small>
|
||||
<div class="mt-2">{% trans "Remote Interview" %}</div>
|
||||
<small class="d-block">{% trans "Via Zoom/Video Conference" %}</small>
|
||||
</div>
|
||||
</a>
|
||||
|
||||
<a href="{% url 'interview_create_onsite' candidate_slug=candidate.slug %}"
|
||||
<a href="{% url 'interview_create_onsite' application_slug=application.slug %}"
|
||||
class="btn btn-outline-primary btn-lg h-100 p-3 text-decoration-none">
|
||||
<div class="text-center">
|
||||
<i class="fas fa-building me-2"></i>
|
||||
<div class="mt-2">Onsite Interview</div>
|
||||
<small class="d-block">In-person at our facility</small>
|
||||
<div class="mt-2">{% trans "Onsite Interview" %}</div>
|
||||
<small class="d-block">{% trans "In-person at our facility" %}</small>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="mt-4">
|
||||
<a href="{% url 'candidate_interview_view' slug=job.slug %}"
|
||||
<a href="{% url 'applications_interview_view' slug=job.slug %}"
|
||||
class="btn btn-outline-primary">
|
||||
<i class="fas fa-arrow-left me-2"></i>
|
||||
Back to Candidate List
|
||||
{% trans "Back to application List" %}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
{% extends "portal_base.html" %}
|
||||
{% load static %}
|
||||
|
||||
{% block title %}{% if form.instance.pk %}{% trans "Reply to Message"%}{% else %}{% trans"Compose Message"%}{% endif %}{% endblock %}
|
||||
{% load i18n %}
|
||||
{% block title %}{% if form.instance.pk %}{% trans "Reply to Message" %}{% else %}{% trans "Compose Message" %}{% endif %}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="container-fluid">
|
||||
@ -27,12 +27,15 @@
|
||||
<i class="fas fa-envelope"></i> {% trans "Mark Unread" %}
|
||||
</a>
|
||||
{% endif %}
|
||||
<a href="{% url 'message_delete' message.id %}"
|
||||
class="btn btn-outline-danger"
|
||||
hx-get="{% url 'message_delete' message.id %}"
|
||||
hx-confirm="{% trans 'Are you sure you want to delete this message?' %}">
|
||||
<i class="fas fa-trash"></i> {% trans "Delete" %}
|
||||
</a>
|
||||
<button type="button"
|
||||
class="btn btn-danger btn-lg"
|
||||
hx-post="{% url 'message_delete' message.id %}"
|
||||
hx-confirm="{% trans 'Are you sure you want to permanently delete this message? This action cannot be undone.' %}"
|
||||
hx-redirect="{% url 'message_list' %}"
|
||||
onclick="this.disabled=true;"
|
||||
title="{% trans 'Delete Message' %}">
|
||||
<i class="fas fa-trash me-1"></i> {% trans "Delete Message" %}
|
||||
</button>
|
||||
<a href="{% url 'message_list' %}" class="btn btn-outline-secondary">
|
||||
<i class="fas fa-arrow-left"></i> {% trans "Back to Messages" %}
|
||||
</a>
|
||||
|
||||
@ -138,7 +138,7 @@
|
||||
</a>
|
||||
<a href="{% url 'message_delete' message.id %}"
|
||||
class="btn btn-sm btn-outline-danger"
|
||||
hx-get="{% url 'message_delete' message.id %}"
|
||||
hx-post="{% url 'message_delete' message.id %}"
|
||||
hx-confirm="{% trans 'Are you sure you want to delete this message?' %}"
|
||||
title="{% trans 'Delete' %}">
|
||||
<i class="fas fa-trash"></i>
|
||||
|
||||
@ -370,13 +370,13 @@
|
||||
class="btn btn-sm btn-outline-secondary">
|
||||
<i class="fas fa-edit"></i> {% trans "Edit" %}
|
||||
</a>
|
||||
<button type="button" class="btn btn-outline-danger btn-sm"
|
||||
{% comment %} <button type="button" class="btn btn-outline-danger btn-sm"
|
||||
title="{% trans 'Delete' %}"
|
||||
data-bs-toggle="modal" data-bs-target="#deleteModal"
|
||||
data-delete-url="{% url 'person_delete' person.slug %}"
|
||||
data-item-name="{{ person.get_full_name }}">
|
||||
<i class="fas fa-trash-alt"></i>
|
||||
</button>
|
||||
</button> {% endcomment %}
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -224,7 +224,7 @@
|
||||
<div class="d-flex justify-content-between align-items-center mb-4">
|
||||
<h5 class="mb-0" style="color: var(--kaauh-teal-dark);">
|
||||
<i class="fas fa-users me-2"></i>
|
||||
{% trans "Submitted Applications" %} ({{ total_candidates }})
|
||||
{% trans "Submitted Applications" %} ({{ total_applications }})
|
||||
</h5>
|
||||
{% if access_link %}
|
||||
<a href="{% url 'agency_portal_login' %}" target="_blank" class="btn btn-outline-info btn-sm">
|
||||
@ -327,19 +327,19 @@
|
||||
style="stroke-dasharray: 326.73; stroke-dashoffset: {{ stroke_dashoffset }};"/>
|
||||
</svg>
|
||||
<div class="position-absolute top-50 start-50 translate-middle text-center">
|
||||
<div class="h3 fw-bold mb-0 text-dark">{{ total_candidates }}</div>
|
||||
<div class="h3 fw-bold mb-0 text-dark">{{ total_applications }}</div>
|
||||
<div class="small text-muted text-uppercase">{% trans "of" %} {{ assignment.max_candidates}}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="text-center">
|
||||
<div class="h4 mb-1">{{ total_candidates }}</div>
|
||||
<div class="h4 mb-1">{{ total_applications }}</div>
|
||||
<div class="text-muted">/ {{ assignment.max_candidates }} {% trans "applications" %}</div>
|
||||
</div>
|
||||
|
||||
<div class="progress mt-3" style="height: 8px;">
|
||||
{% widthratio total_candidates assignment.max_candidates 100 as progress %}
|
||||
{% widthratio total_applications assignment.max_candidates 100 as progress %}
|
||||
<div class="progress-bar" style="width: {{ progress }}%"></div>
|
||||
</div>
|
||||
</div>
|
||||
@ -353,7 +353,7 @@
|
||||
</h5>
|
||||
|
||||
<div class="d-grid gap-2">
|
||||
<a href=""
|
||||
<a href="{}"
|
||||
class="btn btn-outline-primary">
|
||||
<i class="fas fa-envelope me-1"></i> {% trans "Send Message" %}
|
||||
</a>
|
||||
|
||||
@ -313,7 +313,7 @@
|
||||
{{ agency.name }}
|
||||
</h1>
|
||||
<p class="text-muted mb-0">
|
||||
{% trans "Hiring Agency Details and Candidate Management" %}
|
||||
{% trans "Hiring Agency Details and Application Management" %}
|
||||
</p>
|
||||
</div>
|
||||
<div>
|
||||
@ -531,7 +531,7 @@
|
||||
aria-selected="true"
|
||||
>
|
||||
<i class="fas fa-users me-1"></i>
|
||||
{% trans "Recent Candidates" %}
|
||||
{% trans "Recent Applications" %}
|
||||
</button>
|
||||
</li>
|
||||
<li class="nav-item" role="presentation">
|
||||
@ -560,25 +560,25 @@
|
||||
role="tabpanel"
|
||||
aria-labelledby="candidates-tab"
|
||||
>
|
||||
{% if candidates %}
|
||||
{% for candidate in candidates %}
|
||||
{% if applications %}
|
||||
{% for application in applications %}
|
||||
<div class="candidate-item">
|
||||
<div class="d-flex justify-content-between align-items-center">
|
||||
<div>
|
||||
<div class="candidate-name">{{ candidate.name }}</div>
|
||||
<div class="candidate-name">{{ application.name }}</div>
|
||||
<div class="candidate-details">
|
||||
<i class="fas fa-envelope me-1"></i> {{ candidate.email }}
|
||||
{% if candidate.phone %}
|
||||
<span class="ms-3"><i class="fas fa-phone me-1"></i> {{ candidate.phone }}</span>
|
||||
<i class="fas fa-envelope me-1"></i> {{ application.email }}
|
||||
{% if application.phone %}
|
||||
<span class="ms-3"><i class="fas fa-phone me-1"></i> {{ application.phone }}</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
<div class="text-end">
|
||||
<span class="stage-badge stage-{{ candidate.stage }}">
|
||||
{{ candidate.get_stage_display }}
|
||||
<span class="stage-badge stage-{{ application.stage }}">
|
||||
{{ application.get_stage_display }}
|
||||
</span>
|
||||
<div class="small text-muted mt-1">
|
||||
{{ candidate.created_at|date:"M d, Y" }}
|
||||
{{ application.created_at|date:"M d, Y" }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -587,8 +587,8 @@
|
||||
{% else %}
|
||||
<div class="empty-state">
|
||||
<i class="fas fa-user-slash"></i>
|
||||
<h6>{% trans "No candidates yet" %}</h6>
|
||||
<p class="mb-0">{% trans "This agency hasn't submitted any candidates yet." %}</p>
|
||||
<h6>{% trans "No applications yet" %}</h6>
|
||||
<p class="mb-0">{% trans "This agency hasn't submitted any applications yet." %}</p>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
@ -654,32 +654,32 @@
|
||||
<div class="card-header bg-white border-bottom">
|
||||
<h5 class="mb-0" style="color: var(--kaauh-teal-dark);">
|
||||
<i class="fas fa-chart-bar me-2"></i>
|
||||
{% trans "Candidate Statistics" %}
|
||||
{% trans "Application Statistics" %}
|
||||
</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="row g-3">
|
||||
<div class="col-6">
|
||||
<div class="stat-card">
|
||||
<div class="stat-number">{{ total_candidates }}</div>
|
||||
<div class="stat-number">{{ total_applications }}</div>
|
||||
<div class="stat-label">{% trans "Total" %}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-6">
|
||||
<div class="stat-card">
|
||||
<div class="stat-number">{{ active_candidates }}</div>
|
||||
<div class="stat-number">{{ active_applications }}</div>
|
||||
<div class="stat-label">{% trans "Active" %}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-6">
|
||||
<div class="stat-card">
|
||||
<div class="stat-number">{{ hired_candidates }}</div>
|
||||
<div class="stat-number">{{ hired_applications }}</div>
|
||||
<div class="stat-label">{% trans "Hired" %}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-6">
|
||||
<div class="stat-card">
|
||||
<div class="stat-number">{{ rejected_candidates }}</div>
|
||||
<div class="stat-number">{{ rejected_applications }}</div>
|
||||
<div class="stat-label">{% trans "Rejected" %}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -115,7 +115,7 @@
|
||||
<i class="fas fa-arrow-left me-1"></i> {% trans "Back to Dashboard" %}
|
||||
</a>
|
||||
<a href="{% url 'agency_portal_submit_application_page' assignment.slug %}" class="btn btn-sm btn-main-action {% if assignment.is_full %}disabled{% endif %}" >
|
||||
<i class="fas fa-user-plus me-1"></i> {% trans "Submit New Candidate" %}
|
||||
<i class="fas fa-user-plus me-1"></i> {% trans "Submit New application" %}
|
||||
</a>
|
||||
{% comment %} <a href="#" class="btn btn-outline-info">
|
||||
<i class="fas fa-envelope me-1"></i> {% trans "Messages" %}
|
||||
@ -170,8 +170,8 @@
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="text-muted small">{% trans "Maximum Candidates" %}</label>
|
||||
<div class="fw-bold">{{ assignment.max_candidates }} {% trans "candidates" %}</div>
|
||||
<label class="text-muted small">{% trans "Maximum applications" %}</label>
|
||||
<div class="fw-bold">{{ assignment.max_candidates }} {% trans "applications" %}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -195,18 +195,18 @@
|
||||
<div class="d-grid gap-2">
|
||||
{% if assignment.can_submit %}
|
||||
<a href="{% url 'agency_portal_submit_application_page' assignment.slug %}" class="btn btn-main-action">
|
||||
<i class="fas fa-user-plus me-1"></i> {% trans "Submit New Candidate" %}
|
||||
<i class="fas fa-user-plus me-1"></i> {% trans "Submit New application" %}
|
||||
</a>
|
||||
{% else %}
|
||||
<button class="btn btn-outline-secondary" disabled>
|
||||
<i class="fas fa-user-plus me-1"></i> {% trans "Cannot Submit Candidates" %}
|
||||
<i class="fas fa-user-plus me-1"></i> {% trans "Cannot Submit applications" %}
|
||||
</button>
|
||||
<div class="alert alert-warning mt-2">
|
||||
<i class="fas fa-exclamation-triangle me-2"></i>
|
||||
{% if assignment.is_expired %}
|
||||
{% trans "This assignment has expired. Submissions are no longer accepted." %}
|
||||
{% elif assignment.is_full %}
|
||||
{% trans "Maximum candidate limit reached for this assignment." %}
|
||||
{% trans "Maximum application limit reached for this assignment." %}
|
||||
{% else %}
|
||||
{% trans "This assignment is not currently active." %}
|
||||
{% endif %}
|
||||
@ -221,14 +221,14 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Submitted Candidates --> {% endcomment %}
|
||||
<!-- Submitted applications --> {% endcomment %}
|
||||
<div class="kaauh-card p-4">
|
||||
<div class="d-flex justify-content-between align-items-center mb-4">
|
||||
<h5 class="mb-0" style="color: var(--kaauh-teal-dark);">
|
||||
<i class="fas fa-users me-2"></i>
|
||||
{% trans "Submitted Candidates" %} ({{ total_candidates }})
|
||||
{% trans "Submitted applications" %} ({{ total_applications }})
|
||||
</h5>
|
||||
<span class="badge bg-info">{{ total_candidates }}/{{ assignment.max_candidates }}</span>
|
||||
<span class="badge bg-info">{{ total_applications }}/{{ assignment.max_applications }}</span>
|
||||
</div>
|
||||
|
||||
{% if page_obj %}
|
||||
@ -244,27 +244,27 @@
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for candidate in page_obj %}
|
||||
{% for application in page_obj %}
|
||||
<tr>
|
||||
<td>
|
||||
<div class="fw-bold">{{ candidate.name }}</div>
|
||||
<div class="fw-bold">{{ application.name }}</div>
|
||||
</td>
|
||||
<td>
|
||||
<div class="small">
|
||||
<div><i class="fas fa-envelope me-1"></i> {{ candidate.email }}</div>
|
||||
<div><i class="fas fa-phone me-1"></i> {{ candidate.phone }}</div>
|
||||
<div><i class="fas fa-envelope me-1"></i> {{ application.email }}</div>
|
||||
<div><i class="fas fa-phone me-1"></i> {{ application.phone }}</div>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<span class="badge bg-info">{{ candidate.get_stage_display }}</span>
|
||||
<span class="badge bg-info">{{ application.get_stage_display }}</span>
|
||||
</td>
|
||||
<td>
|
||||
<div class="small text-muted">
|
||||
{{ candidate.created_at|date:"Y-m-d H:i" }}
|
||||
{{ application.created_at|date:"Y-m-d H:i" }}
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<a href="{% url 'applicant_application_detail' candidate.slug %}" class="btn btn-sm btn-outline-primary" title="{% trans 'View Profile' %}">
|
||||
<a href="{% url 'applicant_application_detail' application.slug %}" class="btn btn-sm btn-outline-primary" title="{% trans 'View Profile' %}">
|
||||
<i class="fas fa-eye"></i>
|
||||
</a>
|
||||
</td>
|
||||
@ -311,9 +311,9 @@
|
||||
{% else %}
|
||||
<div class="text-center py-4">
|
||||
<i class="fas fa-users fa-2x text-muted mb-3"></i>
|
||||
<h6 class="text-muted">{% trans "No candidates submitted yet" %}</h6>
|
||||
<h6 class="text-muted">{% trans "No applications submitted yet" %}</h6>
|
||||
<p class="text-muted small">
|
||||
{% trans "Submit candidates using the form above to get started." %}
|
||||
{% trans "Submit applications using the form above to get started." %}
|
||||
</p>
|
||||
</div>
|
||||
{% endif %}
|
||||
@ -348,19 +348,19 @@
|
||||
style="stroke-dasharray: 326.73; stroke-dashoffset: {{ stroke_dashoffset }};"/>
|
||||
</svg>
|
||||
<div class="progress-ring-text">
|
||||
{% widthratio total_candidates assignment.max_candidates 100 as progress %}
|
||||
{% widthratio total_applications assignment.max_candidates 100 as progress %}
|
||||
{{ progress|floatformat:0 }}%
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="text-center">
|
||||
<div class="h4 mb-1">{{ total_candidates }}</div>
|
||||
<div class="text-muted">/ {{ assignment.max_candidates }} {% trans "candidates" %}</div>
|
||||
<div class="h4 mb-1">{{ total_applications }}</div>
|
||||
<div class="text-muted">/ {{ assignment.max_candidates }} {% trans "applications" %}</div>
|
||||
</div>
|
||||
|
||||
<div class="progress mt-3" style="height: 8px;">
|
||||
{% widthratio total_candidates assignment.max_candidates 100 as progress %}
|
||||
{% widthratio total_applications assignment.max_candidates 100 as progress %}
|
||||
<div class="progress-bar" style="width: {{ progress }}%"></div>
|
||||
</div>
|
||||
|
||||
@ -415,7 +415,7 @@
|
||||
<div class="mb-3">
|
||||
<label class="text-muted small">{% trans "Submission Rate" %}</label>
|
||||
<div class="fw-bold">
|
||||
{% widthratio total_candidates assignment.max_candidates 100 as progress %}
|
||||
{% widthratio total_applications assignment.max_candidates 100 as progress %}
|
||||
{{ progress|floatformat:1 }}%
|
||||
</div>
|
||||
</div>
|
||||
@ -512,14 +512,14 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Edit Candidate Modal -->
|
||||
<!-- Edit application Modal -->
|
||||
<div class="modal fade" id="editCandidateModal" tabindex="-1">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title">
|
||||
<i class="fas fa-edit me-2"></i>
|
||||
{% trans "Edit Candidate" %}
|
||||
{% trans "Edit application" %}
|
||||
</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
|
||||
</div>
|
||||
@ -584,7 +584,7 @@
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title">
|
||||
<i class="fas fa-trash me-2"></i>
|
||||
{% trans "Remove Candidate" %}
|
||||
{% trans "Remove application" %}
|
||||
</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
|
||||
</div>
|
||||
@ -594,16 +594,16 @@
|
||||
<div class="modal-body">
|
||||
<div class="alert alert-warning">
|
||||
<i class="fas fa-exclamation-triangle me-2"></i>
|
||||
{% trans "Are you sure you want to remove this candidate? This action cannot be undone." %}
|
||||
{% trans "Are you sure you want to remove this application? This action cannot be undone." %}
|
||||
</div>
|
||||
<p><strong>{% trans "Candidate:" %}</strong> <span id="delete_candidate_name"></span></p>
|
||||
<p><strong>{% trans "Application:" %}</strong> <span id="delete_candidate_name"></span></p>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">
|
||||
{% trans "Cancel" %}
|
||||
</button>
|
||||
<button type="submit" class="btn btn-danger">
|
||||
<i class="fas fa-trash me-1"></i> {% trans "Remove Candidate" %}
|
||||
<i class="fas fa-trash me-1"></i> {% trans "Remove Application" %}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
@ -634,12 +634,12 @@ function editCandidate(candidateId) {
|
||||
new bootstrap.Modal(document.getElementById('editCandidateModal')).show();
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error fetching candidate:', error);
|
||||
alert('{% trans "Error loading candidate data. Please try again." %}');
|
||||
console.error('Error fetching Application:', error);
|
||||
alert('{% trans "Error loading Application data. Please try again." %}');
|
||||
});
|
||||
}
|
||||
|
||||
// Delete Candidate
|
||||
// Delete Application
|
||||
function deleteCandidate(candidateId, candidateName) {
|
||||
// Update form action URL with candidate ID
|
||||
const deleteForm = document.getElementById('deleteCandidateForm');
|
||||
@ -670,12 +670,12 @@ document.getElementById('editCandidateForm').addEventListener('submit', function
|
||||
bootstrap.Modal.getInstance(document.getElementById('editCandidateModal')).hide();
|
||||
location.reload();
|
||||
} else {
|
||||
alert(data.message || '{% trans "Error updating candidate. Please try again." %}');
|
||||
alert(data.message || '{% trans "Error updating Application. Please try again." %}');
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error:', error);
|
||||
alert('{% trans "Error updating candidate. Please try again." %}');
|
||||
alert('{% trans "Error updating Application. Please try again." %}');
|
||||
});
|
||||
});
|
||||
|
||||
@ -697,12 +697,12 @@ document.getElementById('deleteCandidateForm').addEventListener('submit', functi
|
||||
bootstrap.Modal.getInstance(document.getElementById('deleteCandidateModal')).hide();
|
||||
location.reload();
|
||||
} else {
|
||||
alert(data.message || '{% trans "Error removing candidate. Please try again." %}');
|
||||
alert(data.message || '{% trans "Error removing Application. Please try again." %}');
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error:', error);
|
||||
alert('{% trans "Error removing candidate. Please try again." %}');
|
||||
alert('{% trans "Error removing Application. Please try again." %}');
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@ -560,7 +560,9 @@
|
||||
{# Document List #}
|
||||
<ul class="list-group list-group-flush">
|
||||
{% for document in documents %}
|
||||
<li class="list-group-item d-flex flex-column flex-sm-row justify-content-between align-items-start align-items-sm-center bg-white p-3">
|
||||
{# HTMX FIX: Added id to list item for hx-target #}
|
||||
<li class="list-group-item d-flex flex-column flex-sm-row justify-content-between align-items-start align-items-sm-center bg-white p-3"
|
||||
id="document-{{ document.id }}">
|
||||
<div class="mb-2 mb-sm-0 fw-medium">
|
||||
<i class="fas fa-file-pdf me-2 text-primary-theme"></i> <strong>{{ document.document_type|title }}</strong>
|
||||
<span class="text-muted small">({{ document.file.name|split:"/"|last }})</span>
|
||||
@ -568,7 +570,15 @@
|
||||
<div class="d-flex align-items-center">
|
||||
<span class="text-muted small me-3">{% trans "Uploaded:" %} {{ document.uploaded_at|date:"d M Y" }}</span>
|
||||
<a href="{{ document.file.url }}" target="_blank" class="btn btn-sm btn-outline-secondary me-2"><i class="fas fa-eye"></i></a>
|
||||
<a href="{% url 'application_document_delete' document.id %}" class="btn btn-sm btn-outline-danger" onclick="return confirm('{% trans "Are you sure you want to delete this document?" %}')"><i class="fas fa-trash-alt"></i></a>
|
||||
|
||||
{# HTMX DELETE BUTTON #}
|
||||
<button hx-post="{% url 'application_document_delete' document.id %}"
|
||||
hx-target="#document-{{ document.id }}"
|
||||
hx-swap="outerHTML"
|
||||
hx-confirm="{% trans 'Are you sure you want to delete this file?' %}"
|
||||
class="btn btn-sm btn-danger">
|
||||
<i class="fas fa-trash"></i>
|
||||
</button>
|
||||
</div>
|
||||
</li>
|
||||
{% empty %}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user