diff --git a/NorahUniversity/__pycache__/settings.cpython-312.pyc b/NorahUniversity/__pycache__/settings.cpython-312.pyc index 668c15d..7901847 100644 Binary files a/NorahUniversity/__pycache__/settings.cpython-312.pyc and b/NorahUniversity/__pycache__/settings.cpython-312.pyc differ diff --git a/recruitment/__pycache__/forms.cpython-312.pyc b/recruitment/__pycache__/forms.cpython-312.pyc index 1ae9473..cea0bd5 100644 Binary files a/recruitment/__pycache__/forms.cpython-312.pyc and b/recruitment/__pycache__/forms.cpython-312.pyc differ diff --git a/recruitment/__pycache__/models.cpython-312.pyc b/recruitment/__pycache__/models.cpython-312.pyc index 053cd40..fa57734 100644 Binary files a/recruitment/__pycache__/models.cpython-312.pyc and b/recruitment/__pycache__/models.cpython-312.pyc differ diff --git a/recruitment/__pycache__/urls.cpython-312.pyc b/recruitment/__pycache__/urls.cpython-312.pyc index 7b66768..0c55bfb 100644 Binary files a/recruitment/__pycache__/urls.cpython-312.pyc and b/recruitment/__pycache__/urls.cpython-312.pyc differ diff --git a/recruitment/__pycache__/utils.cpython-312.pyc b/recruitment/__pycache__/utils.cpython-312.pyc index 9bbe4cd..b9c315a 100644 Binary files a/recruitment/__pycache__/utils.cpython-312.pyc and b/recruitment/__pycache__/utils.cpython-312.pyc differ diff --git a/recruitment/__pycache__/views.cpython-312.pyc b/recruitment/__pycache__/views.cpython-312.pyc index f32524d..41776d0 100644 Binary files a/recruitment/__pycache__/views.cpython-312.pyc and b/recruitment/__pycache__/views.cpython-312.pyc differ diff --git a/recruitment/forms.py b/recruitment/forms.py index 9c3c193..30f1ca9 100644 --- a/recruitment/forms.py +++ b/recruitment/forms.py @@ -5,9 +5,11 @@ from django.forms.formsets import formset_factory from django.utils.translation import gettext_lazy as _ from crispy_forms.helper import FormHelper from crispy_forms.layout import Layout, Submit, Row, Column, Field, Div +from django.contrib.auth.models import User from .models import ( ZoomMeeting, Candidate,TrainingMaterial,JobPosting, - FormTemplate,InterviewSchedule,BreakTime,JobPostingImage + FormTemplate,InterviewSchedule,BreakTime,JobPostingImage, + Profile ) # from django_summernote.widgets import SummernoteWidget from django_ckeditor_5.widgets import CKEditor5Widget @@ -505,4 +507,16 @@ class ScheduleInterviewForCandiateForm(forms.ModelForm): 'end_time': forms.TimeInput(attrs={'type': 'time', 'class': 'form-control'}), 'interview_duration': forms.NumberInput(attrs={'class': 'form-control'}), 'buffer_time': forms.NumberInput(attrs={'class': 'form-control'}), - } \ No newline at end of file + } + +class ProfileImageUploadForm(forms.ModelForm): + class Meta: + model=Profile + fields=['profile_image'] + + + +# class UserEditForms(forms.ModelForm): +# class Meta: +# model = User +# fields = ['first_name', 'last_name'] \ No newline at end of file diff --git a/recruitment/models.py b/recruitment/models.py index be2c445..d204f3e 100644 --- a/recruitment/models.py +++ b/recruitment/models.py @@ -10,6 +10,7 @@ from django_countries.fields import CountryField from django.urls import reverse # from ckeditor.fields import RichTextField from django_ckeditor_5.fields import CKEditor5Field +from django.utils.html import strip_tags @@ -246,6 +247,43 @@ class JobPosting(Base): "form_wizard", kwargs={"slug": self.form_template.slug} ) self.save() + + def _check_content(self, field_value): + """Helper to check if a field contains meaningful content.""" + if not field_value: + return False + + # 1. Replace the common HTML non-breaking space entity with a standard space. + content = field_value.replace(' ', ' ') + + # 2. Remove all HTML tags (leaving only text and remaining spaces). + stripped = strip_tags(content) + + # 3. Use .strip() to remove ALL leading/trailing whitespace, including the ones from step 1. + final_content = stripped.strip() + + # Return True if any content remains after stripping tags and spaces. + return bool(final_content) + + + @property + def has_description_content(self): + """Returns True if the description field has meaningful content.""" + return self._check_content(self.description) + + @property + def has_qualifications_content(self): + """Returns True if the qualifications field has meaningful content.""" + return self._check_content(self.qualifications) + + # Add similar properties for benefits and application_instructions + @property + def has_benefits_content(self): + return self._check_content(self.benefits) + + @property + def has_application_instructions_content(self): + return self._check_content(self.application_instructions) class JobPostingImage(models.Model): diff --git a/recruitment/urls.py b/recruitment/urls.py index 4aefef5..4bc00ac 100644 --- a/recruitment/urls.py +++ b/recruitment/urls.py @@ -107,5 +107,7 @@ urlpatterns = [ # users urls - path('user/',views.user_detail,name='user_detail') + path('user/',views.user_detail,name='user_detail'), + path('user/user_profile_image_update/',views.user_profile_image_update,name='user_profile_image_update'), + ] diff --git a/recruitment/views.py b/recruitment/views.py index 58b4be2..68d11b1 100644 --- a/recruitment/views.py +++ b/recruitment/views.py @@ -19,7 +19,9 @@ from .forms import ( FormTemplateForm, InterviewScheduleForm,JobPostingStatusForm, BreakTimeFormSet, - JobPostingImageForm + JobPostingImageForm, + ProfileImageUploadForm, + ) from rest_framework import viewsets from django.contrib import messages @@ -51,7 +53,8 @@ from .models import ( ZoomMeeting, Candidate, JobPosting, - ScheduledInterview + ScheduledInterview, + JobPostingImage ) import logging from datastar_py.django import ( @@ -316,7 +319,7 @@ def job_detail(request, slug): status_form = JobPostingStatusForm(instance=job) - image_upload_form=JobPostingImageForm(instance=job) + image_upload_form=JobPostingImageForm(instance=job.post_images) @@ -334,6 +337,7 @@ def job_detail(request, slug): return redirect('job_detail', slug=slug) else: + messages.error(request, "Failed to update status due to validation errors.") @@ -355,19 +359,32 @@ def job_detail(request, slug): def job_image_upload(request, slug): #only for handling the post request job=get_object_or_404(JobPosting,slug=slug) - if request.method=='POST': - image_upload_form=JobPostingImageForm(request.POST,request.FILES) + try: + instance = JobPostingImage.objects.get(job=job) + except JobPostingImage.DoesNotExist: + # If it doesn't exist, create a new instance placeholder + instance = None + + if request.method == 'POST': + # Pass the existing instance to the form if it exists + image_upload_form = JobPostingImageForm(request.POST, request.FILES, instance=instance) + if image_upload_form.is_valid(): - image_upload_form = image_upload_form.save(commit=False) - - image_upload_form.job = job - image_upload_form.save() - messages.success(request, f"Image uploaded successfully for {job.title}.") - return redirect('job_detail', slug=job.slug) + + # If creating a new one (instance is None), set the job link manually + if instance is None: + image_instance = image_upload_form.save(commit=False) + image_instance.job = job + image_instance.save() + messages.success(request, f"Image uploaded successfully for {job.title}.") + else: + # If updating, the form will update the instance passed to it + image_upload_form.save() + messages.success(request, f"Image updated successfully for {job.title}.") + else: - + messages.error(request, "Image upload failed: Please ensure a valid image file was selected.") - return redirect('job_detail', slug=job.slug) return redirect('job_detail', slug=job.slug) @@ -387,7 +404,7 @@ def kaauh_career(request): # job detail facing the candidate: def job_detail_candidate(request, slug): job = get_object_or_404(JobPosting, slug=slug) - return render(request, "jobs/job_detail_candidate.html", {"job": job}) + return render(request, "forms/job_detail_candidate.html", {"job": job}) def post_to_linkedin(request, slug): @@ -2337,6 +2354,43 @@ def schedule_meeting_for_candidate(request, slug, candidate_pk): -def user_detail(requests,pk): - user=get_object_or_404(User,pk=pk) - return render(requests,'user/profile.html') +def user_profile_image_update(request, pk): + user = get_object_or_404(User, pk=pk) + + if request.method == 'POST': + profile_form = ProfileImageUploadForm(request.POST, request.FILES, instance=user.profile) + if profile_form.is_valid(): + profile_form.save() + messages.success(request, 'Image uploaded successfully') + return redirect('user_detail', pk=user.pk) + else: + messages.error(request, 'An error occurred while uploading the image') + else: + profile_form = ProfileImageUploadForm(instance=user.profile) + + context = { + 'profile_form': profile_form, + 'user': user, + } + return render(request, 'user/profile.html', context) + + + +def user_detail(request, pk): + user = get_object_or_404(User, pk=pk) + profile_form = ProfileImageUploadForm(instance=user.profile) + if request.method == 'POST': + first_name=request.POST.get('first_name') + last_name=request.POST.get('last_name') + if first_name: + user.first_name=first_name + if last_name: + user.last_name=last_name + user.save() + context = { + + 'user': user, + 'profile_form':profile_form + + } + return render(request, 'user/profile.html', context) diff --git a/static/css/style.css b/static/css/style.css deleted file mode 100644 index 24bdb3f..0000000 --- a/static/css/style.css +++ /dev/null @@ -1,947 +0,0 @@ - -/* Custom CSS for NorahUniversity ATS */ -/* Keep only essential custom styles that Bootstrap doesn't handle */ - -/* Primary Brand Color */ -:root { - --primary-color: #1b8354; - --primary-hover: #155f3e; -} - -/* Header and Navigation */ -.header { - background-color: white !important; - box-shadow: 0 2px 4px rgba(0,0,0,0.1) !important; - border-bottom: 1px solid #e0e0e0 !important; -} - -.navbar { - background-color: white !important; - border-bottom: 1px solid #e0e0e0 !important; -} - -.nav-link.active { - background-color: var(--primary-color) !important; - color: white !important; -} - -.nav-link:hover { - background-color: var(--primary-color) !important; - color: white !important; -} - -.logo { - font-size: 1.5rem; - font-weight: 700; - color: var(--primary-color) !important; -} - -/* Buttons - Override Bootstrap primary color */ -.btn-primary { - background-color: var(--primary-color) !important; - border-color: var(--primary-color) !important; -} - -.btn-primary:hover { - background-color: var(--primary-hover) !important; - border-color: var(--primary-hover) !important; -} - -.btn-outline-primary { - border-color: var(--primary-color) !important; - color: var(--primary-color) !important; -} - -.btn-outline-primary:hover { - background-color: var(--primary-color) !important; - border-color: var(--primary-color) !important; - color: white !important; -} - -/* Cards */ -.card { - border: 1px solid #e0e0e0 !important; - box-shadow: 0 4px 12px rgba(0, 0, 0, 0.05) !important; -} - -.card-header { - border-bottom: 1px solid #e0e0e0 !important; - background-color: white !important; -} - -/* Table Improvements */ -.table-hover tbody tr:hover { - background-color: rgba(27, 131, 84, 0.05) !important; -} - -/* Custom Badge Colors */ -.badge.bg-success { - background-color: #28a745 !important; -} - -.badge.bg-warning { - background-color: #ffc107 !important; -} - -/* Form Improvements */ -.form-control:focus { - border-color: var(--primary-color) !important; - box-shadow: 0 0 0 0.2rem rgba(27, 131, 84, 0.25) !important; -} - -/* Responsive adjustments */ -@media (max-width: 768px) { - .nav-list { - flex-direction: column; - } - - .nav-item { - margin: 0; - } -} - -/* Utility classes */ -.text-primary-custom { - color: var(--primary-color) !important; -} - -.bg-primary-custom { - background-color: var(--primary-color) !important; -} - -.border-primary-custom { - border-color: var(--primary-color) !important; -} - -/* Loading states */ -.loading { - opacity: 0.6; - pointer-events: none; -} - -/* Icon Styling */ -.heroicon { - width: 1rem; - height: 1rem; - margin-right: 0.5rem; - vertical-align: middle; - flex-shrink: 0; -} - -.size-6 { - width: 1.5rem !important; - height: 1.5rem !important; -} - -/* Responsive icon sizing */ -.icon-sm { - width: 0.875rem !important; - height: 0.875rem !important; - margin-right: 0.375rem !important; -} - -.icon-md { - width: 1.125rem !important; - height: 1.125rem !important; - margin-right: 0.625rem !important; -} - -.icon-lg { - width: 1.5rem !important; - height: 1.5rem !important; - margin-right: 0.75rem !important; -} - -.icon-xl { - width: 2rem !important; - height: 2rem !important; - margin-right: 1rem !important; -} - -/* Context-specific icon adjustments */ -.btn-sm .heroicon, -.btn-sm .size-6, -.btn-sm .icon-md { - width: 0.875rem !important; - height: 0.875rem !important; - margin-right: 0.375rem !important; -} - -.nav-link .heroicon, -.nav-link .size-6 { - width: 1.25rem !important; - height: 1.25rem !important; - margin-right: 0.5rem !important; -} - -.card-header .heroicon, -.card-header .size-6 { - width: 1.375rem !important; - height: 1.375rem !important; - margin-right: 0.625rem !important; -} - -/* Print styles */ -@media print { - .navbar, - .header, - .btn, - .pagination { - display: none !important; - } - - .card { - box-shadow: none !important; - border: 1px solid #ccc !important; - } -} - -/* Responsive adjustments for icons */ -@media (max-width: 768px) { - .nav-link .heroicon, - .nav-link .size-6 { - width: 1rem !important; - height: 1rem !important; - margin-right: 0.375rem !important; - } - - .card-header .heroicon, - .card-header .size-6 { - width: 1.125rem !important; - height: 1.125rem !important; - margin-right: 0.5rem !important; - } -} - -/* Header and Search Enhancements */ -.card-header { - background-color: white !important; - border-bottom: 1px solid #e0e0e0 !important; - padding: 1.25rem 1.5rem !important; -} - -.card-header h1, -.card-header h2, -.card-header h3 { - margin-bottom: 0 !important; - font-weight: 600 !important; -} - -.card-header h1.h3, -.card-header h2.h3, -.card-header h3.h3 { - font-size: 1.25rem !important; -} - -/* Search Form Enhancements */ -.search-form-container { - display: flex; - align-items: center; - gap: 0.75rem; - flex-wrap: wrap; -} - -.input-group { - flex: 1; - min-width: 250px; -} - -.input-group-text { - background-color: #f8f9fa !important; - border: 1px solid #ced4da !important; - /* border-right: none !important; */ - color: #495057 !important; - transition: all 0.2s ease !important; -} - -.input-group-text:hover { - background-color: #e9ecef !important; -} - -.input-group-text .heroicon { - width: 1rem !important; - height: 1rem !important; - margin-right: 0 !important; -} - - -.form-control:focus { - border-color: var(--primary-color) !important; - box-shadow: 0 0 0 0.2rem rgba(27, 131, 84, 0.15) !important; -} - -.form-control:focus + .input-group-text { - border-color: var(--primary-color) !important; - background-color: rgba(27, 131, 84, 0.05) !important; -} - -/* Button Group Enhancements */ -.d-flex.gap-2 .btn { - white-space: nowrap !important; - transition: all 0.2s ease !important; -} - -.d-flex.gap-2 .btn:hover { - transform: translateY(-1px) !important; -} - -.d-flex.gap-2 .btn svg { - margin-right: 0.375rem !important; -} - -/* Responsive Header Adjustments */ -@media (max-width: 768px) { - .card-header { - padding: 1rem 1.25rem !important; - } - - .card-header h1.h3, - .card-header h2.h3, - .card-header h3.h3 { - font-size: 1.125rem !important; - } - - .input-group { - min-width: 200px !important; - max-width: 100% !important; - } - - .d-flex.gap-2 { - flex-wrap: wrap !important; - gap: 0.5rem !important; - } - - .d-flex.gap-2 .btn { - flex: 1 !important; - min-width: 120px !important; - } -} - -@media (max-width: 576px) { - .card-header { - padding: 0.875rem 1rem !important; - } - - .input-group { - min-width: 100% !important; - } - - .d-flex.gap-2 .btn { - font-size: 0.875rem !important; - padding: 0.375rem 0.75rem !important; - } -} - -/* Search Input Placeholder */ -.form-control::placeholder { - color: #6c757d !important; - opacity: 0.7 !important; -} - -/* Enhanced Focus States */ -.form-control:focus::placeholder { - opacity: 1 !important; -} - -/* Detail Page Enhancements */ -.detail-page-header { - background: linear-gradient(135deg, var(--primary-color) 0%, rgba(27, 131, 84, 0.1) 100%); - border-bottom: 3px solid var(--primary-color) !important; -} - -.detail-page-header h1 { - color: var(--primary-color) !important; -} - -/* Information Cards Enhancement */ -.info-card { - background: #f8f9fa; - border-left: 4px solid var(--primary-color); - border-radius: 0.375rem; - padding: 1rem; - margin-bottom: 1rem; - transition: all 0.3s ease; -} - -.info-card:hover { - background: #e9ecef; - transform: translateY(-2px); - box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1); -} - -.info-card .info-label { - font-size: 0.875rem; - color: #6c757d; - text-transform: uppercase; - letter-spacing: 0.5px; - font-weight: 600; - margin-bottom: 0.25rem; -} - -.info-card .info-value { - font-size: 1rem; - color: #212529; - font-weight: 500; -} - -/* Action Cards Enhancement */ -.action-card { - text-align: center; - padding: 2rem; - background: linear-gradient(135deg, #f8f9fa 0%, #e9ecef 100%); - border-radius: 0.75rem; - transition: all 0.3s ease; -} - -.action-card:hover { - transform: translateY(-3px); - box-shadow: 0 6px 20px rgba(0, 0, 0, 0.1); -} - -/* Resume File Display */ -.resume-file { - background: #f8f9fa; - border: 1px solid #dee2e6; - border-radius: 0.5rem; - padding: 1rem; - transition: all 0.2s ease; -} - -.resume-file:hover { - background: #e9ecef; - border-color: var(--primary-color); -} - -.resume-file .file-name { - font-weight: 600; - color: #495057; - margin-bottom: 0.25rem; -} - -.resume-file .file-info { - font-size: 0.875rem; - color: #6c757d; -} - -/* Parsed Data Grid Enhancement */ -.parsed-data-grid { - display: grid; - grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); - gap: 1rem; - margin-top: 1rem; -} - -.parsed-data-item { - background: white; - border: 1px solid #e0e0e0; - border-radius: 0.5rem; - padding: 1rem; - transition: all 0.2s ease; -} - -.parsed-data-item:hover { - border-color: var(--primary-color); - box-shadow: 0 2px 8px rgba(27, 131, 84, 0.1); -} - -.parsed-data-item .data-key { - font-size: 0.75rem; - text-transform: uppercase; - color: var(--primary-color); - font-weight: 600; - letter-spacing: 0.5px; - margin-bottom: 0.5rem; -} - -.parsed-data-item .data-value { - font-size: 0.875rem; - color: #495057; - line-height: 1.4; -} - -/* Status Badge Enhancement */ -.status-badge { - display: inline-flex; - align-items: center; - gap: 0.375rem; - padding: 0.375rem 0.75rem; - border-radius: 2rem; - font-size: 0.875rem; - font-weight: 600; - transition: all 0.2s ease; -} - -.status-badge .heroicon { - width: 1rem !important; - height: 1rem !important; -} - -/* Contact Information Enhancement */ -.contact-info-item { - padding: 0.75rem 1rem; - margin-bottom: 0.5rem; - background: white; - border-radius: 0.5rem; - border: 1px solid #e0e0e0; - transition: all 0.2s ease; -} - -.contact-info-item:hover { - background: #f8f9fa; - border-color: var(--primary-color); - box-shadow: 0 2px 8px rgba(27, 131, 84, 0.1); -} - -.contact-info-item .contact-label { - font-size: 0.75rem; - text-transform: uppercase; - color: #6c757d; - font-weight: 600; - letter-spacing: 0.5px; -} - -.contact-info-item .contact-value { - font-size: 1rem; - color: #212529; - font-weight: 500; - margin-top: 0.125rem; -} - -/* Responsive Detail Page Adjustments */ -@media (max-width: 768px) { - .detail-page-header { - background: linear-gradient(135deg, var(--primary-color) 0%, rgba(27, 131, 84, 0.05) 100%); - border-bottom: 2px solid var(--primary-color); - } - - .detail-page-header h1 { - font-size: 1.5rem !important; - } - - .info-card { - padding: 0.75rem; - margin-bottom: 0.75rem; - } - - .parsed-data-grid { - grid-template-columns: 1fr; - } - - .contact-info-item { - padding: 0.5rem 0.75rem; - } - - .action-card { - padding: 1.5rem; - } -} - -@media (max-width: 576px) { - .detail-page-header h1 { - font-size: 1.25rem !important; - } - - .info-card .info-label { - font-size: 0.75rem; - } - - .info-card .info-value { - font-size: 0.875rem; - } - - .status-badge { - font-size: 0.75rem; - padding: 0.25rem 0.5rem; - } -} - -/* Loading Animation for Detail Pages */ -.detail-loading { - display: inline-block; - width: 20px; - height: 20px; - border: 3px solid rgba(255, 255, 255, 0.3); - border-radius: 50%; - border-top-color: white; - animation: spin 1s ease-in-out infinite; -} - -@keyframes spin { - to { transform: rotate(360deg); } -} - -/* Print Styles for Detail Pages */ -@media print { - .detail-page-header { - background: white !important; - border: 2px solid #dee2e6 !important; - } - - .detail-page-header h1 { - color: #212529 !important; - } - - .contact-info-item, - .info-card, - .parsed-data-item { - border: 1px solid #dee2e6 !important; - break-inside: avoid; - } - - .btn, - .action-card { - display: none !important; - } -} - -/* Form and Update Page Enhancements */ -.form-page-header { - background: linear-gradient(135deg, var(--primary-color) 0%, rgba(27, 131, 84, 0.1) 100%); - border-bottom: 3px solid var(--primary-color) !important; -} - -.form-page-header h1 { - color: var(--primary-color) !important; -} - -.form-page-header p { - color: rgba(27, 131, 84, 0.8) !important; -} - -/* Form Section Enhancement */ -.form-section { - background: #f8f9fa; - border-left: 4px solid var(--primary-color); - border-radius: 0.375rem; - padding: 1.5rem; - margin-bottom: 2rem; - transition: all 0.3s ease; -} - -.form-section:hover { - background: #e9ecef; - transform: translateY(-2px); - box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1); -} - -.form-section h5 { - color: var(--primary-color); - font-weight: 600; - margin-bottom: 1rem; - display: flex; - align-items: center; - gap: 0.5rem; -} - -.form-section .section-icon { - width: 1.25rem !important; - height: 1.25rem !important; -} - -/* Form Field Enhancement */ -.form-field-wrapper { - position: relative; - margin-bottom: 1.5rem; -} - -.form-field-wrapper label { - font-weight: 600; - color: #495057; - margin-bottom: 0.5rem; - display: flex; - align-items: center; - gap: 0.5rem; -} - -.form-field-wrapper .required-indicator { - color: #dc3545; - font-size: 0.875rem; - font-weight: bold; -} - -.form-field-wrapper .field-icon { - position: absolute; - right: 1rem; - top: 2.5rem; - color: #6c757d; - pointer-events: none; -} - -.form-control, -.form-select { - border: 2px solid #e0e0e0; - border-radius: 0.5rem; - padding: 0.75rem 1rem; - font-size: 1rem; - transition: all 0.2s ease; -} - -.form-control:focus, -.form-select:focus { - border-color: var(--primary-color); - box-shadow: 0 0 0 0.2rem rgba(27, 131, 84, 0.15); - color: #212529; - background-color: #fff; -} - -.form-control.is-invalid, -.form-select.is-invalid { - border-color: #dc3545; - padding-right: 2.5rem; - background-image: none; -} - -.form-control.is-valid, -.form-select.is-valid { - border-color: #28a745; - padding-right: 2.5rem; - background-image: none; -} - -.form-text { - font-size: 0.875rem; - color: #6c757d; - margin-top: 0.25rem; -} - -.invalid-feedback, -.valid-feedback { - font-size: 0.875rem; - margin-top: 0.25rem; -} - -.invalid-feedback { - color: #dc3545; - display: block; -} - -.valid-feedback { - color: #28a745; - display: block; -} - -/* Form Enhancement for Special Fields */ -.form-check-input { - width: 1.25rem; - height: 1.25rem; - margin-top: 0.25rem; -} - -.form-check-input:checked { - background-color: var(--primary-color); - border-color: var(--primary-color); -} - -.form-check-label { - font-weight: 500; - color: #495057; -} - -/* Action Buttons Enhancement */ -.form-action-buttons { - background: white; - border: 1px solid #e0e0e0; - border-radius: 0.75rem; - padding: 1.5rem; - margin-top: 2rem; - box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05); -} - -.form-action-buttons .btn { - min-width: 120px; - font-weight: 500; - transition: all 0.2s ease; -} - -.form-action-buttons .btn:hover { - transform: translateY(-1px); - box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15); -} - -/* Responsive Form Adjustments */ -@media (max-width: 768px) { - .form-section { - padding: 1rem; - margin-bottom: 1.5rem; - } - - .form-section h5 { - font-size: 1.1rem; - margin-bottom: 0.75rem; - } - - .form-control, - .form-select { - font-size: 0.875rem; - padding: 0.625rem 0.75rem; - } - - .form-field-wrapper label { - font-size: 0.875rem; - } - - .form-text, - .invalid-feedback, - .valid-feedback { - font-size: 0.75rem; - } - - .form-action-buttons { - padding: 1rem; - margin-top: 1.5rem; - } - - .form-action-buttons .btn { - min-width: 100px; - font-size: 0.875rem; - padding: 0.5rem 1rem; - } -} - -@media (max-width: 576px) { - .form-section { - padding: 0.75rem; - margin-bottom: 1rem; - } - - .form-section h5 { - font-size: 1rem; - } - - .form-control, - .form-select { - font-size: 0.813rem; - padding: 0.5rem 0.625rem; - } - - .form-field-wrapper { - margin-bottom: 1rem; - } - - .form-action-buttons { - flex-direction: column !important; - gap: 0.5rem !important; - } - - .form-action-buttons .btn { - min-width: auto; - width: 100%; - } -} - -/* Loading State for Forms */ -.form-loading { - position: relative; - pointer-events: none; - opacity: 0.6; -} - -.form-loading::after { - content: ''; - position: absolute; - top: 0; - left: 0; - right: 0; - bottom: 0; - background: rgba(255, 255, 255, 0.8); - z-index: 1; -} - -.form-loading .spinner { - position: absolute; - top: 50%; - left: 50%; - transform: translate(-50%, -50%); - z-index: 2; -} - -/* Print Styles for Forms */ -@media print { - .form-page-header, - .form-section, - .form-action-buttons { - border: 1px solid #dee2e6 !important; - background: white !important; - box-shadow: none !important; - } - - .form-section { - break-inside: avoid; - page-break-inside: avoid; - } - - .btn { - display: none !important; - } - - .form-control, - .form-select { - border: 1px solid #000 !important; - background: white !important; - } -} - -/* File Upload Enhancement */ -.form-control[type="file"] { - padding: 0.5rem; - border: 2px dashed #dee2e6; - background: #f8f9fa; - transition: all 0.2s ease; -} - -.form-control[type="file"]:hover { - border-color: var(--primary-color); - background: #f0f8f4; -} - -.form-control[type="file"]:focus { - border-color: var(--primary-color); - background: white; - box-shadow: 0 0 0 0.2rem rgba(27, 131, 84, 0.15); -} - -/* Checkbox and Radio Enhancement */ -.form-check-input:checked ~ .form-check-label::before { - background-color: var(--primary-color); - border-color: var(--primary-color); -} - -.form-check-input:focus ~ .form-check-label::before { - box-shadow: 0 0 0 0.2rem rgba(27, 131, 84, 0.25); -} - -/* Help Text Enhancement */ -.help-text { - font-size: 0.813rem; - color: #6c757d; - margin-top: 0.25rem; - display: flex; - align-items: center; - gap: 0.25rem; -} - -.help-text .help-icon { - width: 1rem !important; - height: 1rem !important; - color: #6c757d; -} - -/* Error State Enhancement */ -.field-error { - border-color: #dc3545 !important; - background-color: #fff5f5 !important; -} - -.field-error:focus { - box-shadow: 0 0 0 0.2rem rgba(220, 53, 69, 0.25) !important; -} - -/* Success State Enhancement */ -.field-success { - border-color: #28a745 !important; - background-color: #f8fff9 !important; -} - -.field-success:focus { - box-shadow: 0 0 0 0.2rem rgba(40, 167, 69, 0.25) !important; -} diff --git a/templates/base.html b/templates/base.html index ffe4948..3738bdf 100644 --- a/templates/base.html +++ b/templates/base.html @@ -1,5 +1,6 @@ -{% load i18n static %} -{% load partials %} +{% load static i18n %} +{% get_current_language as LANGUAGE_CODE %} + @@ -8,12 +9,17 @@ {% block title %}{% trans 'University ATS' %}{% endblock %} - + {% comment %} Load the correct Bootstrap CSS file for RTL/LTR {% endcomment %} + {% if LANGUAGE_CODE == 'ar' %} + + {% else %} + + {% endif %} + - {% comment %} - {% endcomment %} + {% block customCSS %}{% endblock %} @@ -21,218 +27,219 @@
- -
-
- {% trans 'Saudi Vision 2030' %} -
-
-
جامعة الأميرة نورة بنت عبدالرحمن الأكاديمية
-
ومستشفى الملك عبدالله بن عبدالعزيز التخصصي
-
Princess Nourah bint Abdulrahman University
-
King Abdullah bin Abdulaziz University Hospital
+
+
+
+
+ {% trans 'Saudi Vision 2030' %} + +
+
+
جامعة الأميرة نورة بنت عبدالرحمن الأكاديمية
+
ومستشفى الملك عبدالله بن عبدالرحمن التخصصي
+
Princess Nourah bint Abdulrahman University
+
King Abdullah bin Abdulaziz University Hospital
-
- KAAUH Logo + KAAUH Logo
@@ -272,43 +279,24 @@ {% include 'includes/delete_modal.html' %} + - - {% comment %} {% endcomment %} - {% block customJS %}{% endblock %} diff --git a/templates/forms/form_wizard.html b/templates/forms/form_wizard.html index e13ec75..655db00 100644 --- a/templates/forms/form_wizard.html +++ b/templates/forms/form_wizard.html @@ -1,40 +1,6 @@ +{% extends 'forms/partials/candidate_facing_base.html'%} {% load static i18n %} - - - - - - {% translate "Application Form" %} - - - - - - +
- + - - +{% endblock content %} \ No newline at end of file diff --git a/templates/forms/job_detail_candidate.html b/templates/forms/job_detail_candidate.html new file mode 100644 index 0000000..7bb8472 --- /dev/null +++ b/templates/forms/job_detail_candidate.html @@ -0,0 +1,88 @@ +{% extends 'forms/partials/candidate_facing_base.html'%} +{% load static i18n %} +{% block content %} + + + +
+
+ +
+
+
+
{% trans "Ready to Apply?" %}
+
+
+

{% trans "Review the job details, then apply below." %}

+ + {% if job.form_template %} + + {% trans "Apply for this Position" %} + + {% endif %} +
+
+
+ +
+
+
+

{{ job.title }}

+
+ +
+ +

{% trans "Job Overview" %}

+
+ {% if job.salary_range %} +
+ + {% trans "Salary:" %} + {{ job.salary_range }} +
+ {% endif %} + +
+ + {% trans "Deadline:" %} + {% if job.application_deadline %} + {{ job.application_deadline|date:"M d, Y" }} + {% if job.is_expired %} + {% trans "EXPIRED" %} + {% endif %} + {% else %} + {% trans "Not specified" %} + {% endif %} +
+ +
{% trans "Job Type:" %} {{ job.get_job_type_display }}
+
{% trans "Location:" %} {{ job.get_location_display }}
+
{% trans "Department:" %} {{ job.department|default:"N/A" }}
+
{% trans "JOB ID:" %} {{ job.internal_job_id|default:"N/A" }}
+
{% trans "Workplace:" %} {{ job.get_workplace_type_display }}
+
+ + {% if job.has_description_content %}
{% trans "Job Description" %}
{{ job.description|safe }}
{% endif %} + {% if job.has_qualifications_content %}
{% trans "Qualifications" %}
{{ job.qualifications|safe }}
{% endif %} + {% if job.has_benefits_content %}
{% trans "Benefits" %}
{{ job.benefits|safe }}
{% endif %} + {% if job.has_application_instructions_content %}
{% trans "Application Instructions" %}
{{ job.application_instructions|safe }}
{% endif %} + + +
+
+
+
+
+ +
+ {% if job.form_template %} + + {% trans "Apply for this Position" %} + + {% endif %} +
+{% endblock content%} \ No newline at end of file diff --git a/templates/forms/partials/candidate_facing_base.html b/templates/forms/partials/candidate_facing_base.html new file mode 100644 index 0000000..c171571 --- /dev/null +++ b/templates/forms/partials/candidate_facing_base.html @@ -0,0 +1,265 @@ +{% load static i18n %} +{% get_current_language as LANGUAGE_CODE %} +{% get_available_languages as LANGUAGES %} +{% get_language_info_list for LANGUAGES as language_info_list %} + + + + + + + {% translate "Application Form" %} + + {% comment %} Load the correct Bootstrap CSS file for RTL/LTR {% endcomment %} + {% if LANGUAGE_CODE == 'ar' %} + + {% else %} + + {% endif %} + + + + + + + + {% block content %} + + {% endblock content %} + + + \ No newline at end of file diff --git a/templates/includes/language_options.html b/templates/includes/language_options.html new file mode 100644 index 0000000..f9f7bcf --- /dev/null +++ b/templates/includes/language_options.html @@ -0,0 +1,18 @@ +{% load i18n %} + +
  • +
    {% csrf_token %} + + +
    +
  • +
  • +
    {% csrf_token %} + + +
    +
  • \ No newline at end of file diff --git a/templates/jobs/base_public.html b/templates/jobs/base_public.html index 80b2721..e69de29 100644 --- a/templates/jobs/base_public.html +++ b/templates/jobs/base_public.html @@ -1,628 +0,0 @@ -{% load i18n static %} - - - - - - {% block title %}{% trans 'Careers' %} - KAAUH{% endblock %} - - - - - - {% block customCSS %}{% endblock %} - - - - -
    -
    - - - -
    -
    -
    - -
    - 24/7 {% trans "Online Support" %} - info@kaauh.edu.sa -
    -
    - -
    - -
    - {% trans "Contact Us Free" %} - +966118200000 -
    -
    -
    - - -
    - -
    - -
    -
    - -
    - -
    -
    -
    جامعة الأميرة نورة بنت عبدالرحمن الأكاديمية
    -
    ومستشفى الملك عبدالله بن عبدالعزيز التخصصي
    -
    Princess Nourah bint Abdulrahman University
    -
    King Abdullah bin Abdulaziz University Hospital
    -
    - KAAUH Logo -
    -
    -
    -
    -
    - - - - - -
    - {% block content %}{% endblock %} -
    - - - - - - {% block customJS %}{% endblock %} - - \ No newline at end of file diff --git a/templates/jobs/career.html b/templates/jobs/career.html index 0c1d06c..62768fc 100644 --- a/templates/jobs/career.html +++ b/templates/jobs/career.html @@ -1,154 +1,568 @@ -{% extends "jobs/base_public.html" %} -{% load i18n static %} + + + + + + + + + + + King Abdullah bin Abdulaziz University Hospital + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    +
    +
    +
    +
    +
    +
    +
    + + +
    + ع +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +
    +
    +
    + +
    +
    + +
    +
    +
    +
    + +
    +
    +
    +
    + + +
    + +
    +
    +
    + {% load i18n %} +
    + + + + + + + + + + + + + {% for job in active_jobs %} + + {# The columns below directly correspond to the headers above #} + + + + + + + + {# Hidden on small screens to prioritize space #} + + + {# Hidden only on extra-small screens #} + + + + + {% endfor %} + + +
    {% trans 'Job ID#' %}{% trans 'Job Title' %} {% trans 'Hiring' %} {% trans 'Posting Date' %} {% trans 'Apply Before' %} {% trans 'Link' %}
    {{ job.internal_job_id }}{{ job.title }}{{ job.open_positions }}{{ job.application_start_date }}{{ job.application_deadline }}
    +
    + -{% block customCSS %} -{% endblock %} - -{% block content %} -
    -
    -
    -

    - {% trans "Home Page" %} -

    -

    - {% if LANGUAGE_CODE == 'ar' %}التوظيف{% else %}Careers{% endif %} -

    -
    -
    - -
    -
    -

    - {% trans "Open Positions" %} -

    - -
    - - - - - - - - - - - - - {% comment %} Django loop would typically go here: {% for job in jobs %} {% endcomment %} - {% for job in active_jobs %} - - - - - - - - - {% endfor %} - - -
    {% trans "Job ID" %}{% trans "Job Title" %}{% trans "Hiring" %}{% trans "Posting Date" %}{% trans "Apply Before" %}{% trans "Apply" %}
    {{job.internal_job_id}}{{job.title}}{{job.open_positions}}{{job.application_start_date}}{{job.application_deadline}}
    -
    - -
    +
    + +
    +
    +
    + +
    +
    + +
    +
    + +
    +
    + +
    +
    + +
    +
    + + + + +
    +
      +
    • Need Help
    • +
    +
    - -{% endblock %} \ No newline at end of file + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/templates/jobs/job_detail.html b/templates/jobs/job_detail.html index d0c57cb..1d0ff7b 100644 --- a/templates/jobs/job_detail.html +++ b/templates/jobs/job_detail.html @@ -280,10 +280,10 @@ {% trans "Updated At:" %} {{ job.updated_at|default:"N/A" }}
    -
    +
    -
    {% trans "Financial & Timeline" %}
    diff --git a/templates/jobs/job_detail_candidate.html b/templates/jobs/job_detail_candidate.html deleted file mode 100644 index f74189d..0000000 --- a/templates/jobs/job_detail_candidate.html +++ /dev/null @@ -1,260 +0,0 @@ -{% load static i18n %} - - - - - - {% translate "Application Form" %} - - - - - - - - - - -
    -
    - -
    -
    -
    -
    Ready to Apply?
    -
    -
    -

    Review the job details, then apply below.

    - - {% if job.form_template %} - - Apply for this Position - - {% endif %} -
    -
    -
    - -
    -
    -
    -

    {{ job.title }}

    - - {% comment %} {% with status_class=job.status|lower %} - - {{ job.get_status_display }} - - {% endwith %} {% endcomment %} -
    - -
    - -

    Job Overview

    -
    - {% if job.salary_range %} -
    - - Salary: - {{ job.salary_range }} -
    - {% endif %} - -
    - - Deadline: - {% if job.application_deadline %} - {{ job.application_deadline|date:"M d, Y" }} - {% if job.is_expired %} - EXPIRED - {% endif %} - {% else %} - Not specified - {% endif %} -
    - -
    Job Type: {{ job.get_job_type_display }}
    -
    Location: {{ job.get_location_display }}
    -
    Department: {{ job.department|default:"Not specified" }}
    -
    JOB ID: {{ job.internal_job_id|default:"N/A" }}
    -
    Workplace: {{ job.get_workplace_type_display }}
    - {% comment %}
    Created By: {{ job.created_by|default:"N/A" }}
    {% endcomment %} -
    - - {% if job.description %}
    Job Description
    {{ job.description|safe }}
    {% endif %} - {% if job.qualifications %}
    Qualifications
    {{ job.qualifications|safe }}
    {% endif %} - {% if job.benefits %}
    Benefits
    {{ job.benefits|safe }}
    {% endif %} - {% if job.application_instructions %}
    Application Instructions
    {{ job.application_instructions|safe }}
    {% endif %} - -
    -
    -
    -
    -
    - -
    - {% if job.form_template %} - - Apply for this Position - - {% endif %} -
    - - - - \ No newline at end of file diff --git a/templates/jobs/job_list.html b/templates/jobs/job_list.html index 4b366bc..9fac4b4 100644 --- a/templates/jobs/job_list.html +++ b/templates/jobs/job_list.html @@ -225,14 +225,14 @@
    -
    +
    - {% if job_filter or search_query %} - + {% trans "Clear" %} {% endif %} diff --git a/templates/meetings/list_meetings.html b/templates/meetings/list_meetings.html index 0297876..1985170 100644 --- a/templates/meetings/list_meetings.html +++ b/templates/meetings/list_meetings.html @@ -168,7 +168,7 @@
    {% if search_query %}{% endif %} -
    +
    -
    +
    - {% if status_filter or search_query %} - + {% trans "Clear" %} {% endif %} diff --git a/templates/recruitment/candidate_list.html b/templates/recruitment/candidate_list.html index 8c350b8..dd795d6 100644 --- a/templates/recruitment/candidate_list.html +++ b/templates/recruitment/candidate_list.html @@ -165,7 +165,7 @@ {% if search_query %}{% endif %} -
    +
    -
    +
    - {% if job_filter or search_query %} - + {% trans "Clear" %} {% endif %} diff --git a/templates/user/profile.html b/templates/user/profile.html index d5133cb..eb057e3 100644 --- a/templates/user/profile.html +++ b/templates/user/profile.html @@ -104,7 +104,7 @@ {% endblock %} {% block content %} -
    +
    @@ -122,7 +122,7 @@
    {% trans "Personal Information" %}
    - + {% csrf_token %}
    @@ -139,7 +139,7 @@
    @@ -160,9 +160,9 @@ {% trans "Change Password" %} - {% comment %} - {% trans "Two-Factor Auth" %} - {% endcomment %} +
    @@ -192,4 +192,32 @@
    + + + + {% endblock %} \ No newline at end of file