django-sumernote and language suppport #7

Merged
ismail merged 4 commits from frontend into main 2025-10-09 12:55:30 +03:00
29 changed files with 4292 additions and 1227 deletions

View File

@ -48,6 +48,7 @@ INSTALLED_APPS = [
'channels',
'django_filters',
'crispy_forms',
'django_summernote',
'crispy_bootstrap5',
'django_extensions',
'template_partials',
@ -66,6 +67,7 @@ MIDDLEWARE = [
'corsheaders.middleware.CorsMiddleware',
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.locale.LocaleMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
@ -118,6 +120,8 @@ DATABASES = {
# Password validation
# https://docs.djangoproject.com/en/5.2/ref/settings/#auth-password-validators
AUTH_PASSWORD_VALIDATORS = [
{
'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.urls import path, include
from django.conf.urls.static import static
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 recruitment import views
@ -26,11 +11,20 @@ router = DefaultRouter()
router.register(r'jobs', views.JobPostingViewSet)
router.register(r'candidates', views.CandidateViewSet)
# 1. URLs that DO NOT have a language prefix (admin, API, static files)
urlpatterns = [
path('admin/', admin.site.urls),
path('api/', include(router.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.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 (
JobPosting, Candidate, TrainingMaterial, ZoomMeeting,
FormTemplate, FormStage, FormField, FormSubmission, FieldResponse,
SharedFormTemplate, Source, HiringAgency, IntegrationLog,InterviewSchedule
SharedFormTemplate, Source, HiringAgency, IntegrationLog,InterviewSchedule,Profile
)
class FormFieldInline(admin.TabularInline):
@ -261,4 +261,5 @@ admin.site.register(FormStage)
admin.site.register(FormField)
admin.site.register(FieldResponse)
admin.site.register(InterviewSchedule)
admin.site.register(Profile)
# admin.site.register(HiringAgency)

View File

@ -1,10 +1,12 @@
from django import forms
from .validators import validate_hash_tags
from crispy_forms.helper import FormHelper
from django.core.validators import URLValidator
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 django_summernote.widgets import SummernoteWidget
class CandidateForm(forms.ModelForm):
class Meta:
@ -150,28 +152,33 @@ class TrainingMaterialForm(forms.ModelForm):
'file': _('File'),
}
widgets = {
'title': forms.TextInput(attrs={'class': 'form-control', 'placeholder': _('Enter title')}),
'content': forms.Textarea(attrs={'rows': 6, 'class': 'form-control', 'placeholder': _('Enter material content')}),
'title': forms.TextInput(attrs={'class': 'form-control', 'placeholder': _('Enter material title')}),
# 💡 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=...')}),
'file': forms.FileInput(attrs={'class': 'form-control'}),
}
# The __init__ and FormHelper layout remains the same
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.helper = FormHelper()
self.helper.form_method = 'post'
self.helper.form_class = 'form-horizontal'
self.helper.label_class = 'col-md-3'
self.helper.field_class = 'col-md-9'
self.helper.form_class = 'g-3'
self.helper.layout = Layout(
Field('title', css_class='form-control'),
Field('content', css_class='form-control'),
Div(
Field('video_link', css_class='form-control'),
Field('file', css_class='form-control'),
css_class='row'
'title',
'content', # Summernote is applied via the widgets dictionary
Row(
Column('video_link', css_class='col-md-6'),
Column('file', css_class='col-md-6'),
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'
}),
# Job Details
'description': forms.Textarea(attrs={
'class': 'form-control',
'rows': 6,
# Job Details (Using SummernoteWidget)
'description': SummernoteWidget(attrs={
# Removed 'class' and 'rows' as Summernote handles styling
'placeholder': 'Provide a comprehensive description of the role, responsibilities, and expectations...',
'required': True
}),
'qualifications': forms.Textarea(attrs={
'class': 'form-control',
'rows': 4,
'qualifications': SummernoteWidget(attrs={
# Removed 'class' and 'rows'
'placeholder': 'List required qualifications, skills, education, and experience...'
}),
'salary_range': forms.TextInput(attrs={
'class': 'form-control',
'placeholder': '$60,000 - $80,000'
}),
'benefits': forms.Textarea(attrs={
'class': 'form-control',
'rows': 2,
'benefits': SummernoteWidget(attrs={
# Removed 'class' and 'rows'
'placeholder': 'Health insurance, retirement plans, tuition reimbursement, etc.'
}),
@ -254,10 +258,10 @@ class JobPostingForm(forms.ModelForm):
'class': 'form-control',
'type': 'date'
}),
'application_instructions': forms.Textarea(attrs={
'class': 'form-control',
'rows': 3,
'placeholder': 'Special instructions for applicants (e.g., required documents, reference requirements, etc.)'
'application_instructions': SummernoteWidget(attrs={
'placeholder': 'Special instructions for applicants (e.g., required documents, reference requirements, etc.)',
}),
'open_positions': forms.NumberInput(attrs={
'class': 'form-control',
@ -267,8 +271,7 @@ class JobPostingForm(forms.ModelForm):
'hash_tags': forms.TextInput(attrs={
'class': 'form-control',
'placeholder': '#hiring,#jobopening',
'validators':validate_hash_tags,
# 'validators':validate_hash_tags, # Assuming this is available
}),
# 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.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):
created_at = models.DateTimeField(auto_now_add=True, verbose_name=_('Created at'))
updated_at = models.DateTimeField(auto_now=True, verbose_name=_('Updated at'))
@ -127,7 +130,7 @@ class JobPosting(Base):
verbose_name_plural = "Job Postings"
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):
return self.source.name if self.source else 'System'

View File

@ -83,8 +83,7 @@ class ZoomMeetingListView(ListView):
if search_query:
queryset = queryset.filter(
Q(topic__icontains=search_query) |
Q(meeting_id__icontains=search_query) |
Q(host_email__icontains=search_query)
Q(meeting_id__icontains=search_query)
)
return queryset
@ -143,27 +142,30 @@ def ZoomMeetingDeleteView(request, pk):
#Job Posting
def job_list(request):
"""Display the list of job postings order by creation date descending"""
jobs=JobPosting.objects.all().order_by('-created_at')
# def job_list(request):
# """Display the list of job postings order by creation date descending"""
# jobs=JobPosting.objects.all().order_by('-created_at')
# Filter by status if provided
status=request.GET.get('status')
if status:
jobs=jobs.filter(status=status)
# # Filter by status if provided
# print(f"the request is: {request} ")
# status=request.GET.get('status')
# print(f"DEBUG: Status filter received: {status}")
# if status:
# jobs=jobs.filter(status=status)
#pagination
paginator=Paginator(jobs,10) # Show 10 jobs per page
page_number=request.GET.get('page')
page_obj=paginator.get_page(page_number)
return render(request, 'jobs/job_list.html', {
'page_obj': page_obj,
'status_filter': status
})
# #pagination
# paginator=Paginator(jobs,10) # Show 10 jobs per page
# page_number=request.GET.get('page')
# page_obj=paginator.get_page(page_number)
# return render(request, 'jobs/job_list.html', {
# 'page_obj': page_obj,
# 'status_filter': status
# })
def create_job(request):
"""Create a new job posting"""
if request.method=='POST':
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):
"""Display the form as a step-by-step wizard"""
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"])
def submit_form(request, template_id):
"""Handle form submission"""

View File

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

View File

@ -20,6 +20,7 @@
--kaauh-light-bg: #f9fbfd;
--kaauh-border: #eaeff3;
}
/* === Top Bar === */
.top-bar {
@ -260,8 +261,7 @@
<nav class="navbar navbar-expand-lg navbar-dark sticky-top">
<div class="container">
<a class="navbar-brand text-white" href="{% url 'dashboard' %}">
<i class="fas fa-hospital"></i>
<span class="d-lg-none ms-1">KAAUH ATS</span>
<img src="{% static 'image/kaauh.jpeg' %}" alt="{% trans 'Saudi Vision 2030' %}" style="width: 60px; height: 60px;">
</a>
<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">
<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' %}">
<span class="d-flex align-items-center gap-2">
{% include "icons/dashboard.html" %}
{% trans "Dashboard" %}
</span>
</a>
</li>
<li class="nav-item">
</li> {% endcomment %}
<li class="nav-item me-2">
<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">
{% include "icons/jobs.html" %}
@ -287,7 +287,7 @@
</span>
</a>
</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' %}">
<span class="d-flex align-items-center gap-2">
{% include "icons/users.html" %}
@ -295,7 +295,7 @@
</span>
</a>
</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' %}">
<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">
@ -306,7 +306,7 @@
</span>
</a>
</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' %}">
<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">
@ -317,17 +317,19 @@
</span>
</a>
</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' %}">
<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">
<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>
<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 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>
{% trans "Form Templates" %}
</span>
</a>
</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"
data-bs-offset="0, 8" data-bs-auto-close="outside">
{% trans "More" %}
@ -345,50 +347,40 @@
</li>
</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">
<li class="nav-item 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>
<span class="d-none d-lg-inline">{{ LANGUAGE_CODE|upper }}</span>
</a>
<ul class="dropdown-menu dropdown-menu-end" data-bs-popper="static">
{% get_current_language as LANGUAGE_CODE %}
<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 }}">
<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
</button>
</form>
</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 }}">
<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)
</button>
</form>
</li>
</ul>
</ul>
</li>
</ul>
</ul>
<ul class="navbar-nav">
<ul class="navbar-nav ms-4">
<li class="nav-item dropdown">
<button
class="nav-link p-0 border-0 bg-transparent dropdown-toggle"
@ -399,21 +391,35 @@
data-bs-auto-close="outside"
data-bs-offset="0, 8"
>
<div class="profile-avatar" title="{% trans 'Your account' %}">
{{ user.username|first|upper }}
</div>
{% if user.profile.profile_image %}
<img src="{{ user.profile.profile_image.url }}" alt="{{ user.username }}" class="profile-avatar"
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>
<ul
class="dropdown-menu dropdown-menu-end py-0 shadow border-0 rounded-3"
data-bs-popper="static"
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="me-3">
<div class="profile-avatar" style="width: 44px; height: 44px; background-color: var(--kaauh-teal);">
{{ user.username|first|upper }}
</div>
<div class="me-3 d-flex align-items-center justify-content-center" style="min-width: 48px;">
{% if user.profile.profile_image %}
<img src="{{ user.profile.profile_image.url }}" alt="{{ user.username }}" class="profile-avatar shadow-sm border"
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 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-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="#">
{% 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>
<form method="post" action="" class="d-inline">

View File

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

View File

@ -1,454 +1,548 @@
<!-- templates/form_wizard.html -->
{% load static i18n %}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<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 rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<style>
/* KAAT-S Theme Variables */
:root {
--kaauh-teal: #00636e; /* Main Primary Color */
--kaauh-teal-dark: #004a53; /* Dark Primary Color */
/* Mapping wizard defaults to theme colors */
--primary: var(--kaauh-teal);
--primary-light: #007c89; /* Slightly lighter shade for subtle hover/border */
--secondary: var(--kaauh-teal-dark);
--success: #198754; /* Keeping a standard success green for Submit */
--error: #dc3545; /* Standard danger red */
--light: #f8f9fa;
--dark: #212529;
--gray: #6c757d;
--light-gray: #e9ecef;
--border: #dee2e6;
--shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
--radius: 16px; /* Increased radius for a softer look */
--transition: all 0.3s ease;
}
<style>
/* KAAT-S Theme Variables */
:root {
--kaauh-teal: #00636e; /* Main Primary Color */
--kaauh-teal-dark: #004a53; /* Dark Primary Color */
/* Mapping wizard defaults to theme colors */
--primary: var(--kaauh-teal);
--primary-light: #007c89; /* Slightly lighter shade for subtle hover/border */
--secondary: var(--kaauh-teal-dark);
--success: #198754; /* Keeping a standard success green for Submit */
--error: #dc3545; /* Standard danger red */
--light: #f8f9fa;
--dark: #212529;
--gray: #6c757d;
--light-gray: #e9ecef;
--border: #dee2e6;
--shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
--radius: 16px; /* Increased radius for a softer look */
--transition: all 0.3s ease;
}
body {
/* Dark gradient background to match the theme */
background: linear-gradient(135deg, var(--kaauh-teal-dark) 0%, #1e3a47 100%);
min-height: 100vh;
display: flex;
justify-content: center;
align-items: center;
padding: 20px;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
}
body {
/* Remove centering/flex properties to allow for normal document flow and scrolling */
padding-top: 56px; /* Space for the sticky navbar */
/* Dark gradient background to match the theme */
background: linear-gradient(135deg, var(--kaauh-teal-dark) 0%, #1e3a47 100%);
background-image: url("{% static 'image/vision.svg' %}");
background-repeat: no-repeat;
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 {
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;
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 */
height: 100vh;
border-radius: 0;
max-width: 100%;
max-height: 100vh;
}
.stage-title {
font-size: 1.8rem;
font-weight: 700;
margin-bottom: 25px;
color: var(--dark);
line-height: 1.3;
font-size: 1.5rem;
}
.field-container {
margin-bottom: 25px;
.wizard-header {
padding: 20px;
}
.field-label {
font-size: 1.1rem;
font-weight: 600;
margin-bottom: 12px;
display: flex;
align-items: center;
gap: 8px;
color: var(--dark);
.wizard-content {
padding: 0 20px 20px;
}
.required-indicator {
color: var(--error);
font-weight: bold;
.wizard-footer {
padding: 0 20px 20px;
}
/* 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;
}
/* === FIX FOR SMALL-SCREEN HAMBURGER MENU === */
@media (max-width: 991.98px) {
/* Add vertical spacing to the navigation items when the navbar is collapsed */
#navbarNav .nav-item {
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;
}
.form-textarea {
min-height: 120px;
resize: vertical;
/* Adjust the total margin of the top navbar container when collapsed */
#topNavbar.mb-3 {
margin-bottom: 0 !important;
}
}
/* 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 {
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>
#bottomNavbar {
/* Position the dark navbar 72px from the top of the viewport */
top: 72px;
/* The z-index is already 1030 in the inline style, which is correct */
}
</style>
</head>
<body>
<div class="wizard-container">
<div class="progress-container">
<div class="progress-bar" id="progressBar"></div>
</div>
<div class="wizard-header">
<div class="logo">
<i class="fas fa-file-alt"></i>
<span id="formTitle">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">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> 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>
<nav id="topNavbar" class="navbar navbar-expand-lg sticky-top" style="background-color: white; z-index: 1030;">
<div class="container-fluid">
<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;">
</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav"
aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarNav">
<ul class="navbar-nav ms-auto">
<li class="nav-item">
<a class="nav-link text-secondary" href="/applications/">{% translate "Applications" %}</a>
</li>
<li class="nav-item">
<a class="nav-link text-secondary" href="/profile/">{% translate "Profile" %}</a>
</li>
<li class="nav-item">
<a class="nav-link text-secondary" href="https://kaauh.edu.sa/career">{% translate "Careers" %}</a>
</li>
</ul>
</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
const csrfToken = '{{ csrf_token }}';
const state = {
@ -1154,4 +1248,4 @@
document.addEventListener('DOMContentLoaded', init);
</script>
</body>
</html>
</html>

View File

@ -4,9 +4,12 @@
{% block title %}Create New Job Post - {{ block.super }}{% endblock %}
{% block customCSS %}
{# 💡 1. Add Summernote CSS Media in the head #}
{{ form.media.css }}
<style>
/* ================================================= */
/* THEME VARIABLES AND GLOBAL STYLES */
/* THEME VARIABLES AND GLOBAL STYLES (Keep existing) */
/* ================================================= */
:root {
--kaauh-teal: #00636e;
@ -15,10 +18,7 @@
--kaauh-primary-text: #343a40;
}
/* Primary Color Overrides */
.text-primary { color: var(--kaauh-teal) !important; }
/* Main Action Button Style */
.btn-main-action, .btn-primary {
background-color: var(--kaauh-teal);
border-color: var(--kaauh-teal);
@ -36,8 +36,6 @@
transform: translateY(-1px);
box-shadow: 0 4px 8px rgba(0,0,0,0.15);
}
/* Secondary/Cancel Button Style */
.btn-secondary {
background-color: #f8f9fa;
color: var(--kaauh-teal-dark);
@ -46,11 +44,9 @@
}
.btn-secondary:hover {
background-color: #e9ecef;
color: var(--kaauh-teal-dark);
border-color: #ced4da;
color: white;
border-color: var(--kaauh-teal-dark);
}
/* Card enhancements */
.card {
border: 1px solid var(--kaauh-border);
border-radius: 0.75rem;
@ -58,340 +54,293 @@
box-shadow: 0 4px 12px rgba(0,0,0,0.06);
background-color: white;
}
/* Themed Card Header (replacing bg-light) */
.card-header-themed {
background-color: #f0f8ff; /* Very light blue background */
background-color: #f0f8ff;
border-bottom: 2px solid var(--kaauh-teal);
color: var(--kaauh-teal-dark);
font-weight: 700;
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 {
font-weight: 600;
color: var(--kaauh-primary-text);
margin-bottom: 0.3rem;
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 textarea,
.card-body select {
/* Apply form-control styling */
border-radius: 0.5rem;
border: 1px solid #ced4da;
width: 100%;
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;
}
.card-body textarea {
min-height: 8rem;
}
/* File input styling is kept minimal as it's harder to style universally */
.card-body input[type="file"] {
padding-top: 0.5rem;
/* ================================================= */
/* ✅ CORRECTED SUMMERNOTE FULL-WIDTH STYLING */
/* ================================================= */
/* 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>
{% endblock %}
{% block content %}
<div class="container-fluid py-4">
<div class="container-fluid py-4">
<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>
<form method="post" id="jobForm" class="mb-5">
<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 "Basic Information" %}</h5>
<h5><i class="fas fa-info-circle"></i> {% trans "Core Position Details" %}</h5>
</div>
<div class="card-body">
<div class="row g-4">
<div class="col-md-8">
<div>
<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 }}
{% if form.title.errors %}
<div class="text-danger small mt-1">{{ form.title.errors }}</div>
{% endif %}
{% if form.title.errors %}<div class="text-danger small mt-1">{{ form.title.errors }}</div>{% endif %}
</div>
</div>
<div class="col-md-4">
<div>
<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 }}
{% if form.job_type.errors %}
<div class="text-danger small mt-1">{{ form.job_type.errors }}</div>
{% endif %}
{% if form.job_type.errors %}<div class="text-danger small mt-1">{{ form.job_type.errors }}</div>{% endif %}
</div>
</div>
<div class="col-md-6">
<div>
<label for="{{ form.department.id_for_label }}" class="form-label">{% trans "Department" %}</label>
{# Removed |attr:"class:form-control" #}
{{ form.department }}
{% if form.department.errors %}
<div class="text-danger small mt-1">{{ form.department.errors }}</div>
{% endif %}
{% if form.department.errors %}<div class="text-danger small mt-1">{{ form.department.errors }}</div>{% endif %}
</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>
<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 }}
{% if form.workplace_type.errors %}
<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 %}
{% if form.workplace_type.errors %}<div class="text-danger small mt-1">{{ form.workplace_type.errors }}</div>{% endif %}
</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-map-marker-alt"></i> {% trans "Location" %}</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>
<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>
{# Removed |attr:"class:form-control" #}
{{ form.description }}
{% if form.description.errors %}
<div class="text-danger small mt-1">{{ form.description.errors }}</div>
{% endif %}
{{ 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>
{# Removed |attr:"class:form-control" #}
{{ form.qualifications }}
{% 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 %}
{{ 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-file-signature"></i> {% trans "Application Information" %}</h5>
<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-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>
<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 }}
{% if form.application_url.errors %}
<div class="text-danger small mt-1">{{ form.application_url.errors }}</div>
{% endif %}
{% 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-md-6">
<div class="col-12">
<div>
<label for="{{ form.application_deadline.id_for_label }}" class="form-label">{% trans "Application Deadline" %}</label>
{# Removed |attr:"class:form-control" #}
{{ 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-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 %}
<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>
{# Removed |attr:"class:form-control" #}
{{ 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>
<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 %}
{% 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-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 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>
{# Removed |attr:"class:form-control" #}
{{ form.reporting_to }}
{% if form.reporting_to.errors %}
<div class="text-danger small mt-1">{{ form.reporting_to.errors }}</div>
{% endif %}
{% 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>
{# Removed |attr:"class:form-control" #}
{{ form.open_positions }}
{% if form.open_positions.errors %}
<div class="text-danger small mt-1">{{ form.open_positions.errors }}</div>
{% endif %}
{% 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">
<a href="{% url 'job_list' %}" class="btn btn-secondary">
<i class="fas fa-arrow-left me-1"></i> {% trans "Cancel" %}
</a>
<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>
</div>
</form>
</div>
{# 💡 2. Add Summernote JS Media at the end of the body #}
{{ form.media.js }}
{% endblock %}

View File

@ -1,259 +1,354 @@
{% extends "base.html" %}
{% load static i18n %}
{% 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 %}
<div class="row justify-content-center">
<div class="col-lg-10">
<div class="card">
<div class="card-header">
<h2><i class="fas fa-edit"></i> Edit Job Posting</h2>
<small class="text-muted">Internal ID: {{ job.internal_job_id }}</small>
<div class="container-fluid py-4">
<h1 class="h3 mb-4 text-primary fw-bold">
{# UPDATED TITLE FOR EDIT CONTEXT #}
<i class="fas fa-edit me-2"></i> {% trans "Edit Job Posting" %}
{% if job.title %} - {{ job.title }} {% endif %}
</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 class="card-body">
<form method="post" id="jobForm">
{% csrf_token %}
<!-- Basic Information Section -->
<div class="card mb-4">
<div class="card-header bg-light">
<h5 class="mb-0"><i class="fas fa-info-circle"></i> Basic Information</h5>
<div class="row g-4">
<div class="col-md-8">
<div>
<label for="{{ form.title.id_for_label }}" class="form-label">{% trans "Job Title" %} <span class="text-danger">*</span></label>
{{ form.title }}
{% if form.title.errors %}<div class="text-danger small mt-1">{{ form.title.errors }}</div>{% endif %}
</div>
<div class="card-body">
<div class="row">
<div class="col-md-8">
<div class="mb-3">
<label for="{{ form.title.id_for_label }}" class="form-label">Job Title <span class="text-danger">*</span></label>
{{ form.title }}
{% 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 class="col-md-4">
<div>
<label for="{{ form.job_type.id_for_label }}" class="form-label">{% trans "Job Type" %} <span class="text-danger">*</span></label>
{{ form.job_type }}
{% if form.job_type.errors %}<div class="text-danger small mt-1">{{ form.job_type.errors }}</div>{% endif %}
</div>
</div>
<!-- Location Section -->
<div class="card mb-4">
<div class="card-header bg-light">
<h5 class="mb-0"><i class="fas fa-map-marker-alt"></i> Location</h5>
</div>
<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 class="col-md-6">
<div>
<label for="{{ form.department.id_for_label }}" class="form-label">{% trans "Department" %}</label>
{{ form.department }}
{% if form.department.errors %}<div class="text-danger small mt-1">{{ form.department.errors }}</div>{% endif %}
</div>
</div>
<!-- Job Details Section -->
<div class="card mb-4">
<div class="card-header bg-light">
<h5 class="mb-0"><i class="fas fa-file-alt"></i> Job Details</h5>
</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 class="col-md-6">
<div>
<label for="{{ form.workplace_type.id_for_label }}" class="form-label">{% trans "Workplace Type" %} <span class="text-danger">*</span></label>
{{ form.workplace_type }}
{% if form.workplace_type.errors %}<div class="text-danger small mt-1">{{ form.workplace_type.errors }}</div>{% endif %}
</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>
{# ================================================= #}
{# 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>
{% endblock %}
{# 💡 2. Add Summernote JS Media at the end of the body #}
{{ form.media.js }}
{% endblock %}

View File

@ -249,7 +249,7 @@
{# HEADER SECTION #}
<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">
{{ job.get_status_display }}
</span>
@ -339,19 +339,19 @@
{% if job.description %}
<div class="mb-4">
<h5>{% trans "Job Description" %}</h5>
<div class="text-secondary">{{ job.description|linebreaks }}</div>
<div class="text-secondary">{{ job.description|safe }}</div>
</div>
{% endif %}
{% if job.qualifications %}
<div class="mb-4">
<h5>{% trans "Required Qualifications" %}</h5>
<div class="text-secondary">{{ job.qualifications|linebreaks }}</div>
<div class="text-secondary">{{ job.qualifications|safe }}</div>
</div>
{% endif %}
{% if job.benefits %}
<div class="mb-4">
<h5>{% trans "Benefits" %}</h5>
<div class="text-secondary">{{ job.benefits|linebreaks }}</div>
<div class="text-secondary">{{ job.benefits|safe}}</div>
</div>
{% endif %}
</div>
@ -361,7 +361,7 @@
<div class="tab-pane fade" id="instructions" role="tabpanel" aria-labelledby="instructions-tab">
<div class="mb-4">
<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>
{% endif %}
@ -526,7 +526,7 @@
{% trans "Manage the custom application forms associated with this job posting." %}
</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" %}
</a>
@ -534,9 +534,9 @@
<i class="fas fa-list-alt me-1"></i> {% trans "View All Existing Forms" %}
</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" %}
</a>
</a>
</div>
</div>

View File

@ -1,18 +1,30 @@
{% extends "base.html" %}
{% block title %}{{ job.title }} - University ATS{% endblock %}
{% block customCSS %}
<style>
{% load static i18n %}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<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 {
--kaauh-teal: #00636e;
--kaauh-teal-dark: #004a53;
--success: #198754;
/* Define a subtle background for mobile sticky bar */
--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 {
@ -32,14 +44,67 @@
.bg-kaauh-teal-dark {
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) {
/* 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 {
position: fixed;
bottom: 0;
@ -47,9 +112,9 @@
right: 0;
width: 100%;
padding: 15px;
background-color: var(--light-bg); /* Use a light background */
background-color: var(--light-bg);
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);
}
@ -58,173 +123,138 @@
padding-bottom: 90px;
}
/* Adjust header font size for small screens */
.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;
}
/* Fix job overview grid responsiveness (ensures 1 column) */
.row-cols-md-2 > .col {
flex: 0 0 100%; /* force to 1 column */
flex: 0 0 100%;
max-width: 100%;
}
}
</style>
{% endblock %}
{% block content %}
<div class="row mb-5">
<div class="col-lg-4 order-lg-2 order-1 d-none d-lg-block">
<div class="card shadow-sm sticky-top" style="top: 90px;">
<div class="card-header bg-kaauh-teal-dark text-white">
<h5 class="mb-0"><i class="fas fa-file-signature me-2"></i>Ready to Apply?</h5>
</div>
<div class="card-body text-center">
<p class="text-muted">Review the job details, then apply below.</p>
{% 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 %}
{% comment %} <p class="text-muted mt-3 mb-0">
<small>Application ID: **{{ job.pk }}**</small>
</p> {% endcomment %}
</div>
</head>
<body>
<nav id="topNavbar" class="navbar navbar-expand-lg sticky-top" style="background-color: white; z-index: 1030;">
<div class="container-fluid">
<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;">
</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav"
aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarNav">
<ul class="navbar-nav ms-auto">
<li class="nav-item">
<a class="nav-link text-secondary" href="/applications/">{% translate "Applications" %}</a>
</li>
<li class="nav-item">
<a class="nav-link text-secondary" href="/profile/">{% translate "Profile" %}</a>
</li>
<li class="nav-item">
<a class="nav-link text-secondary" href="https://kaauh.edu.sa/career">{% translate "Careers" %}</a>
</li>
</ul>
</div>
</div>
<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>
{% 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 %}
</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>
</nav>
<nav id="bottomNavbar" class="navbar navbar-expand-lg sticky-top" style="background-color: var(--kaauh-teal); z-index: 1030;">
<div class="container-fluid">
<span class="navbar-text text-white fw-bold">{% translate "Job Overview" %}</span>
</div>
</nav>
<div class="container">
<div class="row mb-5 mt-3 main-content-area">
<div class="col-lg-4 order-lg-2 order-1 d-none d-lg-block">
<div class="card shadow-sm sticky-top" style="top: var(--sticky-navbar-total-height);">
<div class="card-header bg-kaauh-teal-dark text-white">
<h5 class="mb-0"><i class="fas fa-file-signature me-2"></i>Ready to Apply?</h5>
</div>
{% endif %}
<div class="col">
<i class="fas fa-calendar-alt text-muted me-2"></i>
<strong>Deadline:</strong>
{% if job.application_deadline %}
{{ job.application_deadline|date:"M d, Y" }}
{% if job.is_expired %}
<span class="badge bg-danger ms-2">EXPIRED</span>
{% endif %}
{% else %}
<span class="text-muted">Not specified</span>
<div class="card-body text-center">
<p class="text-muted">Review the job details, then apply below.</p>
{% 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 class="col">
<i class="fas fa-briefcase text-muted me-2"></i>
<strong>Job Type:</strong> {{ job.get_job_type_display }}
</div>
</div>
<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 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>
<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 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>
{% endif %}
<div class="col">
<i class="fas fa-calendar-alt text-muted me-2"></i>
<strong>Deadline:</strong>
{% if job.application_deadline %}
{{ job.application_deadline|date:"M d, Y" }}
{% if job.is_expired %}
<span class="badge bg-danger ms-2">EXPIRED</span>
{% endif %}
{% else %}
<span class="text-muted">Not specified</span>
{% 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>
{% 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>
{{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 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>
{% endblock %}
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
</body>
</html>

View File

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

View File

@ -1,8 +1,7 @@
{% 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 %}
<style>
@ -88,56 +87,56 @@
{% block content %}
<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="card">
<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 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 class="card">
<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 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 class="card">
<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 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 class="card">
<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 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 class="card">
<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 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 class="card">
<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 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>
@ -145,7 +144,7 @@
<div class="card-header">
<h2 class="d-flex align-items-center mb-0">
<i class="fas fa-chart-bar stat-icon"></i>
Applications Volume by Job
{% trans "Applications Volume by Job" %}
</h2>
</div>
<div class="chart-container">
@ -156,13 +155,17 @@
<script src="https://cdn.jsdelivr.net/npm/chart.js"></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 chart = new Chart(ctx, {
type: 'bar',
data: {
labels: {{ job_titles|safe }},
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 }},
backgroundColor: ' #00636e', // Green theme
borderColor: ' #004a53',

View File

@ -4,9 +4,12 @@
{% block title %}Create Training Material - {{ block.super }}{% endblock %}
{% block customCSS %}
{# 💡 Required for Summernote if you added it, otherwise remove this line #}
{{ form.media.css }}
<style>
/* ================================================= */
/* THEME VARIABLES AND GLOBAL STYLES */
/* (Your existing CSS is kept here) */
/* ================================================= */
:root {
--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.
This rule ensures the label and input are vertically aligned
and have clean spacing when inside a grid column. */
.card-body .form-group .form-label,
.card-body .form-group .input-group-text {
/* Align text within label if necessary (usually unnecessary) */
vertical-align: middle;
.card-body .form-group .form-label {
font-weight: 600;
color: var(--kaauh-primary-text);
margin-bottom: 0.5rem;
}
/* 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>
{% endblock %}
@ -138,27 +125,19 @@
</h2>
</div>
<div class="card-body">
<form method="post" enctype="multipart/form-data">
<form method="post" enctype="multipart/form-data" class="row">
{% csrf_token %}
{# Use a row structure with g-3 for slightly tighter spacing #}
<div class="row g-3">
{# 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>
{% crispy form %}
<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>
</div>
</div>
</div>
{# 💡 Required for Summernote if you added it, otherwise remove this line #}
{{ form.media.js }}
{% endblock %}