diff --git a/NorahUniversity/__pycache__/settings.cpython-313.pyc b/NorahUniversity/__pycache__/settings.cpython-313.pyc index b4559a9..9395c44 100644 Binary files a/NorahUniversity/__pycache__/settings.cpython-313.pyc and b/NorahUniversity/__pycache__/settings.cpython-313.pyc differ diff --git a/recruitment/__pycache__/models.cpython-313.pyc b/recruitment/__pycache__/models.cpython-313.pyc index 2cf2357..ddb6d5e 100644 Binary files a/recruitment/__pycache__/models.cpython-313.pyc and b/recruitment/__pycache__/models.cpython-313.pyc differ diff --git a/recruitment/__pycache__/urls.cpython-313.pyc b/recruitment/__pycache__/urls.cpython-313.pyc index 4f3d897..eda9760 100644 Binary files a/recruitment/__pycache__/urls.cpython-313.pyc and b/recruitment/__pycache__/urls.cpython-313.pyc differ diff --git a/recruitment/__pycache__/utils.cpython-313.pyc b/recruitment/__pycache__/utils.cpython-313.pyc index fbf2b0d..d6f5ba5 100644 Binary files a/recruitment/__pycache__/utils.cpython-313.pyc and b/recruitment/__pycache__/utils.cpython-313.pyc differ diff --git a/recruitment/__pycache__/views.cpython-313.pyc b/recruitment/__pycache__/views.cpython-313.pyc index 433256d..415073d 100644 Binary files a/recruitment/__pycache__/views.cpython-313.pyc and b/recruitment/__pycache__/views.cpython-313.pyc differ diff --git a/recruitment/__pycache__/views_frontend.cpython-313.pyc b/recruitment/__pycache__/views_frontend.cpython-313.pyc index dd8a218..61611f1 100644 Binary files a/recruitment/__pycache__/views_frontend.cpython-313.pyc and b/recruitment/__pycache__/views_frontend.cpython-313.pyc differ diff --git a/recruitment/urls.py b/recruitment/urls.py index 4aefef5..e48911c 100644 --- a/recruitment/urls.py +++ b/recruitment/urls.py @@ -41,11 +41,11 @@ urlpatterns = [ path('training//delete/', views_frontend.TrainingDeleteView.as_view(), name='training_delete'), # Meeting URLs - path('', views.ZoomMeetingListView.as_view(), name='list_meetings'), - path('create-meeting/', views.ZoomMeetingCreateView.as_view(), name='create_meeting'), - path('meeting-details//', views.ZoomMeetingDetailsView.as_view(), name='meeting_details'), - path('update-meeting//', views.ZoomMeetingUpdateView.as_view(), name='update_meeting'), - path('delete-meeting//', views.ZoomMeetingDeleteView, name='delete_meeting'), + path('meetings/', views.ZoomMeetingListView.as_view(), name='list_meetings'), + path('meetings/create-meeting/', views.ZoomMeetingCreateView.as_view(), name='create_meeting'), + path('meetings/meeting-details//', views.ZoomMeetingDetailsView.as_view(), name='meeting_details'), + path('meetings/update-meeting//', views.ZoomMeetingUpdateView.as_view(), name='update_meeting'), + path('meetings/delete-meeting//', views.ZoomMeetingDeleteView, name='delete_meeting'), # JobPosting functional views URLs (keeping for compatibility) path('api/create/', views.create_job, name='create_job_api'), @@ -105,7 +105,6 @@ urlpatterns = [ 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'), - # users urls path('user/',views.user_detail,name='user_detail') ] diff --git a/recruitment/views_frontend.py b/recruitment/views_frontend.py index 3e1043a..34d3f55 100644 --- a/recruitment/views_frontend.py +++ b/recruitment/views_frontend.py @@ -92,7 +92,7 @@ class JobCandidatesListView(LoginRequiredMixin, ListView): context_object_name = 'candidates' paginate_by = 10 - + def get_queryset(self): # Get the job by slug @@ -239,75 +239,14 @@ def candidate_detail(request, slug): def candidate_update_stage(request, slug): """Handle HTMX stage update requests""" - try: - if not request.user.is_staff: - return render(request, 'recruitment/partials/error.html', {'error': 'Permission denied'}, status=403) - - candidate = get_object_or_404(models.Candidate, slug=slug) - - if request.method != 'POST': - return render(request, 'recruitment/partials/error.html', {'error': 'Only POST method is allowed'}, status=405) - - # Handle form data - form = forms.CandidateStageForm(request.POST, candidate=candidate) - if form.is_valid(): - stage_value = form.cleaned_data['stage'] - - # Validate stage value - valid_stages = [choice[0] for choice in models.Candidate.Stage.choices] - if stage_value not in valid_stages: - return render(request, 'recruitment/partials/error.html', {'error': f'Invalid stage value. Must be one of: {", ".join(valid_stages)}'}, status=400) - - # Check transition rules - if candidate.pk and stage_value != candidate.stage: - old_stage = candidate.stage - if not candidate.can_transition_to(stage_value): - return render(request, 'recruitment/partials/error.html', {'error': f'Cannot transition from "{old_stage}" to "{stage_value}". Transition not allowed.'}, status=400) - - # Update the stage - old_stage = candidate.stage - candidate.stage = stage_value - candidate.save() - - # Return success template - context = { - 'form': form, - 'success': True, - 'message': f'Stage updated from "{old_stage}" to "{candidate.stage}"', - 'new_stage': candidate.stage, - 'new_stage_display': candidate.get_stage_display(), - 'candidate': candidate - } - messages.success(request,"Candidate Stage Updated") - return redirect("candidate_detail",slug=candidate.slug) - def response(): - stage_form = forms.CandidateStageForm(candidate=candidate) - context['stage_form'] = stage_form - stage_form_partial = render_to_string('recruitment/partials/stage_update_modal.html#id-stage', context) - success_html = render_to_string('recruitment/partials/stage_update_success.html', context) - yield SSE.patch_elements(stage_form_partial,"#id_stage") - yield SSE.patch_elements(success_html,"#availableStagesInfo") - yield SSE.patch_signals({'stage':candidate.stage}) - - return DatastarResponse(response()) - # return render(request, 'recruitment/partials/stage_update_success.html', context) - else: - # Return form with errors - context = { - 'form': form, - 'candidate': candidate, - 'stage_form': forms.CandidateStageForm(candidate=candidate) - } - return render(request, 'recruitment/partials/stage_update_form.html', context) - - except Exception as e: - # Log the error for debugging - import traceback - error_details = traceback.format_exc() - print(f"Error in candidate_update_stage: {error_details}") - - return render(request, 'partials/error.html', {'error': f'Internal server error: {str(e)}'}, status=500) - + candidate = get_object_or_404(models.Candidate, slug=slug) + form = forms.CandidateStageForm(request.POST, candidate=candidate) + if form.is_valid(): + stage_value = form.cleaned_data['stage'] + candidate.stage = stage_value + candidate.save(update_fields=['stage']) + messages.success(request,"Candidate Stage Updated") + return redirect("candidate_detail",slug=candidate.slug) class TrainingListView(LoginRequiredMixin, ListView): model = models.TrainingMaterial diff --git a/templates/includes/search_form.html b/templates/includes/search_form.html index 5cdad13..721c99f 100644 --- a/templates/includes/search_form.html +++ b/templates/includes/search_form.html @@ -1,6 +1,6 @@ {% load i18n %}
@@ -15,10 +15,4 @@ value="{{ search_query }}" aria-label="{% trans 'Search' %}">
- {% comment %} {% endcomment %}
diff --git a/templates/meetings/list_meetings.html b/templates/meetings/list_meetings.html index 0297876..a602ac3 100644 --- a/templates/meetings/list_meetings.html +++ b/templates/meetings/list_meetings.html @@ -184,7 +184,7 @@ {% trans "Apply Filters" %} {% if status_filter or search_query %} - + {% trans "Clear" %} {% endif %} diff --git a/templates/recruitment/candidate_detail.html b/templates/recruitment/candidate_detail.html index a35e152..e00e130 100644 --- a/templates/recruitment/candidate_detail.html +++ b/templates/recruitment/candidate_detail.html @@ -118,11 +118,11 @@ padding: 1.5rem 1.25rem; background-color: white; } - + /* ==================================== */ /* NEW: Vertical Timeline Styling */ /* ==================================== */ - + /* Highlight box for the current stage */ .current-stage { border: 1px solid var(--kaauh-border); @@ -131,7 +131,7 @@ .current-stage .text-primary { color: var(--kaauh-teal) !important; } - + .timeline { position: relative; padding-left: 2rem; @@ -163,17 +163,17 @@ color: white; font-size: 0.8rem; z-index: 10; - border: 4px solid white; + border: 4px solid white; } .timeline-item:last-child { margin-bottom: 0; } - + /* Custom Timeline Background Classes for Stages (Using Bootstrap color palette) */ - .timeline-bg-applied { background-color: var(--kaauh-teal) !important; } - .timeline-bg-exam { background-color: #17a2b8 !important; } - .timeline-bg-interview { background-color: #ffc107 !important; } - .timeline-bg-offer { background-color: #28a745 !important; } + .timeline-bg-applied { background-color: var(--kaauh-teal) !important; } + .timeline-bg-exam { background-color: #17a2b8 !important; } + .timeline-bg-interview { background-color: #ffc107 !important; } + .timeline-bg-offer { background-color: #28a745 !important; } .timeline-bg-rejected { background-color: #dc3545 !important; } {% endblock %} @@ -187,7 +187,7 @@ - + {# LEFT COLUMN: MAIN CANDIDATE DETAILS AND TABS #}
@@ -199,7 +199,7 @@

{{ candidate.name }}

- + @@ -345,7 +345,7 @@
{% trans "Candidate Journey" %}
- +
{% trans "Current Stage" %}

{{ candidate.stage }}

@@ -356,8 +356,8 @@
{% trans "Historical Timeline" %}
- - + + {# Base Status: Application Submitted (Always required) #}
@@ -365,11 +365,11 @@

{% trans "Application Submitted" %}

{{ candidate.created_at|date:"M d, Y" }} - | + | {{ candidate.created_at|date:"h:i A" }}
- +
{% if candidate.exam_date %}
@@ -378,11 +378,11 @@

{% trans "Exam" %}

{{ candidate.exam_date|date:"M d, Y" }} - | + | {{ candidate.exam_date|date:"h:i A" }}
- +
{% endif %} {% if candidate.interview_date %} @@ -392,11 +392,11 @@

{% trans "Interview" %}

{{ candidate.interview_date|date:"M d, Y" }} - | + | {{ candidate.interview_date|date:"h:i A" }}
- +
{% endif %} @@ -407,21 +407,18 @@

{% trans "Offer" %}

{{ candidate.offer_date|date:"M d, Y" }} - | + | {{ candidate.offer_date|date:"h:i A" }}
- + {% endif %} - - - - + - + diff --git a/templates/recruitment/partials/stage_update_modal.html b/templates/recruitment/partials/stage_update_modal.html index f6a5f28..bf809f4 100644 --- a/templates/recruitment/partials/stage_update_modal.html +++ b/templates/recruitment/partials/stage_update_modal.html @@ -19,66 +19,21 @@
Current Stage: - -
- - - -
-
Application Pipeline
-
-
-
-
- -
-
Applied
-
- - -
- -
-
- -
-
Exam
-
-
- -
-
- -
-
Interview
-
-
-
-
- -
-
Offer
-
-
+ {{candidate.stage}}
-
- {% comment %}
{% endcomment %} - {% url 'candidate_update_stage' candidate.slug as stage_update_url %} - +
+ {% csrf_token %} -
{% partialdef id-stage %} - {% for value, label in stage_form.stage.field.choices %} {% endfor %} @@ -95,17 +50,15 @@
-
-