diff --git a/.env b/.env index 8d7fbd5..b9e2bf0 100644 --- a/.env +++ b/.env @@ -1,3 +1,3 @@ -DB_NAME=haikal_db -DB_USER=faheed -DB_PASSWORD=Faheed@215 \ No newline at end of file +DB_NAME=norahuniversity +DB_USER=norahuniversity +DB_PASSWORD=norahuniversity \ No newline at end of file diff --git a/NorahUniversity/settings.py b/NorahUniversity/settings.py index 824e132..8c006e0 100644 --- a/NorahUniversity/settings.py +++ b/NorahUniversity/settings.py @@ -214,16 +214,16 @@ ACCOUNT_FORMS = {"signup": "recruitment.forms.StaffSignupForm"} # EMAIL_PORT = 2225 # EMAIL_BACKEND = "django.core.mail.backends.console.EmailBackend" -# EMAIL_HOST_PASSWORD = os.getenv("EMAIL_PASSWORD", "mssp.0Q0rSwb.zr6ke4n2k3e4on12.aHwJqnI") -# EMAIL_HOST = "smtp.mailersend.net" -# EMAIL_PORT = 2525 -# EMAIL_HOST_USER = "MS_lhygCJ@test-65qngkd8nx3lwr12.mlsender.net" -# EMAIL_HOST_PASSWORD = "mssp.0Q0rSwb.zr6ke4n2k3e4on12.aHwJqnI" -# EMAIL_USE_TLS = True -# EMAIL_HOST = 'sandbox.smtp.mailtrap.io' -# EMAIL_HOST_USER = '38e5179debe69a' -# EMAIL_HOST_PASSWORD = 'ffa75647d01ecb' -# EMAIL_PORT = '2525' +EMAIL_HOST_PASSWORD = os.getenv("EMAIL_PASSWORD", "mssp.0Q0rSwb.zr6ke4n2k3e4on12.aHwJqnI") +EMAIL_HOST = "smtp.mailersend.net" +EMAIL_PORT = 2525 +EMAIL_HOST_USER = "MS_lhygCJ@test-65qngkd8nx3lwr12.mlsender.net" +EMAIL_HOST_PASSWORD = "mssp.0Q0rSwb.zr6ke4n2k3e4on12.aHwJqnI" +EMAIL_USE_TLS = True +EMAIL_HOST = 'sandbox.smtp.mailtrap.io' +EMAIL_HOST_USER = '38e5179debe69a' +EMAIL_HOST_PASSWORD = 'ffa75647d01ecb' +EMAIL_PORT = '2525' diff --git a/recruitment/forms.py b/recruitment/forms.py index db7989b..ffacc67 100644 --- a/recruitment/forms.py +++ b/recruitment/forms.py @@ -377,6 +377,13 @@ class ApplicationForm(forms.ModelForm): Submit("submit", _("Submit"), css_class="btn btn-primary"), ) + def clean_job(self): + job = self.cleaned_data.get("job") + if job.max_applications <= Application.objects.filter(job=job).count(): + raise forms.ValidationError( + "The maximum number of applicants for this job has been reached." + ) + return job # def clean(self): # cleaned_data = super().clean() # job = cleaned_data.get("job") @@ -720,7 +727,7 @@ class BulkInterviewTemplateForm(forms.ModelForm): if end_date and start_date and end_date < start_date: raise forms.ValidationError(_("End date must be after start date")) return end_date - + def clean_end_time(self): start_time = self.cleaned_data.get("start_time") end_time = self.cleaned_data.get("end_time") @@ -1465,7 +1472,7 @@ class CandidateEmailForm(forms.Form): f"You will receive a separate email shortly with details regarding your start date, first-day instructions, and onboarding documents.", f"We look forward to seeing you at KAAUH.", f"If you have any questions before your start date, please contact [Onboarding Contact].", - + ] elif candidate: message_parts="" @@ -1639,7 +1646,7 @@ class MessageForm(forms.ModelForm): # Validate messaging permissions if self.user and cleaned_data.get("recipient"): self._validate_messaging_permissions(cleaned_data) - + if self.cleaned_data.get('recipient')==self.user: raise forms.ValidationError(_("You cannot message yourself")) @@ -1805,6 +1812,27 @@ class PasswordResetForm(forms.Form): raise forms.ValidationError(_('New passwords do not match.')) return cleaned_data +class PersonPasswordResetForm(forms.Form): + new_password1 = forms.CharField( + widget=forms.PasswordInput(attrs={'class': 'form-control'}), + label=_('New Password') + ) + new_password2 = forms.CharField( + widget=forms.PasswordInput(attrs={'class': 'form-control'}), + label=_('Confirm New Password') + ) + + def clean(self): + """Custom validation for password reset""" + cleaned_data = super().clean() + new_password1 = cleaned_data.get('new_password1') + new_password2 = cleaned_data.get('new_password2') + + if new_password1 and new_password2: + if new_password1 != new_password2: + raise forms.ValidationError(_('New passwords do not match.')) + + return cleaned_data class StaffAssignmentForm(forms.ModelForm): @@ -2225,7 +2253,7 @@ Location: {interview.physical_address} Room No: {interview.room_number} This is an onsite schedule. Please arrive 10 minutes early.\n\n""" - + self.fields['message'].initial = initial_message diff --git a/recruitment/services/email_service.py b/recruitment/services/email_service.py index cdbcf06..1234954 100644 --- a/recruitment/services/email_service.py +++ b/recruitment/services/email_service.py @@ -28,8 +28,9 @@ class EmailService: try: # Using EmailMessage for more control (e.g., HTML content) - + from time import sleep for recipient in recipient_list: + sleep(2) email = EmailMessage( subject=subject, body=body, @@ -46,8 +47,6 @@ class EmailService: recipient_user=User.objects.filter(email=recipient).first() if result and recipient_user and not context["message_created"]: Message.objects.create(sender=context['sender_user'],recipient=recipient_user,job=context['job'],subject=subject,content=context['email_message'],message_type='DIRECT',is_read=False) - - return len(recipient_list) except Exception as e: @@ -111,7 +110,7 @@ class EmailService: context=context, from_email=from_email, html_content=html_content, - + ) # Return the count of recipients if successful, or 0 if failure diff --git a/recruitment/urls.py b/recruitment/urls.py index 91e9b6d..e49764c 100644 --- a/recruitment/urls.py +++ b/recruitment/urls.py @@ -17,6 +17,7 @@ urlpatterns = [ # Job CRUD Operations path("jobs/", views.JobListView.as_view(), name="job_list"), path("jobs/create/", views.create_job, name="job_create"), + path("jobs/bank/", views.job_bank_view, name="job_bank"), path("jobs//", views.job_detail, name="job_detail"), path("jobs//update/", views.edit_job, name="job_update"), path("jobs//upload-image/", views.job_image_upload, name="job_image_upload"), @@ -25,7 +26,6 @@ urlpatterns = [ path("jobs//applicants/", views.job_applicants_view, name="job_applicants"), path("jobs//applications/", views.JobApplicationListView.as_view(), name="job_applications_list"), path("jobs//calendar/", views.interview_calendar_view, name="interview_calendar"), - path("jobs/bank/", views.job_bank_view, name="job_bank"), # Job Actions & Integrations path("jobs//post-to-linkedin/", views.post_to_linkedin, name="post_to_linkedin"), @@ -103,6 +103,7 @@ urlpatterns = [ path("persons//", views.PersonDetailView.as_view(), name="person_detail"), path("persons//update/", views.PersonUpdateView.as_view(), name="person_update"), path("persons//delete/", views.PersonDeleteView.as_view(), name="person_delete"), + path("persons//password_reset/", views.password_reset, name="password_reset"), # ======================================================================== # FORM & TEMPLATE MANAGEMENT diff --git a/recruitment/views.py b/recruitment/views.py index 0221b4a..353423f 100644 --- a/recruitment/views.py +++ b/recruitment/views.py @@ -280,6 +280,11 @@ class PersonDetailView(DetailView, LoginRequiredMixin, StaffRequiredMixin): template_name = "people/person_detail.html" context_object_name = "person" + def get_context_data(self, **kwargs): + from .forms import PersonPasswordResetForm + context = super().get_context_data(**kwargs) + context['password_form'] = PersonPasswordResetForm() + return context class PersonUpdateView(UpdateView, LoginRequiredMixin, StaffOrAgencyRequiredMixin): model = Person @@ -1302,11 +1307,10 @@ def delete_form_template(request, template_id): # @staff_or_candidate_required def application_submit_form(request, slug): """Display the form as a step-by-step wizard""" - form_template = get_object_or_404(FormTemplate, slug=slug, is_active=True) + job = get_object_or_404(JobPosting, slug=slug) if not request.user.is_authenticated: return redirect("application_signup", slug=slug) - print(form_template.job.slug) - job = get_object_or_404(JobPosting, slug=form_template.job.slug) + if request.user.user_type == "candidate": person = request.user.person_profile if job.has_already_applied_to_this_job(person): @@ -1316,23 +1320,16 @@ def application_submit_form(request, slug): "You have already applied to this job: Multiple applications are not allowed." ), ) - return redirect("job_application_detail", slug=job.slug) + return redirect("job_application_detail", slug=slug) - # template = get_object_or_404(FormTemplate, slug=slug, is_active=True) - template = job.form_template - stage = template.stages.filter(name="Contact Information") - - job_id = template.job.internal_job_id - job = template.job - is_limit_exceeded = job.is_application_limit_reached - if is_limit_exceeded: + if job.is_application_limit_reached: messages.error( request, _( "Application limit reached: This job is no longer accepting new applications." ), ) - return redirect("application_detail", slug=job.slug) + return redirect("job_application_detail", slug=slug) if job.is_expired: messages.error( request, @@ -1340,12 +1337,12 @@ def application_submit_form(request, slug): "Application deadline passed: This job is no longer accepting new applications." ), ) - return redirect("application_detail", slug=job.slug) + return redirect("job_application_detail", slug=slug) return render( request, "applicant/application_submit_form.html", - {"template_slug": template.slug, "job_id": job_id}, + {"template_slug": job.form_template.slug, "job_id": job.internal_job_id}, ) @@ -1357,8 +1354,9 @@ def application_submit(request, template_slug): import re """Handle form submission""" - if not request.user.is_authenticated: # or request.user.user_type != "candidate": + if not request.user.is_authenticated or request.user.user_type != "candidate": return JsonResponse({"success": False, "message": "Unauthorized access."}) + template = get_object_or_404(FormTemplate, slug=template_slug) job = template.job if request.method == "POST": @@ -1414,38 +1412,38 @@ def application_submit(request, template_slug): except FormField.DoesNotExist: continue try: - gpa = submission.responses.get(field__label="GPA") - if gpa and gpa.value: - gpa_str = gpa.value.replace("/", "").strip() + # gpa = submission.responses.get(field__label="GPA") + # if gpa and gpa.value: + # gpa_str = gpa.value.replace("/", "").strip() - if not re.match(r"^\d+(\.\d+)?$", gpa_str): - # --- FIX APPLIED HERE --- - return JsonResponse( - { - "success": False, - "message": _("GPA must be a numeric value."), - } - ) + # if not re.match(r"^\d+(\.\d+)?$", gpa_str): + # # --- FIX APPLIED HERE --- + # return JsonResponse( + # { + # "success": False, + # "message": _("GPA must be a numeric value."), + # } + # ) - try: - gpa_float = float(gpa_str) - except ValueError: - # --- FIX APPLIED HERE --- - return JsonResponse( - { - "success": False, - "message": _("GPA must be a numeric value."), - } - ) + # try: + # gpa_float = float(gpa_str) + # except ValueError: + # # --- FIX APPLIED HERE --- + # return JsonResponse( + # { + # "success": False, + # "message": _("GPA must be a numeric value."), + # } + # ) - if not (0.0 <= gpa_float <= 4.0): - # --- FIX APPLIED HERE --- - return JsonResponse( - { - "success": False, - "message": _("GPA must be between 0.0 and 4.0."), - } - ) + # if not (0.0 <= gpa_float <= 4.0): + # # --- FIX APPLIED HERE --- + # return JsonResponse( + # { + # "success": False, + # "message": _("GPA must be between 0.0 and 4.0."), + # } + # ) resume = submission.responses.get(field__label="Resume Upload") @@ -1456,7 +1454,7 @@ def application_submit(request, template_slug): submission.save() # time=timezone.now() person = request.user.person_profile - person.gpa = gpa.value if gpa else None + # person.gpa = gpa.value if gpa else None person.save() Application.objects.create( person=person, @@ -2257,60 +2255,60 @@ def reschedule_meeting_for_application(request, slug): def interview_calendar_view(request, slug): job = get_object_or_404(JobPosting, slug=slug) - # # Get all scheduled interviews for this job - # scheduled_interviews = ScheduledInterview.objects.filter(job=job).select_related( - # "applicaton", "zoom_meeting" - # ) + # Get all scheduled interviews for this job + scheduled_interviews = ScheduledInterview.objects.filter(job=job).select_related( + "interview","application" + ) - # # Convert interviews to calendar events - # events = [] - # for interview in scheduled_interviews: - # # Create start datetime - # start_datetime = datetime.combine( - # interview.interview_date, interview.interview_time - # ) + # Convert interviews to calendar events + events = [] + for interview in scheduled_interviews: + # Create start datetime + start_datetime = datetime.combine( + interview.interview_date, interview.interview_time + ) - # # Calculate end datetime based on interview duration - # duration = interview.zoom_meeting.duration if interview.zoom_meeting else 60 - # end_datetime = start_datetime + timedelta(minutes=duration) + # Calculate end datetime based on interview duration + duration = interview.interview.duration if interview.interview else 60 + end_datetime = start_datetime + timedelta(minutes=duration) - # # Determine event color based on status - # color = "#00636e" # Default color - # if interview.status == "confirmed": - # color = "#00a86b" # Green for confirmed - # elif interview.status == "cancelled": - # color = "#e74c3c" # Red for cancelled - # elif interview.status == "completed": - # color = "#95a5a6" # Gray for completed + # Determine event color based on status + color = "#00636e" # Default color + if interview.status == "confirmed": + color = "#00a86b" # Green for confirmed + elif interview.status == "cancelled": + color = "#e74c3c" # Red for cancelled + elif interview.status == "completed": + color = "#95a5a6" # Gray for completed - # events.append( - # { - # "title": f"Interview: {interview.candidate.name}", - # "start": start_datetime.isoformat(), - # "end": end_datetime.isoformat(), - # "url": f"{request.path}interview/{interview.id}/", - # "color": color, - # "extendedProps": { - # "candidate": interview.candidate.name, - # "email": interview.candidate.email, - # "status": interview.status, - # "meeting_id": interview.zoom_meeting.meeting_id - # if interview.zoom_meeting - # else None, - # "join_url": interview.zoom_meeting.join_url - # if interview.zoom_meeting - # else None, - # }, - # } - # ) + events.append( + { + "title": f"Interview: {interview.application.person.full_name}", + "start": start_datetime.isoformat(), + "end": end_datetime.isoformat(), + "url": f"{request.path}interview/{interview.id}/", + "color": color, + "extendedProps": { + "candidate": interview.application.person.full_name, + "email": interview.application.person.email, + "status": interview.interview.status, + "meeting_id": interview.interview.meeting_id + if interview.interview + else None, + "join_url": interview.interview.join_url + if interview.interview + else None, + }, + } + ) - # context = { - # "job": job, - # "events": events, - # "calendar_color": "#00636e", - # } + context = { + "job": job, + "events": events, + "calendar_color": "#00636e", + } - # return render(request, "recruitment/interview_calendar.html", context) + return render(request, "recruitment/interview_calendar.html", context) def user_profile_image_update(request, pk): @@ -2990,6 +2988,22 @@ def portal_password_reset(request, pk): for error in errors: messages.error(request, f"{field}: {error}") +@require_POST +def password_reset(request, slug): + from .forms import PersonPasswordResetForm + person = get_object_or_404(Person, slug=slug) + if request.method == "POST": + form = PersonPasswordResetForm(request.POST) + if form.is_valid(): + person.user.set_password(form.cleaned_data["new_password1"]) + person.user.save() + messages.success(request, "Password reset successfully.") + return redirect("person_detail", slug=person.slug) + else: + for field, errors in form.errors.items(): + for error in errors: + messages.error(request, f"{field}: {error}") + def portal_login(request): """Unified portal login for agency and applicant""" @@ -4506,7 +4520,7 @@ def source_list(request): """List all sources with search and pagination""" search_query = request.GET.get("q", "") sources = Source.objects.all() - + if search_query: sources = sources.filter( Q(name__icontains=search_query) diff --git a/templates/applicant/application_submit_form.html b/templates/applicant/application_submit_form.html index 5242007..cbb8b5c 100644 --- a/templates/applicant/application_submit_form.html +++ b/templates/applicant/application_submit_form.html @@ -877,33 +877,39 @@ } function renderCurrentStage() { - if (state.isPreview) { - renderPreview(); - return; - } + // Always show stage container and hide preview container initially + elements.stageContainer.style.display = 'block'; + elements.previewContainer.style.display = 'none'; - const currentStage = state.stages[state.currentStage]; - elements.stageContainer.innerHTML = ''; - elements.previewContainer.style.display = 'none'; + if (state.isPreview) { + renderPreview(); + return; + } - const stageTitle = document.createElement('h2'); - stageTitle.className = 'stage-title'; - stageTitle.textContent = currentStage.name; - elements.stageContainer.appendChild(stageTitle); + const currentStage = state.stages[state.currentStage]; + elements.stageContainer.innerHTML = ''; - currentStage.fields.forEach(field => { - const fieldElement = createFieldElement(field); - elements.stageContainer.appendChild(fieldElement); - }); + const stageTitle = document.createElement('h2'); + stageTitle.className = 'stage-title'; + stageTitle.textContent = currentStage.name; + elements.stageContainer.appendChild(stageTitle); + + currentStage.fields.forEach(field => { + const fieldElement = createFieldElement(field); + elements.stageContainer.appendChild(fieldElement); + }); + + // Update navigation buttons + elements.backBtn.style.display = state.currentStage > 0 ? 'flex' : 'none'; + elements.submitBtn.style.display = 'none'; + elements.nextBtn.style.display = 'flex'; + + // Fix: Update the Next button text correctly + elements.nextBtn.innerHTML = state.currentStage === state.stages.length - 1 ? + 'Preview ' : + 'Next '; +} - // Update navigation buttons - elements.backBtn.style.display = state.currentStage > 0 ? 'flex' : 'none'; - elements.submitBtn.style.display = 'none'; - elements.nextBtn.style.display = 'flex'; - elements.nextBtn.textContent = state.currentStage === state.stages.length - 1 ? - 'Preview' : - 'Next' - } function createFieldElement(field) { const fieldDiv = document.createElement('div'); @@ -1158,103 +1164,106 @@ } function renderPreview() { - elements.stageContainer.style.display = 'none'; - elements.previewContainer.style.display = 'block'; - elements.previewContent.innerHTML = ''; + elements.stageContainer.style.display = 'none'; + elements.previewContainer.style.display = 'block'; + elements.previewContent.innerHTML = ''; - // Add applicant info if available - if (state.formData.applicant_name || state.formData.applicant_email) { - const applicantDiv = document.createElement('div'); - applicantDiv.className = 'preview-item'; - applicantDiv.innerHTML = ` -
Applicant Information
-
- ${state.formData.applicant_name ? `Name: ${state.formData.applicant_name}
` : ''} - ${state.formData.applicant_email ? `Email: ${state.formData.applicant_email}` : ''} -
- `; - elements.previewContent.appendChild(applicantDiv); - } + // Add applicant info if available + if (state.formData.applicant_name || state.formData.applicant_email) { + const applicantDiv = document.createElement('div'); + applicantDiv.className = 'preview-item'; + applicantDiv.innerHTML = ` +
Applicant Information
+
+ ${state.formData.applicant_name ? `Name: ${state.formData.applicant_name}
` : ''} + ${state.formData.applicant_email ? `Email: ${state.formData.applicant_email}` : ''} +
+ `; + elements.previewContent.appendChild(applicantDiv); + } - // Add stage data - state.stages.forEach(stage => { - const stageDiv = document.createElement('div'); - stageDiv.className = 'preview-item'; + // Add stage data + state.stages.forEach(stage => { + const stageDiv = document.createElement('div'); + stageDiv.className = 'preview-item'; - const stageTitle = document.createElement('div'); - stageTitle.className = 'preview-label'; - stageTitle.textContent = stage.name; - stageDiv.appendChild(stageTitle); + const stageTitle = document.createElement('div'); + stageTitle.className = 'preview-label'; + stageTitle.textContent = stage.name; + stageDiv.appendChild(stageTitle); - const stageContent = document.createElement('div'); - stageContent.className = 'preview-value'; + const stageContent = document.createElement('div'); + stageContent.className = 'preview-value'; - stage.fields.forEach(field => { - let value = state.formData[field.id]; - if (value === undefined || value === null || value === '') { - value = 'Not provided'; - } else if (field.type === 'file' && value instanceof File) { - value = value.name; - } else if (field.type === 'checkbox' && Array.isArray(value)) { - value = value.join(', '); - } + stage.fields.forEach(field => { + let value = state.formData[field.id]; + if (value === undefined || value === null || value === '') { + value = 'Not provided'; + } else if (field.type === 'file' && value instanceof File) { + value = value.name; + } else if (field.type === 'checkbox' && Array.isArray(value)) { + value = value.join(', '); + } - const fieldDiv = document.createElement('div'); - fieldDiv.innerHTML = `${field.label}: ${value}`; - stageContent.appendChild(fieldDiv); - }); + const fieldDiv = document.createElement('div'); + fieldDiv.innerHTML = `${field.label}: ${value}`; + stageContent.appendChild(fieldDiv); + }); - stageDiv.appendChild(stageContent); - elements.previewContent.appendChild(stageDiv); - }); + stageDiv.appendChild(stageContent); + elements.previewContent.appendChild(stageDiv); + }); - // Update navigation buttons - elements.backBtn.style.display = 'flex'; - elements.nextBtn.style.display = 'none'; - elements.submitBtn.style.display = 'flex'; - } + // Update navigation buttons + elements.backBtn.style.display = 'flex'; + elements.nextBtn.style.display = 'none'; + elements.submitBtn.style.display = 'flex'; +} // Navigation Functions function nextStage() { - if (state.isPreview) { - submitForm(); - return; - } + if (state.isPreview) { + submitForm(); + return; + } - if (!validateCurrentStage()) { - // Scroll to first error - const firstError = document.querySelector('.error-message.show'); - if (firstError) { - firstError.scrollIntoView({ behavior: 'smooth', block: 'center' }); - } - return; - } + if (!validateCurrentStage()) { + // Scroll to first error + const firstError = document.querySelector('.error-message.show'); + if (firstError) { + firstError.scrollIntoView({ behavior: 'smooth', block: 'center' }); + } + return; + } - if (state.currentStage === state.stages.length - 1) { - // Go to preview - state.isPreview = true; - renderCurrentStage(); - updateProgress(); - } else { - // Go to next stage - state.currentStage++; - renderCurrentStage(); - updateProgress(); - } - } + if (state.currentStage === state.stages.length - 1) { + // Go to preview + state.isPreview = true; + renderCurrentStage(); + updateProgress(); + } else { + // Go to next stage + state.currentStage++; + renderCurrentStage(); + updateProgress(); + } +} - function prevStage() { - if (state.isPreview) { - // Go back to last stage - state.isPreview = false; - renderCurrentStage(); - updateProgress(); - } else if (state.currentStage > 0) { - state.currentStage--; - renderCurrentStage(); - updateProgress(); - } - } + function prevStage() { + if (state.isPreview) { + // Go back to last stage from preview + state.isPreview = false; + // Set to the last form stage + state.currentStage = state.stages.length - 1; + renderCurrentStage(); + updateProgress(); + } else if (state.currentStage > 0) { + // Go to previous stage + state.currentStage--; + renderCurrentStage(); + updateProgress(); + } +} // Initialize Application function init() { diff --git a/templates/applicant/job_application_detail.html b/templates/applicant/job_application_detail.html index 5412c05..dac4a07 100644 --- a/templates/applicant/job_application_detail.html +++ b/templates/applicant/job_application_detail.html @@ -41,7 +41,7 @@ {% trans "You already applied for this position" %} {% else %} - + {% trans "Apply for this Position" %} {% endif %} @@ -220,12 +220,10 @@ {% trans "You already applied for this position" %} {% else %} - + {% trans "Apply for this Position" %} {% endif %} {% endif %} - - {% endblock content%} \ No newline at end of file diff --git a/templates/base.html b/templates/base.html index 4eb2044..3095000 100644 --- a/templates/base.html +++ b/templates/base.html @@ -234,11 +234,7 @@ {% endif %} - -
  • - -
  • {% csrf_token %} diff --git a/templates/forms/form_templates_list.html b/templates/forms/form_templates_list.html index 8f9440e..035d5eb 100644 --- a/templates/forms/form_templates_list.html +++ b/templates/forms/form_templates_list.html @@ -231,7 +231,7 @@
    - + @@ -286,7 +286,7 @@ {{ template.updated_at|date:"M d, Y" }}
    - + diff --git a/templates/jobs/job_detail.html b/templates/jobs/job_detail.html index 6b0c0e8..304101e 100644 --- a/templates/jobs/job_detail.html +++ b/templates/jobs/job_detail.html @@ -147,6 +147,30 @@ {% block content %}
    + +
    {# Share Public Link Button #} @@ -223,7 +248,14 @@ {% endif %}
    - {% trans "Assigned to :" %} {{ job.assigned_to|default:"" }} + {% if job.assigned_to %} + + {% trans "Assigned to :" %} {{ job.assigned_to|default:"" }} + {% else %} + + {% endif %}
    @@ -333,7 +365,10 @@
    {% trans "Manage Applications" %} - + + {% trans "View Calendar" %} + + {% if not job.form_template.is_active %} {% if not jobzip_created %} @@ -369,10 +404,11 @@

    {% trans "Manage the custom application forms associated with this job posting." %}

    - -
    + {% if user.is_staff and user == application.job.assigned_to or user.is_superuser %} + {% trans "Manage Job Form" %} + {% endif %} {% comment %} {% if not job.form_template %} {% trans "Create New Form Template" %} @@ -412,31 +448,6 @@ {% endif %} - - - {% if not job.assigned_to %}
    diff --git a/templates/recruitment/applicant_signup.html b/templates/recruitment/applicant_signup.html index 11dadc8..c655b8b 100644 --- a/templates/recruitment/applicant_signup.html +++ b/templates/recruitment/applicant_signup.html @@ -231,7 +231,7 @@ {# Change Stage button #} - {% if user.is_staff %} + {% if user.is_staff and user == application.job.assigned_to or user.is_superuser %} diff --git a/templates/recruitment/applications_document_review_view.html b/templates/recruitment/applications_document_review_view.html index 5993b23..144f94c 100644 --- a/templates/recruitment/applications_document_review_view.html +++ b/templates/recruitment/applications_document_review_view.html @@ -402,7 +402,7 @@ data-bs-toggle="modal" data-bs-target="#noteModal" hx-get="{% url 'application_add_note' application.slug %}" - hx-swap="outerHTML" + hx-swap="innerHTML" hx-target=".notemodal"> Add note diff --git a/templates/recruitment/applications_exam_view.html b/templates/recruitment/applications_exam_view.html index 1fffb2c..2844bab 100644 --- a/templates/recruitment/applications_exam_view.html +++ b/templates/recruitment/applications_exam_view.html @@ -329,7 +329,7 @@ data-bs-toggle="modal" data-bs-target="#noteModal" hx-get="{% url 'application_add_note' application.slug %}" - hx-swap="outerHTML" + hx-swap="innerHTML" hx-target=".notemodal"> Add note diff --git a/templates/recruitment/applications_offer_view.html b/templates/recruitment/applications_offer_view.html index 2b91ec4..612f21a 100644 --- a/templates/recruitment/applications_offer_view.html +++ b/templates/recruitment/applications_offer_view.html @@ -358,7 +358,7 @@ data-bs-toggle="modal" data-bs-target="#noteModal" hx-get="{% url 'application_add_note' application.slug %}" - hx-swap="outerHTML" + hx-swap="innerHTML" hx-target=".notemodal"> Add note diff --git a/templates/recruitment/applications_screening_view.html b/templates/recruitment/applications_screening_view.html index 1c2e8b4..fddff0a 100644 --- a/templates/recruitment/applications_screening_view.html +++ b/templates/recruitment/applications_screening_view.html @@ -383,6 +383,9 @@ {% trans "GPA" %} + + {% trans "Years of Experience" %} + {% trans "AI Score" %} @@ -426,6 +429,11 @@
    {{application.person.gpa|default:"0"}} + + + {{ application.years_of_experience }} + + {% if application.is_resume_parsed %} {% if application.match_score %} @@ -473,7 +481,7 @@ data-bs-toggle="modal" data-bs-target="#noteModal" hx-get="{% url 'application_add_note' application.slug %}" - hx-swap="outerHTML" + hx-swap="innerHTML" hx-target=".notemodal"> Add note diff --git a/templates/user/settings.html b/templates/user/settings.html index 7ef4468..0fb7fb3 100644 --- a/templates/user/settings.html +++ b/templates/user/settings.html @@ -161,7 +161,7 @@
    - +
    @@ -199,25 +199,44 @@
    {% if not request.session.linkedin_authenticated %} - - - - - - - - {% else %} -

    - - {% trans "LinkedIn Connected" %} -

    - + + + + {% else %} +

    + + {% trans "LinkedIn Connected" %} +

    {% endif %}
    + + + {% endblock %} \ No newline at end of file