Merge pull request 'django-sumernote and language suppport' (#7) from frontend into main
Reviewed-on: #7
This commit is contained in:
commit
41cf8ea28a
Binary file not shown.
Binary file not shown.
@ -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',
|
||||
|
||||
@ -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)
|
||||
BIN
db.sqlite3
BIN
db.sqlite3
Binary file not shown.
1428
locale/ar/LC_MESSAGES/django.po
Normal file
1428
locale/ar/LC_MESSAGES/django.po
Normal file
File diff suppressed because it is too large
Load Diff
1427
locale/en/LC_MESSAGES/django.po
Normal file
1427
locale/en/LC_MESSAGES/django.po
Normal file
File diff suppressed because it is too large
Load Diff
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -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)
|
||||
|
||||
@ -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
|
||||
|
||||
24
recruitment/migrations/0027_profile.py
Normal file
24
recruitment/migrations/0027_profile.py
Normal 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)),
|
||||
],
|
||||
),
|
||||
]
|
||||
@ -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'
|
||||
|
||||
@ -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"""
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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">
|
||||
|
||||
@ -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">
|
||||
|
||||
@ -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: {{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>
|
||||
|
||||
@ -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 %}
|
||||
@ -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 %}
|
||||
@ -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>
|
||||
|
||||
|
||||
@ -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>
|
||||
@ -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>
|
||||
|
||||
|
||||
@ -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',
|
||||
|
||||
@ -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 %}
|
||||
Loading…
x
Reference in New Issue
Block a user