diff --git a/db.sqlite3 b/db.sqlite3 index feeaaa2..f69db19 100644 Binary files a/db.sqlite3 and b/db.sqlite3 differ diff --git a/recruitment/__pycache__/forms.cpython-312.pyc b/recruitment/__pycache__/forms.cpython-312.pyc index 3921c26..676af2b 100644 Binary files a/recruitment/__pycache__/forms.cpython-312.pyc and b/recruitment/__pycache__/forms.cpython-312.pyc differ diff --git a/recruitment/__pycache__/urls.cpython-312.pyc b/recruitment/__pycache__/urls.cpython-312.pyc index 68d94c0..f89276b 100644 Binary files a/recruitment/__pycache__/urls.cpython-312.pyc and b/recruitment/__pycache__/urls.cpython-312.pyc differ diff --git a/recruitment/__pycache__/views.cpython-312.pyc b/recruitment/__pycache__/views.cpython-312.pyc index 1c2171f..5166bda 100644 Binary files a/recruitment/__pycache__/views.cpython-312.pyc and b/recruitment/__pycache__/views.cpython-312.pyc differ diff --git a/recruitment/__pycache__/views_frontend.cpython-312.pyc b/recruitment/__pycache__/views_frontend.cpython-312.pyc index 6d78c62..1b05fc2 100644 Binary files a/recruitment/__pycache__/views_frontend.cpython-312.pyc and b/recruitment/__pycache__/views_frontend.cpython-312.pyc differ diff --git a/recruitment/forms.py b/recruitment/forms.py index 866307e..6806366 100644 --- a/recruitment/forms.py +++ b/recruitment/forms.py @@ -464,4 +464,12 @@ class InterviewScheduleForm(forms.ModelForm): def clean_working_days(self): working_days = self.cleaned_data.get('working_days') # Convert string values to integers - return [int(day) for day in working_days] \ No newline at end of file + return [int(day) for day in working_days] + +class JobStatusUpdateForm(forms.ModelForm): + class Meta: + model=JobPosting + + fields=[ + 'status' + ] \ No newline at end of file diff --git a/recruitment/urls.py b/recruitment/urls.py index b981ed8..ae6fead 100644 --- a/recruitment/urls.py +++ b/recruitment/urls.py @@ -13,7 +13,8 @@ urlpatterns = [ # path('jobs//delete/', views., name='job_delete'), path('jobs//', views.job_detail, name='job_detail'), path('jobs//candidate/', views.job_detail_candidate, name='job_detail_candidate'), - path('jobs//candidate/application/success', views.application_sucess, name='application_success'), + path('jobs//candidate/application/success', views.application_success, name='application_success'), + # LinkedIn Integration URLs path('jobs//post-to-linkedin/', views.post_to_linkedin, name='post_to_linkedin'), diff --git a/recruitment/views.py b/recruitment/views.py index 95a2b40..78dc2d7 100644 --- a/recruitment/views.py +++ b/recruitment/views.py @@ -10,7 +10,7 @@ from django.db.models import Q from django.urls import reverse from django.conf import settings from django.utils import timezone -from .forms import ZoomMeetingForm,JobPostingForm,FormTemplateForm,InterviewScheduleForm +from .forms import ZoomMeetingForm,JobPostingForm,FormTemplateForm,InterviewScheduleForm,JobStatusUpdateForm from rest_framework import viewsets from django.contrib import messages from django.core.paginator import Paginator @@ -225,6 +225,7 @@ def edit_job(request,slug): def job_detail(request, slug): """View details of a specific job""" job = get_object_or_404(JobPosting, slug=slug) + # Get all candidates for this job, ordered by most recent candidates = job.candidates.all().order_by('-created_at') @@ -235,6 +236,27 @@ def job_detail(request, slug): interview_count = candidates.filter(stage='Interview').count() offer_count = candidates.filter(stage='Offer').count() + status_form = JobStatusUpdateForm(instance=job) + + # 2. Check for POST request (Status Update Submission) + if request.method == 'POST': + + status_form = JobStatusUpdateForm(request.POST, instance=job) + + if status_form.is_valid(): + status_form.save() + + # Add a success message + messages.success(request, f"Status for '{job.title}' updated to '{job.get_status_display()}' successfully!") + + + return redirect('job_detail', slug=slug) + else: + + messages.error(request, "Failed to update status due to validation errors.") + + + context = { 'job': job, 'candidates': candidates, @@ -242,6 +264,7 @@ def job_detail(request, slug): 'applied_count': applied_count, 'interview_count': interview_count, 'offer_count': offer_count, + 'status_form':status_form } return render(request, 'jobs/job_detail.html', context) @@ -345,6 +368,13 @@ def applicant_job_detail(request,slug): job=get_object_or_404(JobPosting,slug=slug,status='ACTIVE') return render(request,'jobs/applicant_job_detail.html',{'job':job}) +def application_success(request,slug): + job=get_object_or_404(JobPosting,slug=slug) + return render(request,'jobs/application_success.html',{'job':job}) + + + + # Form Preview Views # from django.http import JsonResponse diff --git a/recruitment/views_frontend.py b/recruitment/views_frontend.py index f323882..54a79d2 100644 --- a/recruitment/views_frontend.py +++ b/recruitment/views_frontend.py @@ -1,4 +1,5 @@ -from django.shortcuts import render, get_object_or_404 +from django.shortcuts import render, get_object_or_404,redirect +from django.contrib import messages from django.http import JsonResponse from . import models from django.utils.translation import get_language @@ -188,10 +189,13 @@ class CandidateDeleteView(LoginRequiredMixin, SuccessMessageMixin, DeleteView): slug_url_kwarg = 'slug' -def job_detail(request, slug): - job = get_object_or_404(models.JobPosting, slug=slug, status='Published') - form = forms.CandidateForm() - return render(request, 'jobs/job_detail.html', {'job': job, 'form': form}) +# def job_detail(request, slug): +# job = get_object_or_404(models.JobPosting, slug=slug, status='Published') +# form = forms.CandidateForm() + +# return render(request, 'jobs/job_detail.html', {'job': job, 'form': form}) + + @login_required diff --git a/templates/forms/form_wizard.html b/templates/forms/form_wizard.html index 8aedf54..01f99c4 100644 --- a/templates/forms/form_wizard.html +++ b/templates/forms/form_wizard.html @@ -460,9 +460,9 @@
-
+
@@ -168,7 +168,7 @@

{% translate "Thank You!" %}

{% translate "Your application has been submitted successfully" %}

- {# JOB INFO BLOCK #} + {% comment %} {# JOB INFO BLOCK #}

{% translate "Position" %}: {{ job.title }}

{% translate "Job ID" %}: {{ job.internal_job_id }}

@@ -176,14 +176,14 @@ {% if job.application_deadline %}

{% translate "Application Deadline" %}: {{ job.application_deadline|date:"F j, Y" }}

{% endif %} -
+
{% endcomment %}

{% translate "We appreciate your interest in joining our team. Our hiring team will review your application and contact you if there's a potential match for this position." %}

- + {% translate "Return to Job Listings" %} {# You can add a link to view the saved application here if applicable #} diff --git a/templates/jobs/job_detail.html b/templates/jobs/job_detail.html index 7acfd11..56f7a5f 100644 --- a/templates/jobs/job_detail.html +++ b/templates/jobs/job_detail.html @@ -36,6 +36,8 @@ font-weight: 700; text-transform: uppercase; letter-spacing: 0.7px; + display: inline-flex; + align-items: center; } /* Mapped color classes for status badges */ @@ -109,7 +111,6 @@ /* ==================================== */ .right-column-tabs { - /* Use the .card wrapper for the main structure */ padding: 0; margin-bottom: 0; border-bottom: 1px solid var(--kaauh-border); @@ -119,41 +120,38 @@ margin-bottom: 0; border-bottom: none; background-color: transparent; - display: flex; /* Ensure the nav-items take up equal space */ + display: flex; } .right-column-tabs .nav-item { flex-grow: 1; text-align: center; } .right-column-tabs .nav-link { - /* Base style for all right column tabs */ - padding: 0.9rem 1rem; /* Slightly larger padding for better spacing */ + padding: 0.9rem 1rem; font-size: 0.95rem; font-weight: 600; color: var(--kaauh-primary-text); border-radius: 0; border-right: 1px solid var(--kaauh-border); - border-bottom: 1px solid var(--kaauh-border); /* Separator line */ - background-color: #f8f9fa; /* Subtle background for non-active tabs */ + border-bottom: 1px solid var(--kaauh-border); + background-color: #f8f9fa; } .right-column-tabs .nav-item:last-child .nav-link { border-right: none; } - /* Active Tab */ .right-column-tabs .nav-link.active { - background-color: white; /* Lift the active tab */ + background-color: white; color: var(--kaauh-teal-dark); - border-bottom: 3px solid var(--kaauh-teal); /* Strong accent */ - border-right-color: transparent; /* Clean up border next to it */ - margin-bottom: -1px; /* Overlap the border for a cleaner look */ + border-bottom: 3px solid var(--kaauh-teal); + border-right-color: transparent; + margin-bottom: -1px; } - /* Hover state for non-active tabs */ .right-column-tabs .nav-link:not(.active):hover { - background-color: #f0f4f7; /* Darken slightly on hover */ + background-color: #f0f4f7; color: var(--kaauh-teal); } .right-column-tabs .tab-content { - padding: 1.5rem 1.25rem; /* Increased padding inside the content for breathing room */ + padding: 1.5rem 1.25rem; background-color: white; } @@ -229,13 +227,11 @@ /* Table styling for the Applicant preview */ .table-applicants tbody tr:hover { - background-color: #f3f9f9; /* Light teal hover for rows */ + background-color: #f3f9f9; } .table-applicants td { border-top: 1px solid var(--kaauh-border); } - - {% endblock %} @@ -249,10 +245,19 @@ {# HEADER SECTION #}
-

{{job}}

- - {{ job.get_status_display }} - +
+

{{ job.title }}

+ {{ job.internal_job_id }} +
+ +
+ + {{ job.get_status_display }} + + +
{# LEFT TABS NAVIGATION #} @@ -566,4 +571,37 @@
{% include "jobs/partials/image_upload.html" %} + + + + {% endblock %} \ No newline at end of file diff --git a/templates/jobs/job_detail_candidate.html b/templates/jobs/job_detail_candidate.html index ae369e4..5d752fc 100644 --- a/templates/jobs/job_detail_candidate.html +++ b/templates/jobs/job_detail_candidate.html @@ -135,9 +135,9 @@