third refactoring for candidate in ai_ovverview branch
This commit is contained in:
parent
8b51416e10
commit
2486eb791e
@ -25,7 +25,7 @@ urlpatterns = [
|
|||||||
|
|
||||||
path('application/<slug:template_slug>/', views.application_submit_form, name='application_submit_form'),
|
path('application/<slug:template_slug>/', views.application_submit_form, name='application_submit_form'),
|
||||||
path('application/<slug:template_slug>/submit/', views.application_submit, name='application_submit'),
|
path('application/<slug:template_slug>/submit/', views.application_submit, name='application_submit'),
|
||||||
path('application/<slug:slug>/apply/', views.application_detail, name='application_detail'),
|
path('application/<slug:slug>/apply/', views.job_application_detail, name='job_application_detail'),
|
||||||
path('application/<slug:slug>/signup/', views.application_signup, name='application_signup'),
|
path('application/<slug:slug>/signup/', views.application_signup, name='application_signup'),
|
||||||
path('application/<slug:slug>/success/', views.application_success, name='application_success'),
|
path('application/<slug:slug>/success/', views.application_success, name='application_success'),
|
||||||
# path('application/applicant/profile', views.applicant_profile, name='applicant_profile'),
|
# path('application/applicant/profile', views.applicant_profile, name='applicant_profile'),
|
||||||
|
|||||||
@ -293,6 +293,7 @@ class ApplicationForm(forms.ModelForm):
|
|||||||
"resume",
|
"resume",
|
||||||
]
|
]
|
||||||
labels = {
|
labels = {
|
||||||
|
"person":_("Applicant"),
|
||||||
"resume": _("Resume"),
|
"resume": _("Resume"),
|
||||||
"hiring_source": _("Hiring Type"),
|
"hiring_source": _("Hiring Type"),
|
||||||
"hiring_agency": _("Hiring Agency"),
|
"hiring_agency": _("Hiring Agency"),
|
||||||
@ -903,7 +904,7 @@ class FormTemplateIsActiveForm(forms.ModelForm):
|
|||||||
fields = ["is_active"]
|
fields = ["is_active"]
|
||||||
|
|
||||||
|
|
||||||
class CandidateExamDateForm(forms.ModelForm):
|
class ApplicationExamDateForm(forms.ModelForm):
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Application
|
model = Application
|
||||||
fields = ["exam_date"]
|
fields = ["exam_date"]
|
||||||
|
|||||||
@ -23,7 +23,7 @@ from .views import (
|
|||||||
applications_exam_view, applications_interview_view, api_schedule_application_meeting
|
applications_exam_view, applications_interview_view, api_schedule_application_meeting
|
||||||
)
|
)
|
||||||
from .views_frontend import CandidateListView, JobListView
|
from .views_frontend import CandidateListView, JobListView
|
||||||
from .utils import create_zoom_meeting, get_candidates_from_request
|
from .utils import create_zoom_meeting, get_applications_from_request
|
||||||
|
|
||||||
|
|
||||||
class BaseTestCase(TestCase):
|
class BaseTestCase(TestCase):
|
||||||
@ -586,8 +586,8 @@ class UtilityFunctionTests(BaseTestCase):
|
|||||||
self.assertEqual(result['status'], 'success')
|
self.assertEqual(result['status'], 'success')
|
||||||
self.assertIn('meeting_id', result['meeting_details'])
|
self.assertIn('meeting_id', result['meeting_details'])
|
||||||
|
|
||||||
def test_get_candidates_from_request(self):
|
def get_applications_from_request(self):
|
||||||
"""Test the get_candidates_from_request utility function"""
|
"""Test the get_applications_from_request utility function"""
|
||||||
# This would be tested with a request that has candidate_ids
|
# This would be tested with a request that has candidate_ids
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|||||||
@ -41,7 +41,7 @@ from .views import (
|
|||||||
# from .views_frontend import CandidateListView, JobListView, JobCreateView
|
# from .views_frontend import CandidateListView, JobListView, JobCreateView
|
||||||
from .utils import (
|
from .utils import (
|
||||||
create_zoom_meeting, delete_zoom_meeting, update_zoom_meeting,
|
create_zoom_meeting, delete_zoom_meeting, update_zoom_meeting,
|
||||||
get_zoom_meeting_details, get_candidates_from_request,
|
get_zoom_meeting_details, get_applications_from_request,
|
||||||
get_available_time_slots
|
get_available_time_slots
|
||||||
)
|
)
|
||||||
# from .zoom_api import ZoomAPIError
|
# from .zoom_api import ZoomAPIError
|
||||||
|
|||||||
@ -194,7 +194,7 @@ urlpatterns = [
|
|||||||
views_frontend.update_application_status,
|
views_frontend.update_application_status,
|
||||||
name="update_application_status",
|
name="update_application_status",
|
||||||
),
|
),
|
||||||
# Sync URLs
|
# Sync URLs (check)
|
||||||
path(
|
path(
|
||||||
"jobs/<slug:job_slug>/sync-hired-applications/",
|
"jobs/<slug:job_slug>/sync-hired-applications/",
|
||||||
views_frontend.sync_hired_applications,
|
views_frontend.sync_hired_applications,
|
||||||
@ -205,6 +205,8 @@ urlpatterns = [
|
|||||||
views_frontend.test_source_connection,
|
views_frontend.test_source_connection,
|
||||||
name="test_source_connection",
|
name="test_source_connection",
|
||||||
),
|
),
|
||||||
|
|
||||||
|
|
||||||
path(
|
path(
|
||||||
"jobs/<slug:slug>/<int:application_id>/reschedule_meeting_for_application/<int:meeting_id>/",
|
"jobs/<slug:slug>/<int:application_id>/reschedule_meeting_for_application/<int:meeting_id>/",
|
||||||
views.reschedule_meeting_for_application,
|
views.reschedule_meeting_for_application,
|
||||||
@ -357,7 +359,7 @@ urlpatterns = [
|
|||||||
path("agencies/<slug:slug>/", views.agency_detail, name="agency_detail"),
|
path("agencies/<slug:slug>/", views.agency_detail, name="agency_detail"),
|
||||||
path("agencies/<slug:slug>/update/", views.agency_update, name="agency_update"),
|
path("agencies/<slug:slug>/update/", views.agency_update, name="agency_update"),
|
||||||
path("agencies/<slug:slug>/delete/", views.agency_delete, name="agency_delete"),
|
path("agencies/<slug:slug>/delete/", views.agency_delete, name="agency_delete"),
|
||||||
path(
|
path( #check the html of this url it is not used anywhere
|
||||||
"agencies/<slug:slug>/applications/",
|
"agencies/<slug:slug>/applications/",
|
||||||
views.agency_applications,
|
views.agency_applications,
|
||||||
name="agency_applications",
|
name="agency_applications",
|
||||||
@ -369,12 +371,12 @@ urlpatterns = [
|
|||||||
views.agency_assignment_list,
|
views.agency_assignment_list,
|
||||||
name="agency_assignment_list",
|
name="agency_assignment_list",
|
||||||
),
|
),
|
||||||
path(
|
path( #check
|
||||||
"agency-assignments/create/",
|
"agency-assignments/create/",
|
||||||
views.agency_assignment_create,
|
views.agency_assignment_create,
|
||||||
name="agency_assignment_create",
|
name="agency_assignment_create",
|
||||||
),
|
),
|
||||||
path(
|
path(#check
|
||||||
"agency-assignments/<slug:slug>/create/",
|
"agency-assignments/<slug:slug>/create/",
|
||||||
views.agency_assignment_create,
|
views.agency_assignment_create,
|
||||||
name="agency_assignment_create",
|
name="agency_assignment_create",
|
||||||
@ -423,7 +425,7 @@ urlpatterns = [
|
|||||||
# path('admin/messages/<int:message_id>/mark-read/', views.admin_mark_message_read, name='admin_mark_message_read'),
|
# path('admin/messages/<int:message_id>/mark-read/', views.admin_mark_message_read, name='admin_mark_message_read'),
|
||||||
# path('admin/messages/<int:message_id>/delete/', views.admin_delete_message, name='admin_delete_message'),
|
# path('admin/messages/<int:message_id>/delete/', views.admin_delete_message, name='admin_delete_message'),
|
||||||
# Agency Portal URLs (for external agencies)
|
# Agency Portal URLs (for external agencies)
|
||||||
path("portal/login/", views.agency_portal_login, name="agency_portal_login"),
|
# path("portal/login/", views.agency_portal_login, name="agency_portal_login"),
|
||||||
path("portal/<int:pk>/reset/", views.portal_password_reset, name="portal_password_reset"),
|
path("portal/<int:pk>/reset/", views.portal_password_reset, name="portal_password_reset"),
|
||||||
path(
|
path(
|
||||||
"portal/dashboard/",
|
"portal/dashboard/",
|
||||||
|
|||||||
@ -571,10 +571,10 @@ def json_to_markdown_table(data_list):
|
|||||||
return markdown
|
return markdown
|
||||||
|
|
||||||
|
|
||||||
def get_candidates_from_request(request):
|
def get_applications_from_request(request):
|
||||||
for c in request.POST.items():
|
for c in request.POST.items():
|
||||||
try:
|
try:
|
||||||
yield models.Candidate.objects.get(pk=c[0])
|
yield models.Application.objects.get(pk=c[0])
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(e)
|
logger.error(e)
|
||||||
yield None
|
yield None
|
||||||
|
|||||||
@ -61,7 +61,7 @@ from django.urls import reverse_lazy
|
|||||||
from django.db.models import Count, Avg, F, Q
|
from django.db.models import Count, Avg, F, Q
|
||||||
from .forms import (
|
from .forms import (
|
||||||
ZoomMeetingForm,
|
ZoomMeetingForm,
|
||||||
CandidateExamDateForm,
|
ApplicationExamDateForm,
|
||||||
JobPostingForm,
|
JobPostingForm,
|
||||||
JobPostingImageForm,
|
JobPostingImageForm,
|
||||||
InterviewNoteForm,
|
InterviewNoteForm,
|
||||||
@ -98,7 +98,7 @@ from django.views.generic import (
|
|||||||
from .utils import (
|
from .utils import (
|
||||||
create_zoom_meeting,
|
create_zoom_meeting,
|
||||||
delete_zoom_meeting,
|
delete_zoom_meeting,
|
||||||
get_candidates_from_request,
|
get_applications_from_request,
|
||||||
update_meeting,
|
update_meeting,
|
||||||
update_zoom_meeting,
|
update_zoom_meeting,
|
||||||
get_zoom_meeting_details,
|
get_zoom_meeting_details,
|
||||||
@ -864,9 +864,9 @@ def kaauh_career(request):
|
|||||||
|
|
||||||
|
|
||||||
# job detail facing the candidate:
|
# job detail facing the candidate:
|
||||||
def application_detail(request, slug):
|
def job_application_detail(request, slug):
|
||||||
job = get_object_or_404(JobPosting, slug=slug)
|
job = get_object_or_404(JobPosting, slug=slug)
|
||||||
return render(request, "applicant/application_detail.html", {"job": job})
|
return render(request, "applicant/job_application_detail.html", {"job": job})
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
@ -1854,18 +1854,18 @@ def applications_exam_view(request, slug):
|
|||||||
|
|
||||||
@staff_user_required
|
@staff_user_required
|
||||||
def update_application_exam_status(request, slug):
|
def update_application_exam_status(request, slug):
|
||||||
candidate = get_object_or_404(Application, slug=slug)
|
application = get_object_or_404(Application, slug=slug)
|
||||||
if request.method == "POST":
|
if request.method == "POST":
|
||||||
form = CandidateExamDateForm(request.POST, instance=candidate)
|
form = ApplicationExamDateForm(request.POST, instance=application)
|
||||||
if form.is_valid():
|
if form.is_valid():
|
||||||
form.save()
|
form.save()
|
||||||
return redirect("applications_exam_view", slug=candidate.job.slug)
|
return redirect("applications_exam_view", slug=application.job.slug)
|
||||||
else:
|
else:
|
||||||
form = CandidateExamDateForm(request.POST, instance=candidate)
|
form = ApplicationExamDateForm(request.POST, instance=application)
|
||||||
return render(
|
return render(
|
||||||
request,
|
request,
|
||||||
"includes/candidate_exam_status_form.html",
|
"includes/application_exam_status_form.html",
|
||||||
{"candidate": candidate, "form": form},
|
{"application": application, "form": form},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@ -1874,17 +1874,17 @@ def bulk_update_application_exam_status(request, slug):
|
|||||||
job = get_object_or_404(JobPosting, slug=slug)
|
job = get_object_or_404(JobPosting, slug=slug)
|
||||||
status = request.headers.get("status")
|
status = request.headers.get("status")
|
||||||
if status:
|
if status:
|
||||||
for candidate in get_candidates_from_request(request):
|
for application in get_applications_from_request(request):
|
||||||
try:
|
try:
|
||||||
if status == "pass":
|
if status == "pass":
|
||||||
candidate.exam_status = "Passed"
|
application.exam_status = "Passed"
|
||||||
candidate.stage = "Interview"
|
application.stage = "Interview"
|
||||||
else:
|
else:
|
||||||
candidate.exam_status = "Failed"
|
application.exam_status = "Failed"
|
||||||
candidate.save()
|
application.save()
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(e)
|
print(e)
|
||||||
messages.success(request, f"Updated exam status selected candidates")
|
messages.success(request, f"Updated exam status selected applications")
|
||||||
return redirect("applications_exam_view", slug=job.slug)
|
return redirect("applications_exam_view", slug=job.slug)
|
||||||
|
|
||||||
|
|
||||||
@ -1897,13 +1897,13 @@ def application_criteria_view_htmx(request, pk):
|
|||||||
|
|
||||||
@staff_user_required
|
@staff_user_required
|
||||||
def application_set_exam_date(request, slug):
|
def application_set_exam_date(request, slug):
|
||||||
candidate = get_object_or_404(Application, slug=slug)
|
application = get_object_or_404(Application, slug=slug)
|
||||||
candidate.exam_date = timezone.now()
|
application.exam_date = timezone.now()
|
||||||
candidate.save()
|
application.save()
|
||||||
messages.success(
|
messages.success(
|
||||||
request, f"Set exam date for {candidate.name} to {candidate.exam_date}"
|
request, f"Set exam date for {application.name} to {application.exam_date}"
|
||||||
)
|
)
|
||||||
return redirect("applications_screening_view", slug=candidate.job.slug)
|
return redirect("applications_screening_view", slug=application.job.slug)
|
||||||
|
|
||||||
|
|
||||||
@staff_user_required
|
@staff_user_required
|
||||||
@ -1911,9 +1911,9 @@ def application_update_status(request, slug):
|
|||||||
job = get_object_or_404(JobPosting, slug=slug)
|
job = get_object_or_404(JobPosting, slug=slug)
|
||||||
mark_as = request.POST.get("mark_as")
|
mark_as = request.POST.get("mark_as")
|
||||||
if mark_as != "----------":
|
if mark_as != "----------":
|
||||||
candidate_ids = request.POST.getlist("candidate_ids")
|
application_ids = request.POST.getlist("candidate_ids")
|
||||||
print(candidate_ids)
|
|
||||||
if c := Application.objects.filter(pk__in=candidate_ids):
|
if c := Application.objects.filter(pk__in=application_ids):
|
||||||
if mark_as == "Exam":
|
if mark_as == "Exam":
|
||||||
print("exam")
|
print("exam")
|
||||||
c.update(
|
c.update(
|
||||||
@ -1979,7 +1979,7 @@ def application_update_status(request, slug):
|
|||||||
else "Applicant",
|
else "Applicant",
|
||||||
)
|
)
|
||||||
|
|
||||||
messages.success(request, f"Candidates Updated")
|
messages.success(request, f"Applications Updated")
|
||||||
response = HttpResponse(redirect("applications_screening_view", slug=job.slug))
|
response = HttpResponse(redirect("applications_screening_view", slug=job.slug))
|
||||||
response.headers["HX-Refresh"] = "true"
|
response.headers["HX-Refresh"] = "true"
|
||||||
return response
|
return response
|
||||||
@ -3710,22 +3710,22 @@ def agency_delete(request, slug):
|
|||||||
|
|
||||||
@staff_user_required
|
@staff_user_required
|
||||||
def agency_applications(request, slug):
|
def agency_applications(request, slug):
|
||||||
"""View all candidates from a specific agency"""
|
"""View all applications from a specific agency"""
|
||||||
agency = get_object_or_404(HiringAgency, slug=slug)
|
agency = get_object_or_404(HiringAgency, slug=slug)
|
||||||
candidates = Application.objects.filter(hiring_agency=agency).order_by(
|
applications = Application.objects.filter(hiring_agency=agency).order_by(
|
||||||
"-created_at"
|
"-created_at"
|
||||||
)
|
)
|
||||||
|
|
||||||
# Filter by stage if provided
|
# Filter by stage if provided
|
||||||
stage_filter = request.GET.get("stage")
|
stage_filter = request.GET.get("stage")
|
||||||
if stage_filter:
|
if stage_filter:
|
||||||
candidates = candidates.filter(stage=stage_filter)
|
applications = applications.filter(stage=stage_filter)
|
||||||
|
|
||||||
# Get total candidates before pagination for accurate count
|
# Get total applications before pagination for accurate count
|
||||||
total_candidates = candidates.count()
|
total_applications = applications.count()
|
||||||
|
|
||||||
# Pagination
|
# Pagination
|
||||||
paginator = Paginator(candidates, 20) # Show 20 candidates per page
|
paginator = Paginator(applications, 20) # Show 20 applications per page
|
||||||
page_number = request.GET.get("page")
|
page_number = request.GET.get("page")
|
||||||
page_obj = paginator.get_page(page_number)
|
page_obj = paginator.get_page(page_number)
|
||||||
|
|
||||||
@ -3733,9 +3733,9 @@ def agency_applications(request, slug):
|
|||||||
"agency": agency,
|
"agency": agency,
|
||||||
"page_obj": page_obj,
|
"page_obj": page_obj,
|
||||||
"stage_filter": stage_filter,
|
"stage_filter": stage_filter,
|
||||||
"total_candidates": total_candidates,
|
"total_applications": total_applications,
|
||||||
}
|
}
|
||||||
return render(request, "recruitment/agency_candidates.html", context)
|
return render(request, "recruitment/agency_applications.html", context)
|
||||||
|
|
||||||
|
|
||||||
# Agency Portal Management Views
|
# Agency Portal Management Views
|
||||||
@ -3817,8 +3817,8 @@ def agency_assignment_detail(request, slug):
|
|||||||
AgencyJobAssignment.objects.select_related("agency", "job"), slug=slug
|
AgencyJobAssignment.objects.select_related("agency", "job"), slug=slug
|
||||||
)
|
)
|
||||||
|
|
||||||
# Get candidates submitted by this agency for this job
|
# Get applications submitted by this agency for this job
|
||||||
candidates = Application.objects.filter(
|
applications = Application.objects.filter(
|
||||||
hiring_agency=assignment.agency, job=assignment.job
|
hiring_agency=assignment.agency, job=assignment.job
|
||||||
).order_by("-created_at")
|
).order_by("-created_at")
|
||||||
|
|
||||||
@ -3827,21 +3827,21 @@ def agency_assignment_detail(request, slug):
|
|||||||
|
|
||||||
# Get messages for this assignment
|
# Get messages for this assignment
|
||||||
|
|
||||||
total_candidates = candidates.count()
|
total_applications = applications.count()
|
||||||
max_candidates = assignment.max_candidates
|
max_applications = assignment.max_candidates
|
||||||
circumference = 326.73 # 2 * π * r where r=52
|
circumference = 326.73 # 2 * π * r where r=52
|
||||||
|
|
||||||
if max_candidates > 0:
|
if max_applications > 0:
|
||||||
progress_percentage = total_candidates / max_candidates
|
progress_percentage = total_applications / max_applications
|
||||||
stroke_dashoffset = circumference - (circumference * progress_percentage)
|
stroke_dashoffset = circumference - (circumference * progress_percentage)
|
||||||
else:
|
else:
|
||||||
stroke_dashoffset = circumference
|
stroke_dashoffset = circumference
|
||||||
|
|
||||||
context = {
|
context = {
|
||||||
"assignment": assignment,
|
"assignment": assignment,
|
||||||
"candidates": candidates,
|
"applications": applications,
|
||||||
"access_link": access_link,
|
"access_link": access_link,
|
||||||
"total_candidates": candidates.count(),
|
"total_applications": applications.count(),
|
||||||
"stroke_dashoffset": stroke_dashoffset,
|
"stroke_dashoffset": stroke_dashoffset,
|
||||||
}
|
}
|
||||||
return render(request, "recruitment/agency_assignment_detail.html", context)
|
return render(request, "recruitment/agency_assignment_detail.html", context)
|
||||||
@ -3973,40 +3973,40 @@ def portal_password_reset(request,pk):
|
|||||||
for error in errors:
|
for error in errors:
|
||||||
messages.error(request, f"{field}: {error}")
|
messages.error(request, f"{field}: {error}")
|
||||||
|
|
||||||
# Agency Portal Views (for external agencies)
|
# # Agency Portal Views (for external agencies)
|
||||||
def agency_portal_login(request):
|
# def agency_portal_login(request):
|
||||||
"""Agency login page"""
|
# """Agency login page"""
|
||||||
# if request.session.get("agency_assignment_id"):
|
# # if request.session.get("agency_assignment_id"):
|
||||||
# return redirect("agency_portal_dashboard")
|
# # return redirect("agency_portal_dashboard")
|
||||||
if request.method == "POST":
|
# if request.method == "POST":
|
||||||
form = AgencyLoginForm(request.POST)
|
# form = AgencyLoginForm(request.POST)
|
||||||
|
|
||||||
if form.is_valid():
|
# if form.is_valid():
|
||||||
# Check if validated_access_link attribute exists
|
# # Check if validated_access_link attribute exists
|
||||||
|
|
||||||
# if hasattr(form, "validated_access_link"):
|
# # if hasattr(form, "validated_access_link"):
|
||||||
# access_link = form.validated_access_link
|
# # access_link = form.validated_access_link
|
||||||
# access_link.record_access()
|
# # access_link.record_access()
|
||||||
|
|
||||||
# Store assignment in session
|
# # Store assignment in session
|
||||||
# request.session["agency_assignment_id"] = access_link.assignment.id
|
# # request.session["agency_assignment_id"] = access_link.assignment.id
|
||||||
# request.session["agency_name"] = access_link.assignment.agency.name
|
# # request.session["agency_name"] = access_link.assignment.agency.name
|
||||||
|
|
||||||
messages.success(request, f"Welcome, {access_link.assignment.agency.name}!")
|
# messages.success(request, f"Welcome, {access_link.assignment.agency.name}!")
|
||||||
return redirect("agency_portal_dashboard")
|
# return redirect("agency_portal_dashboard")
|
||||||
else:
|
# else:
|
||||||
messages.error(request, "Invalid token or password.")
|
# messages.error(request, "Invalid token or password.")
|
||||||
else:
|
# else:
|
||||||
form = AgencyLoginForm()
|
# form = AgencyLoginForm()
|
||||||
|
|
||||||
context = {
|
# context = {
|
||||||
"form": form,
|
# "form": form,
|
||||||
}
|
# }
|
||||||
return render(request, "recruitment/agency_portal_login.html", context)
|
# return render(request, "recruitment/agency_portal_login.html", context)
|
||||||
|
|
||||||
|
|
||||||
def portal_login(request):
|
def portal_login(request):
|
||||||
"""Unified portal login for agency and candidate"""
|
"""Unified portal login for agency and applicant"""
|
||||||
if request.user.is_authenticated:
|
if request.user.is_authenticated:
|
||||||
if request.user.user_type == "agency":
|
if request.user.user_type == "agency":
|
||||||
return redirect("agency_portal_dashboard")
|
return redirect("agency_portal_dashboard")
|
||||||
@ -4077,24 +4077,24 @@ def portal_login(request):
|
|||||||
@login_required
|
@login_required
|
||||||
@candidate_user_required
|
@candidate_user_required
|
||||||
def applicant_portal_dashboard(request):
|
def applicant_portal_dashboard(request):
|
||||||
"""Candidate portal dashboard"""
|
"""applicant portal dashboard"""
|
||||||
if not request.user.is_authenticated:
|
if not request.user.is_authenticated:
|
||||||
return redirect("account_login")
|
return redirect("account_login")
|
||||||
|
|
||||||
# Get candidate profile (Person record)
|
# Get candidate profile (Person record)
|
||||||
try:
|
try:
|
||||||
candidate = request.user.person_profile
|
applicant = request.user.person_profile
|
||||||
except:
|
except:
|
||||||
messages.error(request, "No candidate profile found.")
|
messages.error(request, "No candidate profile found.")
|
||||||
return redirect("account_login")
|
return redirect("account_login")
|
||||||
|
|
||||||
# Get candidate's applications with related job data
|
# Get candidate's applications with related job data
|
||||||
applications = Application.objects.filter(
|
applications = Application.objects.filter(
|
||||||
person=candidate
|
person=applicant
|
||||||
).select_related('job').order_by('-created_at')
|
).select_related('job').order_by('-created_at')
|
||||||
|
|
||||||
# Get candidate's documents using the Person documents property
|
# Get candidate's documents using the Person documents property
|
||||||
documents = candidate.documents.order_by('-created_at')
|
documents = applicant.documents.order_by('-created_at')
|
||||||
|
|
||||||
# Add password change form for modal
|
# Add password change form for modal
|
||||||
password_form = PasswordResetForm()
|
password_form = PasswordResetForm()
|
||||||
@ -4104,13 +4104,13 @@ def applicant_portal_dashboard(request):
|
|||||||
document_form = DocumentUploadForm()
|
document_form = DocumentUploadForm()
|
||||||
|
|
||||||
context = {
|
context = {
|
||||||
"candidate": candidate,
|
"applicant": applicant,
|
||||||
"applications": applications,
|
"applications": applications,
|
||||||
"documents": documents,
|
"documents": documents,
|
||||||
"password_form": password_form,
|
"password_form": password_form,
|
||||||
"document_form": document_form,
|
"document_form": document_form,
|
||||||
}
|
}
|
||||||
return render(request, "recruitment/candidate_profile.html", context)
|
return render(request, "recruitment/applicant_profile.html", context)
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
@ -4163,7 +4163,7 @@ def applicant_application_detail(request, slug):
|
|||||||
"interviews": interviews,
|
"interviews": interviews,
|
||||||
"documents": documents,
|
"documents": documents,
|
||||||
}
|
}
|
||||||
return render(request, "recruitment/candidate_application_detail.html", context)
|
return render(request, "recruitment/applicant_application_detail.html", context)
|
||||||
|
|
||||||
|
|
||||||
@agency_user_required
|
@agency_user_required
|
||||||
@ -4243,17 +4243,17 @@ def agency_portal_dashboard(request):
|
|||||||
# Calculate statistics for each assignment
|
# Calculate statistics for each assignment
|
||||||
assignment_stats = []
|
assignment_stats = []
|
||||||
for assignment in assignments:
|
for assignment in assignments:
|
||||||
candidates = Application.objects.filter(
|
applications = Application.objects.filter(
|
||||||
hiring_agency=agency, job=assignment.job
|
hiring_agency=agency, job=assignment.job
|
||||||
).order_by("-created_at")
|
).order_by("-created_at")
|
||||||
|
|
||||||
unread_messages = 0
|
unread_messages = Message.objects.filter(job=assignment.job,recipient=agency.user,is_read=False).count()
|
||||||
|
|
||||||
assignment_stats.append(
|
assignment_stats.append(
|
||||||
{
|
{
|
||||||
"assignment": assignment,
|
"assignment": assignment,
|
||||||
"candidates": candidates,
|
"applications": applications,
|
||||||
"candidate_count": candidates.count(),
|
"application_count": applications.count(),
|
||||||
"unread_messages": unread_messages,
|
"unread_messages": unread_messages,
|
||||||
"days_remaining": assignment.days_remaining,
|
"days_remaining": assignment.days_remaining,
|
||||||
"is_active": assignment.is_currently_active,
|
"is_active": assignment.is_currently_active,
|
||||||
@ -4262,7 +4262,7 @@ def agency_portal_dashboard(request):
|
|||||||
)
|
)
|
||||||
|
|
||||||
# Get overall statistics
|
# Get overall statistics
|
||||||
total_candidates = sum(stats["candidate_count"] for stats in assignment_stats)
|
total_applications = sum(stats["application_count"] for stats in assignment_stats)
|
||||||
total_unread_messages = sum(stats["unread_messages"] for stats in assignment_stats)
|
total_unread_messages = sum(stats["unread_messages"] for stats in assignment_stats)
|
||||||
active_assignments = sum(1 for stats in assignment_stats if stats["is_active"])
|
active_assignments = sum(1 for stats in assignment_stats if stats["is_active"])
|
||||||
|
|
||||||
@ -4272,7 +4272,7 @@ def agency_portal_dashboard(request):
|
|||||||
"assignment_stats": assignment_stats,
|
"assignment_stats": assignment_stats,
|
||||||
"total_assignments": assignments.count(),
|
"total_assignments": assignments.count(),
|
||||||
"active_assignments": active_assignments,
|
"active_assignments": active_assignments,
|
||||||
"total_candidates": total_candidates,
|
"total_applications": total_applications,
|
||||||
"total_unread_messages": total_unread_messages,
|
"total_unread_messages": total_unread_messages,
|
||||||
}
|
}
|
||||||
return render(request, "recruitment/agency_portal_dashboard.html", context)
|
return render(request, "recruitment/agency_portal_dashboard.html", context)
|
||||||
@ -4280,7 +4280,7 @@ def agency_portal_dashboard(request):
|
|||||||
|
|
||||||
@agency_user_required
|
@agency_user_required
|
||||||
def agency_portal_submit_application_page(request, slug):
|
def agency_portal_submit_application_page(request, slug):
|
||||||
"""Dedicated page for submitting a candidate"""
|
"""Dedicated page for submitting a application """
|
||||||
# assignment_id = request.session.get("agency_assignment_id")
|
# assignment_id = request.session.get("agency_assignment_id")
|
||||||
# if not assignment_id:
|
# if not assignment_id:
|
||||||
# return redirect("agency_portal_login")
|
# return redirect("agency_portal_login")
|
||||||
@ -4296,7 +4296,7 @@ def agency_portal_submit_application_page(request, slug):
|
|||||||
current_job=assignment.job
|
current_job=assignment.job
|
||||||
|
|
||||||
if assignment.is_full:
|
if assignment.is_full:
|
||||||
messages.error(request, "Maximum candidate limit reached for this assignment.")
|
messages.error(request, "Maximum Application limit reached for this assignment.")
|
||||||
return redirect("agency_portal_assignment_detail", slug=assignment.slug)
|
return redirect("agency_portal_assignment_detail", slug=assignment.slug)
|
||||||
# Verify this assignment belongs to the same agency as the logged-in session
|
# Verify this assignment belongs to the same agency as the logged-in session
|
||||||
if assignment.agency.id != assignment.agency.id:
|
if assignment.agency.id != assignment.agency.id:
|
||||||
@ -4309,11 +4309,11 @@ def agency_portal_submit_application_page(request, slug):
|
|||||||
if not assignment.can_submit:
|
if not assignment.can_submit:
|
||||||
messages.error(
|
messages.error(
|
||||||
request,
|
request,
|
||||||
"Cannot submit candidates: Assignment is not active, expired, or full.",
|
"Cannot submit applications: Assignment is not active, expired, or full.",
|
||||||
)
|
)
|
||||||
return redirect("agency_portal_assignment_detail", slug=assignment.slug)
|
return redirect("agency_portal_assignment_detail", slug=assignment.slug)
|
||||||
|
|
||||||
# Get total submitted candidates for this assignment
|
# Get total submitted applications for this assignment
|
||||||
total_submitted = Application.objects.filter(
|
total_submitted = Application.objects.filter(
|
||||||
hiring_agency=assignment.agency, job=assignment.job
|
hiring_agency=assignment.agency, job=assignment.job
|
||||||
).count()
|
).count()
|
||||||
|
|||||||
@ -25,7 +25,6 @@ from django.urls import reverse_lazy
|
|||||||
from django.db.models import FloatField
|
from django.db.models import FloatField
|
||||||
from django.db.models import F, IntegerField, Count, Avg, Sum, Q, ExpressionWrapper, fields, Value,CharField
|
from django.db.models import F, IntegerField, Count, Avg, Sum, Q, ExpressionWrapper, fields, Value,CharField
|
||||||
from django.db.models.functions import Cast, Coalesce, TruncDate
|
from django.db.models.functions import Cast, Coalesce, TruncDate
|
||||||
from django.contrib.auth.decorators import login_required
|
|
||||||
from django.shortcuts import render
|
from django.shortcuts import render
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
@ -33,7 +32,7 @@ import json
|
|||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
|
||||||
# Add imports for user type restrictions
|
# Add imports for user type restrictions
|
||||||
from recruitment.decorators import StaffRequiredMixin, staff_user_required
|
from recruitment.decorators import StaffRequiredMixin, staff_user_required,candidate_user_required,staff_or_candidate_required
|
||||||
|
|
||||||
|
|
||||||
from datastar_py.django import (
|
from datastar_py.django import (
|
||||||
@ -907,7 +906,7 @@ def export_applications_csv(request, job_slug, stage):
|
|||||||
@login_required
|
@login_required
|
||||||
@staff_user_required
|
@staff_user_required
|
||||||
def sync_hired_applications(request, job_slug):
|
def sync_hired_applications(request, job_slug):
|
||||||
"""Sync hired candidates to external sources using Django-Q"""
|
"""Sync hired applications to external sources using Django-Q"""
|
||||||
from django_q.tasks import async_task
|
from django_q.tasks import async_task
|
||||||
from .tasks import sync_hired_candidates_task
|
from .tasks import sync_hired_candidates_task
|
||||||
|
|
||||||
|
|||||||
@ -191,7 +191,7 @@
|
|||||||
|
|
||||||
{% for job in active_jobs %}
|
{% for job in active_jobs %}
|
||||||
{# Optimized Job Listing Card #}
|
{# Optimized Job Listing Card #}
|
||||||
<a href="{% url 'application_detail' job.slug %}"
|
<a href="{% url 'job_application_detail' job.slug %}"
|
||||||
class="card d-block text-decoration-none text-dark job-listing-card p-4 border-2 shadow-hover transition-all">
|
class="card d-block text-decoration-none text-dark job-listing-card p-4 border-2 shadow-hover transition-all">
|
||||||
|
|
||||||
<div class="d-flex justify-content-between align-items-start mb-2">
|
<div class="d-flex justify-content-between align-items-start mb-2">
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
{% url 'update_application_exam_status' slug=candidate.slug as url %}
|
{% url 'update_application_exam_status' slug=application.slug as url %}
|
||||||
<form data-on-submit="@post('{{url}}', {contentType: 'form', headers: {'X-CSRFToken': '{{ csrf_token }}'}})">
|
<form data-on-submit="@post('{{url}}', {contentType: 'form', headers: {'X-CSRFToken': '{{ csrf_token }}'}})">
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
{{ form.as_p }}
|
{{ form.as_p }}
|
||||||
@ -75,7 +75,7 @@
|
|||||||
<div class="collapse navbar-collapse" id="agencyNavbar">
|
<div class="collapse navbar-collapse" id="agencyNavbar">
|
||||||
|
|
||||||
<div class="navbar-nav ms-auto">
|
<div class="navbar-nav ms-auto">
|
||||||
<li class="nav-item me-2">
|
<li class="nav-item me-2">
|
||||||
{% if LANGUAGE_CODE == 'en' %}
|
{% if LANGUAGE_CODE == 'en' %}
|
||||||
<form action="{% url 'set_language' %}" method="post" class="d-inline">{% csrf_token %}
|
<form action="{% url 'set_language' %}" method="post" class="d-inline">{% csrf_token %}
|
||||||
<input name="next" type="hidden" value="{{ request.get_full_path }}">
|
<input name="next" type="hidden" value="{{ request.get_full_path }}">
|
||||||
@ -120,7 +120,7 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
{% comment %}
|
||||||
<li class="nav-item dropdown">
|
<li class="nav-item dropdown">
|
||||||
<a class="nav-link dropdown-toggle text-white" href="#" role="button" data-bs-toggle="dropdown"
|
<a class="nav-link dropdown-toggle text-white" href="#" role="button" data-bs-toggle="dropdown"
|
||||||
data-bs-offset="0, 8" aria-expanded="false" aria-label="{% trans 'Toggle language menu' %}">
|
data-bs-offset="0, 8" aria-expanded="false" aria-label="{% trans 'Toggle language menu' %}">
|
||||||
@ -145,8 +145,8 @@
|
|||||||
</form>
|
</form>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</li>
|
</li> {% endcomment %}
|
||||||
{% endcomment %}
|
|
||||||
{% if request.user.is_authenticated %}
|
{% if request.user.is_authenticated %}
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<a class="nav-link text-white" href="{% url 'user_detail' request.user.pk %}">
|
<a class="nav-link text-white" href="{% url 'user_detail' request.user.pk %}">
|
||||||
|
|||||||
@ -207,7 +207,7 @@
|
|||||||
|
|
||||||
<div class="alert alert-info mx-2">
|
<div class="alert alert-info mx-2">
|
||||||
<i class="fas fa-info-circle me-2"></i>
|
<i class="fas fa-info-circle me-2"></i>
|
||||||
{% trans "Share these credentials securely with the agency. They can use this information to log in and submit candidates." %}
|
{% trans "Share these credentials securely with the agency. They can use this information to log in and submit applications." %}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{% if access_link %}
|
{% if access_link %}
|
||||||
@ -219,12 +219,12 @@
|
|||||||
</div>
|
</div>
|
||||||
</div> {% endcomment %}
|
</div> {% endcomment %}
|
||||||
|
|
||||||
<!-- Candidates Card -->
|
<!-- Applications Card -->
|
||||||
<div class="kaauh-card p-4">
|
<div class="kaauh-card p-4">
|
||||||
<div class="d-flex justify-content-between align-items-center mb-4">
|
<div class="d-flex justify-content-between align-items-center mb-4">
|
||||||
<h5 class="mb-0" style="color: var(--kaauh-teal-dark);">
|
<h5 class="mb-0" style="color: var(--kaauh-teal-dark);">
|
||||||
<i class="fas fa-users me-2"></i>
|
<i class="fas fa-users me-2"></i>
|
||||||
{% trans "Submitted Candidates" %} ({{ total_candidates }})
|
{% trans "Submitted Applications" %} ({{ total_candidates }})
|
||||||
</h5>
|
</h5>
|
||||||
{% if access_link %}
|
{% if access_link %}
|
||||||
<a href="{% url 'agency_portal_login' %}" target="_blank" class="btn btn-outline-info btn-sm">
|
<a href="{% url 'agency_portal_login' %}" target="_blank" class="btn btn-outline-info btn-sm">
|
||||||
@ -233,12 +233,12 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{% if candidates %}
|
{% if applications %}
|
||||||
<div class="table-responsive">
|
<div class="table-responsive">
|
||||||
<table class="table table-hover">
|
<table class="table table-hover">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th class="px-4 py-3 text-uppercase small fw-bold text-muted">{% trans "Candidate"%}</th>
|
<th class="px-4 py-3 text-uppercase small fw-bold text-muted">{% trans "Application"%}</th>
|
||||||
<th class="px-4 py-3 text-uppercase small fw-bold text-muted">{% trans "Contact" %}
|
<th class="px-4 py-3 text-uppercase small fw-bold text-muted">{% trans "Contact" %}
|
||||||
</th>
|
</th>
|
||||||
<th class="px-4 py-3 text-uppercase small fw-bold text-muted">{% trans "Stage" %}
|
<th class="px-4 py-3 text-uppercase small fw-bold text-muted">{% trans "Stage" %}
|
||||||
@ -248,36 +248,36 @@
|
|||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{% for candidate in candidates %}
|
{% for application in applications %}
|
||||||
<tr>
|
<tr>
|
||||||
<td>
|
<td>
|
||||||
<div class="fw-bold">{{ candidate.name }}</div>
|
<div class="fw-bold">{{ application.name }}</div>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<div class="small">
|
<div class="small">
|
||||||
<div><i class="fas fa-envelope me-1"></i> {{ candidate.email }}</div>
|
<div><i class="fas fa-envelope me-1"></i> {{ application.email }}</div>
|
||||||
<div><i class="fas fa-phone me-1"></i> {{ candidate.phone }}</div>
|
<div><i class="fas fa-phone me-1"></i> {{ application.phone }}</div>
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<span class="badge bg-info">{{ candidate.get_stage_display }}</span>
|
<span class="badge bg-info">{{ application.get_stage_display }}</span>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<div class="small text-muted">
|
<div class="small text-muted">
|
||||||
<div class="mb-1"><i class="fas fa-envelope me-2 w-20"></i>
|
<div class="mb-1"><i class="fas fa-envelope me-2 w-20"></i>
|
||||||
{{candidate.email }}</div>
|
{{application.email }}</div>
|
||||||
<div><i class="fas fa-phone me-2 w-20"></i>{{ candidate.phone }}</div>
|
<div><i class="fas fa-phone me-2 w-20"></i>{{ application.phone }}</div>
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
<td class="px-4">
|
<td class="px-4">
|
||||||
<span class="badge bg-soft-info text-info rounded-pill px-3">
|
<span class="badge bg-soft-info text-info rounded-pill px-3">
|
||||||
{{candidate.get_stage_display }}</span>
|
{{application.get_stage_display }}</span>
|
||||||
</td>
|
</td>
|
||||||
<td class="px-4">
|
<td class="px-4">
|
||||||
<span class="small text-muted">{{ candidate.created_at|date:"M d, Y" }}</span>
|
<span class="small text-muted">{{ application.created_at|date:"M d, Y" }}</span>
|
||||||
</td>
|
</td>
|
||||||
<td class="px-4 text-end">
|
<td class="px-4 text-end">
|
||||||
<a href="{% url 'application_detail' candidate.slug %}"
|
<a href="{% url 'application_detail' application.slug %}"
|
||||||
class="btn btn-sm btn-outline-primary" title="{% trans 'View Details' %}">
|
class="btn btn-sm btn-outline-primary" title="{% trans 'View Details' %}">
|
||||||
<i class="fas fa-eye"></i>
|
<i class="fas fa-eye"></i>
|
||||||
</a>
|
</a>
|
||||||
@ -290,9 +290,9 @@
|
|||||||
{% else %}
|
{% else %}
|
||||||
<div class="text-center py-4">
|
<div class="text-center py-4">
|
||||||
<i class="fas fa-users fa-2x text-muted mb-3"></i>
|
<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">
|
<p class="text-muted small">
|
||||||
{% trans "Candidates will appear here once the agency submits them through their portal." %}
|
{% trans "Applications will appear here once the agency submits them through their portal." %}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
@ -335,7 +335,7 @@
|
|||||||
|
|
||||||
<div class="text-center">
|
<div class="text-center">
|
||||||
<div class="h4 mb-1">{{ total_candidates }}</div>
|
<div class="h4 mb-1">{{ total_candidates }}</div>
|
||||||
<div class="text-muted">/ {{ assignment.max_candidates }} {% trans "candidates" %}</div>
|
<div class="text-muted">/ {{ assignment.max_candidates }} {% trans "applications" %}</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="progress mt-3" style="height: 8px;">
|
<div class="progress mt-3" style="height: 8px;">
|
||||||
|
|||||||
@ -50,7 +50,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
{% comment %} <a href="{% url 'agency_portal_submit_application' %}" class="btn btn-main-action me-2">
|
{% comment %} <a href="{% url 'agency_portal_submit_application' %}" class="btn btn-main-action me-2">
|
||||||
<i class="fas fa-user-plus me-1"></i> {% trans "Submit Candidate" %}
|
<i class="fas fa-user-plus me-1"></i> {% trans "Submit Application" %}
|
||||||
</a>
|
</a>
|
||||||
<a href="#" class="btn btn-outline-secondary position-relative">
|
<a href="#" class="btn btn-outline-secondary position-relative">
|
||||||
<i class="fas fa-envelope me-1"></i> {% trans "Messages" %}
|
<i class="fas fa-envelope me-1"></i> {% trans "Messages" %}
|
||||||
@ -93,8 +93,8 @@
|
|||||||
<div class="text-info mb-2">
|
<div class="text-info mb-2">
|
||||||
<i class="fas fa-users fa-2x"></i>
|
<i class="fas fa-users fa-2x"></i>
|
||||||
</div>
|
</div>
|
||||||
<h4 class="card-title">{{ total_candidates }}</h4>
|
<h4 class="card-title">{{ total_applications }}</h4>
|
||||||
<p class="card-text text-muted">{% trans "Total Candidates" %}</p>
|
<p class="card-text text-muted">{% trans "Total Applications" %}</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -169,8 +169,8 @@
|
|||||||
</strong>
|
</strong>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-6">
|
<div class="col-6">
|
||||||
<small class="text-muted d-block">{% trans "Candidates" %}</small>
|
<small class="text-muted d-block">{% trans "Applications" %}</small>
|
||||||
<strong>{{ stats.candidate_count }} / {{ stats.assignment.max_candidates }}</strong>
|
<strong>{{ stats.application_count }} / {{ stats.assignment.max_applications }}</strong>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -178,10 +178,10 @@
|
|||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
<div class="d-flex justify-content-between mb-1">
|
<div class="d-flex justify-content-between mb-1">
|
||||||
<small class="text-muted">{% trans "Submission Progress" %}</small>
|
<small class="text-muted">{% trans "Submission Progress" %}</small>
|
||||||
<small class="text-muted">{{ stats.candidate_count }}/{{ stats.assignment.max_candidates }}</small>
|
<small class="text-muted">{{ stats.application_count }}/{{ stats.assignment.max_applications }}</small>
|
||||||
</div>
|
</div>
|
||||||
<div class="progress" style="height: 6px;">
|
<div class="progress" style="height: 6px;">
|
||||||
{% with progress=stats.candidate_count %}
|
{% with progress=stats.application_count %}
|
||||||
<div class="progress-bar {% if progress >= 90 %}bg-danger{% elif progress >= 70 %}bg-warning{% else %}bg-success{% endif %}"
|
<div class="progress-bar {% if progress >= 90 %}bg-danger{% elif progress >= 70 %}bg-warning{% else %}bg-success{% endif %}"
|
||||||
style="width: {{ progress|floatformat:0 }}%"></div>
|
style="width: {{ progress|floatformat:0 }}%"></div>
|
||||||
{% endwith %}
|
{% endwith %}
|
||||||
@ -194,7 +194,7 @@
|
|||||||
{% if stats.can_submit %}
|
{% if stats.can_submit %}
|
||||||
<a href="{% url 'agency_portal_submit_application_page' stats.assignment.slug %}"
|
<a href="{% url 'agency_portal_submit_application_page' stats.assignment.slug %}"
|
||||||
class="btn btn-sm btn-main-action">
|
class="btn btn-sm btn-main-action">
|
||||||
<i class="fas fa-user-plus me-1"></i> {% trans "Submit Candidate" %}
|
<i class="fas fa-user-plus me-1"></i> {% trans "Submit Application" %}
|
||||||
</a>
|
</a>
|
||||||
{% else %}
|
{% else %}
|
||||||
<button class="btn btn-sm btn-secondary" disabled>
|
<button class="btn btn-sm btn-secondary" disabled>
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
{% extends 'portal_base.html' %}
|
{% extends 'portal_base.html' %}
|
||||||
{% load static i18n crispy_forms_tags %}
|
{% load static i18n crispy_forms_tags %}
|
||||||
|
|
||||||
{% block title %}{% trans "Submit Candidate" %} - {{ assignment.job.title }} - Agency Portal{% endblock %}
|
{% block title %}{% trans "Submit Application" %} - {{ assignment.job.title }} - Agency Portal{% endblock %}
|
||||||
|
|
||||||
{% block customCSS %}
|
{% block customCSS %}
|
||||||
<style>
|
<style>
|
||||||
@ -97,13 +97,13 @@
|
|||||||
</li>
|
</li>
|
||||||
|
|
||||||
{% comment %} <li class="breadcrumb-item active" aria-current="page">
|
{% comment %} <li class="breadcrumb-item active" aria-current="page">
|
||||||
{% trans "Submit Candidate" %}
|
{% trans "Submit Application" %}
|
||||||
</li> {% endcomment %}
|
</li> {% endcomment %}
|
||||||
|
|
||||||
<li class="breadcrumb-item active" aria-current="page" style="
|
<li class="breadcrumb-item active" aria-current="page" style="
|
||||||
color: #F43B5E; /* Rosy Accent Color */
|
color: #F43B5E; /* Rosy Accent Color */
|
||||||
font-weight: 600; ">
|
font-weight: 600; ">
|
||||||
{% trans "Submit Candidate" %}</li>
|
{% trans "Submit Application" %}</li>
|
||||||
</ol>
|
</ol>
|
||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
@ -112,11 +112,11 @@
|
|||||||
<div>
|
<div>
|
||||||
<h1 class="h3 mb-1" style="color: var(--kaauh-teal-dark); font-weight: 700;">
|
<h1 class="h3 mb-1" style="color: var(--kaauh-teal-dark); font-weight: 700;">
|
||||||
<i class="fas fa-user-plus me-2"></i>
|
<i class="fas fa-user-plus me-2"></i>
|
||||||
{% trans "Submit New Candidate" %}
|
{% trans "Submit New Application" %}
|
||||||
</h1>
|
</h1>
|
||||||
<p class="text-muted mb-0">
|
<p class="text-muted mb-0">
|
||||||
<!-- Button trigger modal -->
|
<!-- Button trigger modal -->
|
||||||
{% trans "Submit a candidate for" %}
|
{% trans "Submit a Application for" %}
|
||||||
{{ assignment.job.title }}
|
{{ assignment.job.title }}
|
||||||
|
|
||||||
</p>
|
</p>
|
||||||
@ -171,16 +171,16 @@
|
|||||||
<div class="kaauh-card p-4">
|
<div class="kaauh-card p-4">
|
||||||
<h5 class="mb-4" style="color: var(--kaauh-teal-dark);">
|
<h5 class="mb-4" style="color: var(--kaauh-teal-dark);">
|
||||||
<i class="fas fa-user-edit me-2"></i>
|
<i class="fas fa-user-edit me-2"></i>
|
||||||
{% trans "Candidate Information" %}
|
{% trans "Application Information" %}
|
||||||
</h5>
|
</h5>
|
||||||
|
|
||||||
<form method="post" enctype="multipart/form-data" id="candidateForm"
|
<form method="post" enctype="multipart/form-data" id="ApplicationForm"
|
||||||
action="{% url 'agency_portal_submit_application_page' assignment.slug %}">
|
action="{% url 'agency_portal_submit_application_page' assignment.slug %}">
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
{{form|crispy}}
|
{{form|crispy}}
|
||||||
<button type="submit" class="btn btn-main-action">
|
<button type="submit" class="btn btn-main-action">
|
||||||
<i class="fas fa-user-plus me-2"></i>
|
<i class="fas fa-user-plus me-2"></i>
|
||||||
{% trans "Submit Candidate" %}
|
{% trans "Submit Application" %}
|
||||||
</button>
|
</button>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
@ -188,14 +188,14 @@
|
|||||||
<div class="kaauh-card p-4">
|
<div class="kaauh-card p-4">
|
||||||
<div class="text-center py-5">
|
<div class="text-center py-5">
|
||||||
<i class="fas fa-exclamation-triangle fa-4x text-warning mb-4"></i>
|
<i class="fas fa-exclamation-triangle fa-4x text-warning mb-4"></i>
|
||||||
<h4 class="text-warning mb-3">{% trans "Cannot Submit Candidates" %}</h4>
|
<h4 class="text-warning mb-3">{% trans "Cannot Submit Applications" %}</h4>
|
||||||
<div class="alert alert-warning d-inline-block">
|
<div class="alert alert-warning d-inline-block">
|
||||||
{% if assignment.is_expired %}
|
{% if assignment.is_expired %}
|
||||||
<i class="fas fa-clock me-2"></i>
|
<i class="fas fa-clock me-2"></i>
|
||||||
{% trans "This assignment has expired. Submissions are no longer accepted." %}
|
{% trans "This assignment has expired. Submissions are no longer accepted." %}
|
||||||
{% elif assignment.is_full %}
|
{% elif assignment.is_full %}
|
||||||
<i class="fas fa-users me-2"></i>
|
<i class="fas fa-users me-2"></i>
|
||||||
{% trans "Maximum candidate limit reached for this assignment." %}
|
{% trans "Maximum Application limit reached for this assignment." %}
|
||||||
{% else %}
|
{% else %}
|
||||||
<i class="fas fa-pause me-2"></i>
|
<i class="fas fa-pause me-2"></i>
|
||||||
{% trans "This assignment is not currently active." %}
|
{% trans "This assignment is not currently active." %}
|
||||||
@ -222,7 +222,7 @@
|
|||||||
<div class="spinner-border text-primary mb-3" role="status">
|
<div class="spinner-border text-primary mb-3" role="status">
|
||||||
<span class="visually-hidden">{% trans "Loading..." %}</span>
|
<span class="visually-hidden">{% trans "Loading..." %}</span>
|
||||||
</div>
|
</div>
|
||||||
<h6>{% trans "Submitting candidate..." %}</h6>
|
<h6>{% trans "Submitting Application..." %}</h6>
|
||||||
<p class="text-muted small">{% trans "Please wait while we process your submission." %}</p>
|
<p class="text-muted small">{% trans "Please wait while we process your submission." %}</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -240,7 +240,7 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||||||
const filePreview = document.getElementById('filePreview');
|
const filePreview = document.getElementById('filePreview');
|
||||||
const fileName = document.getElementById('fileName');
|
const fileName = document.getElementById('fileName');
|
||||||
const removeFileBtn = document.getElementById('removeFile');
|
const removeFileBtn = document.getElementById('removeFile');
|
||||||
const form = document.getElementById('candidateForm');
|
const form = document.getElementById('ApplicationForm');
|
||||||
const submitBtn = document.getElementById('submitBtn');
|
const submitBtn = document.getElementById('submitBtn');
|
||||||
|
|
||||||
// File upload area click handler
|
// File upload area click handler
|
||||||
@ -340,7 +340,7 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||||||
successAlert.style.cssText = 'position: fixed; top: 20px; right: 20px; z-index: 9999; min-width: 300px;';
|
successAlert.style.cssText = 'position: fixed; top: 20px; right: 20px; z-index: 9999; min-width: 300px;';
|
||||||
successAlert.innerHTML = `
|
successAlert.innerHTML = `
|
||||||
<i class="fas fa-check-circle me-2"></i>
|
<i class="fas fa-check-circle me-2"></i>
|
||||||
{% trans "Candidate submitted successfully!" %}
|
{% trans "Application submitted successfully!" %}
|
||||||
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
|
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
|
||||||
`;
|
`;
|
||||||
document.body.appendChild(successAlert);
|
document.body.appendChild(successAlert);
|
||||||
@ -371,7 +371,7 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||||||
errorAlert.style.cssText = 'position: fixed; top: 20px; right: 20px; z-index: 9999; min-width: 300px;';
|
errorAlert.style.cssText = 'position: fixed; top: 20px; right: 20px; z-index: 9999; min-width: 300px;';
|
||||||
errorAlert.innerHTML = `
|
errorAlert.innerHTML = `
|
||||||
<i class="fas fa-exclamation-triangle me-2"></i>
|
<i class="fas fa-exclamation-triangle me-2"></i>
|
||||||
${data.message || '{% trans "Error submitting candidate. Please try again." %}'}
|
${data.message || '{% trans "Error submitting Application. Please try again." %}'}
|
||||||
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
|
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
|
||||||
`;
|
`;
|
||||||
document.body.appendChild(errorAlert);
|
document.body.appendChild(errorAlert);
|
||||||
@ -401,7 +401,7 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||||||
.finally(() => {
|
.finally(() => {
|
||||||
// Re-enable submit button
|
// Re-enable submit button
|
||||||
submitBtn.disabled = false;
|
submitBtn.disabled = false;
|
||||||
submitBtn.innerHTML = '<i class="fas fa-paper-plane me-1"></i> {% trans "Submit Candidate" %}';
|
submitBtn.innerHTML = '<i class="fas fa-paper-plane me-1"></i> {% trans "Submit Application" %}';
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@ -292,23 +292,23 @@
|
|||||||
{# Header: Larger, more dynamic on large screens. Stacks cleanly on mobile. #}
|
{# Header: Larger, more dynamic on large screens. Stacks cleanly on mobile. #}
|
||||||
<div class="d-flex flex-column flex-md-row justify-content-between align-items-md-center mb-5">
|
<div class="d-flex flex-column flex-md-row justify-content-between align-items-md-center mb-5">
|
||||||
<h1 class="display-6 display-md-5 fw-extrabold mb-3 mb-md-0" style="color: var(--kaauh-teal-dark);">
|
<h1 class="display-6 display-md-5 fw-extrabold mb-3 mb-md-0" style="color: var(--kaauh-teal-dark);">
|
||||||
{% trans "Your Candidate Dashboard" %}
|
{% trans "Your Applicant Dashboard" %}
|
||||||
</h1>
|
</h1>
|
||||||
{% comment %} <a href="#profile-details" data-bs-toggle="tab" class="btn btn-main-action btn-sm btn-md-lg px-4 py-2 rounded-pill shadow-sm shadow-md-lg">
|
{% comment %} <a href="#profile-details" data-bs-toggle="tab" class="btn btn-main-action btn-sm btn-md-lg px-4 py-2 rounded-pill shadow-sm shadow-md-lg">
|
||||||
<i class="fas fa-edit me-2"></i> {% trans "Update Profile" %}
|
<i class="fas fa-edit me-2"></i> {% trans "Update Profile" %}
|
||||||
</a> {% endcomment %}
|
</a> {% endcomment %}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{# Candidate Quick Overview Card: Use a softer background color #}
|
{# Applicant Quick Overview Card: Use a softer background color #}
|
||||||
<div class="card kaauh-card mb-5 p-4 bg-white">
|
<div class="card kaauh-card mb-5 p-4 bg-white">
|
||||||
<div class="d-flex align-items-center flex-column flex-sm-row text-center text-sm-start">
|
<div class="d-flex align-items-center flex-column flex-sm-row text-center text-sm-start">
|
||||||
<img src="{% if candidate.user.profile_image %}{{ candidate.user.profile_image.url }}{% else %}{% static 'image/default_avatar.png' %}{% endif %}"
|
<img src="{% if applicant.user.profile_image %}{{ applicant.user.profile_image.url }}{% else %}{% static 'image/default_avatar.png' %}{% endif %}"
|
||||||
alt="{% trans 'Profile Picture' %}"
|
alt="{% trans 'Profile Picture' %}"
|
||||||
class="rounded-circle me-sm-4 mb-3 mb-sm-0 shadow-lg"
|
class="rounded-circle me-sm-4 mb-3 mb-sm-0 shadow-lg"
|
||||||
style="width: 80px; height: 80px; object-fit: cover; border: 4px solid var(--kaauh-teal-accent);">
|
style="width: 80px; height: 80px; object-fit: cover; border: 4px solid var(--kaauh-teal-accent);">
|
||||||
<div>
|
<div>
|
||||||
<h3 class="card-title mb-1 fw-bold text-dark">{{ candidate.full_name|default:"Candidate Name" }}</h3>
|
<h3 class="card-title mb-1 fw-bold text-dark">{{ applicant.full_name|default:"Applicant Name" }}</h3>
|
||||||
<p class="text-gray-subtle mb-0">{{ candidate.email }}</p>
|
<p class="text-gray-subtle mb-0">{{ applicant.email }}</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -356,21 +356,21 @@
|
|||||||
<ul class="list-unstyled profile-data-list p-0">
|
<ul class="list-unstyled profile-data-list p-0">
|
||||||
<li class="d-flex justify-content-between align-items-center">
|
<li class="d-flex justify-content-between align-items-center">
|
||||||
<div><i class="fas fa-id-card me-2 text-primary-theme"></i> <strong>{% trans "First Name" %}</strong></div>
|
<div><i class="fas fa-id-card me-2 text-primary-theme"></i> <strong>{% trans "First Name" %}</strong></div>
|
||||||
<span class="text-end">{{ candidate.first_name|default:"" }}</span>
|
<span class="text-end">{{ applicant.first_name|default:"" }}</span>
|
||||||
</li>
|
</li>
|
||||||
<li class="d-flex justify-content-between align-items-center">
|
<li class="d-flex justify-content-between align-items-center">
|
||||||
<div><i class="fas fa-id-card me-2 text-primary-theme"></i> <strong>{% trans "Last Name" %}</strong></div>
|
<div><i class="fas fa-id-card me-2 text-primary-theme"></i> <strong>{% trans "Last Name" %}</strong></div>
|
||||||
<span class="text-end">{{ candidate.last_name|default:"" }}</span>
|
<span class="text-end">{{ applicant.last_name|default:"" }}</span>
|
||||||
</li>
|
</li>
|
||||||
{% if candidate.middle_name %}
|
{% if applicant.middle_name %}
|
||||||
<li class="d-flex justify-content-between align-items-center">
|
<li class="d-flex justify-content-between align-items-center">
|
||||||
<div><i class="fas fa-id-card me-2 text-primary-theme"></i> <strong>{% trans "Middle Name" %}</strong></div>
|
<div><i class="fas fa-id-card me-2 text-primary-theme"></i> <strong>{% trans "Middle Name" %}</strong></div>
|
||||||
<span class="text-end">{{ candidate.middle_name }}</span>
|
<span class="text-end">{{ applicant.middle_name }}</span>
|
||||||
</li>
|
</li>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<li class="d-flex justify-content-between align-items-center">
|
<li class="d-flex justify-content-between align-items-center">
|
||||||
<div><i class="fas fa-envelope me-2 text-primary-theme"></i> <strong>{% trans "Email" %}</strong></div>
|
<div><i class="fas fa-envelope me-2 text-primary-theme"></i> <strong>{% trans "Email" %}</strong></div>
|
||||||
<span class="text-end">{{ candidate.email|default:"" }}</span>
|
<span class="text-end">{{ applicant.email|default:"" }}</span>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
@ -383,19 +383,19 @@
|
|||||||
<ul class="list-unstyled profile-data-list p-0">
|
<ul class="list-unstyled profile-data-list p-0">
|
||||||
<li class="d-flex justify-content-between align-items-center">
|
<li class="d-flex justify-content-between align-items-center">
|
||||||
<div><i class="fas fa-phone-alt me-2 text-primary-theme"></i> <strong>{% trans "Phone" %}</strong></div>
|
<div><i class="fas fa-phone-alt me-2 text-primary-theme"></i> <strong>{% trans "Phone" %}</strong></div>
|
||||||
<span class="text-end">{{ candidate.phone|default:"" }}</span>
|
<span class="text-end">{{ applicant.phone|default:"" }}</span>
|
||||||
</li>
|
</li>
|
||||||
{% if candidate.address %}
|
{% if applicant.address %}
|
||||||
<li class="d-flex align-items-start">
|
<li class="d-flex align-items-start">
|
||||||
<div class="mb-1"><i class="fas fa-map-marker-alt me-2 text-primary-theme"></i> <strong>{% trans "Address" %}</strong></div>
|
<div class="mb-1"><i class="fas fa-map-marker-alt me-2 text-primary-theme"></i> <strong>{% trans "Address" %}</strong></div>
|
||||||
<span class="text-end text-break">{{ candidate.address|linebreaksbr }}</span>
|
<span class="text-end text-break">{{ applicant.address|linebreaksbr }}</span>
|
||||||
</li>
|
</li>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if candidate.linkedin_profile %}
|
{% if applicant.linkedin_profile %}
|
||||||
<li class="d-flex justify-content-between align-items-center">
|
<li class="d-flex justify-content-between align-items-center">
|
||||||
<div><i class="fab fa-linkedin me-2 text-primary-theme"></i> <strong>{% trans "LinkedIn Profile" %}</strong></div>
|
<div><i class="fab fa-linkedin me-2 text-primary-theme"></i> <strong>{% trans "LinkedIn Profile" %}</strong></div>
|
||||||
<span class="text-end">
|
<span class="text-end">
|
||||||
<a href="{{ candidate.linkedin_profile }}" target="_blank" class="text-primary-theme text-decoration-none">
|
<a href="{{ applicant.linkedin_profile }}" target="_blank" class="text-primary-theme text-decoration-none">
|
||||||
{% trans "View Profile" %} <i class="fas fa-external-link-alt ms-1"></i>
|
{% trans "View Profile" %} <i class="fas fa-external-link-alt ms-1"></i>
|
||||||
</a>
|
</a>
|
||||||
</span>
|
</span>
|
||||||
@ -412,15 +412,15 @@
|
|||||||
<ul class="list-unstyled profile-data-list p-0">
|
<ul class="list-unstyled profile-data-list p-0">
|
||||||
<li class="d-flex justify-content-between align-items-center">
|
<li class="d-flex justify-content-between align-items-center">
|
||||||
<div><i class="fas fa-calendar-alt me-2 text-primary-theme"></i> <strong>{% trans "Date of Birth" %}</strong></div>
|
<div><i class="fas fa-calendar-alt me-2 text-primary-theme"></i> <strong>{% trans "Date of Birth" %}</strong></div>
|
||||||
<span class="text-end">{{ candidate.date_of_birth|date:"M d, Y"|default:"" }}</span>
|
<span class="text-end">{{ applicant.date_of_birth|date:"M d, Y"|default:"" }}</span>
|
||||||
</li>
|
</li>
|
||||||
<li class="d-flex justify-content-between align-items-center">
|
<li class="d-flex justify-content-between align-items-center">
|
||||||
<div><i class="fas fa-venus-mars me-2 text-primary-theme"></i> <strong>{% trans "Gender" %}</strong></div>
|
<div><i class="fas fa-venus-mars me-2 text-primary-theme"></i> <strong>{% trans "Gender" %}</strong></div>
|
||||||
<span class="text-end">{{ candidate.get_gender_display|default:"" }}</span>
|
<span class="text-end">{{ applicant.get_gender_display|default:"" }}</span>
|
||||||
</li>
|
</li>
|
||||||
<li class="d-flex justify-content-between align-items-center">
|
<li class="d-flex justify-content-between align-items-center">
|
||||||
<div><i class="fas fa-globe me-2 text-primary-theme"></i> <strong>{% trans "Nationality" %}</strong></div>
|
<div><i class="fas fa-globe me-2 text-primary-theme"></i> <strong>{% trans "Nationality" %}</strong></div>
|
||||||
<span class="text-end">{{ candidate.get_nationality_display|default:"" }}</span>
|
<span class="text-end">{{ applicant.get_nationality_display|default:"" }}</span>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
@ -431,16 +431,16 @@
|
|||||||
<i class="fas fa-briefcase me-2 text-primary-theme"></i>{% trans "Professional Information" %}
|
<i class="fas fa-briefcase me-2 text-primary-theme"></i>{% trans "Professional Information" %}
|
||||||
</h4>
|
</h4>
|
||||||
<ul class="list-unstyled profile-data-list p-0">
|
<ul class="list-unstyled profile-data-list p-0">
|
||||||
{% if candidate.user.designation %}
|
{% if applicant.user.designation %}
|
||||||
<li class="d-flex justify-content-between align-items-center">
|
<li class="d-flex justify-content-between align-items-center">
|
||||||
<div><i class="fas fa-user-tie me-2 text-primary-theme"></i> <strong>{% trans "Designation" %}</strong></div>
|
<div><i class="fas fa-user-tie me-2 text-primary-theme"></i> <strong>{% trans "Designation" %}</strong></div>
|
||||||
<span class="text-end">{{ candidate.user.designation }}</span>
|
<span class="text-end">{{ applicant.user.designation }}</span>
|
||||||
</li>
|
</li>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if candidate.gpa %}
|
{% if applicant.gpa %}
|
||||||
<li class="d-flex justify-content-between align-items-center">
|
<li class="d-flex justify-content-between align-items-center">
|
||||||
<div><i class="fas fa-graduation-cap me-2 text-primary-theme"></i> <strong>{% trans "GPA" %}</strong></div>
|
<div><i class="fas fa-graduation-cap me-2 text-primary-theme"></i> <strong>{% trans "GPA" %}</strong></div>
|
||||||
<span class="text-end">{{ candidate.gpa }}</span>
|
<span class="text-end">{{ applicant.gpa }}</span>
|
||||||
</li>
|
</li>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</ul>
|
</ul>
|
||||||
@ -626,7 +626,7 @@
|
|||||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-body" id="passwordModalBody">
|
<div class="modal-body" id="passwordModalBody">
|
||||||
<form action="{% url 'portal_password_reset' candidate.pk %}" method="post">
|
<form action="{% url 'portal_password_reset' applicant.pk %}" method="post">
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
{{ password_reset_form|crispy }}
|
{{ password_reset_form|crispy }}
|
||||||
<button type="submit" class="btn btn-main-action">{% trans "Change Password" %}</button>
|
<button type="submit" class="btn btn-main-action">{% trans "Change Password" %}</button>
|
||||||
@ -645,7 +645,7 @@
|
|||||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
<form method="post" action="{% url 'user_profile_image_update' candidate.pk %}" enctype="multipart/form-data" >
|
<form method="post" action="{% url 'user_profile_image_update' applicant.pk %}" enctype="multipart/form-data" >
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
|
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
@ -709,7 +709,7 @@
|
|||||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
<form method="post" action="{% url 'document_upload' candidate.id %}" enctype="multipart/form-data" id="documentUploadForm">
|
<form method="post" action="{% url 'document_upload' applicant.id %}" enctype="multipart/form-data" id="documentUploadForm">
|
||||||
<input type="hidden" name="upload_target" value="person">
|
<input type="hidden" name="upload_target" value="person">
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
Loading…
x
Reference in New Issue
Block a user