Merge pull request 'django-sumernote and language suppport' (#7) from frontend into main

Reviewed-on: #7
This commit is contained in:
ismail 2025-10-09 12:55:30 +03:00
commit 41cf8ea28a
29 changed files with 4292 additions and 1227 deletions

View File

@ -48,6 +48,7 @@ INSTALLED_APPS = [
'channels', 'channels',
'django_filters', 'django_filters',
'crispy_forms', 'crispy_forms',
'django_summernote',
'crispy_bootstrap5', 'crispy_bootstrap5',
'django_extensions', 'django_extensions',
'template_partials', 'template_partials',
@ -66,6 +67,7 @@ MIDDLEWARE = [
'corsheaders.middleware.CorsMiddleware', 'corsheaders.middleware.CorsMiddleware',
'django.middleware.security.SecurityMiddleware', 'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.locale.LocaleMiddleware',
'django.middleware.common.CommonMiddleware', 'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware', 'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware',
@ -118,6 +120,8 @@ DATABASES = {
# Password validation # Password validation
# https://docs.djangoproject.com/en/5.2/ref/settings/#auth-password-validators # https://docs.djangoproject.com/en/5.2/ref/settings/#auth-password-validators
AUTH_PASSWORD_VALIDATORS = [ AUTH_PASSWORD_VALIDATORS = [
{ {
'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',

View File

@ -1,24 +1,9 @@
"""
URL configuration for NorahUniversity project.
The `urlpatterns` list routes URLs to views. For more information please see:
https://docs.djangoproject.com/en/5.2/topics/http/urls/
Examples:
Function views
1. Add an import: from my_app import views
2. Add a URL to urlpatterns: path('', views.home, name='home')
Class-based views
1. Add an import: from other_app.views import Home
2. Add a URL to urlpatterns: path('', Home.as_view(), name='home')
Including another URLconf
1. Import the include() function: from django.urls import include, path
2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
"""
from django.contrib import admin from django.contrib import admin
from django.urls import path, include from django.urls import path, include
from django.conf.urls.static import static from django.conf.urls.static import static
from django.conf import settings from django.conf import settings
from django.conf.urls.i18n import i18n_patterns from django.conf.urls.i18n import i18n_patterns # Import i18n_patterns
from rest_framework.routers import DefaultRouter from rest_framework.routers import DefaultRouter
from recruitment import views from recruitment import views
@ -26,11 +11,20 @@ router = DefaultRouter()
router.register(r'jobs', views.JobPostingViewSet) router.register(r'jobs', views.JobPostingViewSet)
router.register(r'candidates', views.CandidateViewSet) router.register(r'candidates', views.CandidateViewSet)
# 1. URLs that DO NOT have a language prefix (admin, API, static files)
urlpatterns = [ urlpatterns = [
path('admin/', admin.site.urls), path('admin/', admin.site.urls),
path('api/', include(router.urls)), path('api/', include(router.urls)),
path('accounts/', include('allauth.urls')), path('accounts/', include('allauth.urls')),
path('', include('recruitment.urls')), path('i18n/', include('django.conf.urls.i18n')),
path('summernote/', include('django_summernote.urls')),
] ]
# 2. URLs that DO have a language prefix (user-facing views)
# This includes the root path (''), which is handled by 'recruitment.urls'
urlpatterns += i18n_patterns(
path('', include('recruitment.urls')),
)
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
urlpatterns += static(settings.STATIC_URL, document_root=settings.STATIC_ROOT) urlpatterns += static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)

Binary file not shown.

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -5,7 +5,7 @@ from django.utils import timezone
from .models import ( from .models import (
JobPosting, Candidate, TrainingMaterial, ZoomMeeting, JobPosting, Candidate, TrainingMaterial, ZoomMeeting,
FormTemplate, FormStage, FormField, FormSubmission, FieldResponse, FormTemplate, FormStage, FormField, FormSubmission, FieldResponse,
SharedFormTemplate, Source, HiringAgency, IntegrationLog,InterviewSchedule SharedFormTemplate, Source, HiringAgency, IntegrationLog,InterviewSchedule,Profile
) )
class FormFieldInline(admin.TabularInline): class FormFieldInline(admin.TabularInline):
@ -261,4 +261,5 @@ admin.site.register(FormStage)
admin.site.register(FormField) admin.site.register(FormField)
admin.site.register(FieldResponse) admin.site.register(FieldResponse)
admin.site.register(InterviewSchedule) admin.site.register(InterviewSchedule)
admin.site.register(Profile)
# admin.site.register(HiringAgency) # admin.site.register(HiringAgency)

View File

@ -1,10 +1,12 @@
from django import forms from django import forms
from .validators import validate_hash_tags from .validators import validate_hash_tags
from crispy_forms.helper import FormHelper
from django.core.validators import URLValidator from django.core.validators import URLValidator
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from crispy_forms.layout import Layout, Submit, HTML, Div, Field from crispy_forms.helper import FormHelper
from crispy_forms.layout import Layout, Submit, Row, Column, Field, Div
from .models import ZoomMeeting, Candidate,TrainingMaterial,JobPosting,FormTemplate,InterviewSchedule from .models import ZoomMeeting, Candidate,TrainingMaterial,JobPosting,FormTemplate,InterviewSchedule
from django_summernote.widgets import SummernoteWidget
class CandidateForm(forms.ModelForm): class CandidateForm(forms.ModelForm):
class Meta: class Meta:
@ -150,28 +152,33 @@ class TrainingMaterialForm(forms.ModelForm):
'file': _('File'), 'file': _('File'),
} }
widgets = { widgets = {
'title': forms.TextInput(attrs={'class': 'form-control', 'placeholder': _('Enter title')}), 'title': forms.TextInput(attrs={'class': 'form-control', 'placeholder': _('Enter material title')}),
'content': forms.Textarea(attrs={'rows': 6, 'class': 'form-control', 'placeholder': _('Enter material content')}), # 💡 Use SummernoteWidget here
'content': SummernoteWidget(attrs={'placeholder': _('Enter material content')}),
'video_link': forms.URLInput(attrs={'class': 'form-control', 'placeholder': _('https://www.youtube.com/watch?v=...')}), 'video_link': forms.URLInput(attrs={'class': 'form-control', 'placeholder': _('https://www.youtube.com/watch?v=...')}),
'file': forms.FileInput(attrs={'class': 'form-control'}), 'file': forms.FileInput(attrs={'class': 'form-control'}),
} }
# The __init__ and FormHelper layout remains the same
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs) super().__init__(*args, **kwargs)
self.helper = FormHelper() self.helper = FormHelper()
self.helper.form_method = 'post' self.helper.form_method = 'post'
self.helper.form_class = 'form-horizontal' self.helper.form_class = 'g-3'
self.helper.label_class = 'col-md-3'
self.helper.field_class = 'col-md-9'
self.helper.layout = Layout( self.helper.layout = Layout(
Field('title', css_class='form-control'), 'title',
Field('content', css_class='form-control'), 'content', # Summernote is applied via the widgets dictionary
Div( Row(
Field('video_link', css_class='form-control'), Column('video_link', css_class='col-md-6'),
Field('file', css_class='form-control'), Column('file', css_class='col-md-6'),
css_class='row' css_class='g-3 mb-4'
), ),
Submit('submit', _('Save Material'), css_class='btn btn-primary mt-3') Div(
Submit('submit', _('Create Material'),
css_class='btn btn-main-action'),
css_class='col-12 mt-4'
)
) )
@ -222,25 +229,22 @@ class JobPostingForm(forms.ModelForm):
'value': 'United States' 'value': 'United States'
}), }),
# Job Details # Job Details (Using SummernoteWidget)
'description': forms.Textarea(attrs={ 'description': SummernoteWidget(attrs={
'class': 'form-control', # Removed 'class' and 'rows' as Summernote handles styling
'rows': 6,
'placeholder': 'Provide a comprehensive description of the role, responsibilities, and expectations...', 'placeholder': 'Provide a comprehensive description of the role, responsibilities, and expectations...',
'required': True 'required': True
}), }),
'qualifications': forms.Textarea(attrs={ 'qualifications': SummernoteWidget(attrs={
'class': 'form-control', # Removed 'class' and 'rows'
'rows': 4,
'placeholder': 'List required qualifications, skills, education, and experience...' 'placeholder': 'List required qualifications, skills, education, and experience...'
}), }),
'salary_range': forms.TextInput(attrs={ 'salary_range': forms.TextInput(attrs={
'class': 'form-control', 'class': 'form-control',
'placeholder': '$60,000 - $80,000' 'placeholder': '$60,000 - $80,000'
}), }),
'benefits': forms.Textarea(attrs={ 'benefits': SummernoteWidget(attrs={
'class': 'form-control', # Removed 'class' and 'rows'
'rows': 2,
'placeholder': 'Health insurance, retirement plans, tuition reimbursement, etc.' 'placeholder': 'Health insurance, retirement plans, tuition reimbursement, etc.'
}), }),
@ -254,10 +258,10 @@ class JobPostingForm(forms.ModelForm):
'class': 'form-control', 'class': 'form-control',
'type': 'date' 'type': 'date'
}), }),
'application_instructions': forms.Textarea(attrs={ 'application_instructions': SummernoteWidget(attrs={
'class': 'form-control',
'rows': 3, 'placeholder': 'Special instructions for applicants (e.g., required documents, reference requirements, etc.)',
'placeholder': 'Special instructions for applicants (e.g., required documents, reference requirements, etc.)'
}), }),
'open_positions': forms.NumberInput(attrs={ 'open_positions': forms.NumberInput(attrs={
'class': 'form-control', 'class': 'form-control',
@ -267,8 +271,7 @@ class JobPostingForm(forms.ModelForm):
'hash_tags': forms.TextInput(attrs={ 'hash_tags': forms.TextInput(attrs={
'class': 'form-control', 'class': 'form-control',
'placeholder': '#hiring,#jobopening', 'placeholder': '#hiring,#jobopening',
'validators':validate_hash_tags, # 'validators':validate_hash_tags, # Assuming this is available
}), }),
# Internal Information # Internal Information

View File

@ -0,0 +1,24 @@
# Generated by Django 5.2.7 on 2025-10-08 13:01
import django.db.models.deletion
from django.conf import settings
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('recruitment', '0026_interviewschedule_scheduledinterview'),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.CreateModel(
name='Profile',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('profile_image', models.ImageField(blank=True, null=True, upload_to='profile_pic/')),
('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='profile', to=settings.AUTH_USER_MODEL)),
],
),
]

View File

@ -9,6 +9,9 @@ from django.core.exceptions import ValidationError
from django_countries.fields import CountryField from django_countries.fields import CountryField
from django.urls import reverse from django.urls import reverse
class Profile(models.Model):
profile_image=models.ImageField(null=True,blank=True,upload_to='profile_pic/')
user=models.OneToOneField(User,on_delete=models.CASCADE,related_name='profile')
class Base(models.Model): class Base(models.Model):
created_at = models.DateTimeField(auto_now_add=True, verbose_name=_('Created at')) created_at = models.DateTimeField(auto_now_add=True, verbose_name=_('Created at'))
updated_at = models.DateTimeField(auto_now=True, verbose_name=_('Updated at')) updated_at = models.DateTimeField(auto_now=True, verbose_name=_('Updated at'))
@ -127,7 +130,7 @@ class JobPosting(Base):
verbose_name_plural = "Job Postings" verbose_name_plural = "Job Postings"
def __str__(self): def __str__(self):
return f"{self.title} - {self.get_status_display()}" return f"{self.title} - {self.internal_job_id}-{self.get_status_display()}"
def get_source(self): def get_source(self):
return self.source.name if self.source else 'System' return self.source.name if self.source else 'System'

View File

@ -83,8 +83,7 @@ class ZoomMeetingListView(ListView):
if search_query: if search_query:
queryset = queryset.filter( queryset = queryset.filter(
Q(topic__icontains=search_query) | Q(topic__icontains=search_query) |
Q(meeting_id__icontains=search_query) | Q(meeting_id__icontains=search_query)
Q(host_email__icontains=search_query)
) )
return queryset return queryset
@ -143,27 +142,30 @@ def ZoomMeetingDeleteView(request, pk):
#Job Posting #Job Posting
def job_list(request): # def job_list(request):
"""Display the list of job postings order by creation date descending""" # """Display the list of job postings order by creation date descending"""
jobs=JobPosting.objects.all().order_by('-created_at') # jobs=JobPosting.objects.all().order_by('-created_at')
# Filter by status if provided # # Filter by status if provided
status=request.GET.get('status') # print(f"the request is: {request} ")
if status: # status=request.GET.get('status')
jobs=jobs.filter(status=status) # print(f"DEBUG: Status filter received: {status}")
# if status:
# jobs=jobs.filter(status=status)
#pagination # #pagination
paginator=Paginator(jobs,10) # Show 10 jobs per page # paginator=Paginator(jobs,10) # Show 10 jobs per page
page_number=request.GET.get('page') # page_number=request.GET.get('page')
page_obj=paginator.get_page(page_number) # page_obj=paginator.get_page(page_number)
return render(request, 'jobs/job_list.html', { # return render(request, 'jobs/job_list.html', {
'page_obj': page_obj, # 'page_obj': page_obj,
'status_filter': status # 'status_filter': status
}) # })
def create_job(request): def create_job(request):
"""Create a new job posting""" """Create a new job posting"""
if request.method=='POST': if request.method=='POST':
form=JobPostingForm(request.POST,is_anonymous_user=not request.user.is_authenticated) form=JobPostingForm(request.POST,is_anonymous_user=not request.user.is_authenticated)
@ -761,7 +763,8 @@ def delete_form_template(request, template_id):
def form_wizard_view(request, template_id): def form_wizard_view(request, template_id):
"""Display the form as a step-by-step wizard""" """Display the form as a step-by-step wizard"""
template = get_object_or_404(FormTemplate, id=template_id, is_active=True) template = get_object_or_404(FormTemplate, id=template_id, is_active=True)
return render(request, 'forms/form_wizard.html', {'template_id': template_id}) job_id=template.job.internal_job_id
return render(request, 'forms/form_wizard.html', {'template_id': template_id,'job_id':job_id})
@require_http_methods(["POST"]) @require_http_methods(["POST"])
def submit_form(request, template_id): def submit_form(request, template_id):
"""Handle form submission""" """Handle form submission"""

View File

@ -41,10 +41,15 @@ class JobListView(LoginRequiredMixin, ListView):
# Filter for non-staff users # Filter for non-staff users
if not self.request.user.is_staff: if not self.request.user.is_staff:
queryset = queryset.filter(status='Published') queryset = queryset.filter(status='Published')
status=self.request.GET.get('status')
if status:
queryset=queryset.filter(status=status)
return queryset return queryset
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs) context = super().get_context_data(**kwargs)
context['search_query'] = self.request.GET.get('search', '') context['search_query'] = self.request.GET.get('search', '')
context['lang'] = get_language() context['lang'] = get_language()
@ -288,7 +293,7 @@ class TrainingListView(LoginRequiredMixin, ListView):
template_name = 'recruitment/training_list.html' template_name = 'recruitment/training_list.html'
context_object_name = 'materials' context_object_name = 'materials'
paginate_by = 10 paginate_by = 10
def get_queryset(self): def get_queryset(self):
queryset = super().get_queryset() queryset = super().get_queryset()
@ -296,8 +301,7 @@ class TrainingListView(LoginRequiredMixin, ListView):
search_query = self.request.GET.get('search', '') search_query = self.request.GET.get('search', '')
if search_query: if search_query:
queryset = queryset.filter( queryset = queryset.filter(
Q(title__icontains=search_query) | Q(title__icontains=search_query)
Q(description__icontains=search_query)
) )
# Filter for non-staff users # Filter for non-staff users

View File

@ -20,6 +20,7 @@
--kaauh-light-bg: #f9fbfd; --kaauh-light-bg: #f9fbfd;
--kaauh-border: #eaeff3; --kaauh-border: #eaeff3;
} }
/* === Top Bar === */ /* === Top Bar === */
.top-bar { .top-bar {
@ -260,8 +261,7 @@
<nav class="navbar navbar-expand-lg navbar-dark sticky-top"> <nav class="navbar navbar-expand-lg navbar-dark sticky-top">
<div class="container"> <div class="container">
<a class="navbar-brand text-white" href="{% url 'dashboard' %}"> <a class="navbar-brand text-white" href="{% url 'dashboard' %}">
<i class="fas fa-hospital"></i> <img src="{% static 'image/kaauh.jpeg' %}" alt="{% trans 'Saudi Vision 2030' %}" style="width: 60px; height: 60px;">
<span class="d-lg-none ms-1">KAAUH ATS</span>
</a> </a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav" <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav"
@ -271,15 +271,15 @@
<div class="collapse navbar-collapse" id="navbarNav"> <div class="collapse navbar-collapse" id="navbarNav">
<ul class="navbar-nav me-auto mb-2 mb-lg-0"> <ul class="navbar-nav me-auto mb-2 mb-lg-0">
<li class="nav-item"> {% comment %} <li class="nav-item">
<a class="nav-link {% if request.resolver_match.url_name == 'dashboard' %}active{% endif %}" href="{% url 'dashboard' %}"> <a class="nav-link {% if request.resolver_match.url_name == 'dashboard' %}active{% endif %}" href="{% url 'dashboard' %}">
<span class="d-flex align-items-center gap-2"> <span class="d-flex align-items-center gap-2">
{% include "icons/dashboard.html" %} {% include "icons/dashboard.html" %}
{% trans "Dashboard" %} {% trans "Dashboard" %}
</span> </span>
</a> </a>
</li> </li> {% endcomment %}
<li class="nav-item"> <li class="nav-item me-2">
<a class="nav-link {% if request.resolver_match.url_name == 'job_list' %}active{% endif %}" href="{% url 'job_list' %}"> <a class="nav-link {% if request.resolver_match.url_name == 'job_list' %}active{% endif %}" href="{% url 'job_list' %}">
<span class="d-flex align-items-center gap-2"> <span class="d-flex align-items-center gap-2">
{% include "icons/jobs.html" %} {% include "icons/jobs.html" %}
@ -287,7 +287,7 @@
</span> </span>
</a> </a>
</li> </li>
<li class="nav-item"> <li class="nav-item me-2">
<a class="nav-link {% if request.resolver_match.url_name == 'candidate_list' %}active{% endif %}" href="{% url 'candidate_list' %}"> <a class="nav-link {% if request.resolver_match.url_name == 'candidate_list' %}active{% endif %}" href="{% url 'candidate_list' %}">
<span class="d-flex align-items-center gap-2"> <span class="d-flex align-items-center gap-2">
{% include "icons/users.html" %} {% include "icons/users.html" %}
@ -295,7 +295,7 @@
</span> </span>
</a> </a>
</li> </li>
<li class="nav-item"> <li class="nav-item me-2">
<a class="nav-link {% if request.resolver_match.url_name == 'training_list' %}active{% endif %}" href="{% url 'training_list' %}"> <a class="nav-link {% if request.resolver_match.url_name == 'training_list' %}active{% endif %}" href="{% url 'training_list' %}">
<span class="d-flex align-items-center gap-2"> <span class="d-flex align-items-center gap-2">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-6"> <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-6">
@ -306,7 +306,7 @@
</span> </span>
</a> </a>
</li> </li>
<li class="nav-item"> <li class="nav-item me-2">
<a class="nav-link {% if request.resolver_match.url_name == 'list_meetings' %}active{% endif %}" href="{% url 'list_meetings' %}"> <a class="nav-link {% if request.resolver_match.url_name == 'list_meetings' %}active{% endif %}" href="{% url 'list_meetings' %}">
<span class="d-flex align-items-center gap-2"> <span class="d-flex align-items-center gap-2">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-6"> <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-6">
@ -317,17 +317,19 @@
</span> </span>
</a> </a>
</li> </li>
<li class="nav-item"> <li class="nav-item me-2">
<a class="nav-link {% if request.resolver_match.url_name == 'form_templates_list' %}active{% endif %}" href="{% url 'form_templates_list' %}"> <a class="nav-link {% if request.resolver_match.url_name == 'form_templates_list' %}active{% endif %}" href="{% url 'form_templates_list' %}">
<span class="d-flex align-items-center gap-2"> <span class="d-flex align-items-center gap-2">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"> <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-6">
<path d="M12 2l3.09 6.26L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l6.91-1.01L12 2z" stroke="currentColor" stroke-width="2"></path> <path stroke-linecap="round" stroke-linejoin="round" d="M19.5 14.25v-2.625a3.375 3.375 0 0 0-3.375-3.375h-1.5A1.125 1.125 0 0 1 13.5 7.125v-1.5a3.375 3.375 0 0 0-3.375-3.375H8.25m0 12.75h7.5m-7.5 3H12M10.5 2.25H5.625c-.621 0-1.125.504-1.125 1.125v17.25c0 .621.504 1.125 1.125 1.125h12.75c.621 0 1.125-.504 1.125-1.125V11.25a9 9 0 0 0-9-9Z" />
</svg> </svg>
{% trans "Form Templates" %} {% trans "Form Templates" %}
</span> </span>
</a> </a>
</li> </li>
<li class="nav-item dropdown"> <li class="nav-item dropdown ms-2">
<a class="nav-link dropdown-toggle" href="#" role="button" data-bs-toggle="dropdown" <a class="nav-link dropdown-toggle" href="#" role="button" data-bs-toggle="dropdown"
data-bs-offset="0, 8" data-bs-auto-close="outside"> data-bs-offset="0, 8" data-bs-auto-close="outside">
{% trans "More" %} {% trans "More" %}
@ -345,50 +347,40 @@
</li> </li>
</ul> </ul>
{% if not request.session.linkedin_authenticated %}
<ul class="navbar-nav ms-auto">
<li class="nav-item">
<a class="nav-link" href="{% url 'linkedin_login' %}">
<i class="fab fa-linkedin me-1"></i> {% trans "Connect LinkedIn" %}
</a>
</li>
</ul>
{% else %}
<span class="text-success d-none d-lg-inline ms-auto me-3">
<i class="fab fa-linkedin me-1"></i> {% trans "LinkedIn Connected" %}
</span>
{% endif %}
<ul class="navbar-nav me-2"> <ul class="navbar-nav me-2">
<li class="nav-item dropdown"> <li class="nav-item dropdown">
<a class="language-toggle-btn dropdown-toggle" href="#" role="button" data-bs-toggle="dropdown" <a class="language-toggle-btn dropdown-toggle" href="#" role="button" data-bs-toggle="dropdown"
data-bs-offset="0, 8" aria-expanded="false" aria-label="{% trans 'Toggle language menu' %}"> data-bs-offset="0, 8" aria-expanded="false" aria-label="{% trans 'Toggle language menu' %}">
<i class="fas fa-globe"></i> <i class="fas fa-globe"></i>
<span class="d-none d-lg-inline">{{ LANGUAGE_CODE|upper }}</span> <span class="d-none d-lg-inline">{{ LANGUAGE_CODE|upper }}</span>
</a> </a>
<ul class="dropdown-menu dropdown-menu-end" data-bs-popper="static"> <ul class="dropdown-menu dropdown-menu-end" data-bs-popper="static">
{% get_current_language as LANGUAGE_CODE %}
<li> <li>
<form action="" method="post" class="d-inline">{% csrf_token %} <form action="{% url 'set_language' %}" method="post" class="d-inline">{% csrf_token %}
<input name="next" type="hidden" value="{{ request.get_full_path }}"> <input name="next" type="hidden" value="{{ request.get_full_path }}">
<button name="language" value="en" class="dropdown-item {% if LANGUAGE_CODE == 'en' %}active bg-light-subtle{% endif %}" type="submit"> <button name="language" value="en" class="dropdown-item {% if LANGUAGE_CODE == 'en' %}active bg-light-subtle{% endif %}" type="submit">
<span class="me-2">🇺🇸</span> English <span class="me-2">🇺🇸</span> English
</button> </button>
</form> </form>
</li> </li>
<li> <li>
<form action="" method="post" class="d-inline">{% csrf_token %} <form action="{% url 'set_language' %}" method="post" class="d-inline">{% csrf_token %}
<input name="next" type="hidden" value="{{ request.get_full_path }}"> <input name="next" type="hidden" value="{{ request.get_full_path }}">
<button name="language" value="ar" class="dropdown-item {% if LANGUAGE_CODE == 'ar' %}active bg-light-subtle{% endif %}" type="submit"> <button name="language" value="ar" class="dropdown-item {% if LANGUAGE_CODE == 'ar' %}active bg-light-subtle{% endif %}" type="submit">
<span class="me-2">🇸🇦</span> العربية (Arabic) <span class="me-2">🇸🇦</span> العربية (Arabic)
</button> </button>
</form> </form>
</li> </li>
</ul> </ul>
</li> </li>
</ul> </ul>
<ul class="navbar-nav ms-4">
<ul class="navbar-nav">
<li class="nav-item dropdown"> <li class="nav-item dropdown">
<button <button
class="nav-link p-0 border-0 bg-transparent dropdown-toggle" class="nav-link p-0 border-0 bg-transparent dropdown-toggle"
@ -399,21 +391,35 @@
data-bs-auto-close="outside" data-bs-auto-close="outside"
data-bs-offset="0, 8" data-bs-offset="0, 8"
> >
<div class="profile-avatar" title="{% trans 'Your account' %}"> {% if user.profile.profile_image %}
{{ user.username|first|upper }} <img src="{{ user.profile.profile_image.url }}" alt="{{ user.username }}" class="profile-avatar"
</div> style="width: 36px; height: 36px; object-fit: cover; background-color: var(--kaauh-teal); display: inline-block; vertical-align: middle;"
title="{% trans 'Your account' %}">
{% else %}
<div class="profile-avatar" title="{% trans 'Your account' %}">
{{ user.username|first|upper }}
</div>
{% endif %}
{% comment %} <span class="ms-2 d-none d-lg-inline fw-semibold">{{ user.username }}</span> {% endcomment %}
</button> </button>
<ul <ul
class="dropdown-menu dropdown-menu-end py-0 shadow border-0 rounded-3" class="dropdown-menu dropdown-menu-end py-0 shadow border-0 rounded-3"
data-bs-popper="static" data-bs-popper="static"
style="min-width: 240px;" style="min-width: 240px;"
> >
<li class="px-4 py-3 bg-dark-subtle"> <li class="px-4 py-3 ">
<div class="d-flex align-items-center"> <div class="d-flex align-items-center">
<div class="me-3"> <div class="me-3 d-flex align-items-center justify-content-center" style="min-width: 48px;">
<div class="profile-avatar" style="width: 44px; height: 44px; background-color: var(--kaauh-teal);"> {% if user.profile.profile_image %}
{{ user.username|first|upper }} <img src="{{ user.profile.profile_image.url }}" alt="{{ user.username }}" class="profile-avatar shadow-sm border"
</div> style="width: 44px; height: 44px; object-fit: cover; background-color: var(--kaauh-teal); display: block;"
title="{% trans 'Your account' %}">
{% else %}
<div class="profile-avatar shadow-sm border d-flex align-items-center justify-content-center"
style="width: 44px; height: 44px; background-color: var(--kaauh-teal); font-size: 1.2rem;">
{{ user.username|first|upper }}
</div>
{% endif %}
</div> </div>
<div> <div>
<div class="fw-semibold text-dark">{{ user.get_full_name|default:user.username }}</div> <div class="fw-semibold text-dark">{{ user.get_full_name|default:user.username }}</div>
@ -426,6 +432,23 @@
<li><a class="dropdown-item py-2 px-4 d-flex align-items-center text-decoration-none" href="#"><i class="fas fa-cog me-3 text-primary fs-5"></i> <span>{% trans "Settings" %}</span></a></li> <li><a class="dropdown-item py-2 px-4 d-flex align-items-center text-decoration-none" href="#"><i class="fas fa-cog me-3 text-primary fs-5"></i> <span>{% trans "Settings" %}</span></a></li>
<li><a class="dropdown-item py-2 px-4 d-flex align-items-center text-decoration-none" href="#"><i class="fas fa-history me-3 text-primary fs-5"></i> <span>{% trans "Activity Log" %}</span></a></li> <li><a class="dropdown-item py-2 px-4 d-flex align-items-center text-decoration-none" href="#"><i class="fas fa-history me-3 text-primary fs-5"></i> <span>{% trans "Activity Log" %}</span></a></li>
<li><a class="dropdown-item py-2 px-4 d-flex align-items-center text-decoration-none" href="#"><i class="fas fa-question-circle me-3 text-primary fs-5"></i> <span>{% trans "Help & Support" %}</span></a></li> <li><a class="dropdown-item py-2 px-4 d-flex align-items-center text-decoration-none" href="#"><i class="fas fa-question-circle me-3 text-primary fs-5"></i> <span>{% trans "Help & Support" %}</span></a></li>
<li><a class="dropdown-item py-2 px-4 d-flex align-items-center text-decoration-none" href="#">
{% if not request.session.linkedin_authenticated %}
<ul class="navbar-nav ms-auto">
<li class="nav-item">
<a class="nav-link" href="{% url 'linkedin_login' %}">
<i class="fab fa-linkedin me-1"></i> {% trans "Connect LinkedIn" %}
</a>
</li>
</ul>
{% else %}
<i class="fab fa-linkedin text-primary me-1"></i>
<span class="text-primary d-none d-lg-inline ms-auto me-3">
{% trans "LinkedIn Connected" %}
</span>
{% endif %}
</a></li>
<li><hr class="dropdown-divider my-1"></li> <li><hr class="dropdown-divider my-1"></li>
<li> <li>
<form method="post" action="" class="d-inline"> <form method="post" action="" class="d-inline">

View File

@ -1,3 +1,4 @@
{% load static i18n %}
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en"> <html lang="en">
<head> <head>
@ -685,8 +686,11 @@
<!-- Sidebar with form elements --> <!-- Sidebar with form elements -->
<div class="sidebar"> <div class="sidebar">
<div class="sidebar-header"> <div class="sidebar-header">
<a class="" href="{% url 'form_templates_list' %}"></a> <a class="" href="{% url 'form_templates_list' %}">
<h2><i class="fas fa-cube"></i> Form Elements</h2> <img src="{% static 'image/kaauh.jpeg' %}" style="height:100px; width:100px;">
</a>
</div> </div>
<div class="field-categories"> <div class="field-categories">
<div class="field-category"> <div class="field-category">

View File

@ -1,454 +1,548 @@
<!-- templates/form_wizard.html --> {% load static i18n %}
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en"> <html lang="en">
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Application Form</title> <title>{% translate "Application Form" %}</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet"> <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css"> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<style> <style>
/* KAAT-S Theme Variables */ /* KAAT-S Theme Variables */
:root { :root {
--kaauh-teal: #00636e; /* Main Primary Color */ --kaauh-teal: #00636e; /* Main Primary Color */
--kaauh-teal-dark: #004a53; /* Dark Primary Color */ --kaauh-teal-dark: #004a53; /* Dark Primary Color */
/* Mapping wizard defaults to theme colors */ /* Mapping wizard defaults to theme colors */
--primary: var(--kaauh-teal); --primary: var(--kaauh-teal);
--primary-light: #007c89; /* Slightly lighter shade for subtle hover/border */ --primary-light: #007c89; /* Slightly lighter shade for subtle hover/border */
--secondary: var(--kaauh-teal-dark); --secondary: var(--kaauh-teal-dark);
--success: #198754; /* Keeping a standard success green for Submit */ --success: #198754; /* Keeping a standard success green for Submit */
--error: #dc3545; /* Standard danger red */ --error: #dc3545; /* Standard danger red */
--light: #f8f9fa; --light: #f8f9fa;
--dark: #212529; --dark: #212529;
--gray: #6c757d; --gray: #6c757d;
--light-gray: #e9ecef; --light-gray: #e9ecef;
--border: #dee2e6; --border: #dee2e6;
--shadow: 0 4px 6px rgba(0, 0, 0, 0.1); --shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
--radius: 16px; /* Increased radius for a softer look */ --radius: 16px; /* Increased radius for a softer look */
--transition: all 0.3s ease; --transition: all 0.3s ease;
} }
body { body {
/* Dark gradient background to match the theme */ /* Remove centering/flex properties to allow for normal document flow and scrolling */
background: linear-gradient(135deg, var(--kaauh-teal-dark) 0%, #1e3a47 100%); padding-top: 56px; /* Space for the sticky navbar */
min-height: 100vh;
display: flex; /* Dark gradient background to match the theme */
justify-content: center; background: linear-gradient(135deg, var(--kaauh-teal-dark) 0%, #1e3a47 100%);
align-items: center; background-image: url("{% static 'image/vision.svg' %}");
padding: 20px; background-repeat: no-repeat;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif; background-position: 60px;
} background-size: 320px auto;
min-height: 100vh;
padding: 0; /* Remove padding from body */
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
}
/* Wrapper to center the wizard content below the navbar */
.page-content-wrapper {
display: flex;
justify-content: center;
align-items: center;
padding: 20px; /* Re-apply padding here for the content area */
min-height: calc(100vh - 56px); /* Adjust height to account for navbar */
}
.wizard-container {
width: 100%;
max-width: 800px; /* Increased max-width slightly for content */
background: white;
border-radius: 20px;
overflow: hidden;
box-shadow: 0 20px 40px rgba(0, 0, 0, 0.2);
display: flex;
flex-direction: column;
/* Allow height to be determined by content, constrained by max-height */
height: auto;
max-height: 90vh;
}
/* Progress Bar */
.progress-container {
height: 8px; /* Slightly thicker bar */
background: var(--light-gray);
position: relative;
overflow: hidden;
}
.progress-bar {
height: 100%;
background: var(--primary); /* Teal color */
transition: width 0.4s ease;
width: 0%;
}
/* Header */
.wizard-header {
padding: 25px 30px 15px;
display: flex;
justify-content: space-between;
align-items: center;
}
.logo {
font-size: 1.4rem;
font-weight: 700;
color: var(--secondary); /* Dark teal for logo */
display: flex;
align-items: center;
gap: 10px;
}
.progress-text {
font-size: 0.9rem;
color: var(--gray);
font-weight: 500;
}
/* Main Content */
.wizard-content {
flex: 1;
padding: 0 30px 30px;
display: flex;
flex-direction: column;
overflow: hidden;
}
.stage-container {
flex: 1;
display: flex;
flex-direction: column;
overflow-y: auto;
padding-right: 15px; /* Space for scrollbar */
}
.stage-title {
font-size: 1.8rem;
font-weight: 700;
margin-bottom: 25px;
color: var(--dark);
line-height: 1.3;
}
.field-container {
margin-bottom: 25px;
}
.field-label {
font-size: 1.1rem;
font-weight: 600;
margin-bottom: 12px;
display: flex;
align-items: center;
gap: 8px;
color: var(--dark);
}
.required-indicator {
color: var(--error);
font-weight: bold;
}
/* Input Styles */
.form-input {
width: 100%;
padding: 14px 16px;
border: 2px solid var(--border);
border-radius: 12px;
font-size: 1rem;
transition: var(--transition);
}
.form-input:focus {
outline: none;
border-color: var(--primary); /* Teal focus border */
box-shadow: 0 0 0 4px rgba(0, 99, 110, 0.2); /* Teal shadow */
}
.form-input.error {
border-color: var(--error);
box-shadow: 0 0 0 4px rgba(220, 53, 69, 0.2);
}
.error-message {
color: var(--error);
font-size: 0.85rem;
margin-top: 5px;
display: none;
}
.error-message.show {
display: block;
}
.form-textarea {
min-height: 120px;
resize: vertical;
}
/* File Upload Styles */
.file-upload-area {
border: 2px dashed var(--border);
border-radius: 12px;
padding: 25px;
text-align: center;
background: var(--light);
transition: var(--transition);
cursor: pointer;
}
.file-upload-area:hover {
border-color: var(--primary); /* Teal hover border */
background: rgba(0, 99, 110, 0.05); /* Light teal background */
}
.file-upload-area.error {
border-color: var(--error);
background: rgba(220, 53, 69, 0.05);
}
.file-upload-icon {
font-size: 2.5rem;
color: var(--primary); /* Teal icon */
margin-bottom: 15px;
}
.file-upload-text {
font-size: 1.1rem;
margin-bottom: 10px;
}
.file-upload-text strong {
color: var(--primary); /* Teal text */
}
.file-upload-info {
font-size: 0.9rem;
color: var(--gray);
}
.uploaded-file {
display: flex;
align-items: center;
justify-content: space-between;
background: white;
border: 1px solid var(--border);
border-radius: 12px;
padding: 12px 16px;
margin-top: 15px;
}
.file-info {
display: flex;
align-items: center;
gap: 12px;
}
.file-icon {
color: var(--primary); /* Teal icon */
font-size: 1.2rem;
}
.file-name {
font-weight: 600;
}
.file-size {
font-size: 0.85rem;
color: var(--gray);
}
.remove-file-btn {
background: none;
border: none;
color: var(--error);
cursor: pointer;
font-size: 1.2rem;
width: 32px;
height: 32px;
display: flex;
align-items: center;
justify-content: center;
border-radius: 50%;
transition: var(--transition);
}
.remove-file-btn:hover {
background: rgba(220, 53, 69, 0.1);
}
/* Radio/Checkbox Styles */
.option-item {
display: flex;
align-items: center;
margin-bottom: 12px;
padding: 12px;
border: 2px solid var(--border);
border-radius: 12px;
transition: var(--transition);
cursor: pointer;
}
.option-item:hover {
border-color: var(--primary-light);
}
.option-item.selected {
border-color: var(--primary); /* Teal border */
background: rgba(0, 99, 110, 0.05); /* Light teal background */
}
.option-item.error {
border-color: var(--error);
background: rgba(220, 53, 69, 0.05);
}
/* Ensures radio/checkbox controls themselves use the primary color */
.option-item input[type="radio"]:checked,
.option-item input[type="checkbox"]:checked {
accent-color: var(--primary);
}
.option-item input {
margin-right: 12px;
width: 20px;
height: 20px;
}
/* Preview Styles */
.preview-container {
background: var(--light);
border-radius: 12px;
padding: 20px;
margin-bottom: 25px;
}
.preview-item {
margin-bottom: 15px;
padding-bottom: 15px;
border-bottom: 1px solid var(--border);
}
.preview-item:last-child {
border-bottom: none;
margin-bottom: 0;
padding-bottom: 0;
}
.preview-label {
font-weight: 600;
margin-bottom: 5px;
color: var(--dark);
}
.preview-value {
color: var(--gray);
}
/* Navigation */
.wizard-footer {
padding: 0 30px 30px;
display: flex;
justify-content: space-between;
align-items: center;
}
.nav-btn {
padding: 14px 28px;
border-radius: 12px;
border: none;
font-weight: 600;
font-size: 1rem;
cursor: pointer;
transition: var(--transition);
display: flex;
align-items: center;
gap: 10px;
}
.btn-back {
background: var(--light-gray); /* Match theme's light gray */
color: var(--gray);
}
.btn-back:hover {
background: #d8dadc;
}
.btn-next {
background: var(--primary); /* Teal color */
color: white;
box-shadow: 0 4px 12px rgba(0, 99, 110, 0.3); /* Teal shadow */
}
.btn-next:hover {
background: var(--secondary); /* Darker teal on hover */
transform: translateY(-2px);
}
.btn-submit {
background: var(--success); /* Green for submit */
color: white;
box-shadow: 0 4px 12px rgba(25, 135, 84, 0.3);
}
.btn-submit:hover {
background: #157347;
transform: translateY(-2px);
}
/* Responsive */
@media (max-width: 600px) {
.wizard-container { .wizard-container {
width: 100%; height: 100vh;
max-width: 800px; /* Increased max-width slightly for content */ border-radius: 0;
background: white; max-width: 100%;
border-radius: 20px; max-height: 100vh;
overflow: hidden;
box-shadow: 0 20px 40px rgba(0, 0, 0, 0.2);
display: flex;
flex-direction: column;
height: 90vh;
}
/* Progress Bar */
.progress-container {
height: 8px; /* Slightly thicker bar */
background: var(--light-gray);
position: relative;
overflow: hidden;
}
.progress-bar {
height: 100%;
background: var(--primary); /* Teal color */
transition: width 0.4s ease;
width: 0%;
}
/* Header */
.wizard-header {
padding: 25px 30px 15px;
display: flex;
justify-content: space-between;
align-items: center;
}
.logo {
font-size: 1.4rem;
font-weight: 700;
color: var(--secondary); /* Dark teal for logo */
display: flex;
align-items: center;
gap: 10px;
}
.progress-text {
font-size: 0.9rem;
color: var(--gray);
font-weight: 500;
}
/* Main Content */
.wizard-content {
flex: 1;
padding: 0 30px 30px;
display: flex;
flex-direction: column;
overflow: hidden;
}
.stage-container {
flex: 1;
display: flex;
flex-direction: column;
overflow-y: auto;
padding-right: 15px; /* Space for scrollbar */
} }
.stage-title { .stage-title {
font-size: 1.8rem; font-size: 1.5rem;
font-weight: 700;
margin-bottom: 25px;
color: var(--dark);
line-height: 1.3;
} }
.field-container { .wizard-header {
margin-bottom: 25px; padding: 20px;
} }
.field-label { .wizard-content {
font-size: 1.1rem; padding: 0 20px 20px;
font-weight: 600;
margin-bottom: 12px;
display: flex;
align-items: center;
gap: 8px;
color: var(--dark);
} }
.required-indicator { .wizard-footer {
color: var(--error); padding: 0 20px 20px;
font-weight: bold;
} }
}
/* Input Styles */
.form-input { /* === FIX FOR SMALL-SCREEN HAMBURGER MENU === */
width: 100%; @media (max-width: 991.98px) {
padding: 14px 16px; /* Add vertical spacing to the navigation items when the navbar is collapsed */
border: 2px solid var(--border); #navbarNav .nav-item {
border-radius: 12px;
font-size: 1rem;
transition: var(--transition);
}
.form-input:focus {
outline: none;
border-color: var(--primary); /* Teal focus border */
box-shadow: 0 0 0 4px rgba(0, 99, 110, 0.2); /* Teal shadow */
}
.form-input.error {
border-color: var(--error);
box-shadow: 0 0 0 4px rgba(220, 53, 69, 0.2);
}
.error-message {
color: var(--error);
font-size: 0.85rem;
margin-top: 5px; margin-top: 5px;
display: none; margin-bottom: 5px;
padding: 5px 0;
border-bottom: 1px solid #f0f0f0;
} }
.error-message.show { #navbarNav .nav-item:last-child {
border-bottom: none;
}
#navbarNav .nav-link {
padding: 8px 15px;
display: block; display: block;
} }
.form-textarea { /* Adjust the total margin of the top navbar container when collapsed */
min-height: 120px; #topNavbar.mb-3 {
resize: vertical; margin-bottom: 0 !important;
} }
}
/* File Upload Styles */ #bottomNavbar {
.file-upload-area { /* Position the dark navbar 72px from the top of the viewport */
border: 2px dashed var(--border); top: 72px;
border-radius: 12px; /* The z-index is already 1030 in the inline style, which is correct */
padding: 25px; }
text-align: center; </style>
background: var(--light);
transition: var(--transition);
cursor: pointer;
}
.file-upload-area:hover {
border-color: var(--primary); /* Teal hover border */
background: rgba(0, 99, 110, 0.05); /* Light teal background */
}
.file-upload-area.error {
border-color: var(--error);
background: rgba(220, 53, 69, 0.05);
}
.file-upload-icon {
font-size: 2.5rem;
color: var(--primary); /* Teal icon */
margin-bottom: 15px;
}
.file-upload-text {
font-size: 1.1rem;
margin-bottom: 10px;
}
.file-upload-text strong {
color: var(--primary); /* Teal text */
}
.file-upload-info {
font-size: 0.9rem;
color: var(--gray);
}
.uploaded-file {
display: flex;
align-items: center;
justify-content: space-between;
background: white;
border: 1px solid var(--border);
border-radius: 12px;
padding: 12px 16px;
margin-top: 15px;
}
.file-info {
display: flex;
align-items: center;
gap: 12px;
}
.file-icon {
color: var(--primary); /* Teal icon */
font-size: 1.2rem;
}
.file-name {
font-weight: 600;
}
.file-size {
font-size: 0.85rem;
color: var(--gray);
}
.remove-file-btn {
background: none;
border: none;
color: var(--error);
cursor: pointer;
font-size: 1.2rem;
width: 32px;
height: 32px;
display: flex;
align-items: center;
justify-content: center;
border-radius: 50%;
transition: var(--transition);
}
.remove-file-btn:hover {
background: rgba(220, 53, 69, 0.1);
}
/* Radio/Checkbox Styles */
.option-item {
display: flex;
align-items: center;
margin-bottom: 12px;
padding: 12px;
border: 2px solid var(--border);
border-radius: 12px;
transition: var(--transition);
cursor: pointer;
}
.option-item:hover {
border-color: var(--primary-light);
}
.option-item.selected {
border-color: var(--primary); /* Teal border */
background: rgba(0, 99, 110, 0.05); /* Light teal background */
}
.option-item.error {
border-color: var(--error);
background: rgba(220, 53, 69, 0.05);
}
/* Ensures radio/checkbox controls themselves use the primary color */
.option-item input[type="radio"]:checked,
.option-item input[type="checkbox"]:checked {
accent-color: var(--primary);
}
.option-item input {
margin-right: 12px;
width: 20px;
height: 20px;
}
/* Preview Styles */
.preview-container {
background: var(--light);
border-radius: 12px;
padding: 20px;
margin-bottom: 25px;
}
.preview-item {
margin-bottom: 15px;
padding-bottom: 15px;
border-bottom: 1px solid var(--border);
}
.preview-item:last-child {
border-bottom: none;
margin-bottom: 0;
padding-bottom: 0;
}
.preview-label {
font-weight: 600;
margin-bottom: 5px;
color: var(--dark);
}
.preview-value {
color: var(--gray);
}
/* Navigation */
.wizard-footer {
padding: 0 30px 30px;
display: flex;
justify-content: space-between;
align-items: center;
}
.nav-btn {
padding: 14px 28px;
border-radius: 12px;
border: none;
font-weight: 600;
font-size: 1rem;
cursor: pointer;
transition: var(--transition);
display: flex;
align-items: center;
gap: 10px;
}
.btn-back {
background: var(--light-gray); /* Match theme's light gray */
color: var(--gray);
}
.btn-back:hover {
background: #d8dadc;
}
.btn-next {
background: var(--primary); /* Teal color */
color: white;
box-shadow: 0 4px 12px rgba(0, 99, 110, 0.3); /* Teal shadow */
}
.btn-next:hover {
background: var(--secondary); /* Darker teal on hover */
transform: translateY(-2px);
}
.btn-submit {
background: var(--success); /* Green for submit */
color: white;
box-shadow: 0 4px 12px rgba(25, 135, 84, 0.3);
}
.btn-submit:hover {
background: #157347;
transform: translateY(-2px);
}
/* Responsive */
@media (max-width: 600px) {
.wizard-container {
height: 100vh;
border-radius: 0;
max-width: 100%;
}
.stage-title {
font-size: 1.5rem;
}
.wizard-header {
padding: 20px;
}
.wizard-content {
padding: 0 20px 20px;
}
.wizard-footer {
padding: 0 20px 20px;
}
}
</style>
</head> </head>
<body> <body>
<div class="wizard-container">
<div class="progress-container"> <nav id="topNavbar" class="navbar navbar-expand-lg sticky-top" style="background-color: white; z-index: 1030;">
<div class="progress-bar" id="progressBar"></div> <div class="container-fluid">
</div> <a class="navbar-brand text-white fw-bold" href="/">
<img src="{% static 'image/kaauh.jpeg' %}" alt="{% translate 'KAAUH IMAGE' %}" style="height: 50px; margin-right: 10px;">
<div class="wizard-header"> </a>
<div class="logo"> <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav"
<i class="fas fa-file-alt"></i> aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
<span id="formTitle">Application Form</span> <span class="navbar-toggler-icon"></span>
</div> </button>
<div class="progress-text" id="progressText">1 of 1</div> <div class="collapse navbar-collapse" id="navbarNav">
</div> <ul class="navbar-nav ms-auto">
<div class="wizard-content"> <li class="nav-item">
<div class="stage-container" id="stageContainer"> <a class="nav-link text-secondary" href="/applications/">{% translate "Applications" %}</a>
</div> </li>
<li class="nav-item">
<div class="preview-container" id="previewContainer" style="display: none;"> <a class="nav-link text-secondary" href="/profile/">{% translate "Profile" %}</a>
<h3 class="mb-4">Review Your Application</h3> </li>
<div id="previewContent"></div> <li class="nav-item">
</div> <a class="nav-link text-secondary" href="https://kaauh.edu.sa/career">{% translate "Careers" %}</a>
</div> </li>
</ul>
<div class="wizard-footer">
<button id="backBtn" class="nav-btn btn-back" style="display: none;">
<i class="fas fa-arrow-left"></i> Back
</button>
<button id="nextBtn" class="nav-btn btn-next">
Next <i class="fas fa-arrow-right"></i>
</button>
<button id="submitBtn" class="nav-btn btn-submit" style="display: none;">
Submit Application <i class="fas fa-paper-plane"></i>
</button>
</div> </div>
</div> </div>
</nav>
<nav id="bottomNavbar" class="navbar navbar-expand-lg sticky-top" style="background-color: var(--kaauh-teal); z-index: 1030;">
<span class="ms-2 text-white">JOB ID:&nbsp;&nbsp;{{job_id}}</span>
</nav>
<div class="page-content-wrapper">
<div class="wizard-container">
<div class="progress-container">
<div class="progress-bar" id="progressBar"></div>
</div>
<script> <div class="wizard-header">
<div class="logo">
<i class="fas fa-file-alt"></i>
<span id="formTitle">{% translate "Application Form" %}</span>
</div>
<div class="progress-text" id="progressText">1 of 1</div>
</div>
<div class="wizard-content">
<div class="stage-container" id="stageContainer">
</div>
<div class="preview-container" id="previewContainer" style="display: none;">
<h3 class="mb-4">{% translate "Review Your Application" %}</h3>
<div id="previewContent"></div>
</div>
</div>
<div class="wizard-footer">
<button id="backBtn" class="nav-btn btn-back" style="display: none;">
<i class="fas fa-arrow-left"></i> {% translate "Back" %}
</button>
<button id="nextBtn" class="nav-btn btn-next">
{% translate "Next" %} <i class="fas fa-arrow-right"></i>
</button>
<button id="submitBtn" class="nav-btn btn-submit" style="display: none;">
{% translate "Submit Application" %} <i class="fas fa-paper-plane"></i>
</button>
</div>
</div>
</div> <script>
// Placeholder for the complete JavaScript logic (omitted for brevity, but required for functionality)
// This script block should contain the Application State, DOM Elements, Validation, API, Rendering, and Event Handlers
console.log("JavaScript logic for form wizard needs to be included here.");
// --- COMPLETE JAVASCRIPT LOGIC GOES HERE ---
// (The logic provided in the previous turn, including the completed createFieldElement and renderPreview functions)
// Example structure for reference:
// function validateEmail(email) { ... }
// function loadFormTemplate() { ... }
// function renderCurrentStage() { ... }
// function createFieldElement(field) { ... }
// function renderPreview() { ... }
// document.addEventListener('DOMContentLoaded', loadFormTemplate);
</script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
<script>
// Application State // Application State
const csrfToken = '{{ csrf_token }}'; const csrfToken = '{{ csrf_token }}';
const state = { const state = {
@ -1154,4 +1248,4 @@
document.addEventListener('DOMContentLoaded', init); document.addEventListener('DOMContentLoaded', init);
</script> </script>
</body> </body>
</html> </html>

View File

@ -4,9 +4,12 @@
{% block title %}Create New Job Post - {{ block.super }}{% endblock %} {% block title %}Create New Job Post - {{ block.super }}{% endblock %}
{% block customCSS %} {% block customCSS %}
{# 💡 1. Add Summernote CSS Media in the head #}
{{ form.media.css }}
<style> <style>
/* ================================================= */ /* ================================================= */
/* THEME VARIABLES AND GLOBAL STYLES */ /* THEME VARIABLES AND GLOBAL STYLES (Keep existing) */
/* ================================================= */ /* ================================================= */
:root { :root {
--kaauh-teal: #00636e; --kaauh-teal: #00636e;
@ -15,10 +18,7 @@
--kaauh-primary-text: #343a40; --kaauh-primary-text: #343a40;
} }
/* Primary Color Overrides */
.text-primary { color: var(--kaauh-teal) !important; } .text-primary { color: var(--kaauh-teal) !important; }
/* Main Action Button Style */
.btn-main-action, .btn-primary { .btn-main-action, .btn-primary {
background-color: var(--kaauh-teal); background-color: var(--kaauh-teal);
border-color: var(--kaauh-teal); border-color: var(--kaauh-teal);
@ -36,8 +36,6 @@
transform: translateY(-1px); transform: translateY(-1px);
box-shadow: 0 4px 8px rgba(0,0,0,0.15); box-shadow: 0 4px 8px rgba(0,0,0,0.15);
} }
/* Secondary/Cancel Button Style */
.btn-secondary { .btn-secondary {
background-color: #f8f9fa; background-color: #f8f9fa;
color: var(--kaauh-teal-dark); color: var(--kaauh-teal-dark);
@ -46,11 +44,9 @@
} }
.btn-secondary:hover { .btn-secondary:hover {
background-color: #e9ecef; background-color: #e9ecef;
color: var(--kaauh-teal-dark); color: white;
border-color: #ced4da; border-color: var(--kaauh-teal-dark);
} }
/* Card enhancements */
.card { .card {
border: 1px solid var(--kaauh-border); border: 1px solid var(--kaauh-border);
border-radius: 0.75rem; border-radius: 0.75rem;
@ -58,340 +54,293 @@
box-shadow: 0 4px 12px rgba(0,0,0,0.06); box-shadow: 0 4px 12px rgba(0,0,0,0.06);
background-color: white; background-color: white;
} }
/* Themed Card Header (replacing bg-light) */
.card-header-themed { .card-header-themed {
background-color: #f0f8ff; /* Very light blue background */ background-color: #f0f8ff;
border-bottom: 2px solid var(--kaauh-teal); border-bottom: 2px solid var(--kaauh-teal);
color: var(--kaauh-teal-dark); color: var(--kaauh-teal-dark);
font-weight: 700; font-weight: 700;
padding: 1rem 1.5rem; padding: 1rem 1.5rem;
} }
.card-header-themed h5 {
font-weight: 700;
color: var(--kaauh-teal-dark);
margin: 0;
display: flex;
align-items: center;
gap: 0.75rem;
}
/* Form Fixes for manual rendering consistency */
.form-label { .form-label {
font-weight: 600; font-weight: 600;
color: var(--kaauh-primary-text); color: var(--kaauh-primary-text);
margin-bottom: 0.3rem; margin-bottom: 0.3rem;
display: block; display: block;
} }
/* CRITICAL FIX: Target all relevant input types inside the form to mimic form-control class */
.card-body input:not([type="checkbox"]):not([type="radio"]):not([type="file"]):not([type="submit"]):not([type="button"]):not([type="reset"]), .card-body input:not([type="checkbox"]):not([type="radio"]):not([type="file"]):not([type="submit"]):not([type="button"]):not([type="reset"]),
.card-body textarea,
.card-body select { .card-body select {
/* Apply form-control styling */
border-radius: 0.5rem; border-radius: 0.5rem;
border: 1px solid #ced4da; border: 1px solid #ced4da;
width: 100%; width: 100%;
padding: 0.375rem 0.75rem; padding: 0.375rem 0.75rem;
transition: border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;
/* Ensure inputs rendered without class attribute are styled */
box-sizing: border-box; box-sizing: border-box;
} }
.card-body textarea {
min-height: 8rem; /* ================================================= */
} /* ✅ CORRECTED SUMMERNOTE FULL-WIDTH STYLING */
/* File input styling is kept minimal as it's harder to style universally */ /* ================================================= */
.card-body input[type="file"] {
padding-top: 0.5rem; /* Make every Summernote editor fill its container */
.note-editor {
width: 100% !important;
box-sizing: border-box !important;
margin: 0 !important;
max-width: none !important;
border-radius: 0.5rem;
} }
/* Set minimum heights for specific fields using sibling selector */
#id_description + .note-editor { min-height: 300px; }
#id_qualifications + .note-editor { min-height: 200px; }
#id_benefits + .note-editor,
#id_application_instructions + .note-editor { min-height: 150px; }
</style> </style>
{% endblock %} {% endblock %}
{% block content %} {% block content %}
<div class="container-fluid py-4"> <div class="container-fluid py-4">
<h1 class="h3 mb-4 text-primary fw-bold"> <h1 class="h3 mb-4 text-primary fw-bold">
<i class="fas fa-bullhorn me-2"></i> {% trans "Create New Job Posting" %} <i class="fas fa-bullhorn me-2"></i> {% if form.instance.pk %} {% trans "Edit Job Posting" %} {% else %} {% trans "Create New Job Posting" %} {% endif %}
</h1> </h1>
<form method="post" id="jobForm" class="mb-5"> <form method="post" id="jobForm" class="mb-5" enctype="multipart/form-data">
{% csrf_token %} {% csrf_token %}
{# ================================================= #}
{# SECTION 1: CORE POSITION DETAILS #}
{# ================================================= #}
<div class="card mb-4 shadow-sm"> <div class="card mb-4 shadow-sm">
<div class="card-header-themed"> <div class="card-header-themed">
<h5><i class="fas fa-info-circle"></i> {% trans "Basic Information" %}</h5> <h5><i class="fas fa-info-circle"></i> {% trans "Core Position Details" %}</h5>
</div> </div>
<div class="card-body"> <div class="card-body">
<div class="row g-4"> <div class="row g-4">
<div class="col-md-8"> <div class="col-md-8">
<div> <div>
<label for="{{ form.title.id_for_label }}" class="form-label">{% trans "Job Title" %} <span class="text-danger">*</span></label> <label for="{{ form.title.id_for_label }}" class="form-label">{% trans "Job Title" %} <span class="text-danger">*</span></label>
{# Removed |attr:"class:form-control" #}
{{ form.title }} {{ form.title }}
{% if form.title.errors %} {% if form.title.errors %}<div class="text-danger small mt-1">{{ form.title.errors }}</div>{% endif %}
<div class="text-danger small mt-1">{{ form.title.errors }}</div>
{% endif %}
</div> </div>
</div> </div>
<div class="col-md-4"> <div class="col-md-4">
<div> <div>
<label for="{{ form.job_type.id_for_label }}" class="form-label">{% trans "Job Type" %} <span class="text-danger">*</span></label> <label for="{{ form.job_type.id_for_label }}" class="form-label">{% trans "Job Type" %} <span class="text-danger">*</span></label>
{# Removed |attr:"class:form-control" #}
{{ form.job_type }} {{ form.job_type }}
{% if form.job_type.errors %} {% if form.job_type.errors %}<div class="text-danger small mt-1">{{ form.job_type.errors }}</div>{% endif %}
<div class="text-danger small mt-1">{{ form.job_type.errors }}</div>
{% endif %}
</div> </div>
</div> </div>
<div class="col-md-6"> <div class="col-md-6">
<div> <div>
<label for="{{ form.department.id_for_label }}" class="form-label">{% trans "Department" %}</label> <label for="{{ form.department.id_for_label }}" class="form-label">{% trans "Department" %}</label>
{# Removed |attr:"class:form-control" #}
{{ form.department }} {{ form.department }}
{% if form.department.errors %} {% if form.department.errors %}<div class="text-danger small mt-1">{{ form.department.errors }}</div>{% endif %}
<div class="text-danger small mt-1">{{ form.department.errors }}</div>
{% endif %}
</div> </div>
</div> </div>
<div class="col-md-6">
<div>
<label for="{{ form.position_number.id_for_label }}" class="form-label">{% trans "Position Number" %}</label>
{# Removed |attr:"class:form-control" #}
{{ form.position_number }}
{% if form.position_number.errors %}
<div class="text-danger small mt-1">{{ form.position_number.errors }}</div>
{% endif %}
</div>
</div>
<div class="col-md-6"> <div class="col-md-6">
<div> <div>
<label for="{{ form.workplace_type.id_for_label }}" class="form-label">{% trans "Workplace Type" %} <span class="text-danger">*</span></label> <label for="{{ form.workplace_type.id_for_label }}" class="form-label">{% trans "Workplace Type" %} <span class="text-danger">*</span></label>
{# Removed |attr:"class:form-control" #}
{{ form.workplace_type }} {{ form.workplace_type }}
{% if form.workplace_type.errors %} {% if form.workplace_type.errors %}<div class="text-danger small mt-1">{{ form.workplace_type.errors }}</div>{% endif %}
<div class="text-danger small mt-1">{{ form.workplace_type.errors }}</div>
{% endif %}
</div>
</div>
<div class="col-md-6">
<div>
<label for="{{ form.created_by.id_for_label }}" class="form-label">{% trans "Created By" %}</label>
{# Removed |attr:"class:form-control" #}
{{ form.created_by }}
{% if form.created_by.errors %}
<div class="text-danger small mt-1">{{ form.created_by.errors }}</div>
{% endif %}
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
{# ================================================= #}
{# SECTION 2: JOB CONTENT (All Summernote Fields) #}
{# ================================================= #}
<div class="card mb-4 shadow-sm"> <div class="card mb-4 shadow-sm">
<div class="card-header-themed"> <div class="card-header-themed">
<h5><i class="fas fa-map-marker-alt"></i> {% trans "Location" %}</h5> <h5><i class="fas fa-file-alt"></i> {% trans "Job Content" %}</h5>
</div>
<div class="card-body">
<div class="row g-4">
<div class="col-md-4">
<div>
<label for="{{ form.location_city.id_for_label }}" class="form-label">{% trans "City" %}</label>
{# Removed |attr:"class:form-control" #}
{{ form.location_city }}
{% if form.location_city.errors %}
<div class="text-danger small mt-1">{{ form.location_city.errors }}</div>
{% endif %}
</div>
</div>
<div class="col-md-4">
<div>
<label for="{{ form.location_state.id_for_label }}" class="form-label">{% trans "State/Province" %}</label>
{# Removed |attr:"class:form-control" #}
{{ form.location_state }}
{% if form.location_state.errors %}
<div class="text-danger small mt-1">{{ form.location_state.errors }}</div>
{% endif %}
</div>
</div>
<div class="col-md-4">
<div>
<label for="{{ form.location_country.id_for_label }}" class="form-label">{% trans "Country" %}</label>
{# Removed |attr:"class:form-control" #}
{{ form.location_country }}
{% if form.location_country.errors %}
<div class="text-danger small mt-1">{{ form.location_country.errors }}</div>
{% endif %}
</div>
</div>
</div>
</div>
</div>
<div class="card mb-4 shadow-sm">
<div class="card-header-themed">
<h5><i class="fas fa-file-alt"></i> {% trans "Job Details" %}</h5>
</div> </div>
<div class="card-body"> <div class="card-body">
<div class="row g-4"> <div class="row g-4">
<div class="col-12"> <div class="col-12">
<div> <div>
<label for="{{ form.description.id_for_label }}" class="form-label">{% trans "Job Description" %} <span class="text-danger">*</span></label> <label for="{{ form.description.id_for_label }}" class="form-label">{% trans "Job Description" %} <span class="text-danger">*</span></label>
{# Removed |attr:"class:form-control" #} {{ form.description}}
{{ form.description }} {% if form.description.errors %}<div class="text-danger small mt-1">{{ form.description.errors }}</div>{% endif %}
{% if form.description.errors %}
<div class="text-danger small mt-1">{{ form.description.errors }}</div>
{% endif %}
</div> </div>
</div> </div>
<div class="col-12"> <div class="col-12">
<div> <div>
<label for="{{ form.qualifications.id_for_label }}" class="form-label">{% trans "Qualifications and Requirements" %}</label> <label for="{{ form.qualifications.id_for_label }}" class="form-label">{% trans "Qualifications and Requirements" %}</label>
{# Removed |attr:"class:form-control" #} {{ form.qualifications}}
{{ form.qualifications }} {% if form.qualifications.errors %}<div class="text-danger small mt-1">{{ form.qualifications.errors }}</div>{% endif %}
{% if form.qualifications.errors %}
<div class="text-danger small mt-1">{{ form.qualifications.errors }}</div>
{% endif %}
</div>
</div>
<div class="col-md-6">
<div>
<label for="{{ form.salary_range.id_for_label }}" class="form-label">{% trans "Salary Range" %}</label>
{# Removed |attr:"class:form-control" #}
{{ form.salary_range }}
{% if form.salary_range.errors %}
<div class="text-danger small mt-1">{{ form.salary_range.errors }}</div>
{% endif %}
</div>
</div>
<div class="col-md-6">
<div>
<label for="{{ form.benefits.id_for_label }}" class="form-label">{% trans "Benefits" %}</label>
{# Removed |attr:"class:form-control" #}
{{ form.benefits }}
{% if form.benefits.errors %}
<div class="text-danger small mt-1">{{ form.benefits.errors }}</div>
{% endif %}
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
{# ================================================= #}
{# SECTION 3: COMPENSATION AND APPLICATION #}
{# ================================================= #}
<div class="card mb-4 shadow-sm"> <div class="card mb-4 shadow-sm">
<div class="card-header-themed"> <div class="card-header-themed">
<h5><i class="fas fa-file-signature"></i> {% trans "Application Information" %}</h5> <h5><i class="fas fa-dollar-sign"></i> {% trans "Compensation & Application" %}</h5>
</div> </div>
<div class="card-body"> <div class="card-body">
<div class="row g-4"> <div class="row g-4">
<div class="col-12"> <div class="col-md-6">
<div>
<label for="{{ form.salary_range.id_for_label }}" class="form-label">{% trans "Salary Range" %}</label>
{{ form.salary_range }}
{% if form.salary_range.errors %}<div class="text-danger small mt-1">{{ form.salary_range.errors }}</div>{% endif %}
</div>
</div>
<div class="col-md-6">
<div> <div>
<label for="{{ form.application_url.id_for_label }}" class="form-label">{% trans "Application URL" %} <span class="text-danger">*</span></label> <label for="{{ form.application_url.id_for_label }}" class="form-label">{% trans "Application URL" %} <span class="text-danger">*</span></label>
{# Removed |attr:"class:form-control" #}
{{ form.application_url }} {{ form.application_url }}
{% if form.application_url.errors %} {% if form.application_url.errors %}<div class="text-danger small mt-1">{{ form.application_url.errors }}</div>{% endif %}
<div class="text-danger small mt-1">{{ form.application_url.errors }}</div>
{% endif %}
<div class="form-text">{% trans "Full URL where candidates will apply" %}</div> <div class="form-text">{% trans "Full URL where candidates will apply" %}</div>
</div> </div>
</div> </div>
<div class="col-md-6"> <div class="col-12">
<div> <div>
<label for="{{ form.application_deadline.id_for_label }}" class="form-label">{% trans "Application Deadline" %}</label> <label for="{{ form.benefits.id_for_label }}" class="form-label">{% trans "Benefits" %}</label>
{# Removed |attr:"class:form-control" #} {{ form.benefits }}
{{ form.application_deadline }} {% if form.benefits.errors %}<div class="text-danger small mt-1">{{ form.benefits.errors }}</div>{% endif %}
{% if form.application_deadline.errors %}
<div class="text-danger small mt-1">{{ form.application_deadline.errors }}</div>
{% endif %}
</div>
</div>
<div class="col-md-6">
<div>
<label for="{{ form.start_date.id_for_label }}" class="form-label">{% trans "Desired Start Date" %}</label>
{# Removed |attr:"class:form-control" #}
{{ form.start_date }}
{% if form.start_date.errors %}
<div class="text-danger small mt-1">{{ form.start_date.errors }}</div>
{% endif %}
</div> </div>
</div> </div>
<div class="col-12"> <div class="col-12">
<div> <div>
<label for="{{ form.application_instructions.id_for_label }}" class="form-label">{% trans "Application Instructions" %}</label> <label for="{{ form.application_instructions.id_for_label }}" class="form-label">{% trans "Application Instructions" %}</label>
{# Removed |attr:"class:form-control" #}
{{ form.application_instructions }} {{ form.application_instructions }}
{% if form.application_instructions.errors %} {% if form.application_instructions.errors %}<div class="text-danger small mt-1">{{ form.application_instructions.errors }}</div>{% endif %}
<div class="text-danger small mt-1">{{ form.application_instructions.errors }}</div>
{% endif %}
</div>
</div>
</div>
</div>
</div>
<div class="card mb-4 shadow-sm">
<div class="card-header-themed">
<h5><i class="fas fa-star"></i>{% trans "Post Reach Field" %}</h5>
</div>
<div class="card-body">
<div class="row g-4">
<div class="col-md-12">
<div>
<label for="{{ form.hash_tags.id_for_label }}" class="form-label">{% trans "Hashtags" %}</label>
{# Removed |attr:"class:form-control" #}
{{ form.hash_tags }}
{% if form.hash_tags.errors %}
<div class="text-danger small mt-1">{{ form.hash_tags.errors }}</div>
{% endif %}
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
{# ================================================= #}
{# SECTION 4: LOCATION AND DATES #}
{# ================================================= #}
<div class="card mb-4 shadow-sm"> <div class="card mb-4 shadow-sm">
<div class="card-header-themed"> <div class="card-header-themed">
<h5><i class="fas fa-building"></i> {% trans "Internal Information" %}</h5> <h5><i class="fas fa-map-marker-alt"></i> {% trans "Location, Dates, & Status" %}</h5>
</div>
<div class="card-body">
<div class="row g-4">
<div class="col-md-4">
<div>
<label for="{{ form.location_city.id_for_label }}" class="form-label">{% trans "City" %}</label>
{{ form.location_city }}
{% if form.location_city.errors %}<div class="text-danger small mt-1">{{ form.location_city.errors }}</div>{% endif %}
</div>
</div>
<div class="col-md-4">
<div>
<label for="{{ form.location_state.id_for_label }}" class="form-label">{% trans "State/Province" %}</label>
{{ form.location_state }}
{% if form.location_state.errors %}<div class="text-danger small mt-1">{{ form.location_state.errors }}</div>{% endif %}
</div>
</div>
<div class="col-md-4">
<div>
<label for="{{ form.location_country.id_for_label }}" class="form-label">{% trans "Country" %}</label>
{{ form.location_country }}
{% if form.location_country.errors %}<div class="text-danger small mt-1">{{ form.location_country.errors }}</div>{% endif %}
</div>
</div>
<div class="col-md-4">
<div>
<label for="{{ form.application_deadline.id_for_label }}" class="form-label">{% trans "Application Deadline" %}</label>
{{ form.application_deadline }}
{% if form.application_deadline.errors %}<div class="text-danger small mt-1">{{ form.application_deadline.errors }}</div>{% endif %}
</div>
</div>
<div class="col-md-4">
<div>
<label for="{{ form.start_date.id_for_label }}" class="form-label">{% trans "Desired Start Date" %}</label>
{{ form.start_date }}
{% if form.start_date.errors %}<div class="text-danger small mt-1">{{ form.start_date.errors }}</div>{% endif %}
</div>
</div>
<div class="col-md-4">
<div>
<label for="{{ form.status.id_for_label }}" class="form-label">{% trans "Status" %}</label>
{{ form.status }}
{% if form.status.errors %}<div class="text-danger small mt-1">{{ form.status.errors }}</div>{% endif %}
</div>
</div>
</div>
</div>
</div>
{# ================================================= #}
{# SECTION 5: INTERNAL AND PROMOTION #}
{# ================================================= #}
<div class="card mb-4 shadow-sm">
<div class="card-header-themed">
<h5><i class="fas fa-tags"></i> {% trans "Internal & Promotion" %}</h5>
</div> </div>
<div class="card-body"> <div class="card-body">
<div class="row g-4"> <div class="row g-4">
<div class="col-md-6">
<div>
<label for="{{ form.position_number.id_for_label }}" class="form-label">{% trans "Position Number" %}</label>
{{ form.position_number }}
{% if form.position_number.errors %}<div class="text-danger small mt-1">{{ form.position_number.errors }}</div>{% endif %}
</div>
</div>
<div class="col-md-6"> <div class="col-md-6">
<div> <div>
<label for="{{ form.reporting_to.id_for_label }}" class="form-label">{% trans "Reports To" %}</label> <label for="{{ form.reporting_to.id_for_label }}" class="form-label">{% trans "Reports To" %}</label>
{# Removed |attr:"class:form-control" #}
{{ form.reporting_to }} {{ form.reporting_to }}
{% if form.reporting_to.errors %} {% if form.reporting_to.errors %}<div class="text-danger small mt-1">{{ form.reporting_to.errors }}</div>{% endif %}
<div class="text-danger small mt-1">{{ form.reporting_to.errors }}</div>
{% endif %}
</div> </div>
</div> </div>
<div class="col-md-6"> <div class="col-md-6">
<div> <div>
<label for="{{ form.open_positions.id_for_label }}" class="form-label">{% trans "Open Positions" %}</label> <label for="{{ form.open_positions.id_for_label }}" class="form-label">{% trans "Open Positions" %}</label>
{# Removed |attr:"class:form-control" #}
{{ form.open_positions }} {{ form.open_positions }}
{% if form.open_positions.errors %} {% if form.open_positions.errors %}<div class="text-danger small mt-1">{{ form.open_positions.errors }}</div>{% endif %}
<div class="text-danger small mt-1">{{ form.open_positions.errors }}</div> </div>
{% endif %} </div>
<div class="col-md-6">
<div>
<label for="{{ form.created_by.id_for_label }}" class="form-label">{% trans "Created By" %}</label>
{{ form.created_by }}
{% if form.created_by.errors %}<div class="text-danger small mt-1">{{ form.created_by.errors }}</div>{% endif %}
</div>
</div>
<div class="col-12">
<div>
<label for="{{ form.hash_tags.id_for_label }}" class="form-label">{% trans "Hashtags (For Promotion/Search)" %}</label>
{{ form.hash_tags }}
{% if form.hash_tags.errors %}<div class="text-danger small mt-1">{{ form.hash_tags.errors }}</div>{% endif %}
<div class="form-text">{% trans "Comma-separated list of hashtags, e.g., #hiring, #professor" %}</div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
{# ================================================= #}
{# ACTION BUTTONS #}
{# ================================================= #}
<div class="d-flex justify-content-between pt-2"> <div class="d-flex justify-content-between pt-2">
<a href="{% url 'job_list' %}" class="btn btn-secondary"> <a href="{% url 'job_list' %}" class="btn btn-secondary">
<i class="fas fa-arrow-left me-1"></i> {% trans "Cancel" %} <i class="fas fa-arrow-left me-1"></i> {% trans "Cancel" %}
</a> </a>
<button type="submit" class="btn btn-main-action"> <button type="submit" class="btn btn-main-action">
<i class="fas fa-save me-1"></i> {% trans "Create Job" %} <i class="fas fa-save me-1"></i> {% trans "Save Job" %}
</button> </button>
</div> </div>
</form> </form>
</div> </div>
{# 💡 2. Add Summernote JS Media at the end of the body #}
{{ form.media.js }}
{% endblock %} {% endblock %}

View File

@ -1,259 +1,354 @@
{% extends "base.html" %} {% extends "base.html" %}
{% load static i18n %}
{% block title %}Edit {{ job.title }} - University ATS{% endblock %} {% block title %}Edit {{ job.title }} - University ATS{% endblock %}
{% block customCSS %}
{# 💡 1. Add Summernote CSS Media in the head #}
{{ form.media.css }}
<style>
/* ================================================= */
/* THEME VARIABLES AND GLOBAL STYLES (Keep existing) */
/* ================================================= */
:root {
--kaauh-teal: #00636e;
--kaauh-teal-dark: #004a53;
--kaauh-border: #eaeff3;
--kaauh-primary-text: #343a40;
}
.text-primary { color: var(--kaauh-teal) !important; }
.btn-main-action, .btn-primary {
background-color: var(--kaauh-teal);
border-color: var(--kaauh-teal);
color: white;
font-weight: 600;
padding: 0.6rem 1.2rem;
transition: all 0.2s ease;
display: inline-flex;
align-items: center;
gap: 0.5rem;
}
.btn-main-action:hover, .btn-primary:hover {
background-color: var(--kaauh-teal-dark);
border-color: var(--kaauh-teal-dark);
transform: translateY(-1px);
box-shadow: 0 4px 8px rgba(0,0,0,0.15);
}
.btn-secondary {
background-color: #f8f9fa;
color: var(--kaauh-teal-dark);
border: 1px solid var(--kaauh-border);
font-weight: 500;
}
.btn-secondary:hover {
background-color: #e9ecef;
color: var(--kaauh-teal-dark); /* Reverting color to dark theme text */
border-color: var(--kaauh-teal-dark);
}
.card {
border: 1px solid var(--kaauh-border);
border-radius: 0.75rem;
overflow: hidden;
box-shadow: 0 4px 12px rgba(0,0,0,0.06);
background-color: white;
}
.card-header-themed {
background-color: #f0f8ff;
border-bottom: 2px solid var(--kaauh-teal);
color: var(--kaauh-teal-dark);
font-weight: 700;
padding: 1rem 1.5rem;
}
.form-label {
font-weight: 600;
color: var(--kaauh-primary-text);
margin-bottom: 0.3rem;
display: block;
}
.card-body input:not([type="checkbox"]):not([type="radio"]):not([type="file"]):not([type="submit"]):not([type="button"]):not([type="reset"]),
.card-body select {
border-radius: 0.5rem;
border: 1px solid #ced4da;
width: 100%;
padding: 0.375rem 0.75rem;
box-sizing: border-box;
}
/* ================================================= */
/* ✅ CORRECTED SUMMERNOTE FULL-WIDTH STYLING (Active Fix) */
/* ================================================= */
/* The most aggressive, universal fix for Summernote within Bootstrap columns */
.col-12 .note-editor {
/* This compensates for the 0.75rem padding on each side of the col-12 */
width: calc(100% + 1.5rem) !important;
margin-left: -0.75rem !important;
margin-right: -0.75rem !important;
/* General cleanup to maintain look */
box-sizing: border-box;
margin-bottom: 0 !important;
border-radius: 0.5rem;
}
/* Set minimum heights for specific fields using sibling selector */
#id_description + .note-editor { min-height: 300px; }
#id_qualifications + .note-editor { min-height: 200px; }
#id_benefits + .note-editor,
#id_application_instructions + .note-editor { min-height: 150px; }
</style>
{% endblock %}
{% block content %} {% block content %}
<div class="row justify-content-center"> <div class="container-fluid py-4">
<div class="col-lg-10"> <h1 class="h3 mb-4 text-primary fw-bold">
<div class="card"> {# UPDATED TITLE FOR EDIT CONTEXT #}
<div class="card-header"> <i class="fas fa-edit me-2"></i> {% trans "Edit Job Posting" %}
<h2><i class="fas fa-edit"></i> Edit Job Posting</h2> {% if job.title %} - {{ job.title }} {% endif %}
<small class="text-muted">Internal ID: {{ job.internal_job_id }}</small> </h1>
<form method="post" id="jobForm" class="mb-5" enctype="multipart/form-data">
{% csrf_token %}
{# ================================================= #}
{# SECTION 1: CORE POSITION DETAILS #}
{# ================================================= #}
<div class="card mb-4 shadow-sm">
<div class="card-header-themed">
<h5><i class="fas fa-info-circle"></i> {% trans "Core Position Details" %}</h5>
</div> </div>
<div class="card-body"> <div class="card-body">
<form method="post" id="jobForm"> <div class="row g-4">
{% csrf_token %} <div class="col-md-8">
<div>
<!-- Basic Information Section --> <label for="{{ form.title.id_for_label }}" class="form-label">{% trans "Job Title" %} <span class="text-danger">*</span></label>
<div class="card mb-4"> {{ form.title }}
<div class="card-header bg-light"> {% if form.title.errors %}<div class="text-danger small mt-1">{{ form.title.errors }}</div>{% endif %}
<h5 class="mb-0"><i class="fas fa-info-circle"></i> Basic Information</h5>
</div> </div>
<div class="card-body"> </div>
<div class="row"> <div class="col-md-4">
<div class="col-md-8"> <div>
<div class="mb-3"> <label for="{{ form.job_type.id_for_label }}" class="form-label">{% trans "Job Type" %} <span class="text-danger">*</span></label>
<label for="{{ form.title.id_for_label }}" class="form-label">Job Title <span class="text-danger">*</span></label> {{ form.job_type }}
{{ form.title }} {% if form.job_type.errors %}<div class="text-danger small mt-1">{{ form.job_type.errors }}</div>{% endif %}
{% if form.title.errors %}
<div class="text-danger mt-1">{{ form.title.errors }}</div>
{% endif %}
</div>
</div>
<div class="col-md-4">
<div class="mb-3">
<label for="{{ form.job_type.id_for_label }}" class="form-label">Job Type <span class="text-danger">*</span></label>
{{ form.job_type }}
{% if form.job_type.errors %}
<div class="text-danger mt-1">{{ form.job_type.errors }}</div>
{% endif %}
</div>
</div>
</div>
<div class="row">
<div class="col-md-6">
<div class="mb-3">
<label for="{{ form.department.id_for_label }}" class="form-label">Department</label>
{{ form.department }}
{% if form.department.errors %}
<div class="text-danger mt-1">{{ form.department.errors }}</div>
{% endif %}
</div>
</div>
<div class="col-md-6">
<div class="mb-3">
<label for="{{ form.position_number.id_for_label }}" class="form-label">Position Number</label>
{{ form.position_number }}
{% if form.position_number.errors %}
<div class="text-danger mt-1">{{ form.position_number.errors }}</div>
{% endif %}
</div>
</div>
</div>
<div class="row">
<div class="col-md-6">
<div class="mb-3">
<label for="{{ form.workplace_type.id_for_label }}" class="form-label">Workplace Type <span class="text-danger">*</span></label>
{{ form.workplace_type }}
{% if form.workplace_type.errors %}
<div class="text-danger mt-1">{{ form.workplace_type.errors }}</div>
{% endif %}
</div>
</div>
<div class="col-md-6">
<div class="mb-3">
<label for="{{ form.created_by.id_for_label }}" class="form-label">Created By</label>
{{ form.created_by }}
{% if form.created_by.errors %}
<div class="text-danger mt-1">{{ form.created_by.errors }}</div>
{% endif %}
</div>
</div>
</div>
</div> </div>
</div> </div>
<!-- Location Section --> <div class="col-md-6">
<div class="card mb-4"> <div>
<div class="card-header bg-light"> <label for="{{ form.department.id_for_label }}" class="form-label">{% trans "Department" %}</label>
<h5 class="mb-0"><i class="fas fa-map-marker-alt"></i> Location</h5> {{ form.department }}
</div> {% if form.department.errors %}<div class="text-danger small mt-1">{{ form.department.errors }}</div>{% endif %}
<div class="card-body">
<div class="row">
<div class="col-md-4">
<div class="mb-3">
<label for="{{ form.location_city.id_for_label }}" class="form-label">City</label>
{{ form.location_city }}
{% if form.location_city.errors %}
<div class="text-danger mt-1">{{ form.location_city.errors }}</div>
{% endif %}
</div>
</div>
<div class="col-md-4">
<div class="mb-3">
<label for="{{ form.location_state.id_for_label }}" class="form-label">State/Province</label>
{{ form.location_state }}
{% if form.location_state.errors %}
<div class="text-danger mt-1">{{ form.location_state.errors }}</div>
{% endif %}
</div>
</div>
<div class="col-md-4">
<div class="mb-3">
<label for="{{ form.location_country.id_for_label }}" class="form-label">Country</label>
{{ form.location_country }}
{% if form.location_country.errors %}
<div class="text-danger mt-1">{{ form.location_country.errors }}</div>
{% endif %}
</div>
</div>
</div>
</div> </div>
</div> </div>
<div class="col-md-6">
<!-- Job Details Section --> <div>
<div class="card mb-4"> <label for="{{ form.workplace_type.id_for_label }}" class="form-label">{% trans "Workplace Type" %} <span class="text-danger">*</span></label>
<div class="card-header bg-light"> {{ form.workplace_type }}
<h5 class="mb-0"><i class="fas fa-file-alt"></i> Job Details</h5> {% if form.workplace_type.errors %}<div class="text-danger small mt-1">{{ form.workplace_type.errors }}</div>{% endif %}
</div>
<div class="card-body">
<div class="mb-3">
<label for="{{ form.description.id_for_label }}" class="form-label">Job Description <span class="text-danger">*</span></label>
{{ form.description }}
{% if form.description.errors %}
<div class="text-danger mt-1">{{ form.description.errors }}</div>
{% endif %}
</div>
<div class="mb-3">
<label for="{{ form.qualifications.id_for_label }}" class="form-label">Qualifications and Requirements</label>
{{ form.qualifications }}
{% if form.qualifications.errors %}
<div class="text-danger mt-1">{{ form.qualifications.errors }}</div>
{% endif %}
</div>
<div class="row">
<div class="col-md-6">
<div class="mb-3">
<label for="{{ form.salary_range.id_for_label }}" class="form-label">Salary Range</label>
{{ form.salary_range }}
{% if form.salary_range.errors %}
<div class="text-danger mt-1">{{ form.salary_range.errors }}</div>
{% endif %}
</div>
</div>
<div class="col-md-6">
<div class="mb-3">
<label for="{{ form.benefits.id_for_label }}" class="form-label">Benefits</label>
{{ form.benefits }}
{% if form.benefits.errors %}
<div class="text-danger mt-1">{{ form.benefits.errors }}</div>
{% endif %}
</div>
</div>
</div>
</div> </div>
</div> </div>
</div>
<!-- Application Information Section -->
<div class="card mb-4">
<div class="card-header bg-light">
<h5 class="mb-0"><i class="fas fa-file-signature"></i> Application Information</h5>
</div>
<div class="card-body">
<div class="mb-3">
<label for="{{ form.application_url.id_for_label }}" class="form-label">Application URL <span class="text-danger">*</span></label>
{{ form.application_url }}
{% if form.application_url.errors %}
<div class="text-danger mt-1">{{ form.application_url.errors }}</div>
{% endif %}
</div>
<div class="row">
<div class="col-md-6">
<div class="mb-3">
<label for="{{ form.application_deadline.id_for_label }}" class="form-label">Application Deadline</label>
{{ form.application_deadline }}
{% if form.application_deadline.errors %}
<div class="text-danger mt-1">{{ form.application_deadline.errors }}</div>
{% endif %}
</div>
</div>
<div class="col-md-6">
<div class="mb-3">
<label for="{{ form.start_date.id_for_label }}" class="form-label">Desired Start Date</label>
{{ form.start_date }}
{% if form.start_date.errors %}
<div class="text-danger mt-1">{{ form.start_date.errors }}</div>
{% endif %}
</div>
</div>
</div>
<div class="mb-3">
<label for="{{ form.application_instructions.id_for_label }}" class="form-label">Application Instructions</label>
{{ form.application_instructions }}
{% if form.application_instructions.errors %}
<div class="text-danger mt-1">{{ form.application_instructions.errors }}</div>
{% endif %}
</div>
</div>
</div>
<!-- Internal Information Section -->
<div class="card mb-4">
<div class="card-header bg-light">
<h5 class="mb-0"><i class="fas fa-building"></i> Internal Information</h5>
</div>
<div class="card-body">
<div class="row">
<div class="col-md-6">
<div class="mb-3">
<label for="{{ form.reporting_to.id_for_label }}" class="form-label">Reports To</label>
{{ form.reporting_to }}
{% if form.reporting_to.errors %}
<div class="text-danger mt-1">{{ form.reporting_to.errors }}</div>
{% endif %}
</div>
</div>
<div class="col-md-6">
<div class="mb-3">
<label for="{{ form.open_positions.id_for_label }}" class="form-label">Open positions</label>
{{ form.open_positions }}
{% if form.open_positions.errors %}
<div class="text-danger mt-1">{{ form.open_positions.errors }}</div>
{% endif %}
</div>
</div>
</div>
</div>
</div>
<!-- Form Actions -->
<div class="d-flex justify-content-between">
<a href="{% url 'job_detail' job.slug %}" class="btn btn-secondary">
<i class="fas fa-arrow-left"></i> Cancel
</a>
<button type="submit" class="btn btn-primary">
<i class="fas fa-save"></i> Update Job
</button>
</div>
</form>
</div> </div>
</div> </div>
</div>
{# ================================================= #}
{# SECTION 2: JOB CONTENT (All Summernote Fields) #}
{# ================================================= #}
<div class="card mb-4 shadow-sm">
<div class="card-header-themed">
<h5><i class="fas fa-file-alt"></i> {% trans "Job Content" %}</h5>
</div>
<div class="card-body">
<div class="row g-4">
<div class="col-12">
<div>
<label for="{{ form.description.id_for_label }}" class="form-label">{% trans "Job Description" %} <span class="text-danger">*</span></label>
{{ form.description }}
{% if form.description.errors %}<div class="text-danger small mt-1">{{ form.description.errors }}</div>{% endif %}
</div>
</div>
<div class="col-12">
<div>
<label for="{{ form.qualifications.id_for_label }}" class="form-label">{% trans "Qualifications and Requirements" %}</label>
{{ form.qualifications }}
{% if form.qualifications.errors %}<div class="text-danger small mt-1">{{ form.qualifications.errors }}</div>{% endif %}
</div>
</div>
</div>
</div>
</div>
{# ================================================= #}
{# SECTION 3: COMPENSATION AND APPLICATION #}
{# ================================================= #}
<div class="card mb-4 shadow-sm">
<div class="card-header-themed">
<h5><i class="fas fa-dollar-sign"></i> {% trans "Compensation & Application" %}</h5>
</div>
<div class="card-body">
<div class="row g-4">
<div class="col-md-6">
<div>
<label for="{{ form.salary_range.id_for_label }}" class="form-label">{% trans "Salary Range" %}</label>
{{ form.salary_range }}
{% if form.salary_range.errors %}<div class="text-danger small mt-1">{{ form.salary_range.errors }}</div>{% endif %}
</div>
</div>
<div class="col-md-6">
<div>
<label for="{{ form.application_url.id_for_label }}" class="form-label">{% trans "Application URL" %} <span class="text-danger">*</span></label>
{{ form.application_url }}
{% if form.application_url.errors %}<div class="text-danger small mt-1">{{ form.application_url.errors }}</div>{% endif %}
<div class="form-text">{% trans "Full URL where candidates will apply" %}</div>
</div>
</div>
<div class="col-12">
<div>
<label for="{{ form.benefits.id_for_label }}" class="form-label">{% trans "Benefits" %}</label>
{{ form.benefits }}
{% if form.benefits.errors %}<div class="text-danger small mt-1">{{ form.benefits.errors }}</div>{% endif %}
</div>
</div>
<div class="col-12">
<div>
<label for="{{ form.application_instructions.id_for_label }}" class="form-label">{% trans "Application Instructions" %}</label>
{{ form.application_instructions }}
{% if form.application_instructions.errors %}<div class="text-danger small mt-1">{{ form.application_instructions.errors }}</div>{% endif %}
</div>
</div>
</div>
</div>
</div>
{# ================================================= #}
{# SECTION 4: LOCATION AND DATES #}
{# ================================================= #}
<div class="card mb-4 shadow-sm">
<div class="card-header-themed">
<h5><i class="fas fa-map-marker-alt"></i> {% trans "Location, Dates, & Status" %}</h5>
</div>
<div class="card-body">
<div class="row g-4">
<div class="col-md-4">
<div>
<label for="{{ form.location_city.id_for_label }}" class="form-label">{% trans "City" %}</label>
{{ form.location_city }}
{% if form.location_city.errors %}<div class="text-danger small mt-1">{{ form.location_city.errors }}</div>{% endif %}
</div>
</div>
<div class="col-md-4">
<div>
<label for="{{ form.location_state.id_for_label }}" class="form-label">{% trans "State/Province" %}</label>
{{ form.location_state }}
{% if form.location_state.errors %}<div class="text-danger small mt-1">{{ form.location_state.errors }}</div>{% endif %}
</div>
</div>
<div class="col-md-4">
<div>
<label for="{{ form.location_country.id_for_label }}" class="form-label">{% trans "Country" %}</label>
{{ form.location_country }}
{% if form.location_country.errors %}<div class="text-danger small mt-1">{{ form.location_country.errors }}</div>{% endif %}
</div>
</div>
<div class="col-md-4">
<div>
<label for="{{ form.application_deadline.id_for_label }}" class="form-label">{% trans "Application Deadline" %}</label>
{{ form.application_deadline }}
{% if form.application_deadline.errors %}<div class="text-danger small mt-1">{{ form.application_deadline.errors }}</div>{% endif %}
</div>
</div>
<div class="col-md-4">
<div>
<label for="{{ form.start_date.id_for_label }}" class="form-label">{% trans "Desired Start Date" %}</label>
{{ form.start_date }}
{% if form.start_date.errors %}<div class="text-danger small mt-1">{{ form.start_date.errors }}</div>{% endif %}
</div>
</div>
<div class="col-md-4">
<div>
<label for="{{ form.status.id_for_label }}" class="form-label">{% trans "Status" %}</label>
{{ form.status }}
{% if form.status.errors %}<div class="text-danger small mt-1">{{ form.status.errors }}</div>{% endif %}
</div>
</div>
</div>
</div>
</div>
{# ================================================= #}
{# SECTION 5: INTERNAL AND PROMOTION #}
{# ================================================= #}
<div class="card mb-4 shadow-sm">
<div class="card-header-themed">
<h5><i class="fas fa-tags"></i> {% trans "Internal & Promotion" %}</h5>
</div>
<div class="card-body">
<div class="row g-4">
<div class="col-md-6">
<div>
<label for="{{ form.position_number.id_for_label }}" class="form-label">{% trans "Position Number" %}</label>
{{ form.position_number }}
{% if form.position_number.errors %}<div class="text-danger small mt-1">{{ form.position_number.errors }}</div>{% endif %}
</div>
</div>
<div class="col-md-6">
<div>
<label for="{{ form.reporting_to.id_for_label }}" class="form-label">{% trans "Reports To" %}</label>
{{ form.reporting_to }}
{% if form.reporting_to.errors %}<div class="text-danger small mt-1">{{ form.reporting_to.errors }}</div>{% endif %}
</div>
</div>
<div class="col-md-6">
<div>
<label for="{{ form.open_positions.id_for_label }}" class="form-label">{% trans "Open Positions" %}</label>
{{ form.open_positions }}
{% if form.open_positions.errors %}<div class="text-danger small mt-1">{{ form.open_positions.errors }}</div>{% endif %}
</div>
</div>
<div class="col-md-6">
<div>
<label for="{{ form.created_by.id_for_label }}" class="form-label">{% trans "Created By" %}</label>
{{ form.created_by }}
{% if form.created_by.errors %}<div class="text-danger small mt-1">{{ form.created_by.errors }}</div>{% endif %}
</div>
</div>
<div class="col-12">
<div>
<label for="{{ form.hash_tags.id_for_label }}" class="form-label">{% trans "Hashtags (For Promotion/Search)" %}</label>
{{ form.hash_tags }}
{% if form.hash_tags.errors %}<div class="text-danger small mt-1">{{ form.hash_tags.errors }}</div>{% endif %}
<div class="form-text">{% trans "Comma-separated list of hashtags, e.g., #hiring, #professor" %}</div>
</div>
</div>
</div>
</div>
</div>
{# ================================================= #}
{# ACTION BUTTONS #}
{# ================================================= #}
<div class="d-flex justify-content-between pt-2">
{# UPDATED CANCEL URL for Job Detail #}
<a href="{% url 'job_detail' job.slug %}" class="btn btn-secondary">
<i class="fas fa-arrow-left me-1"></i> {% trans "Cancel" %}
</a>
<button type="submit" class="btn btn-main-action">
{# UPDATED BUTTON TEXT for Edit action #}
<i class="fas fa-save me-1"></i> {% trans "Update Job" %}
</button>
</div>
</form>
</div> </div>
{% endblock %}
{# 💡 2. Add Summernote JS Media at the end of the body #}
{{ form.media.js }}
{% endblock %}

View File

@ -249,7 +249,7 @@
{# HEADER SECTION #} {# HEADER SECTION #}
<div class="job-header-card d-flex justify-content-between align-items-center flex-wrap"> <div class="job-header-card d-flex justify-content-between align-items-center flex-wrap">
<h2>{{ job.title }}</h2> <h2>{{job}}</h2>
<span class="badge bg-{{ job.status|lower|striptags|yesno:'success,warning,secondary,danger' }} status-badge"> <span class="badge bg-{{ job.status|lower|striptags|yesno:'success,warning,secondary,danger' }} status-badge">
{{ job.get_status_display }} {{ job.get_status_display }}
</span> </span>
@ -339,19 +339,19 @@
{% if job.description %} {% if job.description %}
<div class="mb-4"> <div class="mb-4">
<h5>{% trans "Job Description" %}</h5> <h5>{% trans "Job Description" %}</h5>
<div class="text-secondary">{{ job.description|linebreaks }}</div> <div class="text-secondary">{{ job.description|safe }}</div>
</div> </div>
{% endif %} {% endif %}
{% if job.qualifications %} {% if job.qualifications %}
<div class="mb-4"> <div class="mb-4">
<h5>{% trans "Required Qualifications" %}</h5> <h5>{% trans "Required Qualifications" %}</h5>
<div class="text-secondary">{{ job.qualifications|linebreaks }}</div> <div class="text-secondary">{{ job.qualifications|safe }}</div>
</div> </div>
{% endif %} {% endif %}
{% if job.benefits %} {% if job.benefits %}
<div class="mb-4"> <div class="mb-4">
<h5>{% trans "Benefits" %}</h5> <h5>{% trans "Benefits" %}</h5>
<div class="text-secondary">{{ job.benefits|linebreaks }}</div> <div class="text-secondary">{{ job.benefits|safe}}</div>
</div> </div>
{% endif %} {% endif %}
</div> </div>
@ -361,7 +361,7 @@
<div class="tab-pane fade" id="instructions" role="tabpanel" aria-labelledby="instructions-tab"> <div class="tab-pane fade" id="instructions" role="tabpanel" aria-labelledby="instructions-tab">
<div class="mb-4"> <div class="mb-4">
<h5>{% trans "Application Instructions" %}</h5> <h5>{% trans "Application Instructions" %}</h5>
<div class="text-secondary">{{ job.application_instructions|linebreaks }}</div> <div class="text-secondary">{{ job.application_instructions|safe }}</div>
</div> </div>
</div> </div>
{% endif %} {% endif %}
@ -526,7 +526,7 @@
{% trans "Manage the custom application forms associated with this job posting." %} {% trans "Manage the custom application forms associated with this job posting." %}
</p> </p>
<a href="" class="btn btn-main-action"> <a href="{% url 'create_form_template' %}" class="btn btn-main-action">
<i class="fas fa-plus-circle me-2"></i> {% trans "Create New Form" %} <i class="fas fa-plus-circle me-2"></i> {% trans "Create New Form" %}
</a> </a>
@ -534,9 +534,9 @@
<i class="fas fa-list-alt me-1"></i> {% trans "View All Existing Forms" %} <i class="fas fa-list-alt me-1"></i> {% trans "View All Existing Forms" %}
</a> </a>
<a href="{% url 'candidate_create_for_job' job.slug %}" class="btn btn-success me-2"> <a href="{% url 'candidate_create_for_job' job.slug %}" class="btn btn-main-action">
<i class="fas fa-user-plus"></i> {% trans "Create Candidate" %} <i class="fas fa-user-plus"></i> {% trans "Create Candidate" %}
</a> </a>
</div> </div>
</div> </div>

View File

@ -1,18 +1,30 @@
{% extends "base.html" %} {% load static i18n %}
<!DOCTYPE html>
{% block title %}{{ job.title }} - University ATS{% endblock %} <html lang="en">
{% block customCSS %} <head>
<meta charset="UTF-8">
<style> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{% translate "Application Form" %}</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<style>
/* ---------------------------------------------------------------------- */ /* ---------------------------------------------------------------------- */
/* THEME STYLES (Keep from previous response) */ /* THEME & UTILITY VARIABLES */
/* ---------------------------------------------------------------------- */ /* ---------------------------------------------------------------------- */
:root { :root {
--kaauh-teal: #00636e; --kaauh-teal: #00636e;
--kaauh-teal-dark: #004a53; --kaauh-teal-dark: #004a53;
--success: #198754; --success: #198754;
/* Define a subtle background for mobile sticky bar */
--light-bg: #f8f9fa; --light-bg: #f8f9fa;
/* CALCULATED STICKY HEIGHTS */
/* Standard Bootstrap Navbar Height (approx 56px) */
--navbar-height: 56px;
/* Bootstrap mb-3 spacing (1rem or 16px) */
--navbar-gap: 16px;
/* Combined height of the two navbars when collapsed on desktop (56 + 16 + 56 = 128px) */
--sticky-navbar-total-height: 128px;
} }
.btn-main-action { .btn-main-action {
@ -32,14 +44,67 @@
.bg-kaauh-teal-dark { .bg-kaauh-teal-dark {
background-color: var(--kaauh-teal-dark) !important; background-color: var(--kaauh-teal-dark) !important;
} }
/* ---------------------------------------------------------------------- */ /* ---------------------------------------------------------------------- */
/* MOBILE RESPONSIVE STYLES */ /* LAYOUT & STICKY POSITIONING FIXES (Desktop/Tablet) */
/* ---------------------------------------------------------------------- */ /* ---------------------------------------------------------------------- */
/* 1. Mobile Fixed Footer Bar for Application */ /* 1. Position the dark navbar below the white navbar + gap */
#bottomNavbar {
/* 56px (white nav) + 16px (mb-3) = 72px */
top: calc(var(--navbar-height) + var(--navbar-gap));
z-index: 1030;
}
/* 2. Pushes the main content down so it's not hidden under the navbars */
.main-content-area {
/* Total Sticky Height (128px) + Extra Margin (12px) = 140px */
margin-top: calc(var(--sticky-navbar-total-height) + 12px);
}
/* 3. Positions the sticky sidebar correctly */
.card.sticky-top {
/* Start scrolling the sidebar just below the two navbars + a small gap */
top: calc(var(--sticky-navbar-total-height) + 15px);
}
/* ---------------------------------------------------------------------- */
/* MOBILE RESPONSIVE STYLES (Below 992px) */
/* ---------------------------------------------------------------------- */
@media (max-width: 991.98px) { @media (max-width: 991.98px) {
/* Fix the "Apply" button bar to the bottom on mobile/tablet */
/* --- FIX: Spacing for Collapsed Hamburger Menu --- */
#navbarNav .nav-item {
margin-top: 5px;
margin-bottom: 5px;
padding: 5px 0;
border-bottom: 1px solid #f0f0f0;
}
#navbarNav .nav-item:last-child {
border-bottom: none;
}
#navbarNav .nav-link {
padding: 8px 15px;
display: block;
}
/* --- END FIX --- */
/* On mobile, the top navbar is generally only 56px tall when collapsed.
The bottom navbar position remains correct (72px down). */
#bottomNavbar {
top: calc(var(--navbar-height) + var(--navbar-gap));
}
/* Main content area needs less margin on mobile since the sidebar isn't active
and the navs are collapsed by default. */
.main-content-area {
/* Reduced margin-top for smaller screens */
margin-top: calc(var(--sticky-navbar-total-height) / 2);
}
/* Mobile Fixed Footer Bar for Application */
.mobile-fixed-apply-bar { .mobile-fixed-apply-bar {
position: fixed; position: fixed;
bottom: 0; bottom: 0;
@ -47,9 +112,9 @@
right: 0; right: 0;
width: 100%; width: 100%;
padding: 15px; padding: 15px;
background-color: var(--light-bg); /* Use a light background */ background-color: var(--light-bg);
border-top: 1px solid #ddd; border-top: 1px solid #ddd;
z-index: 1000; /* Ensure it stays above everything */ z-index: 1000;
box-shadow: 0 -4px 10px rgba(0, 0, 0, 0.08); box-shadow: 0 -4px 10px rgba(0, 0, 0, 0.08);
} }
@ -58,173 +123,138 @@
padding-bottom: 90px; padding-bottom: 90px;
} }
/* Adjust header font size for small screens */ /* Fix job overview grid responsiveness (ensures 1 column) */
.card-header h2 {
font-size: 1.25rem !important;
}
/* Change job overview grid to single column on small phones */
.row-cols-md-2 {
--bs-gutter-x: 1.5rem;
--bs-gutter-y: 1rem;
}
.row-cols-md-2 > .col { .row-cols-md-2 > .col {
flex: 0 0 100%; /* force to 1 column */ flex: 0 0 100%;
max-width: 100%; max-width: 100%;
} }
} }
</style> </style>
{% endblock %} </head>
<body>
{% block content %}
<div class="row mb-5"> <nav id="topNavbar" class="navbar navbar-expand-lg sticky-top" style="background-color: white; z-index: 1030;">
<div class="container-fluid">
<div class="col-lg-4 order-lg-2 order-1 d-none d-lg-block"> <a class="navbar-brand text-white fw-bold" href="/">
<div class="card shadow-sm sticky-top" style="top: 90px;"> <img src="{% static 'image/kaauh.jpeg' %}" alt="{% translate 'KAAUH IMAGE' %}" style="height: 50px; margin-right: 10px;">
<div class="card-header bg-kaauh-teal-dark text-white"> </a>
<h5 class="mb-0"><i class="fas fa-file-signature me-2"></i>Ready to Apply?</h5> <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav"
</div> aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
<div class="card-body text-center"> <span class="navbar-toggler-icon"></span>
<p class="text-muted">Review the job details, then apply below.</p> </button>
<div class="collapse navbar-collapse" id="navbarNav">
{% if job.form_template %} <ul class="navbar-nav ms-auto">
<a href="{% url 'form_wizard' job.form_template.pk %}" class="btn btn-main-action btn-lg w-100">
<i class="fas fa-paper-plane me-2"></i> Apply for this Position <li class="nav-item">
</a> <a class="nav-link text-secondary" href="/applications/">{% translate "Applications" %}</a>
{% endif %} </li>
<li class="nav-item">
{% comment %} <p class="text-muted mt-3 mb-0"> <a class="nav-link text-secondary" href="/profile/">{% translate "Profile" %}</a>
<small>Application ID: **{{ job.pk }}**</small> </li>
</p> {% endcomment %} <li class="nav-item">
</div> <a class="nav-link text-secondary" href="https://kaauh.edu.sa/career">{% translate "Careers" %}</a>
</li>
</ul>
</div> </div>
</div> </div>
</nav>
<div class="col-lg-8 order-lg-1 order-2">
<div class="card shadow-sm"> <nav id="bottomNavbar" class="navbar navbar-expand-lg sticky-top" style="background-color: var(--kaauh-teal); z-index: 1030;">
<div class="card-header bg-kaauh-teal-dark text-white d-flex justify-content-between align-items-center"> <div class="container-fluid">
<h2 class="h3 mb-0 fw-bold">{{ job.title }}</h2> <span class="navbar-text text-white fw-bold">{% translate "Job Overview" %}</span>
</div>
{% with status_class=job.status|lower %} </nav>
<span class="badge
{% if status_class == 'open' %}bg-success <div class="container">
{% elif status_class == 'closed' %}bg-danger <div class="row mb-5 mt-3 main-content-area">
{% elif status_class == 'draft' %}bg-secondary
{% else %}bg-primary <div class="col-lg-4 order-lg-2 order-1 d-none d-lg-block">
{% endif %} <div class="card shadow-sm sticky-top" style="top: var(--sticky-navbar-total-height);">
status-badge fw-bold p-2"> <div class="card-header bg-kaauh-teal-dark text-white">
{{ job.get_status_display }} <h5 class="mb-0"><i class="fas fa-file-signature me-2"></i>Ready to Apply?</h5>
</span>
{% endwith %}
</div>
<div class="card-body">
<h4 class="mb-3" style="color: var(--kaauh-teal-dark);">Job Overview</h4>
<div class="row row-cols-1 row-cols-md-2 g-3 mb-4">
{% if job.salary_range %}
<div class="col">
<i class="fas fa-money-bill-wave text-success me-2"></i>
<strong>Salary:</strong>
<span class="fw-bold text-success">{{ job.salary_range }}</span>
</div> </div>
{% endif %} <div class="card-body text-center">
<p class="text-muted">Review the job details, then apply below.</p>
<div class="col">
<i class="fas fa-calendar-alt text-muted me-2"></i> {% if job.form_template %}
<strong>Deadline:</strong> <a href="{% url 'form_wizard' job.form_template.pk %}" class="btn btn-main-action btn-lg w-100">
{% if job.application_deadline %} <i class="fas fa-paper-plane me-2"></i> Apply for this Position
{{ job.application_deadline|date:"M d, Y" }} </a>
{% if job.is_expired %}
<span class="badge bg-danger ms-2">EXPIRED</span>
{% endif %}
{% else %}
<span class="text-muted">Not specified</span>
{% endif %} {% endif %}
</div> </div>
</div>
<div class="col"> </div>
<i class="fas fa-briefcase text-muted me-2"></i>
<strong>Job Type:</strong> {{ job.get_job_type_display }} <div class="col-lg-8 order-lg-1 order-2">
<div class="card shadow-sm">
<div class="card-header bg-kaauh-teal-dark text-white d-flex justify-content-between align-items-center">
<h2 class="h3 mb-0 fw-bold">{{ job.title }}</h2>
{% comment %} {% with status_class=job.status|lower %}
<span class="badge
{% if status_class == 'open' %}bg-success
{% elif status_class == 'closed' %}bg-danger
{% elif status_class == 'draft' %}bg-secondary
{% else %}bg-primary
{% endif %}
status-badge fw-bold p-2">
{{ job.get_status_display }}
</span>
{% endwith %} {% endcomment %}
</div> </div>
<div class="col"> <div class="card-body">
<i class="fas fa-map-marker-alt text-muted me-2"></i>
<strong>Location:</strong> {{ job.get_location_display }} <h4 class="mb-3" style="color: var(--kaauh-teal-dark);">Job Overview</h4>
</div> <div class="row row-cols-1 row-cols-md-2 g-3 mb-4">
{% if job.salary_range %}
<div class="col"> <div class="col">
<i class="fas fa-building text-muted me-2"></i> <i class="fas fa-money-bill-wave text-success me-2"></i>
<strong>Department:</strong> {{ job.department|default:"Not specified" }} <strong>Salary:</strong>
</div> <span class="fw-bold text-success">{{ job.salary_range }}</span>
</div>
<div class="col"> {% endif %}
<i class="fas fa-hashtag text-muted me-2"></i>
<strong>JOB ID:</strong> {{ job.internal_job_id|default:"N/A" }} <div class="col">
</div> <i class="fas fa-calendar-alt text-muted me-2"></i>
<strong>Deadline:</strong>
<div class="col"> {% if job.application_deadline %}
<i class="fas fa-desktop text-muted me-2"></i> {{ job.application_deadline|date:"M d, Y" }}
<strong>Workplace:</strong> {{ job.get_workplace_type_display }} {% if job.is_expired %}
</div> <span class="badge bg-danger ms-2">EXPIRED</span>
{% endif %}
<div class="col"> {% else %}
<i class="fas fa-user-tie text-muted me-2"></i> <span class="text-muted">Not specified</span>
<strong>Created By:</strong> {{ job.created_by|default:"N/A" }} {% endif %}
</div>
<div class="col"> <i class="fas fa-briefcase text-muted me-2"></i> <strong>Job Type:</strong> {{ job.get_job_type_display }} </div>
<div class="col"> <i class="fas fa-map-marker-alt text-muted me-2"></i> <strong>Location:</strong> {{ job.get_location_display }} </div>
<div class="col"> <i class="fas fa-building text-muted me-2"></i> <strong>Department:</strong> {{ job.department|default:"Not specified" }} </div>
<div class="col"> <i class="fas fa-hashtag text-muted me-2"></i> <strong>JOB ID:</strong> {{ job.internal_job_id|default:"N/A" }} </div>
<div class="col"> <i class="fas fa-desktop text-muted me-2"></i> <strong>Workplace:</strong> {{ job.get_workplace_type_display }} </div>
{% comment %} <div class="col"> <i class="fas fa-user-tie text-muted me-2"></i> <strong>Created By:</strong> {{ job.created_by|default:"N/A" }} </div> {% endcomment %}
</div>
{% if job.description %}<hr class="my-4"><div class="mb-4"><h5 class="fw-bold" style="color: var(--kaauh-teal-dark);"><i class="fas fa-info-circle me-2"></i>Job Description</h5><div class="text-secondary">{{ job.description|safe }}</div></div>{% endif %}
{% if job.qualifications %}<hr class="my-4"><div class="mb-4"><h5 class="fw-bold" style="color: var(--kaauh-teal-dark);"><i class="fas fa-graduation-cap me-2"></i>Qualifications</h5><div class="text-secondary">{{ job.qualifications|safe }}</div></div>{% endif %}
{% if job.benefits %}<hr class="my-4"><div class="mb-4"><h5 class="fw-bold" style="color: var(--kaauh-teal-dark);"><i class="fas fa-hand-holding-usd me-2"></i>Benefits</h5><div class="text-secondary">{{ job.benefits|safe }}</div></div>{% endif %}
{% if job.application_instructions %}<hr class="my-4"><div class="mb-4"><h5 class="fw-bold" style="color: var(--kaauh-teal-dark);"><i class="fas fa-file-alt me-2"></i>Application Instructions</h5><div class="text-secondary">{{ job.application_instructions|safe }}</div></div>{% endif %}
</div> </div>
</div> </div>
{% if job.description %}
<hr class="my-4">
<div class="mb-4">
<h5 class="fw-bold" style="color: var(--kaauh-teal-dark);">
<i class="fas fa-info-circle me-2"></i>Job Description
</h5>
<div class="text-secondary">{{ job.description|linebreaks }}</div>
</div>
{% endif %}
{% if job.qualifications %}
<hr class="my-4">
<div class="mb-4">
<h5 class="fw-bold" style="color: var(--kaauh-teal-dark);">
<i class="fas fa-graduation-cap me-2"></i>Qualifications
</h5>
<div class="text-secondary">{{ job.qualifications|linebreaks }}</div>
</div>
{% endif %}
{% if job.benefits %}
<hr class="my-4">
<div class="mb-4">
<h5 class="fw-bold" style="color: var(--kaauh-teal-dark);">
<i class="fas fa-hand-holding-usd me-2"></i>Benefits
</h5>
<div class="text-secondary">{{ job.benefits|linebreaks }}</div>
</div>
{% endif %}
{% if job.application_instructions %}
<hr class="my-4">
<div class="mb-4">
<h5 class="fw-bold" style="color: var(--kaauh-teal-dark);">
<i class="fas fa-file-alt me-2"></i>Application Instructions
</h5>
<div class="text-secondary">{{ job.application_instructions|linebreaks }}</div>
</div>
{% endif %}
</div> </div>
</div> </div>
</div>
{{job.form_template}}
<div class="mobile-fixed-apply-bar d-lg-none">
{% if job.form_template %}
<a href="{% url 'form_wizard' job.form_template.pk %}" class="btn btn-main-action btn-lg w-100">
<i class="fas fa-paper-plane me-2"></i> Apply for this Position
</a>
{% endif %}
</div> </div>
</div>
<div class="mobile-fixed-apply-bar d-lg-none"> <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
{% if job.form_template %} </body>
<a href="{% url 'form_wizard' job.form_template.pk %}" class="btn btn-main-action btn-lg w-100"> </html>
<i class="fas fa-paper-plane me-2"></i> Apply for this Position
</a>
{% endif %}
</div>
{% endblock %}

View File

@ -123,25 +123,26 @@
<div class="card mb-4 shadow-sm no-hover"> <div class="card mb-4 shadow-sm no-hover">
<div class="card-body"> <div class="card-body">
<h5 class="card-title text-muted mb-3" style="font-weight: 500;">Filter & Search</h5> <div class="row">
<form method="get" class="row g-3 align-items-end">
<div class="col-md-4"> <div class="col-md-4">
<label for="search" class="form-label small text-muted">Search by Title or Department</label> <label for="search" class="form-label small text-muted">Search by Title or Department</label>
<div class="input-group input-group-lg"> <div class="input-group input-group-lg mb-3">
<span class="input-group-text bg-white border-end-0"><i class="fas fa-search text-muted"></i></span> <form method="get" action="" class="w-100">
<input type="text" name="q" id="search" class="form-control form-control-search border-start-0" {% include 'includes/search_form.html' %}
placeholder="e.g., 'Professor' or 'Marketing'" </form>
value="{{ search_query|default_if_none:'' }}">
</div> </div>
</div> </div>
<div class="col-md-8">
{% url 'job_list' as job_list_url %}
<form method="GET" class="row g-3 align-items-end" >
<div class="col-md-3"> <div class="col-md-3">
<label for="status" class="form-label small text-muted">Filter by Status</label> <label for="status" class="form-label small text-muted">Filter by Status</label>
<select name="status" id="status" class="form-select form-select-sm"> <select name="status" id="status" class="form-select form-select-sm">
<option value="">All Statuses</option> <option value="">All Statuses</option>
<option value="DRAFT" {% if status_filter == 'DRAFT' %}selected{% endif %}>Draft</option> <option value="DRAFT" {% if status_filter == 'DRAFT' %}selected{% endif %}>Draft</option>
<option value="ACTIVE" {% if status_filter == 'ACTIVE' %}selected{% endif %}>Active</option> <option value="PUBLISHED" {% if status_filter == 'PUBLISHED' %}selected{% endif %}>Published</option>
<option value="CLOSED" {% if status_filter == 'CLOSED' %}selected{% endif %}>Closed</option> <option value="CLOSED" {% if status_filter == 'CLOSED' %}selected{% endif %}>Closed</option>
<option value="ARCHIVED" {% if status_filter == 'ARCHIVED' %}selected{% endif %}>Archived</option> <option value="ARCHIVED" {% if status_filter == 'ARCHIVED' %}selected{% endif %}>Archived</option>
</select> </select>
@ -152,17 +153,13 @@
<button type="submit" class="btn btn-main-action btn-lg"> <button type="submit" class="btn btn-main-action btn-lg">
<i class="fas fa-filter me-1"></i> Apply Filters <i class="fas fa-filter me-1"></i> Apply Filters
</button> </button>
{# Show Clear button if any filter/search is active #}
{% if status_filter or search_query %}
<a href="{% url 'job_list' %}" class="btn btn-outline-danger btn-sm">
<i class="fas fa-times me-1"></i> Clear Filters
</a>
{% endif %}
</div> </div>
</div> </div>
</form> </form>
</div>
</div>
</div> </div>
</div> </div>

View File

@ -1,8 +1,7 @@
{% extends "base.html" %} {% extends "base.html" %}
{% load static %} {% load static i18n %}
{% block title %}Recruitment Dashboard - {{ block.super }}{% endblock %} {% block title %}{% trans "Recruitment Dashboard" %} - {{ block.super }}{% endblock %}
{% block customCSS %} {% block customCSS %}
<style> <style>
@ -88,56 +87,56 @@
{% block content %} {% block content %}
<div class="container-fluid py-4"> <div class="container-fluid py-4">
<h1 class="mb-4" style="color: var(--kaauh-teal-dark); font-weight: 700;">Recruitment Overview 🚀</h1> <h1 class="mb-4" style="color: var(--kaauh-teal-dark); font-weight: 700;">{% trans "Recruitment Overview" %} 🚀</h1>
<div class="stats"> <div class="stats">
<div class="card"> <div class="card">
<div class="card-header"> <div class="card-header">
<h3><i class="fas fa-briefcase stat-icon"></i> Total Jobs</h3> <h3><i class="fas fa-briefcase stat-icon"></i> {% trans "Total Jobs" %}</h3>
</div> </div>
<div class="stat-value">{{ total_jobs }}</div> <div class="stat-value">{{ total_jobs }}</div>
<div class="stat-caption">Active & Drafted Positions</div> <div class="stat-caption">{% trans "Active & Drafted Positions" %}</div>
</div> </div>
<div class="card"> <div class="card">
<div class="card-header"> <div class="card-header">
<h3><i class="fas fa-users stat-icon"></i> Total Candidates</h3> <h3><i class="fas fa-users stat-icon"></i> {% trans "Total Candidates" %}</h3>
</div> </div>
<div class="stat-value">{{ total_candidates }}</div> <div class="stat-value">{{ total_candidates }}</div>
<div class="stat-caption">All Profiles in ATS</div> <div class="stat-caption">{% trans "All Profiles in ATS" %}</div>
</div> </div>
<div class="card"> <div class="card">
<div class="card-header"> <div class="card-header">
<h3><i class="fas fa-chart-line stat-icon"></i> Avg. Apps per Job</h3> <h3><i class="fas fa-chart-line stat-icon"></i> {% trans "Avg. Apps per Job" %}</h3>
</div> </div>
<div class="stat-value">{{ average_applications|floatformat:1 }}</div> <div class="stat-value">{{ average_applications|floatformat:1 }}</div>
<div class="stat-caption">Key Efficiency Metric</div> <div class="stat-caption">{% trans "Key Efficiency Metric" %}</div>
</div> </div>
<div class="card"> <div class="card">
<div class="card-header"> <div class="card-header">
<h3><i class="fas fa-arrow-alt-circle-up stat-icon" style="color: var(--color-success);"></i> Active Listings</h3> <h3><i class="fas fa-arrow-alt-circle-up stat-icon" style="color: var(--color-success);"></i> {% trans "Active Listings" %}</h3>
</div> </div>
<div class="stat-value">22</div> <div class="stat-value">22</div>
<div class="stat-caption">Jobs currently open for application</div> <div class="stat-caption">{% trans "Jobs currently open for application" %}</div>
</div> </div>
<div class="card"> <div class="card">
<div class="card-header"> <div class="card-header">
<h3><i class="fas fa-clock stat-icon" style="color: var(--color-info);"></i> Time to Hire</h3> <h3><i class="fas fa-clock stat-icon" style="color: var(--color-info);"></i> {% trans "Time to Hire" %}</h3>
</div> </div>
<div class="stat-value">35d</div> <div class="stat-value">35d</div>
<div class="stat-caption">Average days from apply to offer</div> <div class="stat-caption">{% trans "Average days from apply to offer" %}</div>
</div> </div>
<div class="card"> <div class="card">
<div class="card-header"> <div class="card-header">
<h3><i class="fas fa-handshake stat-icon" style="color: var(--kaauh-teal-light);"></i> Offer Acceptance</h3> <h3><i class="fas fa-handshake stat-icon" style="color: var(--kaauh-teal-light);"></i> {% trans "Offer Acceptance" %}</h3>
</div> </div>
<div class="stat-value">85%</div> <div class="stat-value">85%</div>
<div class="stat-caption">Successful offers vs. Total offers</div> <div class="stat-caption">{% trans "Successful offers vs. Total offers" %}</div>
</div> </div>
</div> </div>
@ -145,7 +144,7 @@
<div class="card-header"> <div class="card-header">
<h2 class="d-flex align-items-center mb-0"> <h2 class="d-flex align-items-center mb-0">
<i class="fas fa-chart-bar stat-icon"></i> <i class="fas fa-chart-bar stat-icon"></i>
Applications Volume by Job {% trans "Applications Volume by Job" %}
</h2> </h2>
</div> </div>
<div class="chart-container"> <div class="chart-container">
@ -156,13 +155,17 @@
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script> <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<script> <script>
// if the strings are visible to the user. 'Applications' is used for Chart.js here.
const ctx = document.getElementById('applicationsChart').getContext('2d'); const ctx = document.getElementById('applicationsChart').getContext('2d');
const chart = new Chart(ctx, { const chart = new Chart(ctx, {
type: 'bar', type: 'bar',
data: { data: {
labels: {{ job_titles|safe }}, labels: {{ job_titles|safe }},
datasets: [{ datasets: [{
label: 'Applications',
// For simplicity, we are leaving it as-is for now, assuming the context provides the translated labels.
label: '{% trans "Applications" %}',
data: {{ job_app_counts|safe }}, data: {{ job_app_counts|safe }},
backgroundColor: ' #00636e', // Green theme backgroundColor: ' #00636e', // Green theme
borderColor: ' #004a53', borderColor: ' #004a53',

View File

@ -4,9 +4,12 @@
{% block title %}Create Training Material - {{ block.super }}{% endblock %} {% block title %}Create Training Material - {{ block.super }}{% endblock %}
{% block customCSS %} {% block customCSS %}
{# 💡 Required for Summernote if you added it, otherwise remove this line #}
{{ form.media.css }}
<style> <style>
/* ================================================= */ /* ================================================= */
/* THEME VARIABLES AND GLOBAL STYLES */ /* THEME VARIABLES AND GLOBAL STYLES */
/* (Your existing CSS is kept here) */
/* ================================================= */ /* ================================================= */
:root { :root {
--kaauh-teal: #00636e; --kaauh-teal: #00636e;
@ -81,29 +84,13 @@
} }
/* ================================================= */ /* ================================================= */
/* FIX: CRISPY FORMS ALIGNMENT/SPACING */ /* CLEANUP: CRISPY FORMS STYLES (Kept to ensure good defaults) */
/* ================================================= */ /* ================================================= */
/* Crispy forms wraps each field in a div with .mb-3. .card-body .form-group .form-label {
This rule ensures the label and input are vertically aligned font-weight: 600;
and have clean spacing when inside a grid column. */ color: var(--kaauh-primary-text);
.card-body .form-group .form-label, margin-bottom: 0.5rem;
.card-body .form-group .input-group-text {
/* Align text within label if necessary (usually unnecessary) */
vertical-align: middle;
} }
/* Ensures the form elements themselves are cleanly aligned */
.card-body .form-group {
/* Ensures consistent bottom spacing for all form groups */
margin-bottom: 1.2rem;
}
/* Override for fields inside the grid, using Bootstrap 5's default
g-x for horizontal and g-y for vertical padding from the row */
.card-body .row .col-md-6 .form-group {
margin-bottom: 0 !important; /* Let the row's g-4 handle vertical spacing */
}
</style> </style>
{% endblock %} {% endblock %}
@ -138,27 +125,19 @@
</h2> </h2>
</div> </div>
<div class="card-body"> <div class="card-body">
<form method="post" enctype="multipart/form-data">
<form method="post" enctype="multipart/form-data" class="row">
{% csrf_token %} {% csrf_token %}
{# Use a row structure with g-3 for slightly tighter spacing #}
<div class="row g-3"> {% crispy form %}
{# Iterate through all form fields #}
{% for field in form %}
<div class="col-md-{% if field.field.widget.input_type == 'textarea' or field.field.widget.input_type == 'file' %}12{% else %}6{% endif %}">
{# Crispy field rendering #}
{{ field|as_crispy_field }}
</div>
{% endfor %}
</div>
<hr class="mt-4 mb-4">
<button class="btn btn-main-action" type="submit">
<i class="fas fa-save me-1"></i>
{% trans "Create Material" %}
</button>
</form> </form>
</div> </div>
</div> </div>
</div> </div>
{# 💡 Required for Summernote if you added it, otherwise remove this line #}
{{ form.media.js }}
{% endblock %} {% endblock %}