diff --git a/NorahUniversity/urls.py b/NorahUniversity/urls.py index ae9308a..fe505e6 100644 --- a/NorahUniversity/urls.py +++ b/NorahUniversity/urls.py @@ -26,7 +26,7 @@ urlpatterns = [ path('application//', views.application_submit_form, name='application_submit_form'), path('application//submit/', views.application_submit, name='application_submit'), path('application//apply/', views.application_detail, name='application_detail'), - path('application//signup/', views.candidate_signup, name='candidate_signup'), + path('application//signup/', views.application_signup, name='application_signup'), path('application//success/', views.application_success, name='application_success'), # path('application/applicant/profile', views.applicant_profile, name='applicant_profile'), diff --git a/debug_test.py b/debug_test.py index 5ed93bc..9f16df8 100644 --- a/debug_test.py +++ b/debug_test.py @@ -79,10 +79,10 @@ def debug_url_routing(): print(f"Error with document_upload URL: {e}") try: - url2 = reverse('candidate_document_upload', kwargs={'slug': application.slug}) - print(f"URL pattern 2 (candidate_document_upload): {url2}") + url2 = reverse('pplication_document_upload', kwargs={'slug': application.slug}) + print(f"URL pattern 2 (pplication_document_upload): {url2}") except Exception as e: - print(f"Error with candidate_document_upload URL: {e}") + print(f"Error with pplication_document_upload URL: {e}") # Test GET request to see if the URL is accessible try: diff --git a/recruitment/backends.py b/recruitment/backends.py index 8870012..9a8fcb2 100644 --- a/recruitment/backends.py +++ b/recruitment/backends.py @@ -24,7 +24,7 @@ class CustomAuthenticationBackend(AuthenticationBackend): elif user.user_type == 'agency': redirect_url = reverse('agency_portal_dashboard') elif user.user_type == 'candidate': - redirect_url = reverse('candidate_portal_dashboard') + redirect_url = reverse('applicant_portal_dashboard') else: # Fallback to default redirect URL if user type is unknown redirect_url = '/' diff --git a/recruitment/decorators.py b/recruitment/decorators.py index d232133..7ea41c5 100644 --- a/recruitment/decorators.py +++ b/recruitment/decorators.py @@ -55,7 +55,7 @@ def user_type_required(allowed_types=None, login_url=None): if user.user_type == 'agency': return redirect('agency_portal_dashboard') elif user.user_type == 'candidate': - return redirect('candidate_portal_dashboard') + return redirect('applicant_portal_dashboard') else: return redirect('dashboard') @@ -92,7 +92,7 @@ class UserTypeRequiredMixin(AccessMixin): if request.user.user_type == 'agency': return redirect('agency_portal_dashboard') elif request.user.user_type == 'candidate': - return redirect('candidate_portal_dashboard') + return redirect('applicant_portal_dashboard') else: return redirect('dashboard') diff --git a/recruitment/forms.py b/recruitment/forms.py index 1aa1deb..ee9e610 100644 --- a/recruitment/forms.py +++ b/recruitment/forms.py @@ -2344,7 +2344,7 @@ class MessageForm(forms.ModelForm): ) -class CandidateSignupForm(forms.ModelForm): +class ApplicantSignupForm(forms.ModelForm): password = forms.CharField(widget=forms.PasswordInput(attrs={'class': 'form-control'})) confirm_password = forms.CharField(widget=forms.PasswordInput(attrs={'class': 'form-control'})) diff --git a/recruitment/tests.py b/recruitment/tests.py index 847d494..51d14b3 100644 --- a/recruitment/tests.py +++ b/recruitment/tests.py @@ -19,8 +19,8 @@ from .forms import ( CandidateStageForm, InterviewScheduleForm, CandidateSignupForm ) from .views import ( - ZoomMeetingListView, ZoomMeetingCreateView, job_detail, candidate_screening_view, - candidate_exam_view, candidate_interview_view, api_schedule_candidate_meeting + ZoomMeetingListView, ZoomMeetingCreateView, job_detail, applications_screening_view, + applications_exam_view, applications_interview_view, api_schedule_application_meeting ) from .views_frontend import CandidateListView, JobListView from .utils import create_zoom_meeting, get_candidates_from_request @@ -189,32 +189,32 @@ class ViewTests(BaseTestCase): def test_candidate_screening_view(self): """Test candidate_screening_view""" - response = self.client.get(reverse('candidate_screening_view', kwargs={'slug': self.job.slug})) + response = self.client.get(reverse('applications_screening_view', kwargs={'slug': self.job.slug})) self.assertEqual(response.status_code, 200) self.assertContains(response, 'John Doe') def test_candidate_screening_view_filters(self): """Test candidate_screening_view with filters""" response = self.client.get( - reverse('candidate_screening_view', kwargs={'slug': self.job.slug}), + reverse('applications_screening_view', kwargs={'slug': self.job.slug}), {'min_ai_score': '50', 'tier1_count': '5'} ) self.assertEqual(response.status_code, 200) def test_candidate_exam_view(self): """Test candidate_exam_view""" - response = self.client.get(reverse('candidate_exam_view', kwargs={'slug': self.job.slug})) + response = self.client.get(reverse('applications_exam_view', kwargs={'slug': self.job.slug})) self.assertEqual(response.status_code, 200) self.assertContains(response, 'John Doe') def test_candidate_interview_view(self): - """Test candidate_interview_view""" - response = self.client.get(reverse('candidate_interview_view', kwargs={'slug': self.job.slug})) + """Test applications_interview_view""" + response = self.client.get(reverse('applications_interview_view', kwargs={'slug': self.job.slug})) self.assertEqual(response.status_code, 200) @patch('recruitment.views.create_zoom_meeting') def test_schedule_candidate_meeting(self, mock_create_zoom): - """Test api_schedule_candidate_meeting view""" + """Test api_schedule_application_meeting view""" mock_create_zoom.return_value = { 'status': 'success', 'meeting_details': { @@ -231,7 +231,7 @@ class ViewTests(BaseTestCase): 'duration': 60 } response = self.client.post( - reverse('api_schedule_candidate_meeting', + reverse('api_schedule_application_meeting', kwargs={'job_slug': self.job.slug, 'candidate_pk': self.candidate.pk}), data ) @@ -478,7 +478,7 @@ class PerformanceTests(BaseTestCase): ) # Test pagination - response = self.client.get(reverse('candidate_list')) + response = self.client.get(reverse('application_list')) self.assertEqual(response.status_code, 200) self.assertContains(response, 'Candidate') diff --git a/recruitment/tests_advanced.py b/recruitment/tests_advanced.py index e24c462..81df19c 100644 --- a/recruitment/tests_advanced.py +++ b/recruitment/tests_advanced.py @@ -33,8 +33,8 @@ from .forms import ( ApplicationStageForm, InterviewScheduleForm, BreakTimeFormSet ) from .views import ( - ZoomMeetingListView, ZoomMeetingCreateView, job_detail, candidate_screening_view, - candidate_exam_view, candidate_interview_view, api_schedule_candidate_meeting, + ZoomMeetingListView, ZoomMeetingCreateView, job_detail, applications_screening_view, + applications_exam_view, applications_interview_view, api_schedule_application_meeting, schedule_interviews_view, confirm_schedule_interviews_view, _handle_preview_submission, _handle_confirm_schedule, _handle_get_request ) @@ -421,27 +421,27 @@ class AdvancedViewTests(TestCase): ) # Test search by name - response = self.client.get(reverse('candidate_list'), { + response = self.client.get(reverse('application_list'), { 'search': 'Jane' }) self.assertEqual(response.status_code, 200) self.assertContains(response, 'Jane Smith') # Test search by email - response = self.client.get(reverse('candidate_list'), { + response = self.client.get(reverse('application_list'), { 'search': 'bob@example.com' }) self.assertEqual(response.status_code, 200) self.assertContains(response, 'Bob Johnson') # Test filter by job - response = self.client.get(reverse('candidate_list'), { + response = self.client.get(reverse('application_list'), { 'job': self.job.slug }) self.assertEqual(response.status_code, 200) # Test filter by stage - response = self.client.get(reverse('candidate_list'), { + response = self.client.get(reverse('application_list'), { 'stage': 'Exam' }) self.assertEqual(response.status_code, 200) @@ -521,7 +521,7 @@ class AdvancedViewTests(TestCase): """Test HTMX responses for partial updates""" # Test HTMX request for candidate screening response = self.client.get( - reverse('candidate_screening_view', kwargs={'slug': self.job.slug}), + reverse('applications_screening_view', kwargs={'slug': self.job.slug}), HTTP_HX_REQUEST='true' ) self.assertEqual(response.status_code, 200) @@ -557,7 +557,7 @@ class AdvancedViewTests(TestCase): # This would be tested via a form submission # For now, we test the view logic directly request = self.client.post( - reverse('candidate_update_status', kwargs={'slug': self.job.slug}), + reverse('application_update_status', kwargs={'slug': self.job.slug}), data={'candidate_ids': application_ids, 'mark_as': 'Exam'} ) # Should redirect back to the view @@ -954,7 +954,7 @@ class AdvancedIntegrationTests(TransactionTestCase): ) response = self.client.post( - reverse('api_schedule_candidate_meeting', + reverse('api_schedule_application_meeting', kwargs={'job_slug': job.slug, 'candidate_pk': application.pk}), data={ 'start_time': (timezone.now() + timedelta(hours=1)).isoformat(), diff --git a/recruitment/urls.py b/recruitment/urls.py index f7a7ec3..8a436a6 100644 --- a/recruitment/urls.py +++ b/recruitment/urls.py @@ -41,52 +41,52 @@ urlpatterns = [ # Candidate URLs path( - "candidates/", views_frontend.ApplicationListView.as_view(), name="candidate_list" + "applications/", views_frontend.ApplicationListView.as_view(), name="application_list" ), path( - "candidates/create/", + "application/create/", views_frontend.ApplicationCreateView.as_view(), - name="candidate_create", + name="application_create", ), path( - "candidates/create//", + "application/create//", views_frontend.ApplicationCreateView.as_view(), - name="candidate_create_for_job", + name="application_create_for_job", ), path( - "jobs//candidates/", + "jobs//application/", views_frontend.JobApplicationListView.as_view(), - name="job_candidates_list", + name="job_applications_list", ), path( - "candidates//update/", + "applications//update/", views_frontend.ApplicationUpdateView.as_view(), - name="candidate_update", + name="application_update", ), path( - "candidates//delete/", + "application//delete/", views_frontend.ApplicationDeleteView.as_view(), - name="candidate_delete", + name="application_delete", ), path( - "candidate//view/", - views_frontend.candidate_detail, - name="candidate_detail", + "application//view/", + views_frontend.application_detail, + name="application_detail", ), path( - "candidate//resume-template/", - views_frontend.candidate_resume_template_view, - name="candidate_resume_template", + "application//resume-template/", + views_frontend.application_resume_template_view, + name="application_resume_template", ), path( - "candidate//update-stage/", - views_frontend.candidate_update_stage, - name="candidate_update_stage", + "application//update-stage/", + views_frontend.application_update_stage, + name="application_update_stage", ), path( - "candidate//retry-scoring/", + "application//retry-scoring/", views_frontend.retry_scoring_view, - name="candidate_retry_scoring", + name="application_retry_scoring", ), # Training URLs path("training/", views_frontend.TrainingListView.as_view(), name="training_list"), @@ -155,50 +155,50 @@ urlpatterns = [ name="edit_linkedin_post_content", ), path( - "jobs//candidate_screening_view/", - views.candidate_screening_view, - name="candidate_screening_view", + "jobs//applications_screening_view/", + views.applications_screening_view, + name="applications_screening_view", ), path( - "jobs//candidate_exam_view/", - views.candidate_exam_view, - name="candidate_exam_view", + "jobs//applications_exam_view/", + views.applications_exam_view, + name="applications_exam_view", ), path( - "jobs//candidate_interview_view/", - views.candidate_interview_view, - name="candidate_interview_view", + "jobs//applications_interview_view/", + views.applications_interview_view, + name="applications_interview_view", ), path( - "jobs//candidate_document_review_view/", - views.candidate_document_review_view, - name="candidate_document_review_view", + "jobs//applications_document_review_view/", + views.applications_document_review_view, + name="applications_document_review_view", ), path( - "jobs//candidate_offer_view/", - views_frontend.candidate_offer_view, - name="candidate_offer_view", + "jobs//applications_offer_view/", + views_frontend.applications_offer_view, + name="applications_offer_view", ), path( - "jobs//candidate_hired_view/", - views_frontend.candidate_hired_view, - name="candidate_hired_view", + "jobs//applications_hired_view/", + views_frontend.applications_hired_view, + name="applications_hired_view", ), path( "jobs//export//csv/", - views_frontend.export_candidates_csv, - name="export_candidates_csv", + views_frontend.export_applications_csv, + name="export_applications_csv", ), path( - "jobs//candidates//update_status///", - views_frontend.update_candidate_status, - name="update_candidate_status", + "jobs//application//update_status///", + views_frontend.update_application_status, + name="update_application_status", ), # Sync URLs path( - "jobs//sync-hired-candidates/", - views_frontend.sync_hired_candidates, - name="sync_hired_candidates", + "jobs//sync-hired-applications/", + views_frontend.sync_hired_applications, + name="sync_hired_applications", ), path( "sources//test-connection/", @@ -206,34 +206,34 @@ urlpatterns = [ name="test_source_connection", ), path( - "jobs///reschedule_meeting_for_candidate//", - views.reschedule_meeting_for_candidate, - name="reschedule_meeting_for_candidate", + "jobs///reschedule_meeting_for_application//", + views.reschedule_meeting_for_application, + name="reschedule_meeting_for_application", ), path( - "jobs//update_candidate_exam_status/", - views.update_candidate_exam_status, - name="update_candidate_exam_status", + "jobs//update_application_exam_status/", + views.update_application_exam_status, + name="update_application_exam_status", ), path( - "jobs//bulk_update_candidate_exam_status/", - views.bulk_update_candidate_exam_status, - name="bulk_update_candidate_exam_status", + "jobs//bulk_update_application_exam_status/", + views.bulk_update_application_exam_status, + name="bulk_update_application_exam_status", ), path( - "htmx//candidate_criteria_view/", - views.candidate_criteria_view_htmx, - name="candidate_criteria_view_htmx", + "htmx//application_criteria_view/", + views.application_criteria_view_htmx, + name="application_criteria_view_htmx", ), path( - "htmx//candidate_set_exam_date/", - views.candidate_set_exam_date, - name="candidate_set_exam_date", + "htmx//application_set_exam_date/", + views.application_set_exam_date, + name="application_set_exam_date", ), path( - "htmx//candidate_update_status/", - views.candidate_update_status, - name="candidate_update_status", + "htmx//application_update_status/", + views.application_update_status, + name="application_update_status", ), # path('forms/form//submit/', views.submit_form, name='submit_form'), # path('forms/form//', views.form_wizard_view, name='form_wizard'), @@ -347,9 +347,9 @@ urlpatterns = [ name="delete_meeting_comment", ), path( - "meetings//set_meeting_candidate/", - views.set_meeting_candidate, - name="set_meeting_candidate", + "meetings//set_meeting_application/", + views.set_meeting_application, + name="set_meeting_application", ), # Hiring Agency URLs path("agencies/", views.agency_list, name="agency_list"), @@ -358,9 +358,9 @@ urlpatterns = [ path("agencies//update/", views.agency_update, name="agency_update"), path("agencies//delete/", views.agency_delete, name="agency_delete"), path( - "agencies//candidates/", - views.agency_candidates, - name="agency_candidates", + "agencies//applications/", + views.agency_applications, + name="agency_applications", ), # path('agencies//send-message/', views.agency_detail_send_message, name='agency_detail_send_message'), # Agency Assignment Management URLs @@ -433,19 +433,19 @@ urlpatterns = [ # Unified Portal URLs path("login/", views.portal_login, name="portal_login"), path( - "candidate/dashboard/", - views.candidate_portal_dashboard, - name="candidate_portal_dashboard", + "applicant/dashboard/", + views.applicant_portal_dashboard, + name="applicant_portal_dashboard", ), path( - "candidate/applications//", - views.candidate_application_detail, - name="candidate_application_detail", + "applications/applications//", + views.applicant_application_detail, + name="applicant_application_detail", ), # path( # "candidate//applications//detail//", - # views.candidate_application_detail, - # name="candidate_application_detail", + # views.applicant_application_detail, + # name="applicant_application_detail", # ), path( "portal/dashboard/", @@ -463,35 +463,35 @@ urlpatterns = [ name="agency_portal_assignment_detail", ), path( - "portal/assignment//submit-candidate/", - views.agency_portal_submit_candidate_page, - name="agency_portal_submit_candidate_page", + "portal/assignment//submit-application/", + views.agency_portal_submit_application_page, + name="agency_portal_submit_application_page", ), path( - "portal/submit-candidate/", - views.agency_portal_submit_candidate, - name="agency_portal_submit_candidate", + "portal/submit-application/", + views.agency_portal_submit_application, + name="agency_portal_submit_application", ), path("portal/logout/", views.portal_logout, name="portal_logout"), # Agency Portal Candidate Management URLs path( - "portal/candidates//edit/", - views.agency_portal_edit_candidate, - name="agency_portal_edit_candidate", + "portal/applications//edit/", + views.agency_portal_edit_application, + name="agency_portal_edit_application", ), path( - "portal/candidates//delete/", - views.agency_portal_delete_candidate, - name="agency_portal_delete_candidate", + "portal/applications//delete/", + views.agency_portal_delete_application, + name="agency_portal_delete_application", ), # API URLs for messaging (removed) # path('api/agency/messages//', views.api_agency_message_detail, name='api_agency_message_detail'), # path('api/agency/messages//mark-read/', views.api_agency_mark_message_read, name='api_agency_mark_message_read'), # API URLs for candidate management path( - "api/candidate//", - views.api_candidate_detail, - name="api_candidate_detail", + "api/application//", + views.api_application_detail, + name="api_application_detail", ), # # Admin Notification API # path('api/admin/notification-count/', views.api_notification_count, name='admin_notification_count'), @@ -535,10 +535,11 @@ urlpatterns = [ ), # Email composition URLs path( - "jobs//candidates/compose-email/", - views.compose_candidate_email, - name="compose_candidate_email", + "jobs//applications/compose-email/", + views.compose_application_email, + name="compose_application_email", ), + # Message URLs path("messages/", views.message_list, name="message_list"), path("messages/create/", views.message_create, name="message_create"), @@ -555,15 +556,15 @@ urlpatterns = [ path("documents//delete/", views.document_delete, name="document_delete"), path("documents//download/", views.document_download, name="document_download"), # Candidate Document Management URLs - path("candidate/documents/upload//", views.document_upload, name="candidate_document_upload"), - path("candidate/documents//delete/", views.document_delete, name="candidate_document_delete"), - path("candidate/documents//download/", views.document_download, name="candidate_document_download"), - path('jobs//candidates/compose_email/', views.compose_candidate_email, name='compose_candidate_email'), + path("application/documents/upload//", views.document_upload, name="application_document_upload"), + path("application/documents//delete/", views.document_delete, name="application_document_delete"), + path("application/documents//download/", views.document_download, name="application_document_download"), + path('jobs//applications/compose_email/', views.compose_application_email, name='compose_application_email'), path('interview/partcipants//',views.create_interview_participants,name='create_interview_participants'), path('interview/email//',views.send_interview_email,name='send_interview_email'), # Candidate Signup - path('candidate/signup//', views.candidate_signup, name='candidate_signup'), + path('application/signup//', views.application_signup, name='application_signup'), # Password Reset path('user//password-reset/', views.portal_password_reset, name='portal_password_reset'), @@ -607,43 +608,43 @@ urlpatterns = [ ), # Candidate Meeting Scheduling/Rescheduling URLs path( - "jobs//candidates//schedule-meeting/", - views.schedule_candidate_meeting, - name="schedule_candidate_meeting", + "jobs//applications//schedule-meeting/", + views.schedule_application_meeting, + name="schedule_application_meeting", ), path( - "api/jobs//candidates//schedule-meeting/", - views.api_schedule_candidate_meeting, - name="api_schedule_candidate_meeting", + "api/jobs//applications//schedule-meeting/", + views.api_schedule_application_meeting, + name="api_schedule_application_meeting", ), path( - "jobs//candidates//reschedule-meeting//", - views.reschedule_candidate_meeting, - name="reschedule_candidate_meeting", + "jobs//applications//reschedule-meeting//", + views.reschedule_application_meeting, + name="reschedule_application_meeting", ), path( - "api/jobs//candidates//reschedule-meeting//", - views.api_reschedule_candidate_meeting, - name="api_reschedule_candidate_meeting", + "api/jobs//applications//reschedule-meeting//", + views.api_reschedule_application_meeting, + name="api_reschedule_application_meeting", ), # New URL for simple page-based meeting scheduling path( - "jobs//candidates//schedule-meeting-page/", - views.schedule_meeting_for_candidate, - name="schedule_meeting_for_candidate", - ), - path( - "jobs//candidates//delete_meeting_for_candidate//", - views.delete_meeting_for_candidate, - name="delete_meeting_for_candidate", + "jobs//applications//schedule-meeting-page/", + views.schedule_meeting_for_application, + name="schedule_meeting_for_application", ), + # path( + # "jobs//applications//delete_meeting_for_application//", + # views.delete_meeting_for_candidate, + # name="delete_meeting_for_candidate", + # ), path("interviews/meetings/", views.MeetingListView.as_view(), name="list_meetings"), # 1. Onsite Reschedule URL path( - '/candidate//onsite/reschedule//', + '/application//onsite/reschedule//', views.reschedule_onsite_meeting, name='reschedule_onsite_meeting' ), @@ -651,15 +652,15 @@ urlpatterns = [ # 2. Onsite Delete URL path( - 'job//candidates//delete-onsite-meeting//', - views.delete_onsite_meeting_for_candidate, - name='delete_onsite_meeting_for_candidate' + 'job//applications//delete-onsite-meeting//', + views.delete_onsite_meeting_for_application, + name='delete_onsite_meeting_for_application' ), path( - 'job//candidate//schedule/onsite/', - views.schedule_onsite_meeting_for_candidate, - name='schedule_onsite_meeting_for_candidate' # This is the name used in the button + 'job//application//schedule/onsite/', + views.schedule_onsite_meeting_for_application, + name='schedule_onsite_meeting_for_application' # This is the name used in the button ), @@ -667,7 +668,7 @@ urlpatterns = [ path("interviews/meetings//", views.meeting_details, name="meeting_details"), # Email invitation URLs - path("interviews/meetings//send-candidate-invitation/", views.send_candidate_invitation, name="send_candidate_invitation"), + path("interviews/meetings//send-application-invitation/", views.send_application_invitation, name="send_application_invitation"), path("interviews/meetings//send-participants-invitation/", views.send_participants_invitation, name="send_participants_invitation"), ] diff --git a/recruitment/views.py b/recruitment/views.py index c9c87e3..1724361 100644 --- a/recruitment/views.py +++ b/recruitment/views.py @@ -201,7 +201,7 @@ class PersonCreateView(CreateView): instance.save() return redirect("agency_portal_persons_list") if view == "job": - return redirect("candidate_create") + return redirect("application_create") return super().form_valid(form) @@ -1199,7 +1199,7 @@ def delete_form_template(request, template_id): def application_submit_form(request, template_slug): """Display the form as a step-by-step wizard""" if not request.user.is_authenticated: - return redirect("candidate_signup",slug=template_slug) + return redirect("application_signup",slug=template_slug) template = get_object_or_404(FormTemplate, slug=template_slug, is_active=True) stage = template.stages.filter(name="Contact Information") @@ -1329,7 +1329,7 @@ def application_submit(request, template_slug): # return redirect('application_success',slug=job.slug) except Exception as e: - logger.error(f"Candidate creation failed,{e}") + logger.error(f"Application creation failed,{e}") pass return JsonResponse( { @@ -1760,7 +1760,7 @@ def confirm_schedule_interviews_view(request, slug): @staff_user_required -def candidate_screening_view(request, slug): +def applications_screening_view(request, slug): """ Manage candidate tiers and stage transitions """ @@ -1842,7 +1842,7 @@ def candidate_screening_view(request, slug): @staff_user_required -def candidate_exam_view(request, slug): +def applications_exam_view(request, slug): """ Manage candidate tiers and stage transitions """ @@ -1852,13 +1852,13 @@ def candidate_exam_view(request, slug): @staff_user_required -def update_candidate_exam_status(request, slug): +def update_application_exam_status(request, slug): candidate = get_object_or_404(Application, slug=slug) if request.method == "POST": form = CandidateExamDateForm(request.POST, instance=candidate) if form.is_valid(): form.save() - return redirect("candidate_exam_view", slug=candidate.job.slug) + return redirect("applications_exam_view", slug=candidate.job.slug) else: form = CandidateExamDateForm(request.POST, instance=candidate) return render( @@ -1869,7 +1869,7 @@ def update_candidate_exam_status(request, slug): @staff_user_required -def bulk_update_candidate_exam_status(request, slug): +def bulk_update_application_exam_status(request, slug): job = get_object_or_404(JobPosting, slug=slug) status = request.headers.get("status") if status: @@ -1884,10 +1884,10 @@ def bulk_update_candidate_exam_status(request, slug): except Exception as e: print(e) messages.success(request, f"Updated exam status selected candidates") - return redirect("candidate_exam_view", slug=job.slug) + return redirect("applications_exam_view", slug=job.slug) -def candidate_criteria_view_htmx(request, pk): +def application_criteria_view_htmx(request, pk): candidate = get_object_or_404(Application, pk=pk) return render( request, "includes/candidate_modal_body.html", {"candidate": candidate} @@ -1895,18 +1895,18 @@ def candidate_criteria_view_htmx(request, pk): @staff_user_required -def candidate_set_exam_date(request, slug): +def application_set_exam_date(request, slug): candidate = get_object_or_404(Application, slug=slug) candidate.exam_date = timezone.now() candidate.save() messages.success( request, f"Set exam date for {candidate.name} to {candidate.exam_date}" ) - return redirect("candidate_screening_view", slug=candidate.job.slug) + return redirect("applications_screening_view", slug=candidate.job.slug) @staff_user_required -def candidate_update_status(request, slug): +def application_update_status(request, slug): job = get_object_or_404(JobPosting, slug=slug) mark_as = request.POST.get("mark_as") if mark_as != "----------": @@ -1979,13 +1979,13 @@ def candidate_update_status(request, slug): ) messages.success(request, f"Candidates Updated") - response = HttpResponse(redirect("candidate_screening_view", slug=job.slug)) + response = HttpResponse(redirect("applications_screening_view", slug=job.slug)) response.headers["HX-Refresh"] = "true" return response @staff_user_required -def candidate_interview_view(request, slug): +def applications_interview_view(request, slug): job = get_object_or_404(JobPosting, slug=slug) context = { "job": job, @@ -1997,7 +1997,7 @@ def candidate_interview_view(request, slug): @staff_user_required -def candidate_document_review_view(request, slug): +def applications_document_review_view(request, slug): """ Document review view for candidates after interview stage and before offer stage """ @@ -2025,7 +2025,7 @@ def candidate_document_review_view(request, slug): @staff_user_required -def reschedule_meeting_for_candidate(request, slug, candidate_id, meeting_id): +def reschedule_meeting_for_application(request, slug, candidate_id, meeting_id): job = get_object_or_404(JobPosting, slug=slug) candidate = get_object_or_404(Application, pk=candidate_id) meeting = get_object_or_404(ZoomMeetingDetails, pk=meeting_id) @@ -2043,7 +2043,7 @@ def reschedule_meeting_for_candidate(request, slug, candidate_id, meeting_id): if instance.start_time < timezone.now(): messages.error(request, "Start time must be in the future.") return redirect( - "reschedule_meeting_for_candidate", + "reschedule_meeting_for_application", slug=job.slug, candidate_id=candidate_id, meeting_id=meeting_id, @@ -2056,7 +2056,7 @@ def reschedule_meeting_for_candidate(request, slug, candidate_id, meeting_id): else: messages.error(request, result["message"]) return redirect( - reverse("candidate_interview_view", kwargs={"slug": job.slug}) + reverse("applications_interview_view", kwargs={"slug": job.slug}) ) context = {"job": job, "candidate": candidate, "meeting": meeting, "form": form} @@ -2064,7 +2064,7 @@ def reschedule_meeting_for_candidate(request, slug, candidate_id, meeting_id): @staff_user_required -def delete_meeting_for_candidate(request, slug, candidate_pk, meeting_id): +def schedule_meeting_for_application(request, slug, candidate_pk, meeting_id): job = get_object_or_404(JobPosting, slug=slug) candidate = get_object_or_404(Application, pk=candidate_pk) meeting = get_object_or_404(ZoomMeetingDetails, pk=meeting_id) @@ -2078,14 +2078,14 @@ def delete_meeting_for_candidate(request, slug, candidate_pk, meeting_id): messages.success(request, "Meeting deleted successfully") else: messages.error(request, result["message"]) - return redirect(reverse("candidate_interview_view", kwargs={"slug": job.slug})) + return redirect(reverse("applications_interview_view", kwargs={"slug": job.slug})) context = { "job": job, "candidate": candidate, "meeting": meeting, "delete_url": reverse( - "delete_meeting_for_candidate", + "schedule_meeting_for_application", kwargs={ "slug": job.slug, "candidate_pk": candidate_pk, @@ -2125,7 +2125,7 @@ def delete_zoom_meeting_for_candidate(request, slug, candidate_pk, meeting_id): else: messages.error(request, result["message"]) - return redirect(reverse("candidate_interview_view", kwargs={"slug": job.slug})) + return redirect(reverse("applications_interview_view", kwargs={"slug": job.slug})) context = { "job": job, @@ -2218,7 +2218,7 @@ def interview_detail_view(request, slug, interview_id): # Candidate Meeting Scheduling/Rescheduling Views @require_POST -def api_schedule_candidate_meeting(request, job_slug, candidate_pk): +def api_schedule_application_meeting(request, job_slug, candidate_pk): """ Handle POST request to schedule a Zoom meeting for a candidate via HTMX. Returns JSON response for modal update. @@ -2298,23 +2298,23 @@ def api_schedule_candidate_meeting(request, job_slug, candidate_pk): return JsonResponse({"success": False, "error": result["message"]}, status=400) -def schedule_candidate_meeting(request, job_slug, candidate_pk): +def schedule_application_meeting(request, job_slug, candidate_pk): """ GET: Render modal form to schedule a meeting. (For HTMX) - POST: Handled by api_schedule_candidate_meeting. + POST: Handled by api_schedule_application_meeting. """ job = get_object_or_404(JobPosting, slug=job_slug) candidate = get_object_or_404(Application, pk=candidate_pk, job=job) if request.method == "POST": - return api_schedule_candidate_meeting(request, job_slug, candidate_pk) + return api_schedule_application_meeting(request, job_slug, candidate_pk) # GET request - render the form snippet for HTMX context = { "job": job, "candidate": candidate, "action_url": reverse( - "api_schedule_candidate_meeting", + "api_schedule_application_meeting", kwargs={"job_slug": job_slug, "candidate_pk": candidate_pk}, ), "scheduled_interview": None, # Explicitly None for schedule @@ -2324,7 +2324,7 @@ def schedule_candidate_meeting(request, job_slug, candidate_pk): @require_http_methods(["GET", "POST"]) -def api_schedule_candidate_meeting(request, job_slug, candidate_pk): +def api_schedule_application_meeting(request, job_slug, candidate_pk): """ Handles GET to render form and POST to process scheduling. """ @@ -2337,7 +2337,7 @@ def api_schedule_candidate_meeting(request, job_slug, candidate_pk): "job": job, "candidate": candidate, "action_url": reverse( - "api_schedule_candidate_meeting", + "api_schedule_application_meeting", kwargs={"job_slug": job_slug, "candidate_pk": candidate_pk}, ), "scheduled_interview": None, @@ -2408,7 +2408,7 @@ def api_schedule_candidate_meeting(request, job_slug, candidate_pk): @require_http_methods(["GET", "POST"]) -def api_reschedule_candidate_meeting(request, job_slug, candidate_pk, interview_pk): +def api_reschedule_application_meeting(request, job_slug, candidate_pk, interview_pk): """ Handles GET to render form and POST to process rescheduling. """ @@ -2434,7 +2434,7 @@ def api_reschedule_candidate_meeting(request, job_slug, candidate_pk, interview_ "scheduled_interview": scheduled_interview, # Pass for conditional logic in template "initial_data": initial_data, "action_url": reverse( - "api_reschedule_candidate_meeting", + "api_reschedule_application_meeting", kwargs={ "job_slug": job_slug, "candidate_pk": candidate_pk, @@ -2530,14 +2530,14 @@ def api_reschedule_candidate_meeting(request, job_slug, candidate_pk, interview_ return JsonResponse({"success": False, "error": result["message"]}, status=400) -# The original schedule_candidate_meeting and reschedule_candidate_meeting (without api_ prefix) +# The original schedule_application_meeting and reschedule_application_meeting (without api_ prefix) # can be removed if their only purpose was to be called by the JS onclicks. # If they were intended for other direct URL access, they can be kept as simple redirects # or wrappers to the api_ versions. # For now, let's assume the api_ versions are the primary ones for HTMX. -def reschedule_candidate_meeting(request, job_slug, candidate_pk, interview_pk): +def reschedule_application_meeting(request, job_slug, candidate_pk, interview_pk): """ Handles GET to display a form for rescheduling a meeting. Handles POST to process the rescheduling of a meeting. @@ -2592,7 +2592,7 @@ def reschedule_candidate_meeting(request, job_slug, candidate_pk, interview_pk): else "", "initial_duration": new_duration, "action_url": reverse( - "reschedule_candidate_meeting", + "reschedule_application_meeting", kwargs={ "job_slug": job_slug, "candidate_pk": candidate_pk, @@ -2672,7 +2672,7 @@ def reschedule_candidate_meeting(request, job_slug, candidate_pk, interview_pk): f"Meeting for {application.name} rescheduled. (Note: Could not refresh all details from Zoom.)", ) - return redirect("candidate_interview_view", slug=job.slug) + return redirect("applications_interview_view", slug=job.slug) else: messages.error( request, @@ -2693,7 +2693,7 @@ def reschedule_candidate_meeting(request, job_slug, candidate_pk, interview_pk): else "", "initial_duration": new_duration, "action_url": reverse( - "reschedule_candidate_meeting", + "reschedule_application_meeting", kwargs={ "job_slug": job_slug, "candidate_pk": candidate_pk, @@ -2722,7 +2722,7 @@ def reschedule_candidate_meeting(request, job_slug, candidate_pk, interview_pk): ), "initial_duration": request.POST.get("duration", new_duration), "action_url": reverse( - "reschedule_candidate_meeting", + "reschedule_application_meeting", kwargs={ "job_slug": job_slug, "candidate_pk": candidate_pk, @@ -2749,7 +2749,7 @@ def reschedule_candidate_meeting(request, job_slug, candidate_pk, interview_pk): "application": application, "scheduled_interview": scheduled_interview, # Pass to template for title/differentiation "action_url": reverse( - "reschedule_candidate_meeting", + "reschedule_application_meeting", kwargs={ "job_slug": job_slug, "candidate_pk": candidate_pk, @@ -2761,7 +2761,7 @@ def reschedule_candidate_meeting(request, job_slug, candidate_pk, interview_pk): ) -def schedule_meeting_for_candidate(request, slug, candidate_pk): +def schedule_meeting_for_application(request, slug, candidate_pk): """ Handles GET to display a simple form for scheduling a meeting for a candidate. Handles POST to process the form, create a meeting, and redirect back. @@ -2784,7 +2784,7 @@ def schedule_meeting_for_candidate(request, slug, candidate_pk): if start_time_val <= timezone.now(): messages.error(request, "Start time must be in the future.") # Re-render form with error and initial data - return redirect("candidate_interview_view", slug=job.slug) + return redirect("applications_interview_view", slug=job.slug) # return render(request, "recruitment/schedule_meeting_form.html", { # 'form': form, # 'job': job, @@ -2829,7 +2829,7 @@ def schedule_meeting_for_candidate(request, slug, candidate_pk): status="scheduled", ) messages.success(request, f"Meeting scheduled with {candidate.name}.") - return redirect("candidate_interview_view", slug=job.slug) + return redirect("applications_interview_view", slug=job.slug) else: messages.error( request, @@ -3232,7 +3232,7 @@ def delete_meeting_comment(request, slug, comment_id): @staff_user_required -def set_meeting_candidate(request, slug): +def set_meeting_application(request, slug): meeting = get_object_or_404(ZoomMeetingDetails, slug=slug) if request.method == "POST" and "HX-Request" not in request.headers: form = InterviewForm(request.POST) @@ -3254,7 +3254,7 @@ def set_meeting_candidate(request, slug): form.fields["candidate"].queryset = Application.objects.none() form.fields["job"].widget.attrs.update( { - "hx-get": reverse("set_meeting_candidate", kwargs={"slug": slug}), + "hx-get": reverse("set_meeting_application", kwargs={"slug": slug}), "hx-target": "#div_id_candidate", "hx-select": "#div_id_candidate", "hx-swap": "outerHTML", @@ -3708,7 +3708,7 @@ def agency_delete(request, slug): @staff_user_required -def agency_candidates(request, slug): +def agency_applications(request, slug): """View all candidates from a specific agency""" agency = get_object_or_404(HiringAgency, slug=slug) candidates = Application.objects.filter(hiring_agency=agency).order_by( @@ -4011,7 +4011,7 @@ def portal_login(request): return redirect("agency_portal_dashboard") if request.user.user_type == "candidate": print(request.user) - return redirect("candidate_portal_dashboard") + return redirect("applicant_portal_dashboard") if request.method == "POST": form = PortalLoginForm(request.POST) @@ -4052,7 +4052,7 @@ def portal_login(request): # request, # f"Welcome, {user.candidate_profile.first_name}!", # ) - # return redirect("candidate_portal_dashboard") + # return redirect("applicant_portal_dashboard") # else: # messages.error( # request, "No candidate profile found for this user." @@ -4075,7 +4075,7 @@ def portal_login(request): @login_required @candidate_user_required -def candidate_portal_dashboard(request): +def applicant_portal_dashboard(request): """Candidate portal dashboard""" if not request.user.is_authenticated: return redirect("account_login") @@ -4113,7 +4113,7 @@ def candidate_portal_dashboard(request): @login_required -def candidate_application_detail(request, slug): +def applicant_application_detail(request, slug): """View detailed information about a specific application""" if not request.user.is_authenticated: return redirect("account_login") @@ -4278,7 +4278,7 @@ def agency_portal_dashboard(request): @agency_user_required -def agency_portal_submit_candidate_page(request, slug): +def agency_portal_submit_application_page(request, slug): """Dedicated page for submitting a candidate""" # assignment_id = request.session.get("agency_assignment_id") # if not assignment_id: @@ -4345,7 +4345,7 @@ def agency_portal_submit_candidate_page(request, slug): @agency_user_required -def agency_portal_submit_candidate(request): +def agency_portal_submit_application(request): """Handle candidate submission via AJAX (for embedded form)""" assignment_id = request.session.get("agency_assignment_id") if not assignment_id: @@ -4517,7 +4517,7 @@ def agency_assignment_detail_admin(request, slug): @agency_user_required -def agency_portal_edit_candidate(request, candidate_id): +def agency_portal_edit_application(request, candidate_id): """Edit a candidate for agency portal""" assignment_id = request.session.get("agency_assignment_id") if not assignment_id: @@ -4578,7 +4578,7 @@ def agency_portal_edit_candidate(request, candidate_id): @agency_user_required -def agency_portal_delete_candidate(request, candidate_id): +def agency_portal_delete_application(request, candidate_id): """Delete a candidate for agency portal""" assignment_id = request.session.get("agency_assignment_id") if not assignment_id: @@ -4986,7 +4986,7 @@ def document_upload(request, slug): } }) - return redirect("candidate_portal_dashboard") + return redirect("applicant_portal_dashboard") else: # Create document for Application (existing logic) document = Document.objects.create( @@ -5016,15 +5016,15 @@ def document_upload(request, slug): } }) if upload_target == 'person': - return redirect("candidate_portal_dashboard") + return redirect("applicant_portal_dashboard") else: - return redirect("candidate_application_detail", application_slug=application.slug) + return redirect("applicant_application_detail", application_slug=application.slug) # Handle GET request for AJAX if request.headers.get("X-Requested-With") == "XMLHttpRequest": return JsonResponse({"success": False, "error": "Method not allowed"}) - return redirect("candidate_detail", slug=application.job.slug) + return redirect("application_detail", slug=application.job.slug) @login_required def document_delete(request, document_id): @@ -5043,7 +5043,7 @@ def document_delete(request, document_id): ) return JsonResponse({"success": False, "error": "Permission denied"}) job_slug = document.content_object.job.slug - redirect_url = "candidate_portal_dashboard" if request.user.user_type == "candidate" else "job_detail" + redirect_url = "applicant_portal_dashboard" if request.user.user_type == "candidate" else "job_detail" elif hasattr(document.content_object, "person"): # Person document if request.user.user_type == "candidate": @@ -5053,7 +5053,7 @@ def document_delete(request, document_id): request, "You can only delete your own documents." ) return JsonResponse({"success": False, "error": "Permission denied"}) - redirect_url = "candidate_portal_dashboard" + redirect_url = "applicant_portal_dashboard" else: # Handle other content object types messages.error(request, "You don't have permission to delete this document.") @@ -5070,7 +5070,7 @@ def document_delete(request, document_id): {"success": True, "message": "Document deleted successfully!"} ) else: - return redirect("candidate_detail", slug=job_slug) + return redirect("application_detail", slug=job_slug) return JsonResponse({"success": False, "error": "Method not allowed"}) @@ -5091,7 +5091,7 @@ def document_download(request, document_id): ) return JsonResponse({"success": False, "error": "Permission denied"}) job_slug = document.content_object.job.slug - redirect_url = "candidate_detail" if request.user.user_type == "candidate" else "job_detail" + redirect_url = "application_detail" if request.user.user_type == "candidate" else "job_detail" elif hasattr(document.content_object, "person"): # Person document if request.user.user_type == "candidate": @@ -5101,7 +5101,7 @@ def document_download(request, document_id): request, "You can only download your own documents." ) return JsonResponse({"success": False, "error": "Permission denied"}) - redirect_url = "candidate_portal_dashboard" + redirect_url = "applicant_portal_dashboard" else: # Handle other content object types messages.error(request, "You don't have permission to download this document.") @@ -5210,7 +5210,7 @@ def agency_access_link_reactivate(request, slug): @agency_user_required -def api_candidate_detail(request, candidate_id): +def api_application_detail(request, candidate_id): """API endpoint to get candidate details for agency portal""" try: # Get candidate from session-based agency access @@ -5248,7 +5248,7 @@ def api_candidate_detail(request, candidate_id): @staff_user_required -def compose_candidate_email(request, job_slug): +def compose_application_email(request, job_slug): """Compose email to participants about a candidate""" from .email_service import send_bulk_email @@ -5326,7 +5326,7 @@ def compose_candidate_email(request, job_slug): response = HttpResponse(status=200) response.headers["HX-Refresh"] = "true" return response - # return redirect("candidate_interview_view", slug=job.slug) + # return redirect("applications_interview_view", slug=job.slug) else: messages.error( request, @@ -5556,14 +5556,14 @@ def source_toggle_status(request, slug): return JsonResponse({"success": False, "error": "Method not allowed"}) -def candidate_signup(request, slug): - from .forms import CandidateSignupForm +def application_signup(request, slug): + from .forms import ApplicantSignupForm form_template = get_object_or_404(FormTemplate, slug=slug) job = form_template.job if request.method == "POST": - form = CandidateSignupForm(request.POST) + form = ApplicantSignupForm(request.POST) if form.is_valid(): try: first_name = form.cleaned_data["first_name"] @@ -5599,13 +5599,13 @@ def candidate_signup(request, slug): messages.error(request, f"Error creating application: {str(e)}") return render( request, - "recruitment/candidate_signup.html", + "recruitment/applicant_signup.html", {"form": form, "job": job}, ) - form = CandidateSignupForm() + form = ApplicantSignupForm() return render( - request, "recruitment/candidate_signup.html", {"form": form, "job": job} + request, "recruitment/applicant_signup.html", {"form": form, "job": job} ) @@ -5959,7 +5959,7 @@ def reschedule_onsite_meeting(request, slug, candidate_id, meeting_id): instance.save() messages.success(request, "Onsite meeting successfully rescheduled! ✅") - return redirect(reverse("candidate_interview_view", kwargs={'slug': job.slug})) + return redirect(reverse("applications_interview_view", kwargs={'slug': job.slug})) else: form = OnsiteReshuduleForm(instance=onsite_meeting) @@ -5976,7 +5976,7 @@ def reschedule_onsite_meeting(request, slug, candidate_id, meeting_id): # recruitment/views.py @staff_user_required -def delete_onsite_meeting_for_candidate(request, slug, candidate_pk, meeting_id): +def delete_onsite_meeting_for_application(request, slug, candidate_pk, meeting_id): """ Deletes a specific Onsite Location Details instance. This does not require an external API call. @@ -5993,7 +5993,7 @@ def delete_onsite_meeting_for_candidate(request, slug, candidate_pk, meeting_id) meeting.delete() messages.success(request, f"Onsite meeting for {candidate.name} deleted successfully.") - return redirect(reverse("candidate_interview_view", kwargs={"slug": job.slug})) + return redirect(reverse("applications_interview_view", kwargs={"slug": job.slug})) context = { "job": job, @@ -6001,7 +6001,7 @@ def delete_onsite_meeting_for_candidate(request, slug, candidate_pk, meeting_id) "meeting": meeting, "location_type": "Onsite", "delete_url": reverse( - "delete_onsite_meeting_for_candidate", # Use the specific new URL name + "delete_onsite_meeting_for_application", # Use the specific new URL name kwargs={ "slug": job.slug, "candidate_pk": candidate_pk, @@ -6013,14 +6013,14 @@ def delete_onsite_meeting_for_candidate(request, slug, candidate_pk, meeting_id) -def schedule_onsite_meeting_for_candidate(request, slug, candidate_pk): +def schedule_onsite_meeting_for_application(request, slug, candidate_pk): """ Handles scheduling a NEW Onsite Interview for a candidate using OnsiteScheduleForm. """ job = get_object_or_404(JobPosting, slug=slug) candidate = get_object_or_404(Application, pk=candidate_pk) - action_url = reverse('schedule_onsite_meeting_for_candidate', + action_url = reverse('schedule_onsite_meeting_for_application', kwargs={'slug': job.slug, 'candidate_pk': candidate.pk}) if request.method == 'POST': @@ -6058,7 +6058,7 @@ def schedule_onsite_meeting_for_candidate(request, slug, candidate_pk): ) messages.success(request, "Onsite interview scheduled successfully. ✅") - return redirect(reverse("candidate_interview_view", kwargs={'slug': job.slug})) + return redirect(reverse("applications_interview_view", kwargs={'slug': job.slug})) else: # GET Request: Initialize the hidden fields with the correct objects @@ -6139,7 +6139,7 @@ def meeting_details(request, slug): @login_required -def send_candidate_invitation(request, slug): +def send_application_invitation(request, slug): """Send invitation email to the candidate""" meeting = get_object_or_404(InterviewLocation, slug=slug) diff --git a/recruitment/views_frontend.py b/recruitment/views_frontend.py index 567e2fa..ecc1533 100644 --- a/recruitment/views_frontend.py +++ b/recruitment/views_frontend.py @@ -187,7 +187,7 @@ class ApplicationCreateView(LoginRequiredMixin, StaffRequiredMixin, SuccessMessa model = models.Application form_class = forms.ApplicationForm template_name = 'recruitment/candidate_create.html' - success_url = reverse_lazy('candidate_list') + success_url = reverse_lazy('application_list') success_message = 'Candidate created successfully.' def get_initial(self): @@ -216,7 +216,7 @@ class ApplicationUpdateView(LoginRequiredMixin, StaffRequiredMixin, SuccessMessa model = models.Application form_class = forms.ApplicationForm template_name = 'recruitment/candidate_update.html' - success_url = reverse_lazy('candidate_list') + success_url = reverse_lazy('application_list') success_message = 'Candidate updated successfully.' slug_url_kwarg = 'slug' @@ -224,7 +224,7 @@ class ApplicationUpdateView(LoginRequiredMixin, StaffRequiredMixin, SuccessMessa class ApplicationDeleteView(LoginRequiredMixin, StaffRequiredMixin, SuccessMessageMixin, DeleteView): model = models.Application template_name = 'recruitment/candidate_delete.html' - success_url = reverse_lazy('candidate_list') + success_url = reverse_lazy('application_list') success_message = 'Candidate deleted successfully.' slug_url_kwarg = 'slug' @@ -240,7 +240,7 @@ def retry_scoring_view(request,slug): hook='recruitment.hooks.callback_ai_parsing', sync=True, ) - return redirect('candidate_detail', slug=application.slug) + return redirect('application_detail', slug=application.slug) @@ -253,7 +253,7 @@ def training_list(request): @login_required @staff_user_required -def candidate_detail(request, slug): +def application_detail(request, slug): from rich.json import JSON candidate = get_object_or_404(models.Application, slug=slug) try: @@ -270,7 +270,7 @@ def candidate_detail(request, slug): # parsed = JSON(json.dumps(parsed), indent=2, highlight=True, skip_keys=False, ensure_ascii=False, check_circular=True, allow_nan=True, default=None, sort_keys=False) # parsed = json_to_markdown_table([parsed]) - return render(request, 'recruitment/candidate_detail.html', { + return render(request, 'recruitment/application_detail.html', { 'candidate': candidate, 'parsed': parsed, 'stage_form': stage_form, @@ -279,13 +279,13 @@ def candidate_detail(request, slug): @login_required @staff_user_required -def candidate_resume_template_view(request, slug): +def application_resume_template_view(request, slug): """Display formatted resume template for a candidate""" application = get_object_or_404(models.Application, slug=slug) if not request.user.is_staff: messages.error(request, _("You don't have permission to view this page.")) - return redirect('candidate_list') + return redirect('application_list') return render(request, 'recruitment/candidate_resume_template.html', { 'application': application @@ -293,7 +293,7 @@ def candidate_resume_template_view(request, slug): @login_required @staff_user_required -def candidate_update_stage(request, slug): +def application_update_stage(request, slug): """Handle HTMX stage update requests""" application = get_object_or_404(models.Application, slug=slug) form = forms.ApplicationStageForm(request.POST, instance=application) @@ -302,7 +302,7 @@ def candidate_update_stage(request, slug): application.stage = stage_value application.save(update_fields=['stage']) messages.success(request,"application Stage Updated") - return redirect("candidate_detail",slug=application.slug) + return redirect("application_detail",slug=application.slug) class TrainingListView(LoginRequiredMixin, StaffRequiredMixin, ListView): model = models.TrainingMaterial @@ -637,7 +637,7 @@ def dashboard_view(request): @login_required @staff_user_required -def candidate_offer_view(request, slug): +def applications_offer_view(request, slug): """View for candidates in the Offer stage""" job = get_object_or_404(models.JobPosting, slug=slug) @@ -667,7 +667,7 @@ def candidate_offer_view(request, slug): @login_required @staff_user_required -def candidate_hired_view(request, slug): +def applications_hired_view(request, slug): """View for hired candidates""" job = get_object_or_404(models.JobPosting, slug=slug) @@ -697,7 +697,7 @@ def candidate_hired_view(request, slug): @login_required @staff_user_required -def update_candidate_status(request, job_slug, candidate_slug, stage_type, status): +def update_application_status(request, job_slug, candidate_slug, stage_type, status): """Handle exam/interview/offer status updates""" from django.utils import timezone @@ -723,7 +723,7 @@ def update_candidate_status(request, job_slug, candidate_slug, stage_type, statu candidate.offer_date = timezone.now() candidate.save(update_fields=['offer_status', 'offer_date']) return render(request,'recruitment/partials/offer-results.html',{'candidate':candidate,'job':job}) - return redirect('candidate_detail', candidate.slug) + return redirect('application_detail', candidate.slug) else: if stage_type == 'exam': return render(request,"includes/candidate_update_exam_form.html",{'candidate':candidate,'job':job}) @@ -765,7 +765,7 @@ STAGE_CONFIG = { @login_required @staff_user_required -def export_candidates_csv(request, job_slug, stage): +def export_applications_csv(request, job_slug, stage): """Export candidates for a specific stage as CSV""" job = get_object_or_404(models.JobPosting, slug=job_slug) @@ -905,7 +905,7 @@ def export_candidates_csv(request, job_slug, stage): @login_required @staff_user_required -def sync_hired_candidates(request, job_slug): +def sync_hired_applications(request, job_slug): """Sync hired candidates to external sources using Django-Q""" from django_q.tasks import async_task from .tasks import sync_hired_candidates_task diff --git a/templates/applicant/partials/candidate_facing_base.html b/templates/applicant/partials/candidate_facing_base.html index b2e1c1b..e9ed45e 100644 --- a/templates/applicant/partials/candidate_facing_base.html +++ b/templates/applicant/partials/candidate_facing_base.html @@ -323,7 +323,7 @@ {% translate "Applications" %} {% endcomment %} {% elif request.user.user_type == 'candidate' %} diff --git a/templates/recruitment/agency_assignment_detail.html b/templates/recruitment/agency_assignment_detail.html index 6d85064..0052b67 100644 --- a/templates/recruitment/agency_assignment_detail.html +++ b/templates/recruitment/agency_assignment_detail.html @@ -277,7 +277,7 @@ {{ candidate.created_at|date:"M d, Y" }} - diff --git a/templates/recruitment/agency_portal_assignment_detail.html b/templates/recruitment/agency_portal_assignment_detail.html index 6a19179..645eb1d 100644 --- a/templates/recruitment/agency_portal_assignment_detail.html +++ b/templates/recruitment/agency_portal_assignment_detail.html @@ -114,7 +114,7 @@ {% trans "Back to Dashboard" %} - + {% trans "Submit New Candidate" %} {% comment %} @@ -194,7 +194,7 @@ - + @@ -523,7 +523,7 @@ - + {% csrf_token %} - + {% csrf_token %}
- {% comment %} + {% comment %} {% trans "Submit Candidate" %} @@ -192,7 +192,7 @@
{% if stats.can_submit %} - {% trans "Submit Candidate" %} diff --git a/templates/recruitment/agency_portal_persons_list.html b/templates/recruitment/agency_portal_persons_list.html index 2d3da2c..2fae9c3 100644 --- a/templates/recruitment/agency_portal_persons_list.html +++ b/templates/recruitment/agency_portal_persons_list.html @@ -235,7 +235,7 @@ {% endif %}

{% if not search_query and not stage_filter and agency.assignments.exists %} - {% trans "Add First Person" %} diff --git a/templates/recruitment/agency_portal_submit_candidate.html b/templates/recruitment/agency_portal_submit_candidate.html index 0941c47..13d08bf 100644 --- a/templates/recruitment/agency_portal_submit_candidate.html +++ b/templates/recruitment/agency_portal_submit_candidate.html @@ -175,7 +175,7 @@ + action="{% url 'agency_portal_submit_application_page' assignment.slug %}"> {% csrf_token %} {{form|crispy}} - + {% trans "Back to List" %} diff --git a/templates/recruitment/candidate_detail.html b/templates/recruitment/candidate_detail.html index ff6be2f..3e8555b 100644 --- a/templates/recruitment/candidate_detail.html +++ b/templates/recruitment/candidate_detail.html @@ -648,13 +648,13 @@
{% trans "Management Actions" %}
- {% comment %} + {% comment %} {% trans "Edit Details" %} {% endcomment %} - {% comment %} + {% comment %} {% trans "Delete Candidate" %} {% endcomment %} - + {% trans "Back to List" %} {% if candidate.resume %} @@ -669,7 +669,7 @@ {% trans "Download Resume" %} - {% comment %} + {% comment %} {% trans "View Resume AI Overview" %} {% endcomment %} @@ -711,7 +711,7 @@
{% else %}
- diff --git a/templates/recruitment/candidate_document_management.html b/templates/recruitment/candidate_document_management.html index 893ccb1..0505a04 100644 --- a/templates/recruitment/candidate_document_management.html +++ b/templates/recruitment/candidate_document_management.html @@ -71,7 +71,7 @@
- {% comment %} {% trans "Export CSV" %} @@ -262,7 +262,7 @@
{# Form 1: Status Update #} - + {% csrf_token %} {# Select Input Group - No label needed for this one, so we just flex the select and button #} @@ -290,7 +290,7 @@ data-bs-toggle="modal" hx-boost='true' data-bs-target="#emailModal" - hx-get="{% url 'compose_candidate_email' job.slug %}" + hx-get="{% url 'compose_application_email' job.slug %}" hx-target="#emailModalBody" hx-include="#candidate-form" title="Email Participants"> @@ -300,7 +300,7 @@
- + @@ -398,7 +398,7 @@ - {% trans "Export CSV" %} @@ -227,7 +227,7 @@
{% if candidates %}
- + {% csrf_token %} {# MODIFIED: Using d-flex for horizontal alignment and align-items-end to align everything based on the baseline of the button/select #} @@ -256,7 +256,7 @@ data-bs-toggle="modal" hx-boost='true' data-bs-target="#emailModal" - hx-get="{% url 'compose_candidate_email' job.slug %}" + hx-get="{% url 'compose_application_email' job.slug %}" hx-target="#emailModalBody" hx-include="#candidate-form" title="Email Participants"> @@ -269,7 +269,7 @@ {% endif %}
- + {% csrf_token %}
@@ -337,12 +337,12 @@ - @@ -503,7 +503,7 @@ syncButton.innerHTML = ' {% trans "Syncing..." %}'; // Perform sync request - fetch(`{% url 'sync_hired_candidates' job.slug %}`, { + fetch(`{% url 'sync_hired_applications' job.slug %}`, { method: 'POST', headers: { 'Content-Type': 'application/json', diff --git a/templates/recruitment/candidate_interview_view.html b/templates/recruitment/candidate_interview_view.html index b84ce8b..ad82e07 100644 --- a/templates/recruitment/candidate_interview_view.html +++ b/templates/recruitment/candidate_interview_view.html @@ -181,7 +181,7 @@
@@ -288,7 +288,7 @@ {% if job_filter or stage_filter or search_query %} - + {% trans "Clear" %} {% endif %} @@ -286,7 +286,7 @@ {% for candidate in applications %} - +
{{ candidate.name }}{{ candidate.name }} {{ candidate.email }} {{ candidate.job.title }} @@ -297,7 +297,7 @@ {% endif %} {% else %} - + {{ candidate.created_at|date:"d-m-Y" }}
- + {% if user.is_staff %} - + @@ -356,7 +356,7 @@
- {{ candidate.name }} + {{ candidate.name }}
{{ candidate.stage }}
@@ -379,16 +379,16 @@
- + {% trans "View" %} {% if user.is_staff %} - + {% trans "Edit" %} @@ -411,7 +411,7 @@

{% trans "No application found" %}

{% trans "Create your first application." %}

{% if user.is_staff %} - + {% trans "Add Application" %} {% endif %} diff --git a/templates/recruitment/candidate_offer_view.html b/templates/recruitment/candidate_offer_view.html index 2c8f497..f93a966 100644 --- a/templates/recruitment/candidate_offer_view.html +++ b/templates/recruitment/candidate_offer_view.html @@ -180,7 +180,7 @@
- {% trans "Export CSV" %} @@ -201,7 +201,7 @@
{# Form: Hired/Rejected Status Update #} - + {% csrf_token %} {# Select element #} @@ -236,7 +236,7 @@ data-bs-toggle="modal" hx-boost='true' data-bs-target="#emailModal" - hx-get="{% url 'compose_candidate_email' job.slug %}" + hx-get="{% url 'compose_application_email' job.slug %}" hx-target="#emailModalBody" hx-include="#candidate-form" title="Email Participants"> @@ -247,7 +247,7 @@
{% endif %}
- + {% csrf_token %} @@ -293,7 +293,7 @@
- {% trans "View Details" %} diff --git a/templates/recruitment/candidate_profile.html b/templates/recruitment/candidate_profile.html index 4bdab2c..79d1aed 100644 --- a/templates/recruitment/candidate_profile.html +++ b/templates/recruitment/candidate_profile.html @@ -492,7 +492,7 @@
- {{ application.job.title }} @@ -524,7 +524,7 @@
- {% trans "View Details" %} @@ -568,7 +568,7 @@
{% trans "Uploaded:" %} {{ document.uploaded_at|date:"d M Y" }} - +
{% empty %} diff --git a/templates/recruitment/candidate_screening_view.html b/templates/recruitment/candidate_screening_view.html index f1e4b66..7f20780 100644 --- a/templates/recruitment/candidate_screening_view.html +++ b/templates/recruitment/candidate_screening_view.html @@ -224,7 +224,7 @@
- {% trans "Export CSV" %} @@ -319,7 +319,7 @@
{% if candidates %}
- + {% csrf_token %} {# MODIFIED: Using d-flex for horizontal alignment and align-items-end to align everything based on the baseline of the button/select #} @@ -348,7 +348,7 @@ data-bs-toggle="modal" hx-boost='true' data-bs-target="#emailModal" - hx-get="{% url 'compose_candidate_email' job.slug %}" + hx-get="{% url 'compose_application_email' job.slug %}" hx-target="#emailModalBody" hx-include="#candidate-form" title="Email Participants"> @@ -361,7 +361,7 @@ {% endif %}
- + {% csrf_token %} @@ -471,7 +471,7 @@