translation and pagination to the new page regarding the job bank #132

Merged
Faheed merged 6 commits from frontend into main 2025-12-24 15:30:48 +03:00
102 changed files with 1284 additions and 4059 deletions

6
.env
View File

@ -1,3 +1,3 @@
DB_NAME=norahuniversity
DB_USER=norahuniversity
DB_PASSWORD=norahuniversity
DB_NAME=haikal_db
DB_USER=faheed
DB_PASSWORD=Faheed@215

View File

@ -209,23 +209,32 @@ ACCOUNT_LOGIN_ON_EMAIL_CONFIRMATION = True
ACCOUNT_FORMS = {"signup": "recruitment.forms.StaffSignupForm"}
# MAIL_BACKEND = "django.core.mail.backends.console.EmailBackend"
# EMAIL_HOST = "10.10.1.110"
# EMAIL_PORT = 2225
MAIL_BACKEND = "django.core.mail.backends.console.EmailBackend"
EMAIL_HOST = "10.10.1.110"
EMAIL_PORT = 2225
# EMAIL_BACKEND = "django.core.mail.backends.console.EmailBackend"
EMAIL_HOST_PASSWORD = os.getenv("EMAIL_PASSWORD", "mssp.0Q0rSwb.zr6ke4n2k3e4on12.aHwJqnI")
EMAIL_HOST = "smtp.mailersend.net"
EMAIL_PORT = 2525
EMAIL_HOST_USER = "MS_lhygCJ@test-65qngkd8nx3lwr12.mlsender.net"
EMAIL_HOST_PASSWORD = "mssp.0Q0rSwb.zr6ke4n2k3e4on12.aHwJqnI"
EMAIL_USE_TLS = True
EMAIL_HOST = 'sandbox.smtp.mailtrap.io'
EMAIL_HOST_USER = '38e5179debe69a'
EMAIL_HOST_PASSWORD = 'ffa75647d01ecb'
EMAIL_PORT = '2525'
# EMAIL_HOST_PASSWORD = os.getenv("EMAIL_PASSWORD", "mssp.0Q0rSwb.zr6ke4n2k3e4on12.aHwJqnI")
# EMAIL_HOST = "smtp.mailersend.net"
# EMAIL_PORT = 2525
# EMAIL_HOST_USER = "MS_lhygCJ@test-65qngkd8nx3lwr12.mlsender.net"
# EMAIL_HOST_PASSWORD = "mssp.0Q0rSwb.zr6ke4n2k3e4on12.aHwJqnI"
# EMAIL_USE_TLS = True
# EMAIL_HOST = 'sandbox.smtp.mailtrap.io'
# EMAIL_HOST_USER = '38e5179debe69a'
# EMAIL_HOST_PASSWORD = 'ffa75647d01ecb'
# EMAIL_PORT = '2525'
# EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
# EMAIL_HOST = 'smtp.gmail.com'
# EMAIL_PORT = 587
# EMAIL_USE_TLS = True
# EMAIL_HOST_USER = 'faheedk215@gmail.com' # Use your actual Gmail email address
# EMAIL_HOST_PASSWORD = 'nfxf xpzo bpsb lqje' #
# DEFAULT_FROM_EMAIL='faheedlearn@gmail.com'
# Crispy Forms Configuration
CRISPY_ALLOWED_TEMPLATE_PACKS = "bootstrap5"
CRISPY_TEMPLATE_PACK = "bootstrap5"

View File

@ -23,7 +23,7 @@ urlpatterns = [
path("ckeditor5/", include('django_ckeditor_5.urls')),
path('application/<slug:slug>/', views.application_submit_form, name='application_submit_form'),
path('application/<slug:template_slug>/submit/', views.application_submit, name='application_submit'),
path('application/<slug:slug>/submit/', views.application_submit, name='application_submit'),
path('application/<slug:slug>/apply/', views.job_application_detail, name='job_application_detail'),
path('application/<slug:slug>/signup/', views.application_signup, name='application_signup'),
path('application/<slug:slug>/success/', views.application_success, name='application_success'),
@ -31,7 +31,7 @@ urlpatterns = [
path('api/v1/templates/', views.list_form_templates, name='list_form_templates'),
path('api/v1/templates/save/', views.save_form_template, name='save_form_template'),
path('api/v1/templates/<slug:template_slug>/', views.load_form_template, name='load_form_template'),
path('api/v1/templates/<slug:slug>/', views.load_form_template, name='load_form_template'),
path('api/v1/templates/<slug:template_slug>/delete/', views.delete_form_template, name='delete_form_template'),
path('api/v1/sync/task/<str:task_id>/status/', views.sync_task_status, name='sync_task_status'),

BIN
ats-ERD.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 743 KiB

BIN
ats.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 MiB

View File

@ -119,12 +119,12 @@ urlpatterns = [
path("forms/template/<slug:slug>/submissions/", views.form_template_submissions_list, name="form_template_submissions_list"),
path("forms/template/<int:template_id>/all-submissions/", views.form_template_all_submissions, name="form_template_all_submissions"),
# Application Forms (Public)
path("application/signup/<slug:template_slug>/", views.application_signup, name="application_signup"),
path("application/<slug:slug>/", views.application_submit_form, name="application_submit_form"),
path("application/<slug:slug>/submit/", views.application_submit, name="application_submit"),
path("application/<slug:slug>/apply/", views.job_application_detail, name="job_application_detail"),
path("application/<slug:slug>/success/", views.application_success, name="application_success"),
# # Application Forms (Public)
# path("application/signup/<slug:template_slug>/", views.application_signup, name="application_signup"),
# path("application/<slug:slug>/", views.application_submit_form, name="application_submit_form"),
# path("application/<slug:slug>/submit/", views.application_submit, name="application_submit"),
# path("application/<slug:slug>/apply/", views.job_application_detail, name="job_application_detail"),
# path("application/<slug:slug>/success/", views.application_success, name="application_success"),
# ========================================================================
# INTEGRATION & EXTERNAL SERVICES

View File

@ -215,7 +215,7 @@ class PersonListView(StaffRequiredMixin, ListView, LoginRequiredMixin):
.distinct()
.order_by("nationality")
)
nationality = self.request.GET.get("nationality")
context["nationality"] = nationality
context["nationalities"] = nationalities
@ -879,6 +879,8 @@ def form_builder(request, template_slug=None):
context = {}
if template_slug:
template = get_object_or_404(FormTemplate, slug=template_slug)
job_slug=template.job.slug
context['job_slug']=job_slug
context["template"] = template
context["template_slug"] = template.slug
context["template_name"] = template.name
@ -1142,10 +1144,11 @@ def save_form_template(request):
# )
def load_form_template(request, template_slug):
def load_form_template(request, slug):
"""Load an existing form template"""
try:
template = get_object_or_404(FormTemplate, slug=template_slug)
job = get_object_or_404(JobPosting, slug=slug)
template = job.form_template
# Get stages with fields
stages = []
@ -1308,6 +1311,7 @@ def delete_form_template(request, template_id):
def application_submit_form(request, slug):
"""Display the form as a step-by-step wizard"""
job = get_object_or_404(JobPosting, slug=slug)
if not request.user.is_authenticated:
return redirect("application_signup", slug=slug)
@ -1342,7 +1346,7 @@ def application_submit_form(request, slug):
return render(
request,
"applicant/application_submit_form.html",
{"template_slug": job.form_template.slug, "job_id": job.internal_job_id},
{"template_slug": job.form_template.slug,"job_slug": job.slug, "job_id": job.internal_job_id},
)
@ -1350,24 +1354,19 @@ def application_submit_form(request, slug):
@require_POST
@login_required
@candidate_user_required
def application_submit(request, template_slug):
import re
def application_submit(request, slug):
"""Handle form submission"""
if not request.user.is_authenticated or request.user.user_type != "candidate":
return JsonResponse({"success": False, "message": "Unauthorized access."})
# if not request.user.is_authenticated or request.user.user_type != "candidate":
# return JsonResponse({"success": False, "message": "Unauthorized access."})
template = get_object_or_404(FormTemplate, slug=template_slug)
job = template.job
job = get_object_or_404(JobPosting, slug=slug)
template = job.form_template
if request.method == "POST":
try:
with transaction.atomic():
job_posting = JobPosting.objects.select_for_update().get(
form_template=template
)
current_count = job_posting.applications.count()
if current_count >= job_posting.max_applications:
with transaction.atomic():
current_count = job.applications.count()
if current_count >= job.max_applications:
template.is_active = False
template.save()
return JsonResponse(
@ -2432,8 +2431,11 @@ def create_staff_user(request):
@superuser_required
def admin_settings(request):
staffs = User.objects.filter(user_type="staff", is_superuser=False)
paginator=Paginator(staffs,20)
page_number=request.GET.get('page')
page_obj=paginator.get_page(page_number)
form = ToggleAccountForm()
context = {"staffs": staffs, "form": form}
context = {"staffs": page_obj, "form": form,"page_obj":page_obj}
return render(request, "user/admin_settings.html", context)
@ -2769,7 +2771,7 @@ def agency_assignment_list(request):
assignments = assignments.filter(status=status_filter)
# Pagination
paginator = Paginator(assignments, 15) # Show 15 assignments per page
paginator = Paginator(assignments, 20) # Show 15 assignments per page
page_number = request.GET.get("page")
page_obj = paginator.get_page(page_number)
@ -4514,133 +4516,133 @@ def api_application_detail(request, candidate_id):
# Source CRUD Views
@login_required
@staff_user_required
def source_list(request):
"""List all sources with search and pagination"""
search_query = request.GET.get("q", "")
sources = Source.objects.all()
# @login_required
# @staff_user_required
# def source_list(request):
# """List all sources with search and pagination"""
# search_query = request.GET.get("q", "")
# sources = Source.objects.all()
# if search_query:
# sources = sources.filter(
# Q(name__icontains=search_query)
# | Q(source_type__icontains=search_query)
# | Q(description__icontains=search_query)
# )
if search_query:
sources = sources.filter(
Q(name__icontains=search_query)
| Q(source_type__icontains=search_query)
| Q(description__icontains=search_query)
)
# # Order by most recently created
# sources = sources.order_by("-created_at")
# Order by most recently created
sources = sources.order_by("-created_at")
# # Pagination
# paginator = Paginator(sources, 1) # Show 15 sources per page
# page_number = request.GET.get("page")
# page_obj = paginator.get_page(page_number)
# Pagination
paginator = Paginator(sources, 1) # Show 15 sources per page
page_number = request.GET.get("page")
page_obj = paginator.get_page(page_number)
context = {
"page_obj": page_obj,
"search_query": search_query,
"total_sources": sources.count(),
}
return render(request, "recruitment/source_list.html", context)
# context = {
# "page_obj": page_obj,
# "search_query": search_query,
# "total_sources": sources.count(),
# }
# return render(request, "recruitment/source_list.html", context)
@login_required
@staff_user_required
def source_create(request):
"""Create a new source"""
if request.method == "POST":
form = SourceForm(request.POST)
if form.is_valid():
source = form.save()
messages.success(request, f'Source "{source.name}" created successfully!')
return redirect("source_detail", slug=source.slug)
else:
messages.error(request, "Please correct the errors below.")
else:
form = SourceForm()
# @login_required
# @staff_user_required
# def source_create(request):
# """Create a new source"""
# if request.method == "POST":
# form = SourceForm(request.POST)
# if form.is_valid():
# source = form.save()
# messages.success(request, f'Source "{source.name}" created successfully!')
# return redirect("source_detail", slug=source.slug)
# else:
# messages.error(request, "Please correct the errors below.")
# else:
# form = SourceForm()
context = {
"form": form,
"title": "Create New Source",
"button_text": "Create Source",
}
return render(request, "recruitment/source_form.html", context)
# context = {
# "form": form,
# "title": "Create New Source",
# "button_text": "Create Source",
# }
# return render(request, "recruitment/source_form.html", context)
@login_required
@staff_user_required
def source_detail(request, slug):
"""View details of a specific source"""
source = get_object_or_404(Source, slug=slug)
# @login_required
# @staff_user_required
# def source_detail(request, slug):
# """View details of a specific source"""
# source = get_object_or_404(Source, slug=slug)
# Get integration logs for this source
integration_logs = source.integration_logs.order_by("-created_at")[
:10
] # Show recent 10 logs
# # Get integration logs for this source
# integration_logs = source.integration_logs.order_by("-created_at")[
# :10
# ] # Show recent 10 logs
# Statistics
total_logs = source.integration_logs.count()
successful_logs = source.integration_logs.filter(method="POST").count()
failed_logs = source.integration_logs.filter(
method="POST", status_code__gte=400
).count()
# # Statistics
# total_logs = source.integration_logs.count()
# successful_logs = source.integration_logs.filter(method="POST").count()
# failed_logs = source.integration_logs.filter(
# method="POST", status_code__gte=400
# ).count()
context = {
"source": source,
"integration_logs": integration_logs,
"total_logs": total_logs,
"successful_logs": successful_logs,
"failed_logs": failed_logs,
}
return render(request, "recruitment/source_detail.html", context)
# context = {
# "source": source,
# "integration_logs": integration_logs,
# "total_logs": total_logs,
# "successful_logs": successful_logs,
# "failed_logs": failed_logs,
# }
# return render(request, "recruitment/source_detail.html", context)
@login_required
@staff_user_required
def source_update(request, slug):
"""Update an existing source"""
source = get_object_or_404(Source, slug=slug)
# @login_required
# @staff_user_required
# def source_update(request, slug):
# """Update an existing source"""
# source = get_object_or_404(Source, slug=slug)
if request.method == "POST":
form = SourceForm(request.POST, instance=source)
if form.is_valid():
source = form.save()
messages.success(request, f'Source "{source.name}" updated successfully!')
return redirect("source_detail", slug=source.slug)
else:
messages.error(request, "Please correct the errors below.")
else:
form = SourceForm(instance=source)
# if request.method == "POST":
# form = SourceForm(request.POST, instance=source)
# if form.is_valid():
# source = form.save()
# messages.success(request, f'Source "{source.name}" updated successfully!')
# return redirect("source_detail", slug=source.slug)
# else:
# messages.error(request, "Please correct the errors below.")
# else:
# form = SourceForm(instance=source)
context = {
"form": form,
"source": source,
"title": _("Edit Source: %(name)s") % {"name": source.name},
"button_text": _("Update Source"),
}
return render(request, "recruitment/source_form.html", context)
# context = {
# "form": form,
# "source": source,
# "title": _("Edit Source: %(name)s") % {"name": source.name},
# "button_text": _("Update Source"),
# }
# return render(request, "recruitment/source_form.html", context)
@login_required
@staff_user_required
def source_delete(request, slug):
"""Delete a source"""
source = get_object_or_404(Source, slug=slug)
# @login_required
# @staff_user_required
# def source_delete(request, slug):
# """Delete a source"""
# source = get_object_or_404(Source, slug=slug)
if request.method == "POST":
source_name = source.name
source.delete()
messages.success(request, f'Source "{source_name}" deleted successfully!')
return redirect("source_list")
# if request.method == "POST":
# source_name = source.name
# source.delete()
# messages.success(request, f'Source "{source_name}" deleted successfully!')
# return redirect("source_list")
context = {
"source": source,
"title": _("Delete Source: %(name)s") % {"name": source.name},
"message": _('Are you sure you want to delete the source "%(name)s"?')
% {"name": source.name},
"cancel_url": reverse("source_detail", kwargs={"slug": source.slug}),
}
return render(request, "recruitment/source_confirm_delete.html", context)
# context = {
# "source": source,
# "title": _("Delete Source: %(name)s") % {"name": source.name},
# "message": _('Are you sure you want to delete the source "%(name)s"?')
# % {"name": source.name},
# "cancel_url": reverse("source_detail", kwargs={"slug": source.slug}),
# }
# return render(request, "recruitment/source_confirm_delete.html", context)
@login_required
@ -4696,8 +4698,7 @@ def source_toggle_status(request, slug):
def application_signup(request, slug):
from .forms import ApplicantSignupForm
form_template = get_object_or_404(FormTemplate, slug=slug)
job = form_template.job
job = get_object_or_404(JobPosting, slug=slug)
if request.method == "POST":
form = ApplicantSignupForm(request.POST)
@ -4815,8 +4816,7 @@ def interview_list(request):
return render(request, "interviews/interview_list.html", context)
from django_ratelimit.decorators import ratelimit
@ratelimit(key='user_or_ip', rate='1/m', block=True)
@login_required
@staff_user_required
def generate_ai_questions(request, slug):
@ -5379,7 +5379,7 @@ class JobListView(LoginRequiredMixin, StaffRequiredMixin, ListView):
model = JobPosting
template_name = "jobs/job_list.html"
context_object_name = "jobs"
paginate_by = 10
paginate_by = 20
def get_queryset(self):
queryset = super().get_queryset().order_by("-created_at")
@ -5507,8 +5507,8 @@ class ApplicationListView(LoginRequiredMixin, StaffRequiredMixin, ListView):
queryset = queryset.filter(job__slug=job)
if stage:
queryset = queryset.filter(stage=stage)
return queryset.order_by("-created_at")
queryset=queryset.order_by("-created_at")
return queryset
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)

View File

@ -1,32 +1,45 @@
/*
* KAAT-S Theme Styles (V2.0 - Consolidated Global, Nav, and Components)
* This file contains all variables, global layout styles, navigation, and component-specific styles.
/* * KAAT-S Theme Styles (V3.0 - Integrated Enterprise Suite)
* Consolidated Global, Sidebar CRM, Nav, and Components
*/
/* ---------------------------------- */
/* 1. UI Variables and Global Reset */
/* 1. VARIABLES & RESET */
/* ---------------------------------- */
:root {
/* Brand Colors */
--kaauh-teal: #00636e;
--kaauh-teal-dark: #004a53;
--kaauh-light-bg: #f9fbfd;
--kaauh-border: #eaeff3;
--kaauh-primary-text: #343a40;
/* Functional Colors */
--kaauh-success: #28a745;
--kaauh-info: #17a2b8;
--kaauh-danger: #dc3545;
--kaauh-warning: #ffc107;
--kaauh-gray-light: #f8f9fa;
/* Sidebar Dimensions */
--sidebar-width: 260px;
--sidebar-collapsed-width: 70px;
--topbar-height: 64px;
--sidebar-bg: #00363a;
--sidebar-hover: rgba(255, 255, 255, 0.1);
--sidebar-active: #007c8a;
}
/* Primary Color Overrides */
/* ---------------------------------- */
/* 2. GLOBAL UTILITIES */
/* ---------------------------------- */
.text-success { color: var(--kaauh-success) !important; }
.text-info { color: var(--kaauh-info) !important; }
.text-danger { color: var(--kaauh-danger) !important; }
.text-warning { color: var(--kaauh-warning) !important; }
.text-primary-theme { color: var(--kaauh-teal) !important; }
.text-primary-theme, .text-primary { color: var(--kaauh-teal) !important; }
.bg-primary-theme { background-color: var(--kaauh-teal) !important; }
.text-kaauh-primary { color: var(--kaauh-primary-text); }
/* Global Layout Control */
.max-width-1600 {
max-width: 1600px;
margin-right: auto;
@ -35,118 +48,151 @@
padding-left: var(--bs-gutter-x, 0.75rem);
}
/* Global Container Padding for main content */
.container-fluid.py-4 { padding-top: 1.5rem !important; padding-bottom: 1.5rem !important; }
/* Main content minimum height */
main.container-fluid {
min-height: calc(100vh - 200px);
padding: 1.5rem 0;
}
/* ---------------------------------- */
/* 2. Navigation and Header */
/* 3. CRM SIDEBAR & LAYOUT */
/* ---------------------------------- */
.sidebar {
width: var(--sidebar-width);
height: 100vh;
position: fixed;
top: 0;
left: 0;
background-color: var(--sidebar-bg);
z-index: 1050;
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
display: flex;
flex-direction: column;
box-shadow: 4px 0 10px rgba(0,0,0,0.1);
overflow-x: hidden;
}
/* Top Bar (Contact/Social) */
.sidebar-brand {
padding: 1.25rem;
text-align: center;
border-bottom: 1px solid rgba(255, 255, 255, 0.08);
}
.sidebar-nav {
padding: 1.25rem 0;
flex-grow: 1;
overflow-y: auto;
}
/* Sidebar Links */
.nav-link-custom {
display: flex;
align-items: center;
padding: 0.85rem 1.5rem;
color: rgba(255, 255, 255, 0.7);
text-decoration: none !important;
transition: all 0.2s ease;
margin-bottom: 0.25rem;
font-weight: 500;
font-size: 0.95rem;
border-left: 4px solid transparent;
white-space: nowrap;
}
.nav-link-custom:hover {
color: #fff;
background-color: var(--sidebar-hover);
}
.nav-link-custom.active {
color: #fff;
background-color: var(--sidebar-active);
border-left: 4px solid var(--kaauh-warning);
}
.nav-link-custom i {
width: 28px;
font-size: 1.1rem;
opacity: 0.9;
}
/* Main Content Wrapper */
.main-wrapper {
margin-left: var(--sidebar-width);
min-height: 100vh;
display: flex;
flex-direction: column;
transition: all 0.3s ease;
}
.content-header {
height: var(--topbar-height);
background: #ffffff;
border-bottom: 1px solid var(--kaauh-border);
display: flex;
align-items: center;
padding: 0 1.5rem;
position: sticky;
top: 0;
z-index: 1000;
}
/* Sidebar Collapse Logic */
.sidebar.collapsed {
width: var(--sidebar-collapsed-width);
}
.sidebar.collapsed + .main-wrapper {
margin-left: var(--sidebar-collapsed-width);
}
.sidebar.collapsed .nav-link-custom span,
.sidebar.collapsed .sidebar-brand .fw-bold,
.sidebar.collapsed .badge,
.sidebar.collapsed .brand-subtitle {
display: none !important;
}
.sidebar.collapsed .nav-link-custom i {
margin: 0 !important;
font-size: 1.2rem;
width: 100%;
text-align: center;
}
/* ---------------------------------- */
/* 4. NAVIGATION COMPONENTS */
/* ---------------------------------- */
.top-bar {
background-color: white;
border-bottom: 1px solid var(--kaauh-border);
font-size: 0.825rem;
padding: 0.4rem 0;
}
.top-bar a { text-decoration: none; }
.top-bar .social-icons i {
color: var(--kaauh-teal);
transition: color 0.2s;
}
.top-bar .social-icons i:hover {
color: var(--kaauh-teal-dark);
}
.top-bar .contact-item {
display: flex;
align-items: center;
gap: 0.35rem;
padding: 0.25rem 0.5rem;
}
.top-bar .logo-container img {
height: 60px;
object-fit: contain;
}
@media (max-width: 767.98px) {
.top-bar {
display: none;
}
}
/* Navbar */
.navbar-brand {
font-weight: 700;
letter-spacing: -0.5px;
font-size: 1.25rem;
display: flex;
align-items: center;
gap: 0.5rem;
}
.navbar-dark {
background-color: var(--kaauh-teal) !important;
box-shadow: 0 2px 6px rgba(0,0,0,0.12);
}
/* Ensure the inner container of the navbar stretches to allow max-width-1600 */
.navbar-dark > .container {
max-width: 100%;
}
.nav-link {
font-weight: 500;
transition: all 0.2s ease;
padding: 0.5rem 0.75rem;
}
.nav-link:hover,
.nav-link.active {
.nav-link:hover, .nav-link.active {
color: white !important;
background: rgba(255,255,255,0.12) !important;
border-radius: 4px;
}
/* Dropdown */
.dropdown-menu {
backdrop-filter: blur(4px);
background-color: rgba(255, 255, 255, 0.98);
border: 1px solid var(--kaauh-border);
box-shadow: 0 6px 20px rgba(0,0,0,0.12);
border-radius: 8px;
padding: 0.5rem 0;
min-width: 200px;
will-change: transform, opacity;
transition: transform 0.2s ease, opacity 0.2s ease;
}
.dropdown-item {
padding: 0.5rem 1.25rem;
transition: background-color 0.15s;
}
.dropdown-item:hover {
background-color: var(--kaauh-light-bg);
color: var(--kaauh-teal-dark);
}
/* Language Toggle Button Style */
.language-toggle-btn {
color: white !important;
background: none !important;
border: none !important;
display: flex;
align-items: center;
gap: 0.3rem;
padding: 0.5rem 0.75rem !important;
font-weight: 500;
transition: all 0.2s ease;
}
.language-toggle-btn:hover {
background: rgba(255,255,255,0.12) !important;
border-radius: 4px;
}
/* Profile Avatar */
.profile-avatar {
width: 36px;
height: 36px;
@ -157,42 +203,28 @@ main.container-fluid {
justify-content: center;
color: white;
font-weight: bold;
font-size: 0.85rem;
transition: transform 0.1s ease;
}
.navbar-nav .dropdown-toggle:hover .profile-avatar {
transform: scale(1.05);
}
.navbar-nav .dropdown-toggle.p-0:hover {
background: none !important;
}
/* ---------------------------------- */
/* 3. Component Styles (Cards & Forms)*/
/* 5. CARDS, FORMS & TYPOGRAPHY */
/* ---------------------------------- */
.kaauh-card {
.kaauh-card, .card {
border: 1px solid var(--kaauh-border);
border-radius: 0.75rem;
box-shadow: 0 4px 12px rgba(0,0,0,0.06);
background-color: white;
overflow: hidden;
}
/* NEW: Filter Controls Container Style */
.filter-controls {
.filter-controls, .tier-controls {
border: 1px solid var(--kaauh-border);
border-radius: 0.75rem;
box-shadow: 0 4px 12px rgba(0,0,0,0.06);
background-color: white;
padding: 1.5rem; /* Consistent internal padding */
margin-bottom: 1.5rem; /* Space below filter */
padding: 1.5rem;
margin-bottom: 1.5rem;
}
/* Typography & Headers */
.page-header {
color: var(--kaauh-teal-dark);
font-weight: 700;
}
.page-header { color: var(--kaauh-teal-dark); font-weight: 700; }
.section-header {
color: var(--kaauh-primary-text);
font-weight: 600;
@ -200,14 +232,7 @@ main.container-fluid {
padding-bottom: 0.5rem;
margin-bottom: 1rem;
}
label {
font-weight: 500;
color: var(--kaauh-primary-text);
font-size: 0.9rem;
margin-bottom: 0.25rem;
}
/* Forms - Default Size */
.form-control, .form-select {
border-radius: 0.5rem;
border: 1px solid #ced4da;
@ -215,71 +240,13 @@ label {
font-size: 0.9rem;
}
/* Forms - Compact Size (for modals/tables) */
.form-control-sm,
.form-select-sm,
.btn-sm {
padding: 0.25rem 0.5rem !important; /* Adjusted padding */
.form-control-sm, .form-select-sm, .btn-sm {
padding: 0.25rem 0.5rem !important;
font-size: 0.8rem !important;
line-height: 1.25 !important; /* Standard small line height */
height: auto !important;
}
.form-select-sm {
padding: 0.25rem 2rem 0.25rem 0.5rem !important; /* Increased right padding for arrow */
font-size: 0.8rem !important;
line-height: 1.25 !important;
height: auto !important; /* Remove fixed height */
border-radius: 0.5rem;
border: 1px solid #ced4da;
}
/* Scrollable Multiple Select Fix */
.form-group select[multiple] {
max-height: 450px;
overflow-y: auto;
min-height: 250px;
padding: 0;
}
/* Break Times Section Styling (Schedule Interviews) */
.break-time-form {
background-color: #f8f9fa;
padding: 0.75rem;
border-radius: 0.5rem;
border: 1px solid var(--kaauh-border);
align-items: flex-end;
}
.note-box {
background-color: #fff3cd;
border-left: 5px solid var(--kaauh-warning);
padding: 1rem;
border-radius: 0.25rem;
font-size: 0.9rem;
margin-bottom: 1rem;
}
/* Tier Controls (Kept for consistency/future use) */
.tier-controls {
background-color: var(--kaauh-border);
border-radius: 0.75rem;
padding: 1.25rem;
margin-bottom: 2rem;
border: 1px solid var(--kaauh-border);
}
.tier-controls .form-row {
display: flex;
align-items: end;
gap: 1rem;
}
.tier-controls .form-group {
flex: 1;
margin-bottom: 0;
}
/* ---------------------------------- */
/* 4. Button Styles (Component Themed)*/
/* 6. BUTTONS */
/* ---------------------------------- */
.btn-main-action {
background-color: var(--kaauh-teal);
@ -295,6 +262,15 @@ label {
}
.btn-main-action.btn-sm { font-weight: 600 !important; }
.btn-outline-primary {
color: var(--kaauh-teal);
border-color: var(--kaauh-teal);
}
.btn-outline-primary:hover {
background-color: var(--kaauh-teal);
color: white;
}
.btn-outline-secondary {
color: var(--kaauh-teal-dark);
border-color: var(--kaauh-teal);
@ -305,6 +281,7 @@ label {
border-color: var(--kaauh-teal-dark);
}
.btn-bulk-pass {
background-color: var(--kaauh-success);
border-color: var(--kaauh-success);
@ -344,394 +321,143 @@ label {
}
/* ---------------------------------- */
/* 5. Table & Footer Styles */
/* 7. TABLES & PAGINATION */
/* ---------------------------------- */
.candidate-table {
table-layout: fixed;
.candidate-table, .job-table {
width: 100%;
border-collapse: separate;
border-spacing: 0;
background-color: white;
border-radius: 0.5rem;
overflow: hidden;
box-shadow: 0 4px 12px rgba(0,0,0,0.06);
}
.candidate-table thead {
background-color: var(--kaauh-border);
}
.candidate-table th {
padding: 0.75rem;
font-weight: 600;
color: var(--kaauh-teal-dark);
border-bottom: 2px solid var(--kaauh-teal);
font-size: 0.9rem;
vertical-align: middle;
}
.candidate-table td {
padding: 0.75rem;
border-bottom: 1px solid var(--kaauh-border);
vertical-align: middle;
font-size: 0.9rem;
}
.candidate-table tbody tr:hover {
background-color: #f1f3f4;
}
.candidate-name {
font-weight: 600;
color: var(--kaauh-primary-text);
}
.candidate-details {
font-size: 0.8rem;
color: #6c757d;
}
/* Job Table Specific Styles */
.job-table-wrapper {
background: white;
border-radius: 10px;
overflow: hidden;
box-shadow: 0 4px 16px rgba(0,0,0,0.06);
margin-bottom: 2rem;
.candidate-table th {
background-color: var(--kaauh-border);
color: var(--kaauh-teal-dark);
font-weight: 600;
padding: 0.75rem;
border-bottom: 2px solid var(--kaauh-teal);
}
.job-table thead th {
background: var(--kaauh-teal);
color: white;
font-weight: 600;
padding: 1rem;
text-align: center;
}
.job-table td {
padding: 1rem;
vertical-align: middle;
text-align: center;
}
.job-table tr:hover td {
background-color: rgba(0, 99, 110, 0.03);
}
/* Table Responsiveness */
@media (max-width: 575.98px) {
.table-responsive {
overflow-x: auto;
-webkit-overflow-scrolling: touch;
}
.job-table th,
.job-table td {
white-space: nowrap;
font-size: 0.875rem;
}
.pagination .page-link {
color: var(--kaauh-teal);
border-color: var(--kaauh-border);
}
/* Bulk Action Bar (Interview Management) */
.bulk-action-bar {
display: flex;
align-items: center;
gap: 0.5rem;
padding-bottom: 0.75rem;
margin-bottom: 1rem;
border-bottom: 1px solid var(--kaauh-border);
}
.action-group {
display: flex;
align-items: center;
gap: 0.5rem;
}
/* Badges (Adapted for Interview Tiers/Status) */
.ai-score-badge { /* Used as an all-purpose secondary badge */
background-color: var(--kaauh-teal-dark) !important;
.pagination .page-item.active .page-link {
background-color: var(--kaauh-teal);
border-color: var(--kaauh-teal);
color: white;
font-weight: 700;
padding: 0.4em 0.8em;
border-radius: 0.4rem;
font-size: 0.8rem;
}
.tier-badge { /* Used for Tier labels */
/* ---------------------------------- */
/* 8. BADGES & TIMELINE */
/* ---------------------------------- */
.tier-badge, .stage-badge, .status-badge {
font-size: 0.75rem;
padding: 0.125rem 0.5rem;
border-radius: 0.5rem;
padding: 0.25rem 0.6rem;
border-radius: 0.4rem;
font-weight: 600;
margin-left: 0.5rem;
display: inline-block;
}
.tier-1-badge { background-color: var(--kaauh-success); color: white; }
.tier-2-badge { background-color: var(--kaauh-warning); color: #856404; }
.tier-3-badge { background-color: #d1ecf1; color: #0c5460; }
.cd_interview{ color: #00636e; }
/* NEW: Application Stage Badges */
.stage-badge {
font-size: 0.8rem;
padding: 0.2em 0.6em;
border-radius: 0.4rem;
font-weight: 600;
display: inline-block;
margin-top: 0.25rem;
}
.stage-Application { background-color: #e9ecef; color: #495057; }
.stage-Screening { background-color: #d1ecf1; color: #0c5460; }
.stage-Exam { background-color: #cce5ff; color: #004085; }
.stage-Interview { background-color: #fff3cd; color: #856404; }
.stage-Offer { background-color: #d4edda; color: #155724; }
/* NEW: Applicant Status Badges */
.status-badge {
font-size: 0.8rem;
padding: 0.2em 0.6em;
border-radius: 0.4rem;
font-weight: 500;
display: inline-block;
}
.bg-candidate { background-color: var(--kaauh-teal-dark); color: white; }
.bg-applicant { background-color: #f8f9fa; color: #495057; border: 1px solid #ced4da; }
/* Table Column Width Fixes */
.candidate-table th:nth-child(1) { width: 40px; }
.candidate-table th:nth-child(4) { width: 15%; }
.candidate-table th:nth-child(5) { width: 80px; }
.candidate-table th:nth-child(6) { width: 180px; }
/* Footer & Alerts */
.footer {
background: var(--kaauh-light-bg);
padding: 1.5rem 0;
border-top: 1px solid var(--kaauh-border);
font-size: 0.9rem;
color: #555;
}
.alert {
border: none;
border-radius: 8px;
box-shadow: 0 2px 6px rgba(0,0,0,0.05);
}
/* ---------------------------------- */
/* 6. RTL Support */
/* ---------------------------------- */
html[dir="rtl"] {
text-align: right;
direction: rtl;
}
html[dir="rtl"] .navbar-brand {
flex-direction: row-reverse;
}
html[dir="rtl"] .dropdown-menu {
right: auto;
left: 0;
}
/* RTL Spacing adjustments */
html[dir="rtl"] .me-3 { margin-right: 0 !important; margin-left: 1rem !important; }
html[dir="rtl"] .ms-3 { margin-left: 0 !important; margin-right: 1rem !important; }
html[dir="rtl"] .me-2 { margin-right: 0 !important; margin-left: 0.5rem !important; }
html[dir="rtl"] .ms-2 { margin-left: 0 !important; margin-right: 0.5rem !important; }
html[dir="rtl"] .ms-auto { margin-left: 0 !important; margin-right: auto !important; }
html[dir="rtl"] .me-auto { margin-right: 0 !important; margin-left: auto !important; }
/* ================================================= */
/* 1. THEME VARIABLES AND GLOBAL STYLES */
/* ================================================= */
:root {
--kaauh-teal: #00636e;
--kaauh-teal-dark: #004a53;
--kaauh-border: #eaeff3;
--kaauh-primary-text: #343a40;
}
/* Primary Color Overrides */
.text-primary { color: var(--kaauh-teal) !important; }
.text-info { color: #17a2b8 !important; }
.text-success { color: #28a745 !important; }
.text-secondary { color: #6c757d !important; }
.text-kaauh-primary { color: var(--kaauh-primary-text); } /* Custom class for primary text color if needed */
/* ---------------------------------- */
/* 2. Button Styles */
/* ---------------------------------- */
/* Main Action Button Style (Used for Download Resume) */
.btn-main-action {
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 {
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);
}
/* Outlined Button Styles */
.btn-outline-primary {
color: var(--kaauh-teal);
border-color: var(--kaauh-teal);
}
.btn-outline-primary:hover {
background-color: var(--kaauh-teal);
color: white;
}
.btn-outline-secondary {
color: var(--kaauh-teal-dark);
border-color: var(--kaauh-teal);
}
.btn-outline-secondary:hover {
background-color: var(--kaauh-teal-dark);
color: white;
border-color: var(--kaauh-teal-dark);
}
/* ---------------------------------- */
/* 3. Card/Modal Styles */
/* ---------------------------------- */
/* Card enhancements */
.kaauh-card, .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;
}
/* Candidate Header Card (The teal header) */
.candidate-header-card {
background: linear-gradient(135deg, var(--kaauh-teal), #004d57);
color: white;
border-radius: 0.75rem 0.75rem 0 0;
padding: 1.5rem;
box-shadow: 0 4px 10px rgba(0,0,0,0.15);
}
.candidate-header-card h1 {
font-weight: 700;
margin: 0;
font-size: 1.8rem;
}
.candidate-header-card .badge {
font-size: 0.9rem;
padding: 0.4em 0.8em;
border-radius: 0.4rem;
font-weight: 700;
}
/* ---------------------------------- */
/* 4. Tab Navigation Styles (Candidate Detail View) */
/* ---------------------------------- */
/* Left Column Tabs (Main Content Tabs) */
.main-tabs {
border-bottom: 1px solid var(--kaauh-border);
background-color: #f8f9fa;
padding: 0 1.25rem;
}
.main-tabs .nav-link {
border: none;
border-bottom: 3px solid transparent;
color: var(--kaauh-primary-text);
font-weight: 500;
padding: 0.75rem 1rem;
margin-right: 0.5rem;
transition: all 0.2s;
}
.main-tabs .nav-link:hover {
color: var(--kaauh-teal);
}
.main-tabs .nav-link.active {
color: var(--kaauh-teal-dark) !important;
background-color: white !important;
border-bottom: 3px solid var(--kaauh-teal);
font-weight: 600;
}
/* Right Column Card (General styling for tab content if needed) */
.right-column-card .tab-content {
padding: 1.5rem 1.25rem;
background-color: white;
}
/* ---------------------------------- */
/* 5. Vertical Timeline Styling */
/* ---------------------------------- */
/* Highlight box for the current stage */
.current-stage {
border: 1px solid var(--kaauh-border);
background-color: #f0f8ff; /* Light, subtle blue background */
}
.current-stage .text-primary {
color: var(--kaauh-teal) !important;
}
.timeline {
position: relative;
padding-left: 2rem;
}
.timeline { position: relative; padding-left: 2rem; }
.timeline::before {
content: '';
position: absolute;
top: 0;
bottom: 0;
left: 1.25rem;
width: 2px;
background-color: var(--kaauh-border);
}
.timeline-item {
position: relative;
margin-bottom: 2rem;
padding-left: 1.5rem;
content: ''; position: absolute; top: 0; bottom: 0; left: 1.25rem;
width: 2px; background-color: var(--kaauh-border);
}
.timeline-icon {
position: absolute;
left: 0;
top: 0;
width: 32px;
height: 32px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
color: white;
font-size: 0.8rem;
z-index: 10;
border: 4px solid white;
}
.timeline-item:last-child {
margin-bottom: 0;
position: absolute; left: 0; top: 0; width: 32px; height: 32px;
border-radius: 50%; display: flex; align-items: center; justify-content: center;
color: white; border: 4px solid white; z-index: 10;
}
/* Custom Timeline Background Classes for Stages (Using Bootstrap color palette) */
.timeline-bg-applied { background-color: var(--kaauh-teal) !important; }
.timeline-bg-exam { background-color: #17a2b8 !important; }
.timeline-bg-interview { background-color: #ffc107 !important; }
.timeline-bg-offer { background-color: #28a745 !important; }
.timeline-bg-rejected { background-color: #dc3545 !important; }
.loading {
background: linear-gradient(135deg, var(--kaauh-teal), #004d57);
color: white;
border-radius: 0.75rem 0.75rem 0 0;
padding: 1.5rem;
box-shadow: 0 4px 10px rgba(0,0,0,0.15);
/* ---------------------------------- */
/* 9. RTL SUPPORT */
/* ---------------------------------- */
html[dir="rtl"] { text-align: right; direction: rtl; }
html[dir="rtl"] .sidebar {
left: auto; right: 0;
box-shadow: -4px 0 10px rgba(0,0,0,0.1);
}
html[dir="rtl"] .main-wrapper {
margin-left: 0;
margin-right: var(--sidebar-width);
}
/* Breadcrumb Styling */
.breadcrumb {
background-color: transparent;
padding: 0;
margin-bottom: 1rem;
}
html[dir="rtl"] .sidebar.collapsed + .main-wrapper {
margin-right: var(--sidebar-collapsed-width);
}
.breadcrumb-item + .breadcrumb-item::before {
content: ">";
color: var(--kaauh-teal);
}
html[dir="rtl"] .nav-link-custom {
border-left: none;
border-right: 4px solid transparent;
}
html[dir="rtl"] .nav-link-custom.active {
border-right: 4px solid var(--kaauh-warning);
}
/* ---------------------------------- */
/* 10. RESPONSIVE */
/* ---------------------------------- */
@media (max-width: 991.98px) {
.sidebar { left: calc(-1 * var(--sidebar-width)); }
html[dir="rtl"] .sidebar { right: calc(-1 * var(--sidebar-width)); left: auto; }
.sidebar.show { left: 0; }
html[dir="rtl"] .sidebar.show { right: 0; }
.main-wrapper { margin-left: 0 !important; margin-right: 0 !important; }
}
/* --- Logout Section Improvements --- */
.sidebar .logout-section {
padding: 0.85rem 1.5rem; /* Match nav-link-custom padding */
transition: all 0.3s ease;
border-top: 1px solid rgba(255, 255, 255, 0.08);
}
.sidebar.collapsed .logout-section {
padding: 0.85rem 0;
text-align: center;
}
.sidebar .logout-section button {
color: rgba(255, 255, 255, 0.7) !important;
width: 100%;
justify-content: flex-start;
transition: color 0.2s;
}
.sidebar .logout-section button:hover {
color: var(--kaauh-danger) !important;
}
/* Hide text and handle icon centering in collapsed mode */
.sidebar.collapsed .logout-section span {
display: none !important;
}
.sidebar.collapsed .logout-section i {
margin: 0 !important;
width: 100%;
font-size: 1.1rem;
}
/* Ensure form doesn't add extra margins */
.sidebar .logout-section form {
margin: 0;
}

View File

@ -3,7 +3,7 @@
{% load account %}
{% load crispy_forms_tags %}
{% block title %}{% translate "Email Addresses" %}{% endblock %}
{% block title %}{% trans "Email Addresses" %}{% endblock %}
{% block content %}
<div class="container my-5">

View File

@ -6,7 +6,7 @@
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>KAAUH ATS - Sign In (Bootstrap)</title>
<title>{% trans "KAAUH ATS - Sign In" %} </title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH" crossorigin="anonymous">

View File

@ -2,15 +2,15 @@
{% load i18n %}
{% load account %}
{% block title %}{% translate "Sign Out" %}{% endblock %}
{% block title %}{% trans "Sign Out" %}{% endblock %}
{% block content %}
<div class="container my-5">
<div class="row mb-4">
<div class="col">
<h3 class="fw-bold">{% translate "Account Settings" %}</h3>
<p class="text-muted">{% translate "Manage your personal details and security." %}</p>
<h3 class="fw-bold">{% trans "Account Settings" %}</h3>
<p class="text-muted">{% trans "Manage your personal details and security." %}</p>
</div>
</div>
@ -23,18 +23,18 @@
<div class="list-group list-group-flush rounded-4">
{# Assuming a main 'Profile' or 'Personal Information' page exists #}
<a href="{% url 'user_detail' user.pk %}" class="list-group-item list-group-item-action border-0 rounded-top-4 py-3">
<i class="fas fa-user-circle me-2"></i> {% translate "Personal Information" %}
<i class="fas fa-user-circle me-2"></i> {% trans "Personal Information" %}
</a>
<a href="{% url 'account_email' %}" class="list-group-item list-group-item-action border-0 py-3">
<i class="fas fa-envelope me-2"></i> {% translate "Email Addresses" %}
<i class="fas fa-envelope me-2"></i> {% trans "Email Addresses" %}
</a>
<a href="{% url 'account_change_password' %}" class="list-group-item list-group-item-action border-0 py-3">
<i class="fas fa-lock me-2"></i> {% translate "Change Password" %}
<i class="fas fa-lock me-2"></i> {% trans "Change Password" %}
</a>
{# Highlight the current page (Sign Out) as active #}
<a href="{% url 'account_logout' %}" class="list-group-item list-group-item-action active border-0 rounded-bottom-4 py-3" style="background-color: #fce8e8; color: #dc3545; font-weight: 500;" aria-current="true">
<i class="fas fa-sign-out-alt me-2"></i> {% translate "Sign Out" %}
<i class="fas fa-sign-out-alt me-2"></i> {% trans "Sign Out" %}
</a>
</div>
</div>
@ -47,9 +47,9 @@
<div class="card-body p-4 text-center">
<i class="fas fa-sign-out-alt text-danger mb-4" style="font-size: 3rem;"></i>
<h4 class="fw-bold mb-3">{% translate "Confirm Sign Out" %}</h4>
<h4 class="fw-bold mb-3">{% trans "Confirm Sign Out" %}</h4>
<p class="lead mb-4">{% translate "Are you sure you want to sign out of your account?" %}</p>
<p class="lead mb-4">{% trans "Are you sure you want to sign out of your account?" %}</p>
<form method="post" action="{% url 'account_logout' %}">
{% csrf_token %}
@ -63,12 +63,12 @@
<div class="d-flex justify-content-center gap-3 mt-4">
{# Sign Out button in danger color #}
<button class="btn btn-danger btn-lg px-5" type="submit">
{% translate "Sign Out" %}
{% trans "Sign Out" %}
</button>
{# Cancel/Go Back button with outline #}
<a class="btn btn-outline-secondary btn-lg px-5" href="{% url 'account_email' %}">
{% translate "Cancel" %}
{% trans "Cancel" %}
</a>
</div>
</form>

View File

@ -4,7 +4,7 @@
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>KAAUH ATS - Sign In (Bootstrap)</title>
<title>{% trans Reset Password %}-KAAUH ATS</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH" crossorigin="anonymous">

View File

@ -7,7 +7,7 @@
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>KAAUH ATS - Verify Email</title>
<title>{% trans "Verify Email" %}-KAAUH ATS </title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH" crossorigin="anonymous">

View File

@ -1,4 +1,5 @@
{% extends 'applicant/partials/candidate_facing_base.html'%}
{% load static i18n %}
{% block content %}
<style>
@ -542,6 +543,7 @@
const state = {
templateId: '{{ template_slug }}',
job_slug: '{{ job_slug }}',
stages: [],
currentStage: 0,
formData: {},
@ -765,7 +767,7 @@
// API Functions
async function loadFormTemplate() {
try {
const response = await fetch(`/api/v1/templates/${state.templateId}/`);
const response = await fetch(`/api/v1/templates/${state.job_slug}/`);
const result = await response.json();
if (result.success) {
@ -831,7 +833,7 @@
});
try {
const response = await fetch(`/application/${state.templateId}/submit/`, {
const response = await fetch(`/application/${state.job_slug}/submit/`, {
method: 'POST',
body: formData
// IMPORTANT: Do NOT set Content-Type header when using FormData

View File

@ -1,6 +1,6 @@
{% extends 'applicant/partials/candidate_facing_base.html' %}
{% load static i18n %}
{% block title %}{% trans "Application" %}-{{ job.title }}{% endblock %}
{% block content %}
{# ------------------------------------------------ #}

View File

@ -9,6 +9,11 @@
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{% trans "Careers" %} - {% block title %}{% trans "Application Form" %}{% endblock %}</title>
<link rel="apple-touch-icon" sizes="180x180" href="{% static 'image/favicon/apple-touch-icon.png'%}">
<link rel="icon" type="image/png" sizes="32x32" href="{% static 'image/favicon/favicon-32x32.png'%}">
<link rel="icon" type="image/png" sizes="16x16" href="{% static 'image/favicon/favicon-16x16.png'%}">
<link rel="manifest" href="{% static 'image/favicon/site.webmanifest'%}">
{% comment %} Load the correct Bootstrap CSS file for RTL/LTR {% endcomment %}
{% if LANGUAGE_CODE == 'ar' %}

View File

@ -6,812 +6,192 @@
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="description" content="{% trans 'King Abdullah Academic University Hospital - Applicant Tracking System' %}">
<title>{% block title %}{% trans 'University ATS' %}{% endblock %}</title>
{% comment %} Load correct Bootstrap CSS file for RTL/LTR {% endcomment %}
{% if LANGUAGE_CODE == 'ar' %}
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.rtl.min.css" rel="stylesheet">
{% else %}
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
{% endif %}
<link rel="apple-touch-icon" sizes="180x180" href="{% static 'image/favicon/apple-touch-icon.png'%}">
<link rel="icon" type="image/png" sizes="32x32" href="{% static 'image/favicon/favicon-32x32.png'%}">
<link rel="icon" type="image/png" sizes="16x16" href="{% static 'image/favicon/favicon-16x16.png'%}">
<link rel="manifest" href="{% static 'image/favicon/site.webmanifest'%}">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.6.0/css/all.min.css" integrity="sha512-Kc323vGBEqzTmouAECnVceyQqyqdsSiqLQISBL29aUW4U/M7pSPA/gEUZQqv1cwx4OnYxTxve5UMg5GT6L4JJg==" crossorigin="anonymous" referrerpolicy="no-referrer" />
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.6.0/css/all.min.css" />
<link rel="stylesheet" href="{% static 'css/main.css' %}">
<script src="{% static 'js/main.js' %}"></script>
<script src="{% static 'js/typo.js' %}"></script>
{% block customCSS %}{% endblock %}
</head>
<body class="d-flex flex-column min-vh-100" hx-headers='{"X-CSRFToken": "{{ csrf_token }}"}'>
<body>
<div class="top-bar d-none d-md-block">
<div class="container-fluid">
<div class="d-flex justify-content-between align-items-center gap-2 max-width-1600">
<div class="logo-container d-flex gap-2">
</div>
<div class="clogo-container d-flex gap-2">
</div>
<div class="logo-container d-flex gap-2 align-items-center">
<img src="{% static 'image/vision.svg' %}" alt="{% trans 'Saudi Vision 2030' %}" loading="lazy" style="height: 35px; object-fit: contain;">
<div class="kaauh-logo-container d-flex flex-column flex-md-row align-items-center gap-2 me-0">
<div class="hospital-text text-center text-md-start me-0">
<div class="en text-xs">{% trans "Princess Nourah bint Abdulrahman University"%}</div>
<div class="en text-xs">{% trans "King Abdullah bin Abdulaziz University Hospital"%}</div>
</div>
</div>
<img src="{% static 'image/kaauh.png' %}" alt="KAAUH Logo" style="max-height: 40px; max-width: 40px;">
</div>
</div>
<aside class="sidebar d-flex flex-column" id="sidebar">
<div class="sidebar-brand text-center">
<img src="{% static 'image/kaauh_green1.png' %}" alt="Logo" style="width: 40px;">
<div class="text-white small mt-2 fw-bold brand-subtitle">KAAUH ATS</div>
</div>
</div>
<nav class="navbar navbar-expand-lg navbar-dark sticky-top">
<div class="container-fluid max-width-1600">
<a class="navbar-brand text-white d-none d-lg-block me-4 pe-4" href="{% url 'dashboard' %}" aria-label="Home">
<img src="{% static 'image/kaauh_green1.png' %}" alt="{% trans 'kaauh logo green bg' %}" style="width: 60px; height: 60px;">
<nav class="sidebar-nav">
<a class="nav-link-custom {% if request.resolver_match.url_name == 'dashboard' %}active{% endif %}" href="{% url 'dashboard' %}">
<i class="fas fa-th-large me-2"></i> <span>{% trans "Dashboard" %}</span>
</a>
<a class="nav-link-custom {% if 'message' in request.path %}active{% endif %}" href="{% url 'message_list' %}">
<i class="fas fa-envelope me-2"></i>
<span>{% trans "Messages" %}</span>
{% if request.user.get_unread_message_count > 0 %}
<span class="badge rounded-pill bg-danger ms-auto">
{{ request.user.get_unread_message_count }}
</span>
{% endif %}
</a>
{# Toggler: order-lg-0 ensures it's before navigation links on desktop, but it stays where it is on mobile #}
<button class="navbar-toggler order-lg-0" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav"
aria-controls="navbarNav" aria-expanded="false" aria-label="{% trans 'Toggle navigation' %}">
<span class="navbar-toggler-icon"></span>
</button>
<a class="nav-link-custom {% if 'job' in request.path and 'bank' not in request.path %}active{% endif %}" href="{% url 'job_list' %}">
<i class="fas fa-briefcase me-2"></i> <span>{% trans "Jobs" %}</span>
</a>
{# Language and Profile Controls (Keep outside collapse for mobile access) #}
<div class="d-flex align-items-center order-lg-3">
{% comment %} <ul class="navbar-nav flex-row">
<li class="nav-item me-2">
<li class="nav-item me-2">
<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 class="nav-item me-2">
<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>
</li>
</ul> {% endcomment %}
<a class="nav-link-custom {% if 'job_bank' in request.resolver_match.url_name %}active{% endif %}" href="{% url 'job_bank' %}">
<i class="fas fa-university me-2"></i> <span>{% trans "Job Bank" %}</span>
</a>
<ul class="navbar-nav ms-2 ms-lg-4">
<!-- Notification Bell for Admin Users -->
{% comment %} {% if request.user.is_authenticated and request.user.is_staff %}
<li class="nav-item dropdown me-2">
<a class="nav-link position-relative" href="#" role="button" id="notificationDropdown" data-bs-toggle="dropdown" aria-expanded="false">
<i class="fas fa-bell"></i>
<span id="admin-notification-badge" class="position-absolute top-0 start-100 translate-middle badge rounded-pill bg-danger" style="display: none; font-size: 0.6em; min-width: 18px; height: 18px; line-height: 18px;">
0
</span>
</a>
<ul class="dropdown-menu dropdown-menu-end" style="min-width: 300px;" aria-labelledby="notificationDropdown">
<li class="dropdown-header d-flex justify-content-between align-items-center">
<span>{% trans "Messages" %}</span>
<a href="#" class="text-decoration-none">{% trans "View All" %}</a>
</li>
<li><hr class="dropdown-divider"></li>
<li>
<div id="admin-notification-list" class="px-3 py-2 text-muted text-center">
<small>{% trans "Loading messages..." %}</small>
</div>
</li>
</ul>
</li>
{% endif %} {% endcomment %}
{% comment %} <li class="nav-item me-2">
<a class="nav-link" href="{% url 'message_list' %}">
<i class="fas fa-envelope"></i>
</a>
</li> {% endcomment %}
<a class="nav-link-custom {% if 'application' in request.path %}active{% endif %}" href="{% url 'application_list' %}">
<i class="fas fa-user-tie me-2"></i> <span>{% trans "Applications" %}</span>
</a>
<a class="nav-link-custom {% if 'person' in request.path %}active{% endif %}" href="{% url 'person_list' %}">
<i class="fas fa-user me-2"></i> <span>{% trans "Applicants" %}</span>
</a>
<a class="nav-link-custom {% if 'agency' in request.path %}active{% endif %}" href="{% url 'agency_list' %}">
<i class="fas fa-building me-2"></i> <span>{% trans "Agencies" %}</span>
</a>
<a class="nav-link-custom {% if 'interview' in request.path %}active{% endif %}" href="{% url 'interview_list' %}">
<i class="fas fa-calendar-alt me-2"></i> <span>{% trans "Interviews" %}</span>
</a>
<a class="nav-link-custom {% if request.resolver_match.url_name == 'kaauh_career' %}active{% endif %}" href="{% url 'kaauh_career' %}">
<i class="fas fa-globe me-2"></i> <span>{% trans "Career Page" %}</span>
</a>
<li class="nav-item me-2 d-none d-lg-block">
{% if LANGUAGE_CODE == 'en' %}
<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="btn bg-primary-theme text-white" type="submit">
<span class="me-2">🇸🇦</span> العربية
</button>
</form>
{% elif LANGUAGE_CODE == 'ar' %}
<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="btn bg-primary-theme text-white" type="submit">
<span class="me-2">🇺🇸</span> English
</button>
</form>
{% endif %}
</li>
<li class="nav-item mx-3 d-none d-lg-block mt-2">
<a href="{% url 'message_list' %}" class="btn btn-sm btn-outline-warning position-relative">
<i class="fas fa-envelope me-1"></i>
<span class="position-absolute top-0 start-100 translate-middle badge rounded-pill bg-danger">
{{ request.user.get_unread_message_count }}
</span>
</a>
</li>
<li class="nav-item dropdown">
<button
class="nav-link p-0 border-0 bg-transparent dropdown-toggle"
type="button"
data-bs-toggle="dropdown"
aria-expanded="false"
aria-label="{% trans 'Toggle user menu' %}"
data-bs-auto-close="outside"
data-bs-offset="0, 16"
>
{% if user.profile_image %}
<img src="{{ user.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' %}">
{% if user.first_name %}
{{ user.first_name|first|capfirst }} {{ user.last_name|first|capfirst }}
{% else %}
{{user.username|first|capfirst}}
{% endif %}
</div>
{% endif %}
</button>
<ul class="dropdown-menu dropdown-menu-end py-0 shadow border-0 rounded-3" style="min-width: 240px;">
<li class="px-4 py-3">
<div class="d-flex align-items-center">
<div class="me-3 d-flex align-items-center justify-content-center" style="min-width: 48px;">
{% if user.profile_image %}
<img src="{{ user.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>
<div class="text-muted small">{{ user.email|truncatechars:24 }}</div>
</div>
</div>
</li>
<li><hr class="dropdown-divider my-1"></li>
<li class="nav-item me-3 dropdown-item py-2 px-4 d-flex align-items-center text-decoration-none text-teal d-lg-none">
{% if LANGUAGE_CODE == 'en' %}
<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="btn bg-primary-theme text-white" type="submit">
<span class="me-2">🇸🇦</span> العربية
</button>
</form>
{% elif LANGUAGE_CODE == 'ar' %}
<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="btn bg-primary-theme text-white" type="submit">
<span class="me-2">🇺🇸</span> English
</button>
</form>
{% endif %}
</li>
<li class="d-lg-none">
<a class="dropdown-item py-2 px-4 d-flex align-items-center text-decoration-none text-teal" href="{% url 'message_list' %}">
<i class="fas fa-envelope fs-5 me-3"></i> <span>{% trans "Messages" %}</span>
</a>
</li>
{% if request.user.is_authenticated %}
<li>
<a class="dropdown-item py-2 px-4 d-flex align-items-center text-decoration-none text-teal" href="{% url 'user_detail' request.user.pk %}">
<i class="fas fa-user-circle me-3 fs-5"></i> <span>{% trans "My Profile" %}</span>
</a>
</li>
{% if request.user.is_superuser %}
<li>
<a class="dropdown-item py-2 px-4 d-flex align-items-center text-decoration-none text-teal" href="{% url 'settings' %}">
<i class="fas fa-cog me-3 fs-5"></i> <span>{% trans "Settings" %}</span>
</a>
</li>
{% endif %}
<li><hr class="dropdown-divider my-1"></li>
<li>
<form method="post" action="{% url 'account_logout'%}" class="d-inline">
{% csrf_token %}
<button
type="submit"
class="dropdown-item py-2 px-4 d-flex align-items-center border-0 bg-transparent text-start w-100"
aria-label="{% trans 'Sign out' %}"
>
<i class="fas fa-sign-out-alt me-3 fs-5" style="color:red;"></i>
<span style="color:red;">{% trans "Sign Out" %}</span>
</button>
</form>
</li>
{% endif %}
</ul>
</li>
</ul>
{% if request.user.is_authenticated and request.user.is_superuser %}
<div class="mt-auto">
<div class="mt-4 pt-3 border-top border-secondary mx-3 uppercase">
{% comment %} <small class="text-white-50 px-2">{% trans "System" %}</small> {% endcomment %}
</div>
<a class="nav-link-custom {% if 'settings' in request.path %}active{% endif %}" href="{% url 'settings' %}">
<i class="fas fa-cog me-2"></i> <span>{% trans "Settings" %}</span>
</a>
</div>
{# End Language and Profile Controls #}
{% endif %}
</nav>
{# Main Navigation Links (This collapses on mobile) - order-lg-1 ensures it is centered on desktop #}
<div class="collapse navbar-collapse order-lg-1" id="navbarNav">
<ul class="navbar-nav me-auto mb-2 mb-lg-0">
<li class="nav-item me-lg-4">
<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">
<i class="fas fa-briefcase me-2"></i>
{% trans "Jobs" %}
</span>
</a>
</li>
<li class="nav-item me-lg-4">
<a class="nav-link {% if request.resolver_match.url_name == 'application_list' %}active{% endif %}" href="{% url 'application_list' %}">
<span class="d-flex align-items-center gap-2">
<i class="fas fa-user-tie me-2"></i>
{% trans "Applications" %}
</span>
</a>
</li>
<li class="nav-item me-lg-4">
<a class="nav-link {% if request.resolver_match.url_name == 'person_list' %}active{% endif %}" href="{% url 'person_list' %}">
<span class="d-flex align-items-center gap-2">
<i class="fas fa-user me-2"></i>
{% trans "Applicant" %}
</span>
</a>
</li>
<li class="nav-item me-lg-4">
<a class="nav-link {% if request.resolver_match.url_name == 'agency_list' %}active{% endif %}" href="{% url 'agency_list' %}">
<span class="d-flex align-items-center gap-2">
<i class="fas fa-building me-2"></i>
{% trans "Agencies" %}
</span>
</a>
</li>
<li class="nav-item me-lg-4">
<a class="nav-link {% if request.resolver_match.url_name == 'interview_list' %}active{% endif %}" href="{% url 'interview_list' %}">
<span class="d-flex align-items-center gap-2">
<i class="fas fa-calendar-check me-2"></i>
{% trans "Meetings & interviews" %}
</span>
</a>
</li>
<li class="nav-item me-lg-4">
<a class="nav-link {% if request.resolver_match.url_name == 'kaauh_career' %}active{% endif %}" href="{% url 'kaauh_career' %}">
<span class="d-flex align-items-center gap-2">
<i class="fas fa-globe me-2"></i>
{% trans "Career Page" %}
</span>
</a>
</li>
{% comment %} <li class="nav-item me-lg-4">
<a class="nav-link {% if request.resolver_match.url_name == 'interview_list' %}active{% endif %}" href="{% url 'interview_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">
<path stroke-linecap="round" stroke-linejoin="round" d="m15.75 10.5 4.72-4.72a.75.75 0 0 1 1.28.53v11.38a.75.75 0 0 1-1.28.53l-4.72-4.72M4.5 18.75h9a2.25 2.25 0 0 0 2.25-2.25v-9a2.25 2.25 0 0 0-2.25-2.25h-9A2.25 2.25 0 0 0 2.25 7.5v9a2.25 2.25 0 0 0 2.25 2.25Z" />
</svg>
{% trans "Onsite Interviews" %}
</span>
</a>
</li> {% endcomment %}
{% comment %} <li class="nav-item me-lg-4">
<a class="nav-link {% if request.resolver_match.url_name == 'participants_list' %}active{% endif %}" href="{% url 'participants_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">
<path stroke-linecap="round" stroke-linejoin="round" d="M3.75 6A2.25 2.25 0 0 1 6 3.75h2.25A2.25 2.25 0 0 1 10.5 6v2.25a2.25 2.25 0 0 1-2.25 2.25H6a2.25 2.25 0 0 1-2.25-2.25V6ZM3.75 15.75A2.25 2.25 0 0 1 6 13.5h2.25a2.25 2.25 0 0 1 2.25 2.25V18a2.25 2.25 0 0 1-2.25 2.25H6A2.25 2.25 0 0 1 3.75 18v-2.25ZM13.5 6a2.25 2.25 0 0 1 2.25-2.25H18A2.25 2.25 0 0 1 20.25 6v2.25A2.25 2.25 0 0 1 18 10.5h-2.25a2.25 2.25 0 0 1-2.25-2.25V6ZM13.5 15.75a2.25 2.25 0 0 1 2.25-2.25H18a2.25 2.25 0 0 1 2.25 2.25V18A2.25 2.25 0 0 1 18 20.25h-2.25A2.25 2.25 0 0 1 13.5 18v-2.25Z" />
</svg>
{% trans "Participants" %}
</span>
</a>
</li>
{% comment %} <li class="nav-item dropdown ms-lg-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" %}
</a>
<ul class="dropdown-menu" data-bs-popper="static">
<li><a class="dropdown-item" href="#"><i class="fas fa-calendar me-2"></i> {% trans "Meetings" %}</a></li>
<li><a class="dropdown-item" href="#"><i class="fas fa-clock me-2"></i> {% trans "Schedule" %}</a></li>
<li><hr class="dropdown-divider"></li>
<li><a class="dropdown-item" href="#"><i class="fas fa-briefcase me-2"></i> {% trans "Active Jobs" %}</a></li>
<li><a class="dropdown-item" href="#"><i class="fas fa-file-alt me-2"></i> {% trans "Draft Jobs" %}</a></li>
<li><hr class="dropdown-divider"></li>
<li><a class="dropdown-item" href="#"><i class="fas fa-users me-2"></i> {% trans "All Candidates" %}</a></li>
<li><a class="dropdown-item" href="#"><i class="fas fa-user-plus me-2"></i> {% trans "New Candidates" %}</a></li>
</ul>
</li> {% endcomment %}
</ul>
{% if request.user.is_authenticated %}
<div class="logout-section">
<form method="post" action="{% url 'account_logout'%}">
{% csrf_token %}
<button type="submit" class="btn btn-link text-decoration-none d-flex align-items-center">
<i class="fas fa-sign-out-alt me-2"></i> <span>{% trans "Sign Out" %}</span>
</button>
</form>
</div>
{% endif %}
<div class="mt-auto p-3 border-top border-secondary text-center">
<a href="https://tenhal.sa/" class="text-decoration-none" target="_blank">
<div class="text-white-50 brand-subtitle" style="font-size: 0.7rem; letter-spacing: 0.5px;">
{% trans "POWERED BY" %} <span class="text-white fw-bold">TENHAL</span>
</div>
</a>
</div>
</nav>
</aside>
<div class="main-wrapper">
<header class="content-header justify-content-between">
<div class="d-flex align-items-center">
<button class="btn me-2" id="sidebarCollapse">
<i class="fas fa-bars"></i>
</button>
<div class="hospital-text d-none d-md-block">
<small class="text-teal fw-bold">{% trans "KAAUH Applicant Tracking System" %}</small>
</div>
</div>
<main id="messageContent" class="container-fluid flex-grow-1" style="max-width: 1600px; margin: 0 auto;">
{% if messages %}
<div class="d-flex align-items-center gap-3">
<a href="{% url 'message_list' %}" class="btn btn-sm btn-outline-secondary position-relative d-none d-lg-block">
<i class="fas fa-envelope"></i>
{% if request.user.get_unread_message_count > 0 %}
<span class="position-absolute top-0 start-100 translate-middle badge rounded-pill bg-danger">
{{ request.user.get_unread_message_count }}
</span>
{% endif %}
</a>
{% if LANGUAGE_CODE == 'en' %}
<form action="{% url 'set_language' %}" method="post">{% csrf_token %}
<input name="language" type="hidden" value="ar">
<input name="next" type="hidden" value="{{ request.get_full_path }}">
<button class="btn btn-sm btn-outline-secondary" type="submit">🇸🇦 العربية</button>
</form>
{% else %}
<form action="{% url 'set_language' %}" method="post">{% csrf_token %}
<input name="language" type="hidden" value="en">
<input name="next" type="hidden" value="{{ request.get_full_path }}">
<button class="btn btn-sm btn-outline-secondary" type="submit">🇺🇸 English</button>
</form>
{% endif %}
<div class="dropdown">
<button class="btn p-0 border-0" data-bs-toggle="dropdown">
{% if user.profile_image %}
<img src="{{ user.profile_image.url }}" class="rounded-circle border" width="35" height="35">
{% else %}
<div class="profile-avatar" style="width: 35px; height: 35px; font-size: 0.8rem;">
{{ user.username|first|upper }}
</div>
{% endif %}
</button>
{% if request.user.is_authenticated %}
<ul class="dropdown-menu dropdown-menu-end shadow border-0">
<li class="px-3 py-2 border-bottom">
<div class="fw-bold small">{{ user.get_full_name|default:user.username }}</div>
<div class="text-muted text-xs">{{ user.email|truncatechars:20 }}</div>
</li>
<li><a class="dropdown-item mt-1" href="{% url 'user_detail' request.user.pk %}">{% trans "Profile" %}</a></li>
<li><hr class="dropdown-divider"></li>
<li>
<form method="post" action="{% url 'account_logout'%}">
{% csrf_token %}
<button type="submit" class="dropdown-item text-danger">{% trans "Sign Out" %}</button>
</form>
</li>
</ul>
{% endif %}
</div>
</div>
</header>
<main class="main-container mx-3 my-3">
{% if messages %}
{% for message in messages %}
<div class="alert alert-{{ message.tags }} alert-dismissible fade show mt-2" role="alert">
<div class="alert alert-{{ message.tags }} alert-dismissible fade show" role="alert">
{{ message }}
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="{% trans 'Close' %}"></button>
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
</div>
{% endfor %}
{% endif %}
{% block content %}
{% endblock %}
{% block content %}{% endblock %}
</main>
<footer class="mt-auto">
<div class="footer-bottom py-3 small text-muted" style="background-color: #00363a;">
<div class="container-fluid">
<div class="d-flex justify-content-between align-items-center flex-wrap max-width-1600">
<p class="mb-0 text-white-50">
{% comment %} &copy; {% now "Y" %} {% trans "King Abdullah Academic University Hospital (KAAUH)." %}
{% trans "All rights reserved." %} {% endcomment %}
</p>
<a class="text-decoration-none" href="https://tenhal.sa/" target='_blank'>
<p class="mb-0 text-white-50">
{% trans "Powered by" %} <strong class="text-white">Tenhal</strong>
</p>
</a>
</div>
</div>
</div>
</footer>
{% include 'includes/delete_modal.html' %}
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/htmx.org@2.0.7/dist/htmx.min.js"></script>
<script type="module" src="https://cdn.jsdelivr.net/gh/starfederation/datastar@1.0.0-RC.6/bundles/datastar.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.7.1/jquery.min.js" integrity="sha512-v2CJ7UaYy4JwqLDIrZUI/4hqeoQieOmAZNXBeQyjo21dadnwR+8ZaIJVT8EE2iyI61OV8e6M8PP2/4hpQINQ/g==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<script>
document.addEventListener('DOMContentLoaded', () => {
// Navbar collapse auto-close on link click (Standard Mobile UX)
const navbarCollapse = document.getElementById('navbarNav');
if (navbarCollapse) {
// Select all links, including those inside "More" dropdown
const navLinks = navbarCollapse.querySelectorAll('.nav-link:not(.dropdown-toggle), .dropdown-item');
const bsCollapse = bootstrap.Collapse.getInstance(navbarCollapse) || new bootstrap.Collapse(navbarCollapse, { toggle: false });
navLinks.forEach(link => {
link.addEventListener('click', () => {
// Only collapse if nav is actually shown (i.e., on mobile)
if (navbarCollapse.classList.contains('show')) {
// Check if click was on a non-dropdown-toggle or a dropdown item (which navigate away)
if (!link.classList.contains('dropdown-toggle')) {
bsCollapse.hide();
}
}
});
});
}
// Mobile logout confirmation (Good UX for small screens)
const logoutButton = document.querySelector('form[action$="/logout/"] button');
if (logoutButton) {
logoutButton.addEventListener('click', (e) => {
// Check if screen is small (Bootstrap 'lg' breakpoint is 992px)
if (window.innerWidth < 992) {
const confirmed = confirm('{% trans "Are you sure you want to sign out?" %}');
if (!confirmed) e.preventDefault();
}
});
}
});
function form_loader(){
const forms = document.querySelectorAll('form');
forms.forEach(form => {
form.addEventListener('submit', function(e) {
const submitButton = form.querySelector('button[type="submit"], input[type="submit"]');
if (submitButton) {
submitButton.disabled = true;
submitButton.classList.add('loading');
window.addEventListener('unload', function() {
submitButton.disabled = false;
submitButton.classList.remove('loading');
});
}
});
});
}
function remove_form_loader(){
const forms = document.querySelectorAll('form');
forms.forEach(form => {
form.addEventListener('htmx:afterRequest', function(evt) {
const submitButton = form.querySelector('button[type="submit"], input[type="submit"]');
if (submitButton) {
submitButton.disabled = false;
submitButton.classList.remove('loading');
}
});
});
}
//form_loader();
try{
document.body.addEventListener('htmx:afterRequest', function(evt) {
remove_form_loader();
});
}catch(e){
console.error(e)
}
function closeOpenBootstrapModal() {
const openModalElement = document.querySelector('.modal.show');
if (openModalElement) {
const modal = bootstrap.Modal.getInstance(openModalElement);
if (modal) {
modal.hide();
} else {
console.warn("Found an open modal element, but could not get the Bootstrap Modal instance.");
}
} else {
console.log("No open Bootstrap Modal found to close.");
}
}
</script>
<script>
document.addEventListener('DOMContentLoaded', function() {
// Update unread message count on page load
fetch('/api/unread-count/')
.then(response => response.json())
.then(data => {
const badge = document.getElementById('unread-messages-badge');
if (badge && data.unread_count > 0) {
badge.textContent = data.unread_count;
badge.style.display = 'inline-block';
}
})
.catch(error => console.error('Error fetching unread count:', error));
});
</script>
<!-- Notification JavaScript for Admin Users -->
{% comment %} {% if request.user.is_authenticated and request.user.is_staff %}
<script>
// SSE Notification System
let eventSource = null;
let reconnectAttempts = 0;
let maxReconnectAttempts = 5;
let reconnectDelay = 1000; // Start with 1 second
function connectSSE() {
// Close existing connection if any
if (eventSource) {
eventSource.close();
}
// Create new EventSource connection
eventSource = new EventSource('');
eventSource.onopen = function(event) {
console.log('SSE connection opened');
reconnectAttempts = 0;
reconnectDelay = 1000; // Reset delay on successful connection
// Update connection status indicator if exists
const statusIndicator = document.getElementById('sse-status');
if (statusIndicator) {
statusIndicator.className = 'text-success';
statusIndicator.title = 'Connected';
}
};
eventSource.onmessage = function(event) {
try {
const data = JSON.parse(event.data);
if (event.type === 'new_notification') {
handleNewNotification(data);
} else if (event.type === 'count_update') {
updateNotificationCount(data.count);
} else if (event.type === 'heartbeat') {
console.log('SSE heartbeat received');
}
} catch (error) {
console.error('Error parsing SSE message:', error);
}
};
eventSource.addEventListener('new_notification', function(event) {
try {
const notification = JSON.parse(event.data);
handleNewNotification(notification);
} catch (error) {
console.error('Error parsing new notification:', error);
}
});
eventSource.addEventListener('count_update', function(event) {
try {
const data = JSON.parse(event.data);
updateNotificationCount(data.count);
} catch (error) {
console.error('Error parsing count update:', error);
}
});
eventSource.addEventListener('heartbeat', function(event) {
try {
const data = JSON.parse(event.data);
console.log('SSE heartbeat:', new Date(data.timestamp * 1000));
} catch (error) {
console.error('Error parsing heartbeat:', error);
}
});
eventSource.addEventListener('error', function(event) {
console.error('SSE error:', event);
handleSSEError();
});
eventSource.onerror = function(event) {
console.error('SSE connection error:', event);
handleSSEError();
};
}
function handleNewNotification(notification) {
console.log('New notification received:', notification);
// Update badge
updateNotificationCount();
// Show toast notification
showToast(notification);
// Update dropdown list
addNotificationToList(notification);
// Play sound (optional)
playNotificationSound();
}
function updateNotificationCount(count) {
const badge = document.getElementById('admin-notification-badge');
if (count !== undefined) {
// Use provided count
if (count > 0) {
badge.style.display = 'inline-block';
badge.textContent = count;
} else {
badge.style.display = 'none';
}
} else {
// Fetch current count
fetch('{% url "admin_notification_count" %}')
.then(response => response.json())
.then(data => {
if (data.count > 0) {
badge.style.display = 'inline-block';
badge.textContent = data.count;
} else {
badge.style.display = 'none';
}
})
.catch(error => {
console.error('Error fetching notification count:', error);
});
}
}
function addNotificationToList(notification) {
const list = document.getElementById('admin-notification-list');
if (!list) return;
// Create new notification element
const notificationElement = document.createElement('div');
notificationElement.className = 'notification-item px-3 py-2 border-bottom';
notificationElement.innerHTML = `
<div class="d-flex justify-content-between align-items-start">
<div class="flex-grow-1">
<div class="fw-semibold small">${notification.message}</div>
<div class="text-muted small">${notification.type}</div>
</div>
<span class="badge bg-info ms-2">${notification.status}</span>
</div>
<div class="text-muted small mt-1">${notification.time_ago}</div>
`;
// Add click handler to navigate to notification detail
notificationElement.style.cursor = 'pointer';
notificationElement.addEventListener('click', function() {
window.location.href = notification.url;
});
// Insert at the top of the list
list.insertBefore(notificationElement, list.firstChild);
// Remove "No new messages" placeholder if exists
const placeholder = list.querySelector('.text-muted.text-center');
if (placeholder) {
placeholder.remove();
}
}
function showToast(notification) {
// Create toast container if it doesn't exist
let toastContainer = document.getElementById('toast-container');
if (!toastContainer) {
toastContainer = document.createElement('div');
toastContainer.id = 'toast-container';
toastContainer.className = 'position-fixed top-0 end-0 p-3';
toastContainer.style.zIndex = '1050';
document.body.appendChild(toastContainer);
}
// Create toast element
const toast = document.createElement('div');
toast.className = 'toast show align-items-center text-white bg-primary border-0 mb-2';
toast.setAttribute('role', 'alert');
toast.setAttribute('aria-live', 'assertive');
toast.setAttribute('aria-atomic', 'true');
toast.innerHTML = `
<div class="d-flex">
<div class="toast-body">
<strong>New Notification</strong><br>
<small>${notification.message}</small>
</div>
<button type="button" class="btn-close btn-close-white me-2 m-auto" data-bs-dismiss="toast" aria-label="Close"></button>
</div>
`;
// Add click handler to close toast
const closeButton = toast.querySelector('.btn-close');
closeButton.addEventListener('click', function() {
toast.remove();
});
// Add to container
toastContainer.appendChild(toast);
// Auto-remove after 5 seconds
setTimeout(() => {
if (toast.parentNode) {
toast.remove();
}
}, 5000);
}
function playNotificationSound() {
// Create and play a simple notification sound
try {
const audioContext = new (window.AudioContext || window.webkitAudioContext)();
const oscillator = audioContext.createOscillator();
const gainNode = audioContext.createGain();
oscillator.connect(gainNode);
gainNode.connect(audioContext.destination);
oscillator.frequency.value = 800; // 800 Hz tone
oscillator.type = 'sine';
gainNode.gain.setValueAtTime(0.3, audioContext.currentTime);
gainNode.gain.exponentialRampToValueAtTime(0.01, audioContext.currentTime + 0.1);
oscillator.start(audioContext.currentTime);
oscillator.stop(audioContext.currentTime + 0.1);
} catch (error) {
console.log('Could not play notification sound:', error);
}
}
function handleSSEError() {
// Update connection status indicator if exists
const statusIndicator = document.getElementById('sse-status');
if (statusIndicator) {
statusIndicator.className = 'text-danger';
statusIndicator.title = 'Disconnected';
}
// Attempt to reconnect with exponential backoff
if (reconnectAttempts < maxReconnectAttempts) {
reconnectAttempts++;
console.log(`Attempting to reconnect (${reconnectAttempts}/${maxReconnectAttempts}) in ${reconnectDelay}ms...`);
setTimeout(() => {
connectSSE();
reconnectDelay = Math.min(reconnectDelay * 2, 30000); // Max 30 seconds
}, reconnectDelay);
} else {
console.error('Max reconnection attempts reached. Falling back to polling.');
// Fallback to polling
setInterval(updateNotificationBadge, 30000);
}
}
// Initialize SSE connection on page load
document.addEventListener('DOMContentLoaded', function() {
// Only connect SSE for authenticated staff users
/*if ('{{ request.user.is_authenticated|yesno:"true,false" }}' === 'true' && '{{ request.user.is_staff|yesno:"true,false" }}' === 'true') {
connectSSE();
// Initial notification count update
updateNotificationCount();
}
*/
});
// Cleanup on page unload
window.addEventListener('beforeunload', function() {
if (eventSource) {
eventSource.close();
}
});
// Fallback function for manual refresh
function updateNotificationBadge() {
fetch('{% url "admin_notification_count" %}')
.then(response => response.json())
.then(data => {
const badge = document.getElementById('admin-notification-badge');
const list = document.getElementById('admin-notification-list');
if (data.count > 0) {
badge.style.display = 'inline-block';
badge.textContent = data.count;
} else {
badge.style.display = 'none';
}
// Update notification list
if (data.recent_notifications && data.recent_notifications.length > 0) {
list.innerHTML = data.recent_notifications.map(msg => `
<div class="notification-item px-3 py-2 border-bottom">
<div class="d-flex justify-content-between align-items-start">
<div class="flex-grow-1">
<div class="fw-semibold small">${msg.message}</div>
<div class="text-muted small">${msg.type}</div>
</div>
<span class="badge bg-info ms-2">${msg.status}</span>
</div>
<div class="text-muted small mt-1">${msg.time_ago}</div>
</div>
`).join('');
} else {
list.innerHTML = '<div class="px-3 py-2 text-muted text-center"><small>{% trans "No new messages" %}</small></div>';
}
})
.catch(error => {
console.error('Error fetching notifications:', error);
const list = document.getElementById('admin-notification-list');
list.innerHTML = '<div class="px-3 py-2 text-muted text-center"><small>{% trans "Error loading messages" %}</small></div>';
});
}
</script> {% endcomment %}
{% comment %} {% endif %} {% endcomment %}
{% block customJS %}{% endblock %}
</body>
</html>
</html>

View File

@ -1,10 +1,10 @@
{% load static i18n %}
{% load static %}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>ATS Form Builder - Vanilla JS</title>
<title>ATS Form Builder</title>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
@ -678,7 +678,7 @@
const djangoConfig = {
csrfToken: "{{ csrf_token }}",
saveUrl: "{% url 'save_form_template' %}",
loadUrl: {% if template_slug %}"{% url 'load_form_template' template_slug %}"{% else %}null{% endif %},
loadUrl: {% if template_slug %}"{% url 'load_form_template' job_slug %}"{% else %}null{% endif %},
templateId: {% if template_slug %}'{{ template_slug }}'{% else %}null{% endif %},
jobId: {% if job_id %}{{ job_id }}{% else %}null{% endif %} // Add this if you need it
};

View File

@ -3,7 +3,7 @@
{% load form_filters %}
{% block title %}{{ form.name }} - Submission Details{% endblock %}
{% block title %}{{ form.name }} - {% trans "Submission Details" %}{% endblock %}
{% block customCSS %}
<style>

View File

@ -1,428 +0,0 @@
{% extends "base.html" %}
{% block title %}{{ form.title }} - Submissions{% endblock %}
{% block content %}
<div class="container-fluid">
<div class="row">
<div class="col-12">
<div class="d-flex justify-content-between align-items-center mb-4">
<div>
<h1><i class="fas fa-list"></i> Form Submissions</h1>
<p class="text-muted mb-0">{{ form.title }}</p>
</div>
<div>
<a href="{% url 'form_preview' form.id %}" class="btn btn-outline-primary me-2" target="_blank">
<i class="fas fa-eye"></i> Preview Form
</a>
<a href="{% url 'form_list' %}" class="btn btn-outline-secondary">
<i class="fas fa-arrow-left"></i> Back to Forms
</a>
</div>
</div>
<!-- Statistics Cards -->
<div class="row mb-4">
<div class="col-md-3">
<div class="card bg-primary text-white">
<div class="card-body">
<div class="d-flex justify-content-between">
<div>
<h4 class="mb-0">{{ page_obj.paginator.count }}</h4>
<small>Total Submissions</small>
</div>
<div class="align-self-center">
<i class="fas fa-users fa-2x opacity-75"></i>
</div>
</div>
</div>
</div>
</div>
<div class="col-md-3">
<div class="card bg-success text-white">
<div class="card-body">
<div class="d-flex justify-content-between">
<div>
<h4 class="mb-0">{{ form.submissions.all|length }}</h4>
<small>All Time</small>
</div>
<div class="align-self-center">
<i class="fas fa-chart-line fa-2x opacity-75"></i>
</div>
</div>
</div>
</div>
</div>
<div class="col-md-3">
<div class="card bg-info text-white">
<div class="card-body">
<div class="d-flex justify-content-between">
<div>
<h4 class="mb-0">
{% if form.submissions.first %}
{{ form.submissions.first.submitted_at|timesince }}
{% else %}
No submissions
{% endif %}
</h4>
<small>Latest</small>
</div>
<div class="align-self-center">
<i class="fas fa-clock fa-2x opacity-75"></i>
</div>
</div>
</div>
</div>
</div>
<div class="col-md-3">
<div class="card bg-warning text-white">
<div class="card-body">
<div class="d-flex justify-content-between">
<div>
<h4 class="mb-0">{{ form.created_at|date:"M d" }}</h4>
<small>Form Created</small>
</div>
<div class="align-self-center">
<i class="fas fa-calendar fa-2x opacity-75"></i>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Export Options -->
<div class="card mb-4">
<div class="card-header">
<h5 class="mb-0"><i class="fas fa-download"></i> Export Options</h5>
</div>
<div class="card-body">
<div class="row">
<div class="col-md-6">
<button class="btn btn-outline-primary me-2" onclick="exportSubmissions('csv')">
<i class="fas fa-file-csv"></i> Export as CSV
</button>
<button class="btn btn-outline-success me-2" onclick="exportSubmissions('excel')">
<i class="fas fa-file-excel"></i> Export as Excel
</button>
<button class="btn btn-outline-secondary" onclick="exportSubmissions('json')">
<i class="fas fa-file-code"></i> Export as JSON
</button>
</div>
<div class="col-md-6 text-end">
<small class="text-muted">
Download all submission data for analysis
</small>
</div>
</div>
</div>
</div>
<!-- Submissions List -->
{% if page_obj %}
<div class="card">
<div class="card-header">
<h5 class="mb-0">Recent Submissions</h5>
</div>
<div class="card-body p-0">
<div class="table-responsive">
<table class="table table-hover mb-0">
<thead class="table-light">
<tr>
<th>#</th>
<th>Submitted</th>
<th>IP Address</th>
<th>User Agent</th>
<th>Data Fields</th>
<th>Files</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
{% for submission in page_obj %}
<tr>
<td>
<span class="badge bg-primary">{{ submission.id }}</span>
</td>
<td>
<div>
<strong>{{ submission.submitted_at|date:"M d, Y" }}</strong><br>
<small class="text-muted">{{ submission.submitted_at|time:"g:i A" }}</small>
</div>
</td>
<td>
<code class="text-muted">{{ submission.ip_address|default:"N/A" }}</code>
</td>
<td>
<small class="text-muted" title="{{ submission.user_agent }}">
{% if submission.user_agent %}
{{ submission.user_agent|truncatechars:50 }}
{% else %}
N/A
{% endif %}
</small>
</td>
<td>
<span class="badge bg-info">{{ submission.submission_data.keys|length }} fields</span>
</td>
<td>
<span class="badge bg-success">{{ submission.files.count }} files</span>
</td>
<td>
<div class="btn-group btn-group-sm">
<button type="button" class="btn btn-outline-primary" onclick="viewSubmission({{ submission.id }})" title="View Details">
<i class="fas fa-eye"></i>
</button>
<button type="button" class="btn btn-outline-info" onclick="downloadSubmission({{ submission.id }})" title="Download">
<i class="fas fa-download"></i>
</button>
<button type="button" class="btn btn-outline-danger" onclick="deleteSubmission({{ submission.id }})" title="Delete">
<i class="fas fa-trash"></i>
</button>
</div>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
</div>
<!-- Pagination -->
{% if page_obj.has_other_pages %}
<nav aria-label="Submissions pagination" class="mt-4">
<ul class="pagination justify-content-center">
{% if page_obj.has_previous %}
<li class="page-item">
<a class="page-link" href="?page=1">First</a>
</li>
<li class="page-item">
<a class="page-link" href="?page={{ page_obj.previous_page_number }}">Previous</a>
</li>
{% endif %}
<li class="page-item active">
<span class="page-link">{{ page_obj.number }} of {{ page_obj.paginator.num_pages }}</span>
</li>
{% if page_obj.has_next %}
<li class="page-item">
<a class="page-link" href="?page={{ page_obj.next_page_number }}">Next</a>
</li>
<li class="page-item">
<a class="page-link" href="?page={{ page_obj.paginator.num_pages }}">Last</a>
</li>
{% endif %}
</ul>
</nav>
{% endif %}
{% else %}
<div class="card">
<div class="card-body text-center py-5">
<i class="fas fa-inbox fa-3x text-muted mb-3"></i>
<h4>No submissions yet</h4>
<p class="text-muted">This form hasn't received any submissions yet.</p>
<a href="{% url 'form_preview' form.id %}" class="btn btn-primary" target="_blank">
<i class="fas fa-external-link-alt"></i> Test Form
</a>
</div>
</div>
{% endif %}
</div>
</div>
</div>
<!-- Submission Detail Modal -->
<div class="modal fade" id="submissionModal" tabindex="-1" aria-labelledby="submissionModalLabel" aria-hidden="true">
<div class="modal-dialog modal-lg">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="submissionModalLabel">
<i class="fas fa-file-alt"></i> Submission Details
</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<div id="submissionDetails">
<!-- Submission details will be loaded here -->
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
<button type="button" class="btn btn-primary" id="downloadSubmissionBtn">
<i class="fas fa-download"></i> Download
</button>
</div>
</div>
</div>
</div>
{% endblock %}
{% block extra_js %}
<script>
let currentSubmissionId = null;
function viewSubmission(submissionId) {
currentSubmissionId = submissionId;
fetch(`/recruitment/api/submissions/${submissionId}/`)
.then(response => response.json())
.then(data => {
if (data.success) {
displaySubmissionDetails(data.submission);
const modal = new bootstrap.Modal(document.getElementById('submissionModal'));
modal.show();
} else {
alert('Error loading submission: ' + data.error);
}
})
.catch(error => {
console.error('Error:', error);
alert('Error loading submission details');
});
}
function displaySubmissionDetails(submission) {
const detailsHtml = `
<div class="row">
<div class="col-md-6">
<h6>Submission Information</h6>
<table class="table table-sm">
<tr>
<td><strong>ID:</strong></td>
<td>${submission.id}</td>
</tr>
<tr>
<td><strong>Submitted:</strong></td>
<td>${new Date(submission.submitted_at).toLocaleString()}</td>
</tr>
<tr>
<td><strong>IP Address:</strong></td>
<td>${submission.ip_address || 'N/A'}</td>
</tr>
</table>
</div>
<div class="col-md-6">
<h6>Technical Details</h6>
<table class="table table-sm">
<tr>
<td><strong>User Agent:</strong></td>
<td><small>${submission.user_agent || 'N/A'}</small></td>
</tr>
<tr>
<td><strong>Files:</strong></td>
<td>${submission.files ? submission.files.length : 0}</td>
</tr>
<tr>
<td><strong>Data Fields:</strong></td>
<td>${Object.keys(submission.submission_data).length}</td>
</tr>
</table>
</div>
</div>
<hr>
<h6>Submitted Data</h6>
<div class="table-responsive">
<table class="table table-sm">
<thead>
<tr>
<th>Field</th>
<th>Value</th>
</tr>
</thead>
<tbody>
${Object.entries(submission.submission_data).map(([key, value]) => `
<tr>
<td><strong>${key}:</strong></td>
<td>${formatFieldValue(value)}</td>
</tr>
`).join('')}
</tbody>
</table>
</div>
${submission.files && submission.files.length > 0 ? `
<hr>
<h6>Uploaded Files</h6>
<div class="list-group">
${submission.files.map(file => `
<div class="list-group-item d-flex justify-content-between align-items-center">
<div>
<i class="fas fa-file me-2"></i>
<strong>${file.original_filename}</strong>
<br>
<small class="text-muted">
${file.file_size ? formatFileSize(file.file_size) : 'Unknown size'} •
Uploaded ${new Date(file.uploaded_at).toLocaleString()}
</small>
</div>
<a href="${file.file_url}" class="btn btn-sm btn-outline-primary" target="_blank">
<i class="fas fa-download"></i> Download
</a>
</div>
`).join('')}
</div>
` : ''}
`;
document.getElementById('submissionDetails').innerHTML = detailsHtml;
document.getElementById('downloadSubmissionBtn').onclick = () => downloadSubmission(submission.id);
}
function formatFieldValue(value) {
if (Array.isArray(value)) {
return value.join(', ');
}
if (typeof value === 'object' && value !== null) {
return JSON.stringify(value);
}
return value || 'N/A';
}
function formatFileSize(bytes) {
if (!bytes) return 'Unknown';
const sizes = ['Bytes', 'KB', 'MB', 'GB'];
const i = Math.floor(Math.log(bytes) / Math.log(1024));
return Math.round(bytes / Math.pow(1024, i) * 100) / 100 + ' ' + sizes[i];
}
function downloadSubmission(submissionId) {
window.open(`/recruitment/api/submissions/${submissionId}/download/`, '_blank');
}
function deleteSubmission(submissionId) {
if (confirm('Are you sure you want to delete this submission? This action cannot be undone.')) {
fetch(`/recruitment/api/submissions/${submissionId}/delete/`, {
method: 'DELETE',
headers: {
'X-CSRFToken': getCsrfToken()
}
})
.then(response => response.json())
.then(data => {
if (data.success) {
location.reload();
} else {
alert('Error deleting submission: ' + data.error);
}
})
.catch(error => {
console.error('Error:', error);
alert('Error deleting submission');
});
}
}
function exportSubmissions(format) {
window.open(`/recruitment/api/forms/{{ form.id }}/export/?format=${format}`, '_blank');
}
function getCsrfToken() {
const cookie = document.cookie.split(';').find(c => c.trim().startsWith('csrftoken='));
return cookie ? cookie.split('=')[1] : '';
}
</script>
{% endblock %}

View File

@ -2,7 +2,7 @@
{% load static i18n form_filters %}
{% block title %}All Submissions for {{ template.name }} - ATS{% endblock %}
{% block title %}{% trans "All Submissions for" %} {{ template.name }} - ATS{% endblock %}
{% block customCSS %}
<style>

View File

@ -2,7 +2,7 @@
{% load static i18n crispy_forms_tags %}
{% block title %}Submissions for {{ template.name }} - ATS{% endblock %}
{% block title %}{% trans "Submissions for" %} {{ template.name }} - ATS{% endblock %}
{% block customCSS %}
<style>

View File

@ -2,7 +2,7 @@
{% load static i18n crispy_forms_tags %}
{% block title %}Form Templates - {{ block.super }}{% endblock %}
{% block title %}{% trans "Form Templates" %} - {{ block.super }}{% endblock %}
{% block customCSS %}
<style>

View File

@ -1,10 +1,11 @@
{% load i18n %}
<div class="d-flex justify-content-between align-items-center mb-3">
<div class="btn-group" role="group">
<button type="button" class="btn btn-outline-primary btn-sm view-toggle active" data-view="table" data-list-id="{{ list_id }}">
<i class="fas fa-table me-1"></i> Table
<i class="fas fa-table me-1"></i> {% trans "Table" %}
</button>
<button type="button" class="btn btn-outline-primary btn-sm view-toggle" data-view="card" data-list-id="{{ list_id }}">
<i class="fas fa-th me-1"></i> Card
<i class="fas fa-th me-1"></i> {% trans "Card" %}
</button>
</div>
</div>

View File

@ -1,6 +1,6 @@
<div class="card mt-4">
<div class="card-header text-primary-theme">
<h5 class="card-title mb-0">Add Comment</h5>
<h5 class="card-title mb-0">{% trans "Add Comment" %}</h5>
</div>
<div class="card-body">
<form method="post" action="{% url 'add_meeting_comment' meeting.slug %}">
@ -15,7 +15,7 @@
</div>
{% endif %}
</div>
<button type="submit" class="btn btn-primary">Add Comment</button>
<button type="submit" class="btn btn-primary">{% trans "Add Comment" %}</button>
{% if 'HX-Request' in request.headers %}
<button type="button" class="btn btn-secondary" hx-get="{% url 'meeting_details' meeting.slug %}" hx-select="#comment-section" hx-target="#comment-section">Cancel</button>
{% endif %}

View File

@ -1,10 +1,10 @@
<div id="comment-section">
<div class="card mt-4">
<div class="card-header text-primary-theme d-flex justify-content-between align-items-center">
<h5 class="card-title mb-0">Comments ({{ comments.count }})</h5>
<h5 class="card-title mb-0">{% trans "Comments" %} ({{ comments.count }})</h5>
{% if 'HX-Request' in request.headers %}
<button type="button" class="btn btn-light btn-sm" hx-get="{% url 'meeting_details' meeting.slug %}" hx-select="#comment-section" hx-target="#comment-section">
<i class="bi bi-x-lg"></i> Close
<i class="bi bi-x-lg"></i> {% trans "Close" %}
</button>
{% endif %}
</div>
@ -18,7 +18,7 @@
<div>
<strong>{{ comment.author.get_full_name|default:comment.author.username }}</strong>
{% if comment.author != user %}
<span class="badge bg-secondary ms-2">Comment</span>
<span class="badge bg-secondary ms-2">{% trans "Comment" %}</span>
{% endif %}
</div>
<small class="text-muted">{{ comment.created_at|date:"M d, Y P" }}</small>
@ -49,7 +49,7 @@
{% endfor %}
</div>
{% else %}
<p class="text-muted">No comments yet. Be the first to comment!</p>
<p class="text-muted">{% trans "No comments yet. Be the first to comment!" %}</p>
{% endif %}
</div>
</div>

View File

@ -1,15 +1,15 @@
<div class="card mt-4">
<div class="card-header text-primary-theme">
<h5 class="card-title mb-0">Delete Comment</h5>
<h5 class="card-title mb-0">{% trans "Delete Comment" %}</h5>
</div>
<div class="card-body">
<p>Are you sure you want to delete this comment by <strong>{{ comment.author.get_full_name|default:comment.author.username }}</strong>?</p>
<p>{% trans "Are you sure you want to delete this comment by" %} <strong>{{ comment.author.get_full_name|default:comment.author.username }}</strong>?</p>
<p><small>{{ comment.created_at|date:"F d, Y P" }}</small></p>
<form method="post" action="{{ delete_url }}">
{% csrf_token %}
<button type="submit" class="btn btn-danger">Yes, Delete</button>
<button type="submit" class="btn btn-danger">{% trans "Yes, Delete" %}</button>
{% if 'HX-Request' in request.headers %}
<button type="button" class="btn btn-secondary" hx-get="{% url 'meeting_details' meeting.slug %}" hx-select="#comment-section" hx-target="#comment-section">Cancel</button>
<button type="button" class="btn btn-secondary" hx-get="{% url 'meeting_details' meeting.slug %}" hx-select="#comment-section" hx-target="#comment-section">{% trans "Cancel" %}</button>
{% endif %}
</form>
</div>

View File

@ -1,6 +1,6 @@
<div class="card mt-4">
<div class="card-header text-primary-theme">
<h5 class="card-title mb-0">Edit Comment</h5>
<h5 class="card-title mb-0">{% trans "Edit Comment" %}</h5>
</div>
<div class="card-body">
<form method="post" action="{% url 'edit_meeting_comment' meeting.slug comment.id %}">
@ -15,9 +15,9 @@
</div>
{% endif %}
</div>
<button type="submit" class="btn bg-primary btn-sm">Update Comment</button>
<button type="submit" class="btn bg-primary btn-sm">{% trans "Update Comment" %}</button>
{% if 'HX-Request' in request.headers %}
<button type="button" class="btn btn-secondary btn-sm" hx-get="{% url 'meeting_details' meeting.slug %}" hx-target="#comment-section">Cancel</button>
<button type="button" class="btn btn-secondary btn-sm" hx-get="{% url 'meeting_details' meeting.slug %}" hx-target="#comment-section">{% trans "Cancel" %}</button>
{% endif %}
</form>
</div>

View File

@ -1,60 +1,58 @@
{% if page_obj.has_previous or page_obj.has_next %}
{% load url_extras i18n %}
{% if page_obj.has_other_pages %}
<nav aria-label="Page navigation" class="mt-4">
<ul class="pagination justify-content-center">
{# Helper to build the query string while excluding the 'page' parameter #}
{% load url_extras %}
{# Build a string of all current filters (e.g., &department=IT&type=FULL_TIME) #}
{# Get the filter parameters (e.g., "q=searchterm&job=dev") #}
{% add_get_params request.GET as filter_params %}
{% with filter_params=filter_params %}
{# Define the & prefix logic inline to avoid custom tags #}
{% if filter_params %}{% if "?" in request.get_full_path or "page" in request.get_full_path %}{% endif %}{% endif %}
{% if page_obj.has_previous %}
{# First Page Link #}
<li class="page-item">
<a class="page-link text-primary-theme"
href="?page=1{{ filter_params }}">
First
</a>
</li>
{# Previous Page Link #}
<li class="page-item">
<a class="page-link text-primary-theme"
href="?page={{ page_obj.previous_page_number }}{{ filter_params }}">
Previous
</a>
</li>
{% endif %}
{# Current Page Status - Use your teal/custom background class here #}
<li class="page-item active" aria-current="page">
<span class="page-link bg-kaauh-teal text-white">
{{ page_obj.number }} of {{ page_obj.paginator.num_pages }}
</span>
{# --- First & Previous --- #}
{% if page_obj.has_previous %}
<li class="page-item">
<a class="page-link text-primary-theme" href="?page=1{% if filter_params %}&{{ filter_params }}{% endif %}">
<i class="fas fa-angle-double-left"></i>
</a>
</li>
<li class="page-item">
<a class="page-link text-primary-theme" href="?page={{ page_obj.previous_page_number }}{% if filter_params %}&{{ filter_params }}{% endif %}">
<i class="fas fa-angle-left"></i>
</a>
</li>
{% endif %}
{% if page_obj.has_next %}
{# Next Page Link #}
<li class="page-item">
<a class="page-link text-primary-theme"
href="?page={{ page_obj.next_page_number }}{{ filter_params }}">
Next
</a>
</li>
{# Last Page Link #}
<li class="page-item">
<a class="page-link text-primary-theme"
href="?page={{ page_obj.paginator.num_pages }}{{ filter_params }}">
Last
</a>
</li>
{# --- Page Numbers --- #}
{# --- Page Numbers --- #}
{% for num in page_obj.paginator.page_range %}
{% if page_obj.number == num %}
<li class="page-item active"><span class="page-link bg-kaauh-teal text-white border-kaauh-teal">{{ num }}</span></li>
{% elif num > page_obj.number|add:'-3' and num < page_obj.number|add:'3' %}
<li class="page-item"><a class="page-link text-primary-theme" href="?page={{ num }}{% if filter_params %}&{{ filter_params }}{% endif %}">{{ num }}</a></li>
{% elif num == 1 or num == page_obj.paginator.num_pages %}
{# Optional: Always show first and last page number #}
<li class="page-item"><a class="page-link text-primary-theme" href="?page={{ num }}{% if filter_params %}&{{ filter_params }}{% endif %}">{{ num }}</a></li>
{% elif num == page_obj.number|add:'-3' or num == page_obj.number|add:'3' %}
{# Show the dots #}
<li class="page-item disabled"><span class="page-link">...</span></li>
{% endif %}
{% endfor %}
{% endwith %}
{# --- Next & Last --- #}
{% if page_obj.has_next %}
<li class="page-item">
<a class="page-link text-primary-theme" href="?page={{ page_obj.next_page_number }}{% if filter_params %}&{{ filter_params }}{% endif %}">
<i class="fas fa-angle-right"></i>
</a>
</li>
<li class="page-item">
<a class="page-link text-primary-theme" href="?page={{ page_obj.paginator.num_pages }}{% if filter_params %}&{{ filter_params }}{% endif %}">
<i class="fas fa-angle-double-right"></i>
</a>
</li>
{% endif %}
</ul>
</nav>

View File

@ -7,41 +7,41 @@
{% csrf_token %}
<div class="row">
<div class="col-md-6">
<h5>Select Candidates</h5>
<h5>{% trans "Select Candidates" %}</h5>
<div class="form-group">
{{ form.candidates }}
</div>
</div>
<div class="col-md-6">
<h5>Schedule Details</h5>
<h5>{% trans "Schedule Details" %}</h5>
<div class="form-group mb-3">
<label for="{{ form.start_date.id_for_label }}">Start Date</label>
<label for="{{ form.start_date.id_for_label }}">{% trans "Start Date" %}</label>
{{ form.start_date }}
</div>
<div class="form-group mb-3">
<label for="{{ form.end_date.id_for_label }}">End Date</label>
<label for="{{ form.end_date.id_for_label }}">{% trans "End Date" %}</label>
{{ form.end_date }}
</div>
<div class="form-group mb-3">
<label>Working Days</label>
<label>{% trans "Working Days" %}</label>
{{ form.working_days }}
</div>
<div class="row">
<div class="col-md-6">
<div class="form-group mb-3">
<label for="{{ form.start_time.id_for_label }}">Start Time</label>
<label for="{{ form.start_time.id_for_label }}">{% trans "Start Time" %}</label>
{{ form.start_time }}
</div>
</div>
<div class="col-md-6">
<div class="form-group mb-3">
<label for="{{ form.end_time.id_for_label }}">End Time</label>
<label for="{{ form.end_time.id_for_label }}">{% trans "End Time" %}</label>
{{ form.end_time }}
</div>
</div>
@ -50,14 +50,14 @@
<div class="row">
<div class="col-md-6">
<div class="form-group mb-3">
<label for="{{ form.interview_duration.id_for_label }}">Interview Duration (minutes)</label>
<label for="{{ form.interview_duration.id_for_label }}">{% trans "Interview Duration (minutes)" %}</label>
{{ form.interview_duration }}
</div>
</div>
<div class="col-md-6">
<div class="form-group mb-3">
<label for="{{ form.buffer_time.id_for_label }}">Buffer Time (minutes)</label>
<label for="{{ form.buffer_time.id_for_label }}">{% trans "Buffer Time (minutes)" %}</label>
{{ form.buffer_time }}
</div>
</div>
@ -73,28 +73,28 @@
{% for form in break_formset %}
<div class="break-time-form row mb-2">
<div class="col-md-5">
<label>Start Time</label>
<label>{% trans "Start Time" %}</label>
{{ form.start_time }}
</div>
<div class="col-md-5">
<label>End Time</label>
<label>{% trans "End Time" %}</label>
{{ form.end_time }}
</div>
<div class="col-md-2">
<label>&nbsp;</label><br>
{{ form.DELETE }}
<button type="button" class="btn btn-danger btn-sm remove-break">Remove</button>
<button type="button" class="btn btn-danger btn-sm remove-break">{% trans "Remove" %}</button>
</div>
</div>
{% endfor %}
</div>
<button type="button" id="add-break" class="btn btn-secondary btn-sm mt-2">Add Break</button>
<button type="button" id="add-break" class="btn btn-secondary btn-sm mt-2">{% trans "Add Break" %}</button>
</div>
</div>
<div class="mt-4">
<button type="submit" class="btn btn-primary">Preview Schedule</button>
<a href="{% url 'job_detail' slug=job.slug %}" class="btn btn-secondary">Cancel</a>
<button type="submit" class="btn btn-primary">{% trans "Preview Schedule" %}</button>
<a href="{% url 'job_detail' slug=job.slug %}" class="btn btn-secondary">{% trans "Cancel" %}</a>
</div>
</form>
</div>

View File

@ -1,499 +0,0 @@
{% extends 'base.html' %}
{% load static i18n %}
{% load widget_tweaks %}
{% block customCSS %}
<style>
:root {
--kaauh-teal: #00636e;
--kaauh-teal-dark: #004a53;
--kaauh-teal-light: #e0f7f9;
--kaauh-border: #e9ecef;
--kaauh-primary-text: #212529;
--kaauh-secondary-text: #6c757d;
--kaauh-gray-light: #f8f9fa;
--kaauh-success: #198754;
--kaauh-danger: #dc3545;
--kaauh-link: #007bff;
--kaauh-link-hover: #0056b3;
}
body { background-color: #f0f2f5; font-family: 'Inter', sans-serif; }
.card { border: none; border-radius: 8px; box-shadow: 0 3px 10px rgba(0,0,0,0.04); margin-bottom: 1rem; }
.card-body { padding: 1rem 1.25rem; }
#comments-card .card-header {
background-color: white;
color: var(--kaauh-teal-dark);
padding: 0.75rem 1.25rem;
font-weight: 600;
border-radius: 8px 8px 0 0;
border-bottom: 1px solid var(--kaauh-border);
}
.main-title-container h1 {
font-size: 1.75rem; font-weight: 700;
}
.status-badge {
font-size: 0.7rem; padding: 0.3em 0.7em; border-radius: 12px;
}
.bg-scheduled { background-color: #00636e !important; color: white !important; }
.bg-completed { background-color: #198754 !important; color: white !important; }
.bg-waiting { background-color: #ffc107 !important; color: var(--kaauh-primary-text) !important; }
.bg-started { background-color: var(--kaauh-teal) !important; color: white !important; }
.bg-ended { background-color: var(--kaauh-danger) !important; color: white !important; }
.bg-cancelled { background-color: #6c757d !important; color: white !important; }
.detail-section h2, .card h2 {
color: var(--kaauh-teal-dark); font-weight: 700; font-size: 1.25rem;
margin-bottom: 0.75rem; padding-bottom: 0.5rem; border-bottom: 1px solid var(--kaauh-border);
}
.detail-row-simple {
display: flex; padding: 0.4rem 0; border-bottom: 1px dashed var(--kaauh-border); font-size: 0.85rem;
}
.detail-label-simple { font-weight: 600; color: var(--kaauh-teal-dark); flex-basis: 40%; }
.detail-value-simple { color: var(--kaauh-primary-text); font-weight: 500; flex-basis: 60%; }
.btn-primary-teal {
background-color: var(--kaauh-teal); border-color: var(--kaauh-teal); padding: 0.6rem 1.2rem;
font-size: 0.95rem; border-radius: 6px; color: white;
}
.btn-primary-teal:hover { background-color: var(--kaauh-teal-dark); }
.btn-danger-red {
background-color: var(--kaauh-danger); border-color: var(--kaauh-danger); color: white;
padding: 0.6rem 1.2rem; font-size: 0.95rem; border-radius: 6px; font-weight: 600;
}
.btn-danger-red:hover { background-color: #c82333; border-color: #bd2130; }
.btn-secondary-back {
background-color: transparent; border: none; color: var(--kaauh-secondary-text);
font-weight: 600; font-size: 1rem; padding: 0.5rem 0.75rem; transition: color 0.2s;
}
.btn-secondary-back:hover { color: var(--kaauh-teal); text-decoration: underline; }
.join-url-display {
background-color: white; border: 1px solid var(--kaauh-border); padding: 0.5rem; font-size: 0.85rem;
}
.btn-copy-simple {
padding: 0.5rem 0.75rem; background-color: var(--kaauh-teal-dark); border: none; color: white; border-radius: 4px;
}
.btn-copy-simple:hover { background-color: var(--kaauh-teal); }
.simple-table {
width: 100%; margin-top: 0.5rem; border-collapse: collapse;
}
.simple-table th {
background-color: var(--kaauh-teal); color: white; font-weight: 700;
padding: 8px 12px; border: 1px solid var(--kaauh-border); font-size: 0.8rem;
}
.simple-table td {
padding: 8px 12px; border: 1px solid var(--kaauh-border); background-color: white; font-size: 0.85rem;
}
.comment-item { border: 1px solid var(--kaauh-border); background-color: var(--kaauh-gray-light); border-radius: 6px; }
.btn-edit-comment {
background-color: transparent; border: 1px solid var(--kaauh-teal); color: var(--kaauh-teal);
padding: 0.25rem 0.5rem; font-size: 0.75rem; border-radius: 4px; font-weight: 500;
}
.btn-edit-comment:hover { background-color: var(--kaauh-teal-light); }
</style>
{% endblock %}
{% block content %}
<div class="container-fluid py-4">
{# --- TOP BAR --- #}
<div class="d-flex justify-content-between align-items-center mb-4">
<a href="{% url 'list_meetings' %}" class="btn btn-secondary-back">
<i class="fas fa-arrow-left me-1"></i> {% trans "Back to Meetings" %}
</a>
<div class="d-flex gap-2">
<a href="{% url 'update_meeting' meeting.slug %}" class="btn btn-primary-teal btn-sm">
<i class="fas fa-edit me-1"></i> {% trans "Edit Meeting" %}
</a>
<form method="post" action="{% url 'delete_meeting' meeting.slug %}" style="display: inline;">
{% csrf_token %}
<button type="submit" class="btn btn-danger-red btn-sm" onclick="return confirm('{% trans "Are you sure you want to delete this meeting? This action is permanent." %}')">
<i class="fas fa-trash-alt me-1"></i> {% trans "Delete Meeting" %}
</button>
</form>
</div>
</div>
{# --- MAIN TITLE --- #}
<div class="main-title-container mb-4">
<h1 class="text-start" style="color: var(--kaauh-teal-dark);">
<i class="fas fa-video me-2" style="color: var(--kaauh-teal);"></i>
{{ meeting.topic|default:"[Meeting Topic]" }}
<span class="status-badge bg-{{ interview.status|lower|default:'scheduled' }} ms-3">
{{ interview.get_status_display|default:"Scheduled" }}
</span>
</h1>
</div>
{# --- INTERVIEW & CONNECTION CARDS --- #}
<div class="row g-4 mb-5 align-items-stretch">
{# Interview Detail #}
<div class="col-lg-6">
<div class="p-3 bg-white rounded shadow-sm h-100 d-flex flex-column">
<h2 class="text-start"><i class="fas fa-briefcase me-2"></i> {% trans "Interview Detail" %}</h2>
<div class="detail-row-group flex-grow-1">
<div class="detail-row-simple">
<div class="detail-label-simple">{% trans "Job Title" %}:</div>
<div class="detail-value-simple">{{ job.title|default:"N/A" }}</div>
</div>
<div class="detail-row-simple">
<div class="detail-label-simple">{% trans "Candidate Name" %}:</div>
<div class="detail-value-simple">{{ candidate.full_name|default:"N/A" }}</div>
</div>
<div class="detail-row-simple">
<div class="detail-label-simple">{% trans "Candidate Email" %}:</div>
<div class="detail-value-simple">{{ candidate.email|default:"N/A" }}</div>
</div>
<div class="detail-row-simple">
<div class="detail-label-simple">{% trans "Job Type" %}:</div>
<div class="detail-value-simple">{{ job.job_type|default:"N/A" }}</div>
</div>
{% if candidate.belong_to_agency %}
<div class="detail-row-simple">
<div class="detail-label-simple">{% trans "Agency" %}:</div>
<div class="detail-value-simple">{{ candidate.hiring_agency.name|default:"N/A" }}</div>
</div>
{% endif %}
</div>
</div>
</div>
{# Connection Details #}
<div class="col-lg-6">
<div class="p-3 bg-white rounded shadow-sm h-100 d-flex flex-column">
<h2 class="text-start"><i class="fas fa-info-circle me-2"></i> {% trans "Connection Details" %}</h2>
<div class="detail-row-group flex-grow-1">
<div class="detail-row-simple">
<div class="detail-label-simple">{% trans "Date & Time" %}:</div>
<div class="detail-value-simple">
{{ interview.interview_date }} {{ interview.interview_time }} ({{ meeting.timezone }})
</div>
</div>
<div class="detail-row-simple">
<div class="detail-label-simple">{% trans "Duration" %}:</div>
<div class="detail-value-simple">
{% if meeting.location_type == "Remote" %}
{{ meeting.zoommeetingdetails.duration|default:"N/A" }}
{% elif meeting.location_type == "Onsite" %}
{{ meeting.onsitelocationdetails.duration|default:"N/A" }}
{% else %}
N/A
{% endif %}
{% trans "minutes" %}
</div>
</div>
{% if meeting.location_type == "Remote" %}
{% with zoom=meeting.zoommeetingdetails %}
<div class="detail-row-simple">
<div class="detail-label-simple">{% trans "Meeting ID" %}:</div>
<div class="detail-value-simple">{{ zoom.meeting_id|default:"N/A" }}</div>
</div>
<div class="detail-row-simple">
<div class="detail-label-simple">{% trans "Host Email" %}:</div>
<div class="detail-value-simple">{{ zoom.host_email|default:"N/A" }}</div>
</div>
{% if meeting.details_url %}
<div class="join-url-container pt-3" style="position: relative;">
<div id="copy-message" class="text-white rounded px-2 py-1 small fw-bold mb-2 text-center" style="opacity: 0; transition: opacity 0.3s; position: absolute; right: 0; top: -35px; background-color: var(--kaauh-success); z-index: 10;">
{% trans "Copied!" %}
</div>
<div class="join-url-display d-flex justify-content-between align-items-center">
<div class="text-truncate me-2">
<strong>{% trans "Join URL" %}:</strong>
<span id="meeting-join-url">{{ meeting.details_url }}</span>
</div>
<button class="btn-copy-simple ms-2 flex-shrink-0" onclick="copyLink()" title="{% trans 'Copy URL' %}">
<i class="fas fa-copy"></i>
</button>
</div>
</div>
{% endif %}
{% endwith %}
{% elif meeting.location_type == "Onsite" %}
{% with onsite=meeting.onsitelocationdetails %}
<div class="detail-row-simple">
<div class="detail-label-simple">{% trans "Address" %}:</div>
<div class="detail-value-simple">{{ onsite.physical_address|default:"N/A" }}</div>
</div>
<div class="detail-row-simple">
<div class="detail-label-simple">{% trans "Room" %}:</div>
<div class="detail-value-simple">{{ onsite.room_number|default:"TBD" }}</div>
</div>
{% endwith %}
{% endif %}
</div>
</div>
</div>
</div>
{# --- PARTICIPANTS --- #}
{% comment %} <div class="row g-4 mt-1 mb-5">
<div class="col-lg-12">
<div class="p-3 bg-white rounded shadow-sm">
<div class="d-flex justify-content-between align-items-center">
<h2 class="text-start"><i class="fas fa-users-cog me-2"></i> {% trans "Assigned Participants" %}</h2>
<div class="d-flex gap-2">
<button type="button" class="btn btn-primary-teal btn-sm"
data-bs-toggle="modal" data-bs-target="#assignParticipants">
<i class="fas fa-users-cog me-1"></i> {% trans "Manage Participants" %} ({{ total_participants }})
</button>
<button type="button" class="btn btn-outline-info" data-bs-toggle="modal" data-bs-target="#emailModal">
<i class="fas fa-envelope"></i>
</button>
</div>
</div>
<table class="simple-table">
<thead>
<tr>
<th>{% trans "Name" %}</th>
<th>{% trans "Role" %}</th>
<th>{% trans "Email" %}</th>
<th>{% trans "Phone" %}</th>
<th>{% trans "Type" %}</th>
</tr>
</thead>
<tbody>
{% for participant in external_participants %}
<tr>
<td>{{ participant.name }}</td>
<td>{{ participant.designation|default:"Participant" }}</td>
<td>{{ participant.email|default:"N/A" }}</td>
<td>{{ participant.phone|default:"N/A" }}</td>
<td>{% trans "External" %}</td>
</tr>
{% endfor %}
{% for user in system_participants %}
<tr>
<td>{{ user.get_full_name|default:user.username }}</td>
<td>Admin</td>
<td>{{ user.email|default:"N/A" }}</td>
<td>{{ user.phone|default:"N/A" }}</td>
<td>{% trans "System User" %}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
</div> {% endcomment %}
{# --- COMMENTS --- #}
<div class="row g-4 mt-1">
<div class="col-lg-12">
<div class="card" id="comments-card">
<div class="card-header d-flex justify-content-between align-items-center">
<h5 class="card-title mb-0">
<i class="fas fa-comments me-2"></i>
{% trans "Comments" %} ({{ interview.notes.count }})
</h5>
</div>
<div class="card-body">
<div id="comment-section" class="mb-4">
{% for note in interview.notes.all|dictsortreversed:"created_at" %}
<div class="comment-item mb-3 p-3">
<div id="comment-view-{{ note.pk }}">
<div class="d-flex justify-content-between align-items-start mb-2">
<div class="comment-metadata" style="font-size: 0.9rem;">
<strong>{{ note.author.get_full_name|default:note.author.username }}</strong>
<span class="text-muted small ms-2">{{ note.created_at|date:"M d, Y H:i" }}</span>
</div>
{% if note.author == user or user.is_staff %}
<div class="comment-actions d-flex align-items-center gap-1">
<button type="button" class="btn btn-edit-comment py-0 px-1" onclick="toggleCommentEdit('{{ note.pk }}')" id="edit-btn-{{ note.pk }}">
<i class="fas fa-edit"></i>
</button>
<form method="post" action="{% url 'delete_meeting_comment' meeting.slug note.pk %}" style="display: inline;">
{% csrf_token %}
<button type="submit" class="btn btn-outline-danger py-0 px-1" onclick="return confirm('{% trans "Are you sure you want to delete this comment?" %}')">
<i class="fas fa-trash"></i>
</button>
</form>
</div>
{% endif %}
</div>
<p class="mb-0" style="font-size: 0.85rem; white-space: pre-wrap;">{{ note.content|linebreaksbr }}</p>
</div>
<div id="comment-edit-form-{{ note.pk }}" style="display: none; margin-top: 10px; padding-top: 10px; border-top: 1px dashed var(--kaauh-border);">
<form method="POST" action="{% url 'edit_meeting_comment' meeting.slug note.pk %}">
{% csrf_token %}
<div class="mb-2">
<label class="form-label small">{% trans "Edit Comment" %}</label>
<textarea name="content" class="form-control" rows="3" required>{{ note.content }}</textarea>
</div>
<button type="submit" class="btn btn-success btn-sm me-2">
<i class="fas fa-save me-1"></i> {% trans "Save Changes" %}
</button>
<button type="button" class="btn btn-secondary btn-sm" onclick="toggleCommentEdit('{{ note.pk }}')">
{% trans "Cancel" %}
</button>
</form>
</div>
</div>
{% empty %}
<p class="text-muted">{% trans "No comments yet. Be the first to comment!" %}</p>
{% endfor %}
</div>
<hr>
<h6 class="mb-3">{% trans "Add a New Comment" %}</h6>
<form method="POST" action="{% url 'add_meeting_comment' meeting.slug %}">
{% csrf_token %}
<div class="mb-3">
<label class="form-label small">{% trans "Comment" %}</label>
<textarea name="content" class="form-control" rows="3" required></textarea>
</div>
<button type="submit" class="btn btn-primary-teal btn-sm">
<i class="fas fa-paper-plane me-1"></i> {% trans "Submit Comment" %}
</button>
</form>
</div>
</div>
</div>
</div>
</div>
{# MODALS #}
<!-- Participants Modal -->
<div class="modal fade" id="assignParticipants" tabindex="-1" aria-hidden="true">
<div class="modal-dialog modal-lg">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">{% trans "Manage all participants" %}</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<form method="post" action="{% url 'create_interview_participants' interview.slug %}">
{% csrf_token %}
<div class="modal-body table-responsive">
{{ meeting.name }}
<hr>
<table class="table tab table-bordered mt-3">
<thead>
<th class="col">👥 {% trans "Participants" %}</th>
<th class="col">🧑‍💼 {% trans "Users" %}</th>
</thead>
<tbody>
<tr>
<td>
{{ form.participants.errors }}
{{ form.participants }}
</td>
<td> {{ form.system_users.errors }}
{{ form.system_users }}
</td>
</tr>
</table>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-danger-red" data-bs-dismiss="modal">{% trans "Close" %}</button>
<button type="submit" class="btn btn-primary-teal">{% trans "Save" %}</button>
</div>
</form>
</div>
</div>
</div>
<!-- Email Modal -->
<div class="modal fade" id="emailModal" tabindex="-1" aria-hidden="true">
<div class="modal-dialog modal-xl">
<div class="modal-content">
<div class="modal-header bg-light">
<h5 class="modal-title">📧 {% trans "Compose Interview Invitation" %}</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<form method="post" action="{% url 'send_interview_email' interview.slug %}">
{% csrf_token %}
<div class="modal-body">
<div class="mb-3">
<label class="form-label fw-bold">{% trans "Subject" %}</label>
{{ email_form.subject|add_class:"form-control" }}
</div>
<ul class="nav nav-tabs" id="messageTabs" role="tablist">
<li class="nav-item" role="presentation">
<button class="nav-link active" id="candidate-tab" data-bs-toggle="tab" data-bs-target="#candidate-pane" type="button">
{% if candidate.belong_to_agency %}
{% trans "Agency Message" %}
{% else %}
{% trans "Candidate Message" %}
{% endif %}
</button>
</li>
<li class="nav-item" role="presentation">
<button class="nav-link" id="participants-tab" data-bs-toggle="tab" data-bs-target="#participants-pane" type="button">
{% trans "Panel Message" %}
</button>
</li>
</ul>
<div class="tab-content border border-top-0 p-3 bg-light-subtle">
<div class="tab-pane fade show active" id="candidate-pane">
<p class="text-muted small">
{% if candidate.belong_to_agency %}
{% trans "This email will be sent to the hiring agency." %}
{% else %}
{% trans "This email will be sent to the candidate." %}
{% endif %}
</p>
{% if candidate.belong_to_agency %}
{{ email_form.message_for_agency|add_class:"form-control" }}
{% else %}
{{ email_form.message_for_candidate|add_class:"form-control" }}
{% endif %}
</div>
<div class="tab-pane fade" id="participants-pane">
<p class="text-muted small">{% trans "This email will be sent to all interview participants." %}</p>
{{ email_form.message_for_participants|add_class:"form-control" }}
</div>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-danger-red" data-bs-dismiss="modal">{% trans "Close" %}</button>
<button type="submit" class="btn btn-primary-teal">{% trans "Send Invitation" %}</button>
</div>
</form>
</div>
</div>
</div>
{% endblock %}
{% block customJS %}
<script>
function toggleCommentEdit(commentPk) {
const viewDiv = document.getElementById(`comment-view-${commentPk}`);
const editFormDiv = document.getElementById(`comment-edit-form-${commentPk}`);
if (viewDiv.style.display === 'none') {
viewDiv.style.display = 'block';
editFormDiv.style.display = 'none';
} else {
viewDiv.style.display = 'none';
editFormDiv.style.display = 'block';
}
}
function copyLink() {
const urlElement = document.getElementById('meeting-join-url');
const textToCopy = urlElement.textContent || urlElement.innerText;
const messageElement = document.getElementById('copy-message');
navigator.clipboard.writeText(textToCopy).then(() => {
messageElement.style.opacity = '1';
setTimeout(() => {
messageElement.style.opacity = '0';
}, 2000);
}).catch(err => {
console.error('Failed to copy: ', err);
});
}
</script>
{% endblock %}

View File

@ -11,7 +11,7 @@
<div class="card-header">
<h4 class="mb-0">
<i class="fas fa-building me-2"></i>
Create Onsite Interview for {{ application.name }}
{% trans "Create Onsite Interview for" %} {{ application.name }}
</h4>
<a href="{% url 'interview_create_type_selection' application.slug %}"
class="btn btn-outline-primary">
@ -21,8 +21,8 @@
</div>
<div class="card-body">
<p class="text-muted mb-3">
Schedule an onsite interview for <strong>{{ application.name }}</strong>
for the position of <strong>{{ job.title }}</strong>.
{% trans "Schedule an onsite interview for" %} <strong>{{ application.name }}</strong>
{% trans "for the position of" %} <strong>{{ job.title }}</strong>.
</p>
{% if messages %}

View File

@ -11,7 +11,7 @@
<div class="card-header">
<h4 class="mb-0">
<i class="fas fa-video me-2"></i>
Create Remote Interview for {{ application.name }}
{% trans "Create Remote Interview for" %} {{ application.name }}
</h4>
<a href="{% url 'interview_create_type_selection' application.slug %}"
class="btn btn-outline-primary">
@ -21,8 +21,8 @@
</div>
<div class="card-body">
<p class="text-muted mb-3">
Schedule a remote interview for <strong>{{ application.name }}</strong>
for the position of <strong>{{ job.title }}</strong>.
{% trans "Schedule a remote interview for" %} <strong>{{ application.name }}</strong>
{% trans "for the position of" %} <strong>{{ job.title }}</strong>.
</p>
{% if messages %}

View File

@ -11,13 +11,13 @@
<div class="card-header">
<h4 class="mb-0">
<i class="fas fa-calendar-plus me-2"></i>
Create Interview for {{ application.name }}
{% trans "Create Interview for" %} {{ application.name }}
</h4>
</div>
<div class="card-body" hx-boost="true" hx-push-url="false" hx-select=".card-body" hx-swap="innerHTML" hx-target="#candidateviewModalBody">
<p class="text-muted mb-3">
Select the type of interview you want to schedule for <strong>{{ application.name }}</strong>
for the position of <strong>{{ job.title }}</strong>.
{% trans "Select the type of interview you want to schedule for" %} <strong>{{ application.name }}</strong>
{% trans "for the position of" %} <strong>{{ job.title }}</strong>.
</p>
<div class="d-grid gap-3" style="grid-template-columns: 1fr 1fr;">

View File

@ -518,20 +518,35 @@
<!-- AI Generated Questions Section -->
<div class="kaauh-card shadow-sm p-4 mb-4">
<div class="d-flex align-items-center justify-content-between mb-3">
<h5 class="mb-0" style="color: var(--kaauh-teal-dark); font-weight: 600;">
<i class="fas fa-brain me-2"></i> {% trans "AI Generated Questions" %}
</h5>
<div class="d-flex gap-2">
<form action="{% url 'generate_ai_questions' schedule.slug %}" method="post">
{% csrf_token %}
<button type="submit" class="btn btn-main-action btn-sm">
<span id="button-text-content">
<i class="fas fa-magic me-1"></i> {% trans "Generate Interview Questions" %}
</span>
<div class="d-flex align-items-center justify-content-between mb-3">
<h5 class="mb-0" style="color: var(--kaauh-teal-dark); font-weight: 600;">
<i class="fas fa-brain me-2"></i> {% trans "AI Generated Questions" %}
</h5>
<div class="d-flex gap-2">
<div class="dropdown">
<button class="btn btn-main-action btn-sm dropdown-toggle" type="button" data-bs-toggle="dropdown" aria-expanded="false">
<i class="fas fa-magic me-1"></i> {% trans "AI Actions" %}
</button>
</form>
<ul class="dropdown-menu dropdown-menu-end shadow-sm">
<li>
<div class="px-3 py-2 small text-muted" style="width: 220px;">
{% trans "This will use AI to generate questions based on the job description." %}
</div>
</li>
<li><hr class="dropdown-divider"></li>
<li>
<form action="{% url 'generate_ai_questions' schedule.slug %}" method="post" class="m-0">
{% csrf_token %}
<button type="submit" class="dropdown-item text-primary-theme fw-bold py-2">
<i class="fas fa-check-circle me-2"></i> {% trans "Confirm & Generate" %}
</button>
</form>
</li>
</ul>
</div>
</div>
</div>
<div class="accordion" id="aiQuestionsAccordion">

View File

@ -1,7 +1,7 @@
{% extends 'base.html' %}
{% load static i18n %}
{% block title %}{% trans "Interview Management" %} - ATS{% endblock %}
{% block title %}{% trans "Interviews" %} - ATS{% endblock %}
{% block customCSS %}
<style>
@ -138,19 +138,7 @@
font-size: 0.8rem !important;
}
/* Pagination Styling */
.pagination .page-link {
color: var(--kaauh-teal);
border-color: var(--kaauh-border);
}
.pagination .page-item.active .page-link {
background-color: var(--kaauh-teal);
border-color: var(--kaauh-teal);
}
.pagination .page-link:hover {
color: var(--kaauh-teal-dark);
background-color: #f8f9fa;
}
</style>
{% endblock %}

View File

@ -6,7 +6,7 @@
<div class="col-lg-6 col-md-8">
<div class="card shadow-lg border-0 rounded-lg mt-5">
<div class="card-header bg-main-action text-white">
<h3 class="text-center font-weight-light my-4">Set Interview Location</h3>
<h3 class="text-center font-weight-light my-4">{% trans "Set Interview Location" %}</h3>
</div>
<div class="card-body">
<form method="post" action="{% url 'confirm_schedule_interviews_view' job.slug %}" enctype="multipart/form-data">
@ -18,12 +18,12 @@
<div class="d-flex align-items-center justify-content-between mt-4 mb-0">
<a href="{% url 'list_meetings' %}" class="btn btn-secondary me-2">
<i class="fas fa-times me-1"></i> Close
<i class="fas fa-times me-1"></i> {% trans "Close" %}
</a>
<button type="submit" class="btn btn-primary">
<i class="fas fa-save me-1"></i> Save Location
<i class="fas fa-save me-1"></i> {% trans "Save Location" %}
</button>
</div>
</form>

View File

@ -28,7 +28,7 @@
</span>
</td>
<td><a class="btn btn-outline-primary btn-sm" href="{% url 'interview_detail' interview.slug %}" target="_blank">View</a></td>
<td><a class="btn btn-outline-primary btn-sm" href="{% url 'interview_detail' interview.slug %}" target="_blank">{% trans "View" %}</a></td>
</tr>
{% empty %}
<tr>

View File

@ -1,7 +1,7 @@
{% extends "base.html" %}
{% load static crispy_forms_tags %}
{%load i18n %}
{% block title%} {% trans "Preview Interview Schedule" %}{%endblock%}
{% block customCSS %}
<style>
/* Custom Teal Theme Variables (Adapt these if defined globally) */
@ -90,7 +90,7 @@
<div class="d-flex justify-content-between align-items-center mb-5">
<h1 class="h3 page-header">
<i class="fas fa-calendar-alt me-2 text-primary-theme"></i> Interview Schedule Preview: **{{ job.title }}**
<i class="fas fa-calendar-alt me-2 text-primary-theme"></i> {% trans "Interview Schedule Preview:"%}" **{{ job.title }}**
</h1>
</div>
@ -100,14 +100,14 @@
<div class="row g-4">
<div class="col-md-6">
<p class="mb-2"><strong><i class="fas fa-clock me-2 text-primary-theme"></i> Working Hours:</strong> {{ start_time|time:"g:i A" }} to {{ end_time|time:"g:i A" }}</p>
<p class="mb-2"><strong><i class="fas fa-hourglass-half me-2 text-primary-theme"></i> Interview Duration:</strong> {{ interview_duration }} minutes</p>
<p class="mb-2"><strong><i class="fas fa-shield-alt me-2 text-primary-theme"></i> Buffer Time:</strong> {{ buffer_time }} minutes</p>
<p class="mb-2"><strong><i class="fas fa-clock me-2 text-primary-theme"></i>{% trans "Working Hours:" %}</strong> {{ start_time|time:"g:i A" }} to {{ end_time|time:"g:i A" }}</p>
<p class="mb-2"><strong><i class="fas fa-hourglass-half me-2 text-primary-theme"></i> {% trans "Interview Duration:" %}</strong> {{ interview_duration }} {% trans "minutes" %}</p>
<p class="mb-2"><strong><i class="fas fa-shield-alt me-2 text-primary-theme"></i>{% trans "Buffer Time:" %}</strong> {{ buffer_time }} {% trans "minutes" %}</p>
</div>
<div class="col-md-6">
<p class="mb-2"><strong><i class="fas fa-calendar-day me-2 text-primary-theme"></i> Interview Period:</strong> {{ start_date|date:"F j, Y" }} &mdash; {{ end_date|date:"F j, Y" }}</p>
<p class="mb-2"><strong><i class="fas fa-list-check me-2 text-primary-theme"></i> Active Days:</strong>
<p class="mb-2"><strong><i class="fas fa-calendar-day me-2 text-primary-theme"></i>{% trans "Interview Period:" %}</strong> {{ start_date|date:"F j, Y" }} &mdash; {{ end_date|date:"F j, Y" }}</p>
<p class="mb-2"><strong><i class="fas fa-list-check me-2 text-primary-theme"></i>{% trans "Active Days:" %}</strong>
{% for day_id in working_days %}
{% if day_id == 0 %}Mon{% endif %}
{% if day_id == 1 %}Tue{% endif %}
@ -119,7 +119,7 @@
{% if not forloop.last %}, {% endif %}
{% endfor %}
</p>
<p class="mb-2"><strong><i class="fas fa-calendar-day me-2 text-primary-theme"></i> Interview Type:</strong> {{schedule_interview_type}}</p>
<p class="mb-2"><strong><i class="fas fa-calendar-day me-2 text-primary-theme"></i>{% trans "Interview Type:" %}</strong> {{schedule_interview_type}}</p>
</div>
</div>
@ -134,7 +134,7 @@
{% endfor %}
</div>
{% else %}
<p class="mt-3"><small class="text-muted"><i class="fas fa-exclamation-circle me-1"></i> No daily breaks scheduled.</small></p>
<p class="mt-3"><small class="text-muted"><i class="fas fa-exclamation-circle me-1"></i> {% trans "No daily breaks scheduled." %}</small></p>
{% endif %}
</div>
</div>
@ -152,10 +152,10 @@
<table class="table table-hover table-striped">
<thead class="bg-primary-theme-light">
<tr>
<th scope="col">Date</th>
<th scope="col">Time</th>
<th scope="col">Candidate</th>
<th scope="col">Email</th>
<th scope="col">{% trans "Date" %}</th>
<th scope="col">{% trans "Time" %}</th>
<th scope="col">{% trans "Applicant" %}</th>
<th scope="col">{% trans "Email" %}</th>
</tr>
</thead>
<tbody>
@ -202,10 +202,10 @@
<div class="modal-footer">
<div class="d-flex align-items-center justify-content-between mt-4 mb-0">
<a href="#" class="btn btn-secondary me-2">
<i class="fas fa-times me-1"></i> Close
<i class="fas fa-times me-1"></i> {% trans "Close" %}
</a>
<button type="submit" class="btn btn-primary" form="onsite-form">
<i class="fas fa-save me-1"></i> Save Location
<i class="fas fa-save me-1"></i> {% trans "Save Location" %}
</button>
</div>
</div>

View File

@ -2,7 +2,7 @@
{% load static i18n %}
{% load widget_tweaks %}
{% block title %}Bulk Interview Scheduling - {{ job.title }} - ATS{% endblock %}
{% block title %}{% trans "Bulk Interview Scheduling" %} - {{ job.title }} - ATS{% endblock %}
{% block content %}
<div class="container-fluid py-4">

View File

@ -1,33 +1,39 @@
{% extends "base.html" %}
{% load i18n %}
{% block title %}Apply for {{ job.title }} - University Careers{% endblock %}
{% block title %}{% blocktrans %}Apply for {{ job.title }} - University Careers{% endblocktrans %}{% endblock %}
{% block content %}
<div class="row justify-content-center">
<div class="col-lg-8">
<div class="card">
<div class="card-header">
<h2><i class="fas fa-file-signature"></i> Apply for {{ job.title }}</h2>
<p class="text-muted">{{ job.department }} • {{ job.get_location_display }}</p>
<div class="card shadow-sm">
<div class="card-header bg-white">
<h2 class="h4 mb-1">
<i class="fas fa-file-signature"></i>
{% blocktrans with job_title=job.title %}Apply for {{ job_title }}{% endblocktrans %}
</h2>
<p class="text-muted mb-0">
{{ job.department }} • {{ job.get_location_display }}
</p>
</div>
<div class="card-body">
<div class="card-body p-4">
<form method="post" enctype="multipart/form-data">
{% csrf_token %}
<h4>Personal Information</h4>
<h4 class="mb-3">{% trans "Personal Information" %}</h4>
<div class="row">
<div class="col-md-6">
<div class="mb-3">
<label class="form-label">First Name *</label>
<label class="form-label">{% trans "First Name" %} *</label>
{{ form.first_name }}
{% if form.first_name.errors %}<div class="text-danger">{{ form.first_name.errors }}</div>{% endif %}
{% if form.first_name.errors %}<div class="text-danger small">{{ form.first_name.errors }}</div>{% endif %}
</div>
</div>
<div class="col-md-6">
<div class="mb-3">
<label class="form-label">Last Name *</label>
<label class="form-label">{% trans "Last Name" %} *</label>
{{ form.last_name }}
{% if form.last_name.errors %}<div class="text-danger">{{ form.last_name.errors }}</div>{% endif %}
{% if form.last_name.errors %}<div class="text-danger small">{{ form.last_name.errors }}</div>{% endif %}
</div>
</div>
</div>
@ -35,65 +41,65 @@
<div class="row">
<div class="col-md-6">
<div class="mb-3">
<label class="form-label">Email *</label>
<label class="form-label">{% trans "Email" %} *</label>
{{ form.email }}
{% if form.email.errors %}<div class="text-danger">{{ form.email.errors }}</div>{% endif %}
{% if form.email.errors %}<div class="text-danger small">{{ form.email.errors }}</div>{% endif %}
</div>
</div>
<div class="col-md-6">
<div class="mb-3">
<label class="form-label">Phone</label>
<label class="form-label">{% trans "Phone" %}</label>
{{ form.phone }}
{% if form.phone.errors %}<div class="text-danger">{{ form.phone.errors }}</div>{% endif %}
{% if form.phone.errors %}<div class="text-danger small">{{ form.phone.errors }}</div>{% endif %}
</div>
</div>
</div>
<h4 class="mt-4">Documents</h4>
<h4 class="mt-4 mb-3">{% trans "Documents" %}</h4>
<div class="mb-3">
<label class="form-label">Resume/CV * (PDF or Word)</label>
<label class="form-label">{% trans "Resume/CV * (PDF or Word)" %}</label>
{{ form.resume }}
{% if form.resume.errors %}<div class="text-danger">{{ form.resume.errors }}</div>{% endif %}
{% if form.resume.errors %}<div class="text-danger small">{{ form.resume.errors }}</div>{% endif %}
</div>
<div class="mb-3">
<label class="form-label">Cover Letter (Optional)</label>
<label class="form-label">{% trans "Cover Letter (Optional)" %}</label>
{{ form.cover_letter }}
{% if form.cover_letter.errors %}<div class="text-danger">{{ form.cover_letter.errors }}</div>{% endif %}
{% if form.cover_letter.errors %}<div class="text-danger small">{{ form.cover_letter.errors }}</div>{% endif %}
</div>
<h4 class="mt-4">Additional Information</h4>
<h4 class="mt-4 mb-3">{% trans "Additional Information" %}</h4>
<div class="mb-3">
<label class="form-label">LinkedIn Profile</label>
<label class="form-label">{% trans "LinkedIn Profile" %}</label>
{{ form.linkedin_profile }}
{% if form.linkedin_profile.errors %}<div class="text-danger">{{ form.linkedin_profile.errors }}</div>{% endif %}
{% if form.linkedin_profile.errors %}<div class="text-danger small">{{ form.linkedin_profile.errors }}</div>{% endif %}
</div>
<div class="mb-3">
<label class="form-label">Portfolio/Website</label>
<label class="form-label">{% trans "Portfolio/Website" %}</label>
{{ form.portfolio_url }}
{% if form.portfolio_url.errors %}<div class="text-danger">{{ form.portfolio_url.errors }}</div>{% endif %}
{% if form.portfolio_url.errors %}<div class="text-danger small">{{ form.portfolio_url.errors }}</div>{% endif %}
</div>
<div class="row">
<div class="col-md-6">
<div class="mb-3">
<label class="form-label">Salary Expectations</label>
<label class="form-label">{% trans "Salary Expectations" %}</label>
{{ form.salary_expectations }}
{% if form.salary_expectations.errors %}<div class="text-danger">{{ form.salary_expectations.errors }}</div>{% endif %}
{% if form.salary_expectations.errors %}<div class="text-danger small">{{ form.salary_expectations.errors }}</div>{% endif %}
</div>
</div>
<div class="col-md-6">
<div class="mb-3">
<label class="form-label">Availability</label>
<label class="form-label">{% trans "Availability" %}</label>
{{ form.availability }}
{% if form.availability.errors %}<div class="text-danger">{{ form.availability.errors }}</div>{% endif %}
{% if form.availability.errors %}<div class="text-danger small">{{ form.availability.errors }}</div>{% endif %}
</div>
</div>
</div>
<button type="submit" class="btn btn-primary w-100 mt-3">
<i class="fas fa-paper-plane"></i> Submit Application
<button type="submit" class="btn btn-primary w-100 mt-4 py-2">
<i class="fas fa-paper-plane"></i> {% trans "Submit Application" %}
</button>
</form>
</div>

View File

@ -1,7 +1,7 @@
{% extends "base.html" %}
{% load static i18n %}
{% block title %}Create New Job Post - {{ block.super }}{% endblock %}
{% block title %}{% trans "Create New Job Posting" %} - {{ block.super }}{% endblock %}
{% block customCSS %}

View File

@ -1,7 +1,7 @@
{% extends "base.html" %}
{% load static i18n %}
{% block title %}{% trans "Create New Job Post" %} - {{ block.super }}{% endblock %}
{% block title %}{% trans "Edit Job Posting" %} - {{ block.super }}{% endblock %}
{% block customCSS %}
@ -115,7 +115,7 @@
{% block content %}
<div class="container-fluid py-4">
<h1 class="h3 mb-4 text-primary fw-bold">
<i class="fas fa-bullhorn me-2"></i> {% if form.instance.pk %} {% trans "Edit Job Posting" %} {% else %} {% trans "Create New Job Posting" %} {% endif %}
<i class="fas fa-bullhorn me-2"></i> {% if form.instance.pk %} {% trans "Edit Job Posting" %} {% else %} {% trans "Edit Job Posting" %} {% endif %}
</h1>
<form method="post" id="jobForm" class="mb-5" enctype="multipart/form-data">

View File

@ -1,15 +1,10 @@
{% extends "base.html" %}
{% load static %}
{% block title %}{{ job.title }} - Applicants{% endblock %}
{% load i18n %}
{% block title %}{{ job.title }} - {% trans "Applicants" %}{% endblock %}
{% block customCSS %}
<style>
.job-applicants-container {
max-width: 1400px;
margin: 0 auto;
padding: 20px;
}
.job-header {
background: linear-gradient(135deg, #00636e 0%, #004a53 100%);
@ -374,33 +369,31 @@
{% block content %}
<div class="job-applicants-container">
<!-- Job Header -->
<div class="job-header">
<h1>{{ job.title }}</h1>
<p>{{ job.department|default:"General" }} • {{ job.get_job_type_display }} • {{ job.get_workplace_type_display }}</p>
<div class="job-meta">
<div class="job-meta-item">
<span></span>
<span>{{ total_applications }} Total Applicants</span>
<i class="fas fa-users"></i>
<span>{{ total_applications }} {% trans "Total Applicants" %}</span>
</div>
{% if job.max_applications %}
<div class="job-meta-item">
<span></span>
<span>{{ job.max_applications }} Positions Available</span>
<i class="fas fa-briefcase"></i>
<span>{{ job.max_applications }} {% trans "Positions Available" %}</span>
</div>
{% endif %}
<div class="job-meta-item">
<span></span>
<span>Posted {{ job.created_at|date:"M d, Y" }}</span>
<i class="fas fa-calendar-alt"></i>
<span>{% trans "Posted" %} {{ job.created_at|date:"M d, Y" }}</span>
</div>
</div>
</div>
<!-- Statistics Section -->
<div class="stats-section">
<div class="stat-card">
<div class="stat-number">{{ total_applications }}</div>
<div class="stat-label">Total Applications</div>
<div class="stat-label">{% trans "Total Applications" %}</div>
</div>
{% for stage_key, stage_data in stage_stats.items %}
@ -413,32 +406,29 @@
{% if ai_score_stats.average %}
<div class="stat-card">
<div class="stat-number">{{ ai_score_stats.average|floatformat:1 }}</div>
<div class="stat-label">Average AI Score</div>
<div class="stat-label">{% trans "Average AI Score" %}</div>
</div>
{% endif %}
</div>
<!-- Filters Section -->
<div class="filters-section">
<form method="GET" class="filters-form">
<div class="filters-grid">
<!-- Search Box -->
<div class="filter-group">
<label for="search"> Search Applicants</label>
<label for="search">{% trans "Search Applicants" %}</label>
<input
type="text"
id="search"
name="q"
value="{{ search_query }}"
placeholder="Search by name, email, or phone..."
placeholder="{% trans 'Search by name, email, or phone...' %}"
>
</div>
<!-- Stage Filter -->
<div class="filter-group">
<label for="stage"> Application Stage</label>
<label for="stage">{% trans "Application Stage" %}</label>
<select id="stage" name="stage">
<option value="">All Stages</option>
<option value="">{% trans "All Stages" %}</option>
{% for key, value in stage_choices %}
<option value="{{ key }}" {% if stage_filter == key %}selected{% endif %}>
{{ value }}
@ -447,9 +437,8 @@
</select>
</div>
<!-- AI Score Range -->
<div class="filter-group">
<label for="min_ai_score"> Min AI Score</label>
<label for="min_ai_score">{% trans "Min AI Score" %}</label>
<input
type="number"
id="min_ai_score"
@ -462,7 +451,7 @@
</div>
<div class="filter-group">
<label for="max_ai_score"> Max AI Score</label>
<label for="max_ai_score">{% trans "Max AI Score" %}</label>
<input
type="number"
id="max_ai_score"
@ -474,9 +463,8 @@
>
</div>
<!-- Date Range -->
<div class="filter-group">
<label for="date_from"> From Date</label>
<label for="date_from">{% trans "From Date" %}</label>
<input
type="date"
id="date_from"
@ -486,7 +474,7 @@
</div>
<div class="filter-group">
<label for="date_to"> To Date</label>
<label for="date_to">{% trans "To Date" %}</label>
<input
type="date"
id="date_to"
@ -495,37 +483,33 @@
>
</div>
<!-- Sort By -->
<div class="filter-group">
<label for="sort"> Sort By</label>
<label for="sort">{% trans "Sort By" %}</label>
<select id="sort" name="sort">
<option value="-created_at" {% if sort_by == '-created_at' %}selected{% endif %}>Newest First</option>
<option value="created_at" {% if sort_by == 'created_at' %}selected{% endif %}>Oldest First</option>
<option value="person__first_name" {% if sort_by == 'person__first_name' %}selected{% endif %}>Name (A-Z)</option>
<option value="-person__first_name" {% if sort_by == '-person__first_name' %}selected{% endif %}>Name (Z-A)</option>
<option value="stage" {% if sort_by == 'stage' %}selected{% endif %}>Stage</option>
<option value="-created_at" {% if sort_by == '-created_at' %}selected{% endif %}>{% trans "Newest First" %}</option>
<option value="created_at" {% if sort_by == 'created_at' %}selected{% endif %}>{% trans "Oldest First" %}</option>
<option value="person__first_name" {% if sort_by == 'person__first_name' %}selected{% endif %}>{% trans "Name (A-Z)" %}</option>
<option value="-person__first_name" {% if sort_by == '-person__first_name' %}selected{% endif %}>{% trans "Name (Z-A)" %}</option>
<option value="stage" {% if sort_by == 'stage' %}selected{% endif %}>{% trans "Stage" %}</option>
</select>
</div>
</div>
<!-- Filter Actions -->
<div class="filters-actions">
<button type="submit" class="btn-filter btn-primary">
Apply Filters
<i class="fas fa-filter"></i> {% trans "Apply Filters" %}
</button>
<a href="{% url 'job_applicants' job.slug %}" class="btn-filter btn-secondary">
Clear All
<i class="fas fa-undo"></i> {% trans "Clear All" %}
</a>
</div>
</form>
</div>
<!-- Applicants Grid -->
{% if page_obj.object_list %}
<div class="applicants-grid">
{% for application in page_obj.object_list %}
<div class="applicant-card">
<!-- Applicant Header -->
<div class="applicant-header">
<div class="applicant-info">
<div class="applicant-name">
@ -541,15 +525,14 @@
</div>
</div>
<!-- Applicant Meta -->
<div class="applicant-meta">
<div class="meta-item">
<span></span>
<span>Applied {{ application.created_at|date:"M d, Y" }}</span>
<i class="far fa-clock"></i>
<span>{% trans "Applied" %} {{ application.created_at|date:"M d, Y" }}</span>
</div>
{% if application.ai_analysis_data.analysis_data_en.match_score %}
<div class="meta-item">
<span></span>
<i class="fas fa-robot"></i>
<span class="ai-score {% if application.ai_analysis_data.analysis_data_en.match_score >= 75 %}high{% elif application.ai_analysis_data.analysis_data_en.match_score >= 50 %}medium{% else %}low{% endif %}">
{{ application.ai_analysis_data.analysis_data_en.match_score }}%
</span>
@ -557,66 +540,46 @@
{% endif %}
{% if application.person.gpa %}
<div class="meta-item">
<span></span>
<span>GPA: {{ application.person.gpa|floatformat:2 }}</span>
<i class="fas fa-graduation-cap"></i>
<span>{% trans "GPA" %}: {{ application.person.gpa|floatformat:2 }}</span>
</div>
{% endif %}
</div>
<!-- Applicant Actions -->
<div class="applicant-actions">
{% comment %} <a href="{% url 'person_detail' application.person.pk %}" class="btn-action btn-view-profile">
👁️ Profile
</a> {% endcomment %}
<a href="{% url 'application_detail' application.slug %}" class="btn-filter btn-primary btn-sm">
Application
<i class="fas fa-file-alt"></i> {% trans "Application" %}
</a>
{% if application.stage == 'Interview' %}
<a href="{% url 'interview_list' %}" class="btn-filter btn-primary btn-sm">
Schedule
<i class="fas fa-calendar-check"></i> {% trans "Schedule" %}
</a>
{% endif %}
<a href="{% url 'message_create' %}?job={{ job.pk }}&candidate={{ application.pk }}" class="btn-filter btn-primary btn-sm">
✉️ Email
<i class="fas fa-envelope"></i> {% trans "Email" %}
</a>
</div>
</div>
{% endfor %}
</div>
<!-- Pagination -->
{% if page_obj.has_other_pages %}
<div class="pagination">
{% if page_obj.has_previous %}
<a href="?page=1{% if request.GET.urlencode %}&{{ request.GET.urlencode }}{% endif %}">« First</a>
<a href="?page={{ page_obj.previous_page_number }}{% if request.GET.urlencode %}&{{ request.GET.urlencode }}{% endif %}"> Previous</a>
{% endif %}
<span class="current">
Page {{ page_obj.number }} of {{ page_obj.paginator.num_pages }}
</span>
{% if page_obj.has_next %}
<a href="?page={{ page_obj.next_page_number }}{% if request.GET.urlencode %}&{{ request.GET.urlencode }}{% endif %}">Next </a>
<a href="?page={{ page_obj.paginator.num_pages }}{% if request.GET.urlencode %}&{{ request.GET.urlencode }}{% endif %}">Last »</a>
{% endif %}
</div>
{% include "includes/paginator.html" %}
{% endif %}
{% else %}
<!-- No Results -->
<div class="no-results">
<h3>😔 No Applicants Found</h3>
<h3>😔 {% trans "No Applicants Found" %}</h3>
<p>
{% if search_query or stage_filter or min_ai_score or max_ai_score or date_from or date_to %}
We couldn't find any applicants matching your current filters. Try adjusting your search criteria or clearing some filters.
{% trans "We couldn't find any applicants matching your current filters. Try adjusting your search criteria or clearing some filters." %}
{% else %}
There are currently no applicants for this job.
{% trans "There are currently no applicants for this job." %}
{% endif %}
</p>
<a href="{% url 'job_applicants' job.slug %}" class="btn-filter btn-primary">
🔄 Clear Filters
<i class="fas fa-sync-alt"></i> {% trans "Clear Filters" %}
</a>
</div>
{% endif %}
</div>
{% endblock %}
{% endblock %}

View File

@ -1,7 +1,7 @@
{% extends "base.html" %}
{% load static i18n %}
{% block title %}{{ job.title }} - Applicants{% endblock %}
{% block title %}{{ job.title }} - {% trans "Applicantions" %}{% endblock %}
{% block customCSS %}
<style>

View File

@ -1,15 +1,12 @@
{% extends "base.html" %}
{% load static %}
{% load i18n %}
{% block title %}Job Bank - All Opportunities{% endblock %}
{% block title %}{% trans "Job Bank - All Opportunities" %}{% endblock %}
{% block customCSS %}
<style>
.job-bank-container {
max-width: 1400px;
margin: 0 auto;
padding: 20px;
}
.job-bank-header {
background: linear-gradient(135deg, #00636e 0%, #004a53 100%);
@ -303,36 +300,7 @@
color: white;
}
.pagination {
display: flex;
justify-content: center;
align-items: center;
gap: 0.5rem;
margin-top: 2rem;
}
.pagination a,
.pagination span {
padding: 0.75rem 1rem;
border: 2px solid #e1e5e9;
border-radius: 8px;
text-decoration: none;
color: #333;
font-weight: 500;
transition: all 0.3s ease;
}
.pagination a:hover {
background: #667eea;
color: white;
border-color: #667eea;
}
.pagination .current {
background: #667eea;
color: white;
border-color: #667eea;
}
.no-results {
text-align: center;
@ -387,8 +355,8 @@
<div class="job-bank-container">
<!-- Header Section -->
<div class="job-bank-header">
<h1>🏦 Job Bank</h1>
<p>Explore all available opportunities across departments and find your perfect role</p>
<h1>🏦 {% trans "Job Bank" %}</h1>
<p>{% trans "Explore all available opportunities across departments and find your perfect role" %}</p>
</div>
<!-- Filters Section -->
@ -397,21 +365,21 @@
<div class="filters-grid">
<!-- Search Box -->
<div class="filter-group search-box">
<label for="search">🔍 Search Jobs</label>
<label for="search">🔍 {% trans "Search Jobs" %}</label>
<input
type="text"
id="search"
name="q"
value="{{ search_query }}"
placeholder="Search by title, department, or keywords..."
placeholder="{% trans "Search by title, department, or keywords..." %}"
>
</div>
<!-- Department Filter -->
<div class="filter-group">
<label for="department">📁 Department</label>
<label for="department">📁 {% trans "Department" %}</label>
<select id="department" name="department">
<option value="">All Departments</option>
<option value="">{% trans "All Departments" %}</option>
{% for dept in departments %}
<option value="{{ dept }}" {% if department_filter == dept %}selected{% endif %}>
{{ dept }}
@ -422,9 +390,9 @@
<!-- Job Type Filter -->
<div class="filter-group">
<label for="job_type">💼 Job Type</label>
<label for="job_type">💼 {% trans "Job Type" %}</label>
<select id="job_type" name="job_type">
<option value="">All Types</option>
<option value="">{% trans "All Types" %}</option>
{% for key, value in job_types.items %}
<option value="{{ key }}" {% if job_type_filter == key %}selected{% endif %}>
{{ value }}
@ -435,9 +403,9 @@
<!-- Workplace Type Filter -->
<div class="filter-group">
<label for="workplace_type">🏢 Workplace Type</label>
<label for="workplace_type">🏢 {% trans "Workplace Type" %}</label>
<select id="workplace_type" name="workplace_type">
<option value="">All Types</option>
<option value="">{% trans "All Types" %}</option>
{% for key, value in workplace_types.items %}
<option value="{{ key }}" {% if workplace_type_filter == key %}selected{% endif %}>
{{ value }}
@ -448,9 +416,9 @@
<!-- Status Filter -->
<div class="filter-group">
<label for="status">📊 Status</label>
<label for="status">📊 {% trans "Status" %}</label>
<select id="status" name="status">
<option value="">All Statuses</option>
<option value="">{% trans "All Statuses" %}</option>
{% for key, value in status_choices.items %}
<option value="{{ key }}" {% if status_filter == key %}selected{% endif %}>
{{ value }}
@ -461,25 +429,25 @@
<!-- Date Filter -->
<div class="filter-group">
<label for="date_filter">📅 Posted Within</label>
<label for="date_filter">📅 {% trans "Posted Within" %}</label>
<select id="date_filter" name="date_filter">
<option value="">Any Time</option>
<option value="week" {% if date_filter == 'week' %}selected{% endif %}>Last Week</option>
<option value="month" {% if date_filter == 'month' %}selected{% endif %}>Last Month</option>
<option value="quarter" {% if date_filter == 'quarter' %}selected{% endif %}>Last 3 Months</option>
<option value="">{% trans "Any Time" %}</option>
<option value="week" {% if date_filter == 'week' %}selected{% endif %}>{% trans "Last Week" %}</option>
<option value="month" {% if date_filter == 'month' %}selected{% endif %}>{% trans "Last Month" %}</option>
<option value="quarter" {% if date_filter == 'quarter' %}selected{% endif %}>{% trans "Last 3 Months" %}</option>
</select>
</div>
<!-- Sort By -->
<div class="filter-group">
<label for="sort">🔄 Sort By</label>
<label for="sort">🔄 {% trans "Sort By" %}</label>
<select id="sort" name="sort">
<option value="-created_at" {% if sort_by == '-created_at' %}selected{% endif %}>Newest First</option>
<option value="created_at" {% if sort_by == 'created_at' %}selected{% endif %}>Oldest First</option>
<option value="title" {% if sort_by == 'title' %}selected{% endif %}>Title (A-Z)</option>
<option value="-title" {% if sort_by == '-title' %}selected{% endif %}>Title (Z-A)</option>
<option value="department" {% if sort_by == 'department' %}selected{% endif %}>Department (A-Z)</option>
<option value="-department" {% if sort_by == '-department' %}selected{% endif %}>Department (Z-A)</option>
<option value="-created_at" {% if sort_by == '-created_at' %}selected{% endif %}>{% trans "Newest First" %}</option>
<option value="created_at" {% if sort_by == 'created_at' %}selected{% endif %}>{% trans "Oldest First" %}</option>
<option value="title" {% if sort_by == 'title' %}selected{% endif %}>{% trans Title %} (A-Z)</option>
<option value="-title" {% if sort_by == '-title' %}selected{% endif %}>{% trans "Title" %} (Z-A)</option>
<option value="department" {% if sort_by == 'department' %}selected{% endif %}>{% trans "Department" %} (A-Z)</option>
<option value="-department" {% if sort_by == '-department' %}selected{% endif %}>{% trans "Department" %} (Z-A)</option>
</select>
</div>
</div>
@ -487,10 +455,10 @@
<!-- Filter Actions -->
<div class="filters-actions">
<button type="submit" class="btn-filter btn-primary">
🔍 Apply Filters
🔍 {% trans "Apply Filters" %}
</button>
<a href="{% url 'job_bank' %}" class="btn-filter btn-secondary">
🔄 Clear All
🔄 {% trans "Clear All" %}
</a>
</div>
</form>
@ -499,9 +467,9 @@
<!-- Results Header -->
<div class="results-header">
<div class="results-count">
📊 Found <strong>{{ total_jobs }}</strong> job{{ total_jobs|pluralize }}
📊 {% trans "Found" %} <strong>{{ total_jobs }}</strong> {%trans "job" %}{{ total_jobs|pluralize }}
{% if search_query or department_filter or job_type_filter or workplace_type_filter or status_filter or date_filter %}
with filters applied
{% trans "with filters applied" %}
{% endif %}
</div>
</div>
@ -535,7 +503,7 @@
{% if job.max_applications %}
<div class="meta-item">
<span class="meta-icon">👥</span>
<span>{{ job.max_applications }} positions</span>
<span>{{ job.max_applications }} {% trans "positions" %}</span>
</div>
{% endif %}
</div>
@ -549,11 +517,11 @@
<div class="job-actions">
{% if job.status == 'ACTIVE' %}
<a href="{% url 'job_applicants' job.slug %}" class="btn-apply">
<EFBFBD> View Applicants
{% trans "View Applicants" %}
</a>
{% endif %}
<a href="{% url 'job_detail' job.slug %}" class="btn-view">
👁️ View Details
{% trans "View Details" %}
</a>
</div>
</div>
@ -562,35 +530,21 @@
<!-- Pagination -->
{% if page_obj.has_other_pages %}
<div class="pagination">
{% if page_obj.has_previous %}
<a href="?page=1{% if request.GET.urlencode %}&{{ request.GET.urlencode }}{% endif %}">« First</a>
<a href="?page={{ page_obj.previous_page_number }}{% if request.GET.urlencode %}&{{ request.GET.urlencode }}{% endif %}"> Previous</a>
{% endif %}
<span class="current">
Page {{ page_obj.number }} of {{ page_obj.paginator.num_pages }}
</span>
{% if page_obj.has_next %}
<a href="?page={{ page_obj.next_page_number }}{% if request.GET.urlencode %}&{{ request.GET.urlencode }}{% endif %}">Next </a>
<a href="?page={{ page_obj.paginator.num_pages }}{% if request.GET.urlencode %}&{{ request.GET.urlencode }}{% endif %}">Last »</a>
{% endif %}
</div>
{% include "includes/paginator.html" %}
{% endif %}
{% else %}
<!-- No Results -->
<div class="no-results">
<h3>😔 No Jobs Found</h3>
<h3> {% trans "No Jobs Found" %}</h3>
<p>
{% if search_query or department_filter or job_type_filter or workplace_type_filter or status_filter or date_filter %}
We couldn't find any jobs matching your current filters. Try adjusting your search criteria or clearing some filters.
{% trans "We couldn't find any jobs matching your current filters. Try adjusting your search criteria or clearing some filters." %}
{% else %}
There are currently no job postings in the system. Check back later for new opportunities!
{% trans "There are currently no job postings in the system. Check back later for new opportunities!" %}
{% endif %}
</p>
<a href="{% url 'job_bank' %}" class="btn-filter btn-primary">
🔄 Clear Filters
{% trans "Clear Filters" %}
</a>
</div>
{% endif %}

View File

@ -1,7 +1,7 @@
{% extends "base.html" %}
{% load static i18n %}
{% block title %}Job Postings - University ATS{% endblock %}
{% block title %}{% trans "Job Postings" %} - University ATS{% endblock %}
{% block customCSS %}
<style>

View File

@ -1,30 +1,30 @@
{% extends "base.html" %}
{% block title %}Delete Job - University ATS{% endblock %}
{% load i18n %}
{% block title %}{% trans "Delete Job - University ATS" %}{% endblock %}
{% block content %}
<div class="row justify-content-center">
<div class="col-lg-6">
<div class="card">
<div class="card-header bg-danger text-white">
<h4><i class="fas fa-exclamation-triangle"></i> Delete Job Posting</h4>
<h4><i class="fas fa-exclamation-triangle"></i> {% trans "Delete Job Posting" %}</h4>
</div>
<div class="card-body">
<p>Are you sure you want to delete the job posting "<strong>{{ job.title }}</strong>"?</p>
<p>{% trans "Are you sure you want to delete the job posting" %} "<strong>{{ job.title }}</strong>"?</p>
<div class="alert alert-warning">
<i class="fas fa-exclamation-triangle"></i>
<strong>This action cannot be undone.</strong> All associated data will be permanently removed.
<strong>{% trans "This action cannot be undone."%}</strong> {% trans "All associated data will be permanently removed." %}"
</div>
<form method="post">
{% csrf_token %}
<div class="d-flex justify-content-between">
<a href="{% url 'job_list' %}" class="btn btn-secondary">
<i class="fas fa-arrow-left"></i> Cancel
<i class="fas fa-arrow-left"></i> {% trans "Cancel" %}
</a>
<button type="submit" class="btn btn-danger">
<i class="fas fa-trash"></i> Delete Job
<i class="fas fa-trash"></i> {% trans "Delete Job" %}
</button>
</div>
</form>

View File

@ -1,8 +1,9 @@
{% load i18n %}
<div class="modal fade mt-4" id="myModalForm" tabindex="-1" aria-labelledby="myModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="myModalLabel">Add New Image for the Post</h5>
<h5 class="modal-title" id="myModalLabel">{% trans "Add New Image for the Post" %}</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
@ -11,12 +12,12 @@
{{ image_upload_form.as_p}}
{% if image_upload_form.instance.post_image %}
<p>Current Image:</p>
<p>{% trans "Current Image:" %}</p>
<img src="{{ image_upload_form.instance.post_image.url }}" alt="Post Image" style="max-width: 200px;">
{% endif %}
<div class="modal-footer mt-2">
<button type="button" class="btn btn-lg btn-secondary" data-bs-dismiss="modal">Close</button>
<button type="submit" class="btn btn-main-action ">Save changes</button>
<button type="button" class="btn btn-lg btn-secondary" data-bs-dismiss="modal">{% trans "Close" %}</button>
<button type="submit" class="btn btn-main-action ">{% trans "Save changes" %}</button>
</div>
</form>
</div>

View File

@ -1,9 +1,10 @@
{% load crispy_forms_tags %}
{% load i18n %}
<div class="modal fade mt-4" id="linkedinData" tabindex="-1" aria-labelledby="myModalLabel" aria-hidden="true">
<div class="modal-dialog modal-xl" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="myModalLabel">Edit linkedin Post content</h5>
<h5 class="modal-title" id="myModalLabel">{% trans "Edit linkedin Post content" %}</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
@ -11,8 +12,8 @@
{% csrf_token %}
{{ linkedin_content_form|crispy }}
<div class="modal-footer mt-2">
<button type="button" class="btn btn-lg btn-secondary" data-bs-dismiss="modal">Close</button>
<button type="submit" class="btn btn-main-action ">Save changes</button>
<button type="button" class="btn btn-lg btn-secondary" data-bs-dismiss="modal">{% trans "Close" %}</button>
<button type="submit" class="btn btn-main-action ">{% trans "Save changes" %}</button>
</div>
</form>
</div>

View File

@ -80,7 +80,7 @@
</div>
{% if message.job %}
<div class="col-md-6">
<strong>Related Job:</strong>
<strong>{% trans "Related Job:" %}</strong>
{{ message.job.title }}
</div>
{% endif %}

View File

@ -99,7 +99,7 @@
{{ message.subject|truncatechars:50 }}
</a>
{% if message.parent_message %}
<span class="badge bg-secondary ms-2">Reply</span>
<span class="badge bg-secondary ms-2">{% trans "Reply" %}</span>
{% endif %}
</td>
<td>

View File

@ -182,39 +182,8 @@
</table>
</div>
{% if page_obj.has_other_pages %}
<nav aria-label="Message pagination">
<ul class="pagination justify-content-center">
{% if page_obj.has_previous %}
<li class="page-item">
<a class="page-link text-primary-theme" href="?page={{ page_obj.previous_page_number }}&status={{ status_filter }}&type={{ type_filter }}&q={{ search_query }}">
<i class="fas fa-chevron-left"></i>
</a>
</li>
{% endif %}
{% for num in page_obj.paginator.page_range %}
{% if page_obj.number == num %}
<li class="page-item active">
<span class="page-link bg-primary-theme border-primary-theme">{{ num }}</span>
</li>
{% else %}
<li class="page-item">
<a class="page-link text-primary-theme" href="?page={{ num }}&status={{ status_filter }}&type={{ type_filter }}&q={{ search_query }}">{{ num }}</a>
</li>
{% endif %}
{% endfor %}
{% if page_obj.has_next %}
<li class="page-item">
<a class="page-link text-primary-theme" href="?page={{ page_obj.next_page_number }}&status={{ status_filter }}&type={{ type_filter }}&q={{ search_query }}">
<i class="fas fa-chevron-right"></i>
</a>
</li>
{% endif %}
</ul>
</nav>
{% endif %}
{% include "includes/paginator.html" %}
{% else %}
<div class="text-center text-muted py-5">
<i class="fas fa-inbox fa-3x mb-3 text-primary-theme"></i>

View File

@ -1,7 +1,7 @@
{% extends "base.html" %}
{% load static i18n crispy_forms_tags %}
{% block title %}Create Participant - {{ block.super }}{% endblock %}
{% block title %}{% trans "Create Participant" %} - {{ block.super }}{% endblock %}
{% block customCSS %}
<style>

View File

@ -1,7 +1,7 @@
{% extends "base.html" %}
{% load static i18n %}
{% block title %}{{ participant.name }} - Participant Details{% endblock %}
{% block title %}{{ participant.name }} - {% trans "Participant Details" %}{% endblock %}
{% block customCSS %}
<style>

View File

@ -1,7 +1,7 @@
{% extends "base.html" %}
{% load static i18n %}
{% block title %}Participants - {{ block.super }}{% endblock %}
{% block title %} {% trans "Participants" %} - {{ block.super }}{% endblock %}
{% block customCSS %}
<style>

View File

@ -1,7 +1,7 @@
{% extends "base.html" %}
{% load static i18n crispy_forms_tags %}
{% block title %}Create Applicant - {{ block.super }}{% endblock %}
{% block title %}{% trans "Create Applicant" %} - {{ block.super }}{% endblock %}
{% block customCSS %}
<style>

View File

@ -1,7 +1,7 @@
{% extends "base.html" %}
{% load static i18n %}
{% block title %}{% trans "Delete Person" %} - {{ block.super }}{% endblock %}
{% block title %}{% trans "Delete Applicant" %} - {{ block.super }}{% endblock %}
{% block content %}
<div class="container py-4">
@ -48,7 +48,7 @@
<i class="fas fa-arrow-left me-1"></i> {% trans "Cancel" %}
</a>
<button type="submit" class="btn btn-danger">
<i class="fas fa-trash me-1"></i> {% trans "Delete Person" %}
<i class="fas fa-trash me-1"></i> {% trans "Delete Applicant" %}
</button>
</div>
</form>

View File

@ -1,7 +1,7 @@
{% extends "base.html" %}
{% load static i18n %}
{% block title %}People - {{ block.super }}{% endblock %}
{% block title %}{% trans "Applicants" %} - {{ block.super }}{% endblock %}
{% block customCSS %}
<style>
@ -108,19 +108,7 @@
background-color: var(--kaauh-gray-light);
}
/* Pagination Link Styling */
.pagination .page-item .page-link {
color: var(--kaauh-teal-dark);
border-color: var(--kaauh-border);
}
.pagination .page-item.active .page-link {
background-color: var(--kaauh-teal);
border-color: var(--kaauh-teal);
color: white;
}
.pagination .page-item:hover .page-link:not(.active) {
background-color: #e9ecef;
}
/* Profile Image Styling */
.profile-image-small {
@ -395,10 +383,10 @@
<div class="card-body">
<i class="fas fa-user-friends fa-3x mb-3" style="color: var(--kaauh-teal-dark);"></i>
<h3>{% trans "No people found" %}</h3>
<p class="text-muted">{% trans "Create your first person record." %}</p>
<p class="text-muted">{% trans "Create your first applicant record." %}</p>
{% if user.is_staff %}
<a href="{% url 'person_create' %}" class="btn btn-main-action mt-3">
<i class="fas fa-plus me-1"></i> {% trans "Add Person" %}
<i class="fas fa-plus me-1"></i> {% trans "Add Applicant" %}
</a>
{% endif %}
</div>

View File

@ -1,7 +1,7 @@
{% extends "base.html" %}
{% load static i18n crispy_forms_tags %}
{% block title %}Update {{ person.get_full_name }} - {{ block.super }}{% endblock %}
{% block title %}{% trans "Update" %} {{ person.get_full_name }} - {{ block.super }}{% endblock %}
{% block customCSS %}
<style>

View File

@ -6,292 +6,188 @@
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="description" content="{% trans 'King Abdullah Academic University Hospital - Agency Portal' %}">
<title>{% block title %}{% trans 'KAAUH Agency Portal' %}{% endblock %}</title>
<meta name="description" content="{% trans 'King Abdullah Academic University Hospital - Portal' %}">
<title>{% block title %}{% trans 'KAAUH Portal' %}{% endblock %}</title>
<link rel="apple-touch-icon" sizes="180x180" href="{% static 'image/favicon/apple-touch-icon.png'%}">
<link rel="icon" type="image/png" sizes="32x32" href="{% static 'image/favicon/favicon-32x32.png'%}">
<link rel="icon" type="image/png" sizes="16x16" href="{% static 'image/favicon/favicon-16x16.png'%}">
<link rel="manifest" href="{% static 'image/favicon/site.webmanifest'%}">
{# Load correct Bootstrap CSS file for RTL/LTR #}
{% if LANGUAGE_CODE == 'ar' %}
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.rtl.min.css" rel="stylesheet">
{% else %}
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
{% endif %}
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.6.0/css/all.min.css" integrity="sha512-Kc323vGBEqzTmouAECnVceyQqyqdsSiqLQISBL29aUW4U/M7pSPA/gEUZQqv1cwx4OnYxTxve5UMg5GT6L4JJg==" crossorigin="anonymous" referrerpolicy="no-referrer" />
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.6.0/css/all.min.css" />
<link rel="stylesheet" href="{% static 'css/main.css' %}">
<script src="{% static 'js/main.js' %}"></script>
<script src="{% static 'js/typo.js' %}"></script>
{% block customCSS %}{% endblock %}
</head>
<body class="d-flex flex-column min-vh-100" hx-headers='{"X-CSRFToken": "{{ csrf_token }}"}'>
<body hx-headers='{"X-CSRFToken": "{{ csrf_token }}"}'>
<div class="top-bar d-none d-md-block" style="background-color: #f8f9fa;">
<div class="container-fluid">
<div class="d-flex justify-content-between align-items-center gap-2" style="max-width: 1600px; margin: 0 auto; padding: 0.5rem 0;">
<div class="logo-group d-flex gap-3 align-items-center">
<img src="{% static 'image/vision.svg' %}" alt="{% trans 'Saudi Vision 2030' %}" loading="lazy" style="height: 35px; object-fit: contain;">
</div>
<div class="hospital-info d-flex gap-2 align-items-center">
<div class="hospital-text text-center text-md-end">
<div class="small fw-semibold" style="color: #004a53;">
{% if LANGUAGE_CODE == 'ar' %}
جامعة الأميرة نورة بنت عبدالرحمن الأكاديمية
<br>
ومستشفى الملك عبدالله بن عبدالعزيز التخصصي
{% else %}
Princess Nourah bint Abdulrahman University
<br>
King Abdullah bin Abdulaziz University Hospital
{% endif %}
</div>
</div>
<img src="{% static 'image/kaauh.png' %}" alt="KAAUH Logo" style="max-height: 40px; max-width: 40px;">
</div>
<aside class="sidebar d-flex flex-column" id="sidebar">
<div class="sidebar-brand text-center">
<img src="{% static 'image/kaauh_green1.png' %}" alt="Logo" style="width: 40px;">
<div class="text-white small mt-2 fw-bold brand-subtitle">
{% if request.user.user_type == 'agency' %}{% trans "Agency Portal" %}{% else %}{% trans "Candidate Portal" %}{% endif %}
</div>
</div>
</div>
{# Using inline style for nav background color - replace with a dedicated CSS class (e.g., .bg-kaauh-nav) if defined in main.css #}
<div style="background-color: #00636e;">
<nav class="navbar navbar-expand-lg navbar-dark sticky-top">
<div class="container-fluid" style="max-width: 1600px;">
{% if request.user.user_type == 'candidate' %}
<a class="navbar-brand text-white" href="{% url 'applicant_portal_dashboard' %}" aria-label="Applicant Dashboard">
<img src="{% static 'image/kaauh_green1.png' %}" alt="{% trans 'kaauh logo green bg' %}" style="width: 40px; height: 40px;">
<span class="ms-3 d-none d-md-inline fw-semibold">{% trans "Applicant Portal" %}</span>
<nav class="sidebar-nav">
{% if request.user.user_type == 'agency' %}
<a class="nav-link-custom {% if 'dashboard' in request.path %}active{% endif %}" href="{% url 'agency_portal_dashboard' %}">
<i class="fas fa-th-large me-2"></i> <span>{% trans "Dashboard" %}</span>
</a>
{% elif request.user.user_type == 'agency' %}
<a class="navbar-brand text-white" href="{% url 'agency_portal_dashboard' %}" aria-label="Agency Dashboard">
<img src="{% static 'image/kaauh_green1.png' %}" alt="{% trans 'kaauh logo green bg' %}" style="width: 40px; height: 40px;">
<span class="ms-3 d-none d-md-inline fw-semibold">{% trans "Agency Portal" %}</span>
<a class="nav-link-custom {% if 'persons' in request.path %}active{% endif %}" href="{% url 'agency_portal_persons_list' %}">
<i class="fas fa-users me-2"></i> <span>{% trans "Applicants" %}</span>
</a>
{% else %}
<a class="nav-link-custom {% if 'dashboard' in request.path %}active{% endif %}" href="{% url 'applicant_portal_dashboard' %}">
<i class="fas fa-th-large me-2"></i> <span>{% trans "Dashboard" %}</span>
</a>
<a class="nav-link-custom {% if 'career' in request.path %}active{% endif %}" href="{% url 'kaauh_career' %}">
<i class="fas fa-briefcase me-2"></i> <span>{% trans "Careers" %}</span>
</a>
{% endif %}
<a class="nav-link-custom {% if 'message' in request.path %}active{% endif %}" href="{% url 'message_list' %}">
<i class="fas fa-envelope me-2"></i>
<span>{% trans "Messages" %}</span>
{% if request.user.get_unread_message_count > 0 %}
<span class="badge rounded-pill bg-danger ms-auto">{{ request.user.get_unread_message_count }}</span>
{% endif %}
</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#agencyNavbar"
aria-controls="agencyNavbar" aria-expanded="false" aria-label="{% trans 'Toggle navigation' %}">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="agencyNavbar">
<div class="navbar-nav ms-auto">
<li class="nav-item me-2">
{% if LANGUAGE_CODE == 'en' %}
<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="btn bg-primary-theme text-white" type="submit">
<span class="me-2">🇸🇦</span> العربية
</button>
</form>
{% elif LANGUAGE_CODE == 'ar' %}
<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="btn bg-primary-theme text-white" type="submit">
<span class="me-2">🇺🇸</span> English
</button>
</form>
{% endif %}
</li>
{# NAVIGATION LINKS (Add your portal links here if needed) #}
{% if request.user.user_type == 'agency' %}
<li class="nav-item">
<a class="nav-link text-white" href="{% url 'agency_portal_dashboard' %}">
<i class="fas fa-tachometer-alt me-1"></i> {% trans "Dashboard" %}
</a>
</li>
<li class="nav-item">
<a class="nav-link text-white" href="{% url 'agency_portal_persons_list' %}">
<i class="fas fa-users me-1"></i> {% trans "Applicants" %}
</a>
</li>
{% elif request.user.user_type == 'candidate' %}
<li class="nav-item">
<a class="nav-link text-white" href="{% url 'applicant_portal_dashboard' %}">
<i class="fas fa-tachometer-alt me-1"></i> {% trans "Dashboard" %}
</a>
</li>
<li class="nav-item">
<a class="nav-link text-white" href="{% url 'kaauh_career' %}">
<i class="fas fa-globe me-1"></i> {% trans "Careers" %}
</a>
</li>
{% endif %}
{% comment %}
<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle text-white" href="#" role="button" data-bs-toggle="dropdown"
data-bs-offset="0, 8" aria-expanded="false" aria-label="{% trans 'Toggle language menu' %}">
<i class="fas fa-globe me-1"></i>
<span class="d-none d-lg-inline">{{ LANGUAGE_CODE|upper }}</span>
</a>
<ul class="dropdown-menu {% if LANGUAGE_CODE == 'ar' %}dropdown-menu-start{% else %}dropdown-menu-end{% endif %}" data-bs-popper="static">
<li>
<form action="/i18n/setlang/" 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="/i18n/setlang/" 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>
</li> {% endcomment %}
{% if request.user.is_authenticated %}
<li class="nav-item">
<a class="nav-link text-white" href="{% url 'user_detail' request.user.pk %}">
<i class="fas fa-user-circle me-1"></i> <span>{% trans "My Profile" %}</span></a></li>
{% endif %}
<li class="nav-item mx-2 mt-2">
<a href="{% url 'message_list' %}"
class=" btn btn-sm btn-outline-warning position-relative">
<i class="fas fa-envelope me-1"></i>
<span class="position-absolute top-0 start-100 translate-middle badge rounded-pill bg-danger">
{{ request.user.get_unread_message_count }}
</span>
</a>
</li>
<li class="nav-item ms-3">
{% if request.user.is_authenticated %}
<form method="post" action="{% url 'account_logout' %}" class="d-inline py-2 d-flex align-items-center">
{% csrf_token %}
<button type="submit" class="btn btn-outline-light btn-sm">
<i class="fas fa-sign-out-alt me-1"></i> {% trans "Logout" %}
</button>
</form>
{% endif %}
</li>
</div>
<div class="mt-auto">
<div class="mt-4 pt-3 border-top border-secondary mx-3 brand-subtitle">
<small class="text-white-50 px-2 uppercase">{% trans "Account" %}</small>
</div>
<a class="nav-link-custom {% if 'profile' in request.path %}active{% endif %}" href="{% url 'user_detail' request.user.pk %}">
<i class="fas fa-user-circle me-2"></i> <span>{% trans "My Profile" %}</span>
</a>
</div>
</nav>
</div>
<main id="message-container" class="container-fluid flex-grow-1" style="max-width: 1600px; margin: 0 auto;">
{# Messages Block (Correct) #}
{% if messages %}
{% for message in messages %}
<div class="alert alert-{{ message.tags }} alert-dismissible fade show mt-2" role="alert">
{{ message }}
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="{% trans 'Close' %}"></button>
</div>
{% endfor %}
{% if request.user.is_authenticated %}
<div class="logout-section">
<form method="post" action="{% url 'account_logout'%}">
{% csrf_token %}
<button type="submit" class="btn btn-link text-decoration-none d-flex align-items-center">
<i class="fas fa-sign-out-alt me-2"></i> <span>{% trans "Sign Out" %}</span>
</button>
</form>
</div>
{% endif %}
{% block content %}
{% endblock %}
</main>
{# Footer (Correct) #}
<footer class="mt-auto">
<div class="footer-bottom py-3 small text-muted" style="background-color: #00363a;">
<div class="container-fluid">
<div class="d-flex justify-content-between align-items-center flex-wrap" style="max-width: 1600px; margin: 0 auto;">
<p class="mb-0 text-white-50">
&copy; {% now "Y" %} {% trans "King Abdullah Academic University Hospital (KAAUH)." %}
{% trans "All rights reserved." %}
</p>
<p class="mb-0 text-white-50">
{% if request.user.user_type == 'candidate' %}
{% trans "Candidate Portal" %}
{% elif request.user.user_type == 'agency' %}
{% trans "Agency Portal" %}
{% endif %}
</p>
<div class="mt-auto p-3 border-top border-secondary text-center">
<a href="https://tenhal.sa/" class="text-decoration-none" target="_blank">
<div class="text-white-50 brand-subtitle" style="font-size: 0.7rem; letter-spacing: 0.5px;">
{% trans "POWERED BY" %} <span class="text-white fw-bold">TENHAL</span>
</div>
</a>
</div>
</aside>
<div class="main-wrapper">
<header class="content-header justify-content-between">
<div class="d-flex align-items-center">
<button class="btn me-2" id="sidebarCollapse">
<i class="fas fa-bars"></i>
</button>
<div class="hospital-text d-none d-md-block">
<small class="text-teal fw-bold">
{% if request.user.user_type == 'agency' %}{% trans "Agency Portal" %}{% else %}{% trans "Applicant Portal" %}{% endif %}
<span class="text-muted mx-2">|</span>
<span class="text-secondary">KAAUH</span>
</small>
</div>
</div>
</div>
</footer>
<div class="d-flex align-items-center gap-3">
<a href="{% url 'message_list' %}" class="btn btn-sm btn-outline-secondary position-relative d-none d-lg-block">
<i class="fas fa-envelope"></i>
{% if request.user.get_unread_message_count > 0 %}
<span class="position-absolute top-0 start-100 translate-middle badge rounded-pill bg-danger">
{{ request.user.get_unread_message_count }}
</span>
{% endif %}
</a>
{% if LANGUAGE_CODE == 'en' %}
<form action="{% url 'set_language' %}" method="post">{% csrf_token %}
<input name="language" type="hidden" value="ar">
<input name="next" type="hidden" value="{{ request.get_full_path }}">
<button class="btn btn-sm btn-outline-secondary" type="submit">🇸🇦 العربية</button>
</form>
{% else %}
<form action="{% url 'set_language' %}" method="post">{% csrf_token %}
<input name="language" type="hidden" value="en">
<input name="next" type="hidden" value="{{ request.get_full_path }}">
<button class="btn btn-sm btn-outline-secondary" type="submit">🇺🇸 English</button>
</form>
{% endif %}
<div class="dropdown">
<button class="btn p-0 border-0" data-bs-toggle="dropdown">
{% if user.profile_image %}
<img src="{{ user.profile_image.url }}" class="rounded-circle border" width="35" height="35">
{% else %}
<div class="profile-avatar" style="width: 35px; height: 35px; font-size: 0.8rem;">
{{ user.username|first|upper }}
</div>
{% endif %}
</button>
<ul class="dropdown-menu dropdown-menu-end shadow border-0">
<li class="px-3 py-2 border-bottom">
<div class="fw-bold small">{{ user.get_full_name|default:user.username }}</div>
<div class="text-muted text-xs">{{ user.email|truncatechars:20 }}</div>
</li>
<li><a class="dropdown-item mt-1" href="{% url 'user_detail' request.user.pk %}">{% trans "Profile" %}</a></li>
<li><hr class="dropdown-divider"></li>
<li>
<form method="post" action="{% url 'account_logout'%}">
{% csrf_token %}
<button type="submit" class="dropdown-item text-danger">{% trans "Sign Out" %}</button>
</form>
</li>
</ul>
</div>
</div>
</header>
<main class="main-container mx-3 my-3">
{% if messages %}
{% for message in messages %}
<div class="alert alert-{{ message.tags }} alert-dismissible fade show" role="alert">
{{ message }}
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
</div>
{% endfor %}
{% endif %}
{% block content %}{% endblock %}
</main>
<footer class="mt-auto py-3 border-top bg-white">
<div class="container-fluid px-4 text-center">
<div class="small text-muted">
&copy; {% now "Y" %} KAAUH. {% trans "All rights reserved." %}
</div>
</div>
</footer>
</div>
{% include 'includes/delete_modal.html' %}
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/htmx.org@2.0.7/dist/htmx.min.js"></script>
<script type="module" src="https://cdn.jsdelivr.net/gh/starfederation/datastar@1.0.0-RC.6/bundles/datastar.js"></script>
{# JavaScript (Left unchanged as it was mostly correct) #}
<script>
document.addEventListener('DOMContentLoaded', () => {
// Navbar collapse auto-close on link click (Mobile UX)
const navbarCollapse = document.getElementById('agencyNavbar');
if (navbarCollapse) {
const navLinks = navbarCollapse.querySelectorAll('.nav-link:not(.dropdown-toggle), .dropdown-item');
const bsCollapse = bootstrap.Collapse.getInstance(navbarCollapse) || new bootstrap.Collapse(navbarCollapse, { toggle: false });
navLinks.forEach(link => {
link.addEventListener('click', () => {
if (navbarCollapse.classList.contains('show')) {
if (!link.classList.contains('dropdown-toggle')) {
bsCollapse.hide();
}
}
});
});
}
// Mobile logout confirmation
const logoutButton = document.querySelector('form[action*="logout"] button');
if (logoutButton) {
logoutButton.addEventListener('click', (e) => {
if (window.innerWidth < 992) {
const confirmed = confirm('{% trans "Are you sure you want to logout?" %}');
if (!confirmed) e.preventDefault();
}
});
}
});
function form_loader(){
const forms = document.querySelectorAll('form');
forms.forEach(form => {
form.addEventListener('submit', function(e) {
const submitButton = form.querySelector('button[type="submit"], input[type="submit"]');
if (submitButton) {
submitButton.disabled = true;
submitButton.classList.add('loading');
window.addEventListener('unload', function() {
submitButton.disabled = false;
submitButton.classList.remove('loading');
});
}
});
});
}
try {
document.addEventListener('htmx:afterSwap', form_loader);
} catch(e) {
console.error(e);
}
function closeOpenBootstrapModal() {
const openModalElement = document.querySelector('.modal.show');
if (openModalElement) {
const modal = bootstrap.Modal.getInstance(openModalElement);
if (modal) {
modal.hide();
} else {
console.warn("Found an open modal element, but could not get the Bootstrap Modal instance.");
}
} else {
console.log("No open Bootstrap Modal found to close.");
}
}
</script>
{% block customJS %}{% endblock %}
</body>
</html>
</html>

View File

@ -22,16 +22,16 @@
<div class="d-flex align-items-center mb-3">
<div class="me-3">
<strong>Agency:</strong> {{ access_link.assignment.agency.name }}
<strong>{% trans "Agency:" %}</strong> {{ access_link.assignment.agency.name }}
</div>
<div class="me-3">
<strong>Job:</strong> {{ access_link.assignment.job.title }}
<strong>{% trans "Job:" %}</strong> {{ access_link.assignment.job.title }}
</div>
</div>
<div class="d-flex align-items-center mb-3">
<div class="me-3">
<strong>Current Status:</strong>
<strong>{% trans "Current Status:" %}</strong>
<span class="badge bg-{{ 'success' if access_link.is_active else 'danger' }}">
{{ 'Active' if access_link.is_active else 'Inactive' }}
</span>
@ -40,23 +40,23 @@
<div class="d-flex align-items-center mb-3">
<div class="me-3">
<strong>Expires:</strong> {{ access_link.expires_at|date:"Y-m-d H:i" }}
<strong>{% trans "Expires:" %}</strong> {{ access_link.expires_at|date:"Y-m-d H:i" }}
</div>
</div>
<div class="d-flex align-items-center mb-3">
<div class="me-3">
<strong>Access Count:</strong> {{ access_link.access_count }}
<strong>{% trans "Access Count:" %}</strong> {{ access_link.access_count }}
</div>
</div>
<div class="d-flex align-items-center mb-3">
<div class="me-3">
<strong>Last Accessed:</strong>
<strong>{% trans "Last Accessed:" %}</strong>
{% if access_link.last_accessed %}
{{ access_link.last_accessed|date:"Y-m-d H:i" }}
{% else %}
Never
{% trans "Never" %}
{% endif %}
</div>
</div>
@ -66,7 +66,7 @@
<div class="d-grid gap-2 d-md-flex justify-content-md-end">
<a href="{{ cancel_url }}" class="btn btn-secondary">
<i class="fas fa-times me-2"></i>
Cancel
{% trans "Cancel" %}
</a>
<button type="submit" class="btn btn-warning">
<i class="fas fa-{{ 'toggle-on' if title == 'Reactivate Access Link' else 'toggle-off' }} me-2"></i>

View File

@ -192,47 +192,7 @@
<!-- Pagination -->
{% if page_obj.has_other_pages %}
<nav aria-label="{% trans 'Assignments pagination' %}">
<ul class="pagination justify-content-center mt-4">
{% if page_obj.has_previous %}
<li class="page-item">
<a class="page-link" href="?page=1{% if search_query %}&q={{ search_query }}{% endif %}{% if status_filter %}&status={{ status_filter }}{% endif %}">
<i class="fas fa-angle-double-left"></i>
</a>
</li>
<li class="page-item">
<a class="page-link" href="?page={{ page_obj.previous_page_number }}{% if search_query %}&q={{ search_query }}{% endif %}{% if status_filter %}&status={{ status_filter }}{% endif %}">
<i class="fas fa-angle-left"></i>
</a>
</li>
{% endif %}
{% for num in page_obj.paginator.page_range %}
{% if page_obj.number == num %}
<li class="page-item active">
<span class="page-link">{{ num }}</span>
</li>
{% elif num > page_obj.number|add:'-3' and num < page_obj.number|add:'3' %}
<li class="page-item">
<a class="page-link" href="?page={{ num }}{% if search_query %}&q={{ search_query }}{% endif %}{% if status_filter %}&status={{ status_filter }}{% endif %}">{{ num }}</a>
</li>
{% endif %}
{% endfor %}
{% if page_obj.has_next %}
<li class="page-item">
<a class="page-link" href="?page={{ page_obj.next_page_number }}{% if search_query %}&q={{ search_query }}{% endif %}{% if status_filter %}&status={{ status_filter }}{% endif %}">
<i class="fas fa-angle-right"></i>
</a>
</li>
<li class="page-item">
<a class="page-link" href="?page={{ page_obj.paginator.num_pages }}{% if search_query %}&q={{ search_query }}{% endif %}{% if status_filter %}&status={{ status_filter }}{% endif %}">
<i class="fas fa-angle-double-right"></i>
</a>
</li>
{% endif %}
</ul>
</nav>
{% include "includes/paginator.html" %}
{% endif %}
{% else %}
<div class="text-center py-5">

View File

@ -1,7 +1,7 @@
{% extends 'portal_base.html' %}
{% load static i18n %}
{% block title %}{{ assignment.job.title }} - {{ assignment.agency.name }} - Agency Portal{% endblock %}
{% block title %}{{ assignment.job.title }} - {{ assignment.agency.name }} - {% trans "Agency Portal" %}{% endblock %}
{% block customCSS %}
<style>

View File

@ -1,7 +1,7 @@
{% extends 'portal_base.html' %}
{% load static i18n %}
{% block title %}{% trans "Agency Dashboard" %} - ATS{% endblock %}
{% block title %}{% trans "Agency Dashboard" %} -{% trans "Aagency Portal" %}{% endblock %}
{% block customCSS %}
<style>
:root {
@ -219,6 +219,7 @@
</div>
{% endfor %}
</div>
{% include "includes/paginator.html" %}
{% else %}
<div class="text-center py-5">
<i class="fas fa-briefcase fa-3x text-muted mb-3"></i>

View File

@ -1,7 +1,7 @@
{% extends 'portal_base.html' %}
{% load static i18n crispy_forms_tags %}
{% block title %}{% trans "Persons List" %} - ATS{% endblock %}
{% block title %}{% trans "Agency Applicant List" %} - ATS{% endblock %}
{% block customCSS %}
<style>
:root {

View File

@ -1,7 +1,7 @@
{% extends 'portal_base.html' %}
{% load static i18n crispy_forms_tags %}
{% block title %}{% trans "Submit Application" %} - {{ assignment.job.title }} - Agency Portal{% endblock %}
{% block title %}{% trans "Submit Application" %} - {{ assignment.job.title }} - {% trans "Agency Portal" %}{% endblock %}
{% block customCSS %}
<style>

View File

@ -292,7 +292,7 @@
{# Header: Larger, more dynamic on large screens. Stacks cleanly on mobile. #}
<div class="d-flex flex-column flex-md-row justify-content-between align-items-md-center mb-5">
<h1 class="display-6 display-md-5 fw-extrabold mb-3 mb-md-0" style="color: var(--kaauh-teal-dark);">
{% trans "Your Applicant Dashboard" %}
{% trans "Dashboard" %}
</h1>
{% comment %} <a href="#profile-details" data-bs-toggle="tab" class="btn btn-main-action btn-sm btn-md-lg px-4 py-2 rounded-pill shadow-sm shadow-md-lg">
<i class="fas fa-edit me-2"></i> {% trans "Update Profile" %}

View File

@ -1,7 +1,7 @@
{% extends "base.html" %}
{% load static i18n crispy_forms_tags %}
{% block title %}Create Application - {{ block.super }}{% endblock %}
{% block title %}{% trans "Create Application" %} - {{ block.super }}{% endblock %}
{% block customCSS %}
<style>

View File

@ -9,7 +9,7 @@
{% if LANGUAGE_CODE == 'ar' %}
<title>{{ application.resume_data_ar.full_name|default:"Application" }} - Application Profile</title>
{% else %}
<title>{{ application.resume_data_en.full_name|default:"Application" }} - Application Profile</title>
<title>{{ application.resume_data_en.full_name|default:"Application" }} - {% trans "Application Profile" %}</title>
{% endif %}
<!-- Use a modern icon set -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
@ -915,7 +915,7 @@
{% if LANGUAGE_CODE == 'ar' %}
{% with data_source=application.resume_data_ar analysis_source=application.analysis_data_ar %}
<div class="container container-fluid flex-grow-1" style="max-width: 1600px; margin: 0 auto; direction: rtl; text-align: right;">
<div class="container " style="max-width: 1600px; margin: 0 auto; direction: rtl; text-align: right;">
{% include 'recruitment/partials/ai_overview_breadcromb.html' %}
@ -1264,8 +1264,7 @@
{% else %}
{% with data_source=application.resume_data_en analysis_source=application.analysis_data_en %}
<div class="container container-fluid flex-grow-1" style="max-width: 1600px; margin: 0 auto;">
<div>
{% include 'recruitment/partials/ai_overview_breadcromb.html' %}
<header class="header-box">
<div class="header-info">

View File

@ -1,7 +1,7 @@
{% extends "base.html" %}
{% load static i18n crispy_forms_tags %}
{% block title %}Update {{ object.name }} - {{ block.super }}{% endblock %}
{% block title %}{% trans "Update" %} {{ object.name }} - {{ block.super }}{% endblock %}
{% block customCSS %}
<style>

View File

@ -1,7 +1,7 @@
{% extends 'base.html' %}
{% load static i18n %}
{% block title %}{% blocktrans %} Document Review - {{ job.title }} - University ATS{%endblocktrans %}{% endblock %}
{% block title %}{% blocktrans %} Document Review Stage - {{ job.title }} - University ATS{%endblocktrans %}{% endblock %}
{% block customCSS %}
<style>
/* KAAT-S UI Variables */

View File

@ -1,7 +1,7 @@
{% extends 'base.html' %}
{% load static i18n %}
{% block title %}{% blocktrans %}Application Tier Management - {{ job.title }} - ATS {% endblocktrans %}{% endblock %}
{% block title %}{% blocktrans %}Exam Stage- {{ job.title }} - ATS {% endblocktrans %}{% endblock %}
{% block customCSS %}
<style>
/* KAAT-S UI Variables */

View File

@ -1,7 +1,7 @@
{% extends 'base.html' %}
{% load static i18n %}
{% block title %}- {{ job.title }} - ATS{% endblock %}
{% block title %}{% trans "Hired Stage" %} {{ job.title }} - ATS{% endblock %}
{% block customCSS %}
<style>
/* KAAT-S UI Variables */

View File

@ -1,7 +1,7 @@
{% extends 'base.html' %}
{% load static i18n %}
{% block title %}{% blocktrans %}Application Tier Management - {{ job.title }} - ATS {% endblocktrans %}{% endblock %}
{% block title %}{% blocktrans %}Interview Stage - {{ job.title }} - ATS {% endblocktrans %}{% endblock %}
{% block customCSS %}
<style>
/* KAAT-S UI Variables */

View File

@ -1,7 +1,7 @@
{% extends "base.html" %}
{% load static i18n %}
{% block title %}Applications - {{ block.super }}{% endblock %}
{% block title %}{% trans "Applications" %} - {{ block.super }}{% endblock %}
{% block customCSS %}
<style>
@ -120,20 +120,7 @@
background-color: var(--kaauh-gray-light);
}
/* Pagination Link Styling (Consistent) */
.pagination .page-item .page-link {
color: var(--kaauh-teal-dark);
border-color: var(--kaauh-border);
}
.pagination .page-item.active .page-link {
background-color: var(--kaauh-teal);
border-color: var(--kaauh-teal);
color: white;
}
.pagination .page-item:hover .page-link:not(.active) {
background-color: #e9ecef;
}
/* Filter & Search Layout Adjustments */
.filter-buttons {
display: flex;

View File

@ -1,6 +1,6 @@
{% extends 'base.html' %}
{% load static i18n %}
{% block title %}{% blocktrans %}Application Tier Management - {{ job.title }} - ATS {% endblocktrans %}{% endblock %}
{% block title %}{% blocktrans %}Offer Stage - {{ job.title }} - ATS {% endblocktrans %}{% endblock %}
{% block customCSS %}
<style>
/* KAAT-S UI Variables */

View File

@ -1,7 +1,7 @@
{% extends 'base.html' %}
{% load static i18n %}
{% block title %}Application Management - {{ job.title }} - University ATS{% endblock %}
{% block title %} {% blocktrans %} Screening Stage - {{ job.title }} - University ATS{% endblocktrans %}{% endblock %}
{% block customCSS %}
<style>
/* KAAT-S UI Variables */

View File

@ -1,7 +1,7 @@
{% extends "base.html" %}
{% load static i18n %}
{% block title %}{% trans "Delete Candidate" %} - {{ block.super }}{% endblock %}
{% block title %}{% trans "Delete Applicant" %} - {{ block.super }}{% endblock %}
{% block customCSS %}
<style>
@ -176,14 +176,14 @@
<div>
<h1 class="h3 mb-1" style="color: var(--kaauh-teal-dark); font-weight: 700;">
<i class="fas fa-exclamation-triangle me-2"></i>
{% trans "Delete Candidate" %}
{% trans "Delete Applicant" %}
</h1>
<p class="text-muted mb-0">
{% trans "You are about to delete a candidate application. This action cannot be undone." %}
{% trans "You are about to delete an applicant's application. This action cannot be undone." %}
</p>
</div>
<a href="{% url 'candidate_detail' object.slug %}" class="btn btn-secondary">
<i class="fas fa-arrow-left me-1"></i> {% trans "Back to Candidate" %}
<i class="fas fa-arrow-left me-1"></i> {% trans "Back to Applicant" %}
</a>
</div>
@ -196,7 +196,7 @@
</div>
<h3 class="warning-title">{% trans "Warning: This action cannot be undone!" %}</h3>
<p class="warning-text">
{% trans "Deleting this candidate will permanently remove all associated data. Please review the information below carefully before proceeding." %}
{% trans "Deleting this applicant will permanently remove all associated data. Please review the information below carefully before proceeding." %}
</p>
</div>
@ -205,7 +205,7 @@
<div class="card-header bg-white border-bottom">
<h5 class="mb-0" style="color: var(--kaauh-teal-dark);">
<i class="fas fa-user me-2"></i>
{% trans "Candidate to be Deleted" %}
{% trans "Applicant to be Deleted" %}
</h5>
</div>
<div class="card-body">
@ -288,14 +288,14 @@
<div class="card-header bg-white border-bottom">
<h5 class="mb-0" style="color: var(--kaauh-teal-dark);">
<i class="fas fa-list me-2"></i>
{% trans "What will happen when you delete this candidate?" %}
{% trans "What will happen when you delete this applicant?" %}
</h5>
</div>
<div class="card-body">
<ul class="consequence-list">
<li>
<i class="fas fa-times-circle"></i>
{% trans "The candidate profile and all personal information will be permanently deleted" %}
{% trans "The applicant profile and all personal information will be permanently deleted" %}
</li>
<li>
<i class="fas fa-times-circle"></i>
@ -327,7 +327,7 @@
<div class="form-check">
<input class="form-check-input" type="checkbox" id="confirm_delete" name="confirm_delete" required>
<label class="form-check-label" for="confirm_delete">
<strong>{% trans "I understand that this action cannot be undone and I want to permanently delete this candidate." %}</strong>
<strong>{% trans "I understand that this action cannot be undone and I want to permanently delete this applicant." %}</strong>
</label>
</div>
</div>
@ -342,7 +342,7 @@
id="deleteButton"
disabled>
<i class="fas fa-trash me-2"></i>
{% trans "Delete Candidate Permanently" %}
{% trans "Delete Applicant Permanently" %}
</button>
</div>
</form>

View File

@ -1,15 +1,15 @@
{% extends "portal_base.html" %}
{% load static %}
{% block title %}Document Management{% endblock %}
{% load i18n %}
{% block title %}{% trans "Document Management" %}{% endblock %}
{% block content %}
<div class="container mx-auto px-4 py-8">
<div class="flex justify-between items-center mb-6">
<h1 class="text-2xl font-bold text-gray-900">Document Management</h1>
<h1 class="text-2xl font-bold text-gray-900">{% trans "Document Management" %}</h1>
<button onclick="showUploadModal()" class="bg-blue-600 hover:bg-blue-700 text-white px-4 py-2 rounded-lg font-medium transition-colors">
<i class="fas fa-upload mr-2"></i>
Upload Document
{% trans "Upload Document" %}
</button>
</div>
@ -21,22 +21,22 @@
<thead class="bg-gray-50">
<tr>
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
Document Type
{% trans "Document Type" %}
</th>
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
Description
{% trans "Description" %}
</th>
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
File Name
{% trans "File Name" %}
</th>
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
Size
{% trans "Size" %}
</th>
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
Uploaded
{% trans "Uploaded" %}
</th>
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
Actions
{% trans "Actions" %}
</th>
</tr>
</thead>
@ -88,7 +88,7 @@
{% empty %}
<tr>
<td colspan="6" class="px-6 py-4 text-center text-gray-500">
No documents uploaded yet.
{% trans "No documents uploaded yet." %}
</td>
</tr>
{% endfor %}
@ -105,7 +105,7 @@
<div class="bg-white px-4 pt-5 pb-4 sm:p-6 sm:pb-4">
<div class="flex justify-between items-start mb-4">
<h3 class="text-lg leading-6 font-medium text-gray-900">
Upload New Document
{% trans "Upload New Document" %}
</h3>
<button onclick="hideUploadModal()" class="text-gray-400 hover:text-gray-600">
<i class="fas fa-times"></i>
@ -115,26 +115,26 @@
{% csrf_token %}
<div class="space-y-4">
<div>
<label for="document_type" class="block text-sm font-medium text-gray-700">Document Type</label>
<label for="document_type" class="block text-sm font-medium text-gray-700">{% trans "Document Type" %}</label>
<select id="document_type" name="document_type" required
class="mt-1 block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500 sm:text-sm">
<option value="resume">Resume</option>
<option value="cover_letter">Cover Letter</option>
<option value="transcript">Transcript</option>
<option value="certificate">Certificate</option>
<option value="id_document">ID Document</option>
<option value="portfolio">Portfolio</option>
<option value="other">Other</option>
<option value="resume">{% trans "Resume" %}</option>
<option value="cover_letter">{% trans "Cover Letter" %}</option>
<option value="transcript">{% trans "Transcript" %}</option>
<option value="certificate">{% trans "Certificate" %}</option>
<option value="id_document">{% trans "ID Document" %}</option>
<option value="portfolio">{% trans "Portfolio" %}</option>
<option value="other">{% trans "Other" %}</option>
</select>
</div>
<div>
<label for="description" class="block text-sm font-medium text-gray-700">Description</label>
<label for="description" class="block text-sm font-medium text-gray-700">{% trans "Description" %}</label>
<textarea id="description" name="description" rows="3"
class="mt-1 block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500 sm:text-sm"
placeholder="Optional description of the document"></textarea>
</div>
<div>
<label for="file" class="block text-sm font-medium text-gray-700">File</label>
<label for="file" class="block text-sm font-medium text-gray-700">{% trans "File" %}</label>
<input type="file" id="file" name="file" required
class="mt-1 block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500 sm:text-sm"
accept=".pdf,.doc,.docx,.txt,.jpg,.jpeg,.png">
@ -143,11 +143,11 @@
<div class="bg-gray-50 px-4 py-3 sm:px-6 sm:flex sm:flex-row-reverse">
<button type="button" onclick="hideUploadModal()"
class="w-full inline-flex justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-red-600 text-base font-medium text-white hover:bg-red-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-red-500 sm:ml-3 sm:w-auto sm:text-sm">
Cancel
{% trans "Cancel" %}
</button>
<button type="submit"
class="w-full inline-flex justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-blue-600 text-base font-medium text-white hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 sm:ml-3 sm:w-auto sm:text-sm">
Upload Document
{% trans "Upload Document" %}
</button>
</div>
</form>
@ -167,7 +167,7 @@ function hideUploadModal() {
}
function deleteDocument(documentId) {
if (confirm('Are you sure you want to delete this document?')) {
if (confirm('{% trans "Are you sure you want to delete this document?" %}')) {
fetch(`/candidate/documents/${documentId}/delete/`, {
method: 'POST',
headers: {
@ -184,14 +184,14 @@ function deleteDocument(documentId) {
row.remove();
// Show success message
showNotification('Document deleted successfully!', 'success');
showNotification('{% trans "Document deleted successfully!" %}', 'success');
} else {
showNotification(data.error || 'Failed to delete document', 'error');
showNotification(data.error || '{% trans "Failed to delete document" %}', 'error');
}
})
.catch(error => {
console.error('Error:', error);
showNotification('An error occurred while deleting the document', 'error');
showNotification('{% trans "An error occurred while deleting the document" %}', 'error');
});
}
}
@ -217,12 +217,12 @@ document.getElementById('uploadForm').addEventListener('submit', function(e) {
// Reload the page to show the new document
window.location.reload();
} else {
showNotification(data.error || 'Failed to upload document', 'error');
showNotification(data.error || '{% trans "Failed to upload document" %}', 'error');
}
})
.catch(error => {
console.error('Error:', error);
showNotification('An error occurred while uploading the document', 'error');
showNotification('{% trans "An error occurred while uploading the document" %}', 'error');
});
});

View File

@ -1,7 +1,7 @@
{% extends 'portal_base.html' %}
{% load static i18n %}
{% block title %}{% trans "Candidate Dashboard" %} - ATS{% endblock %}
{% block title %}{% trans "Applicant Dashboard" %} - ATS{% endblock %}
{% block content %}
<div class="container-fluid">

View File

@ -1,6 +1,6 @@
<!-- templates/recruitment/interview_calendar.html -->
{% extends "base.html" %}
{% load static %}
{% load static i18n %}
{% block title %} {% trans "Interview Calendar" %} {% endblock %}
{% block customCSS %}
<link href="https://cdn.jsdelivr.net/npm/fullcalendar@5.10.1/main.min.css" rel="stylesheet">
@ -11,114 +11,79 @@
--calendar-hover: rgba(0, 99, 110, 0.2);
}
.calendar-header {
background-color: var(--calendar-color);
color: white;
padding: 1rem;
border-radius: 0.25rem;
margin-bottom: 1rem;
}
.calendar-container {
background-color: white;
border-radius: 0.25rem;
box-shadow: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.075);
padding: 1rem;
border-radius: 0.75rem;
box-shadow: 0 4px 12px rgba(0,0,0,0.06);
padding: 1.5rem;
border: 1px solid var(--kaauh-border);
}
.fc-toolbar-title {
color: var(--calendar-color) !important;
font-weight: 700 !important;
}
.fc-button-primary {
background-color: var(--calendar-color) !important;
border-color: var(--calendar-color) !important;
text-transform: capitalize;
}
.fc-button-primary:hover {
background-color: #004d56 !important;
border-color: #004d56 !important;
}
.fc-button-primary:not(:disabled):active, .fc-button-primary:not(:disabled).fc-button-active {
background-color: #003a40 !important;
border-color: #003a40 !important;
}
.fc-daygrid-day.fc-day-today {
background-color: var(--calendar-light) !important;
}
.fc-event-title {
font-weight: 500;
.fc-event {
cursor: pointer;
padding: 2px 5px;
border-radius: 4px;
}
.status-badge {
font-size: 0.75rem;
padding: 0.25rem 0.5rem;
padding: 0.25rem 0.6rem;
border-radius: 1rem;
font-weight: 600;
}
.status-scheduled {
background-color: #e3f2fd;
color: #0d47a1;
}
.status-confirmed {
background-color: #e8f5e9;
color: #1b5e20;
}
.status-cancelled {
background-color: #ffebee;
color: #b71c1c;
}
.status-completed {
background-color: #f5f5f5;
color: #424242;
}
.interview-details {
margin-top: 1rem;
}
.interview-details .card {
border-left: 4px solid var(--calendar-color);
}
.status-scheduled { background-color: #e3f2fd; color: #0d47a1; }
.status-confirmed { background-color: #e8f5e9; color: #1b5e20; }
.status-cancelled { background-color: #ffebee; color: #b71c1c; }
.status-completed { background-color: #f5f5f5; color: #424242; }
.calendar-legend {
display: flex;
flex-wrap: wrap;
gap: 1rem;
margin-top: 1rem;
padding: 0.75rem;
background-color: #f8f9fa;
border-radius: 0.25rem;
gap: 1.5rem;
margin-top: 1.5rem;
padding: 1rem;
background-color: #f9fbfd;
border-radius: 0.5rem;
border: 1px dashed #dee2e6;
}
.legend-item {
display: flex;
align-items: center;
gap: 0.5rem;
font-size: 0.85rem;
color: #495057;
}
.legend-color {
width: 1rem;
height: 1rem;
border-radius: 0.25rem;
width: 12px;
height: 12px;
border-radius: 50%;
}
</style>
{% endblock %}
{% block content %}
<div class="container mt-4">
<div class="calendar-header">
<div class="d-flex justify-content-between align-items-center">
<h1 class="h3 mb-0">Interview Calendar</h1>
<div>
<span class="h5">{{ job.title }}</span>
</div>
<div class="container-fluid">
<div class="d-flex justify-content-between align-items-center mb-4">
<div>
<h1 class="h3 mb-1 page-header">{% trans "Interview Calendar" %}</h1>
<p class="text-muted mb-0">{{ job.title|default:"Global Schedule" }}</p>
</div>
<div class="text-center">
<p class="text-muted small mb-0">Tenhal | تنحل</p>
</div>
</div>
@ -128,42 +93,44 @@
<div class="calendar-legend">
<div class="legend-item">
<div class="legend-color" style="background-color: #00636e;"></div>
<span>Scheduled</span>
<span>{% trans "Scheduled" %}</span>
</div>
<div class="legend-item">
<div class="legend-color" style="background-color: #00a86b;"></div>
<span>Confirmed</span>
<span>{% trans "Confirmed" %}</span>
</div>
<div class="legend-item">
<div class="legend-color" style="background-color: #e74c3c;"></div>
<span>Cancelled</span>
<span>{% trans "Cancelled" %}</span>
</div>
<div class="legend-item">
<div class="legend-color" style="background-color: #95a5a6;"></div>
<span>Completed</span>
<span>{% trans "Completed" %}</span>
</div>
</div>
</div>
<div class="interview-details" id="interview-details" style="display: none;">
<div class="card">
<div class="card-header d-flex justify-content-between align-items-center">
<h5 class="mb-0">Interview Details</h5>
<div class="interview-details mt-4" id="interview-details" style="display: none;">
<div class="card shadow-sm border-start border-4 border-info">
<div class="card-header bg-white d-flex justify-content-between align-items-center py-3">
<h5 class="mb-0 text-primary-theme"><i class="fas fa-info-circle me-2"></i>{% trans "Interview Details" %}</h5>
<button type="button" class="btn-close" id="close-details"></button>
</div>
<div class="card-body" id="interview-info">
<!-- Interview details will be loaded here -->
</div>
</div>
</div>
</div>
</div>
<!-- Include FullCalendar JS -->
{{ events|json_script:"calendar-events-data" }}
<script src="https://cdn.jsdelivr.net/npm/fullcalendar@5.10.1/main.min.js"></script>
<script>
document.addEventListener('DOMContentLoaded', function() {
var calendarEl = document.getElementById('calendar');
var events = {{ events|safe }};
// FIX: Parse the JSON script to avoid "None is not defined" error
const eventsData = JSON.parse(document.getElementById('calendar-events-data').textContent);
var calendar = new FullCalendar.Calendar(calendarEl, {
initialView: 'dayGridMonth',
@ -172,77 +139,64 @@ document.addEventListener('DOMContentLoaded', function() {
center: 'title',
right: 'dayGridMonth,timeGridWeek,timeGridDay'
},
events: events,
events: eventsData,
eventClick: function(info) {
// Prevent default browser behavior
info.jsEvent.preventDefault();
// Show interview details
showInterviewDetails(info.event);
},
eventMouseEnter: function(info) {
// Change cursor to pointer on hover
document.body.style.cursor = 'pointer';
},
eventMouseLeave: function() {
// Reset cursor
document.body.style.cursor = 'default';
},
height: 'auto',
aspectRatio: 2,
eventDisplay: 'block',
displayEventTime: true,
displayEventEnd: true,
});
calendar.render();
// Function to show interview details
function showInterviewDetails(event) {
const detailsContainer = document.getElementById('interview-details');
const infoContainer = document.getElementById('interview-info');
const statusClass = `status-${event.extendedProps.status}`;
const statusText = event.extendedProps.status.charAt(0).toUpperCase() + event.extendedProps.status.slice(1);
const status = event.extendedProps.status || 'scheduled';
const statusClass = `status-${status}`;
const statusText = status.charAt(0).toUpperCase() + status.slice(1);
let meetingInfo = '';
// FIX: Corrected translation tags and property checks
if (event.extendedProps.meeting_id) {
meetingInfo = `
<div class="mb-3">
<h6>Meeting Information</h6>
<p><strong>Meeting ID:</strong> ${event.extendedProps.meeting_id}</p>
<p><strong>Join URL:</strong> <a href="${event.extendedProps.join_url}" target="_blank">${event.extendedProps.join_url}</a></p>
<div class="mt-3 p-3 bg-light rounded border">
<h6 class="text-primary-theme"><i class="fas fa-video me-2"></i>{% trans "Meeting Information" %}</h6>
<p class="mb-1"><strong>{% trans "Meeting ID:" %}</strong> ${event.extendedProps.meeting_id}</p>
<p class="mb-0"><strong>{% trans "Join URL:" %}</strong> <a href="${event.extendedProps.join_url}" target="_blank" class="text-break">${event.extendedProps.join_url}</a></p>
</div>
`;
}
infoContainer.innerHTML = `
<div class="row">
<div class="col-md-6">
<h6>Candidate Information</h6>
<p><strong>Name:</strong> ${event.extendedProps.candidate}</p>
<p><strong>Email:</strong> ${event.extendedProps.email}</p>
<div class="col-md-6 border-end">
<h6 class="text-muted text-uppercase small fw-bold">{% trans "Candidate" %}</h6>
<p class="mb-1"><strong>{% trans "Name:" %}</strong> ${event.extendedProps.candidate}</p>
<p><strong>{% trans "Email:" %}</strong> ${event.extendedProps.email}</p>
</div>
<div class="col-md-6">
<h6>Interview Details</h6>
<p><strong>Date:</strong> ${event.start.toLocaleDateString()}</p>
<p><strong>Time:</strong> ${event.start.toLocaleTimeString([], {hour: '2-digit', minute:'2-digit'})} - ${event.end.toLocaleTimeString([], {hour: '2-digit', minute:'2-digit'})}</p>
<p><strong>Status:</strong> <span class="status-badge ${statusClass}">${statusText}</span></p>
<h6 class="text-muted text-uppercase small fw-bold">{% trans "Schedule" %}</h6>
<p class="mb-1"><strong>{% trans "Date:" %}</strong> ${event.start.toLocaleDateString()}</p>
<p class="mb-1"><strong>{% trans "Time:" %}</strong> ${event.start.toLocaleTimeString([], {hour: '2-digit', minute:'2-digit'})}</p>
<p><strong>{% trans "Status:" %}</strong> <span class="status-badge ${statusClass}">${statusText}</span></p>
</div>
</div>
${meetingInfo}
<div class="mt-3">
<a href="${event.url}" class="btn btn-primary btn-sm">View Full Details</a>
<div class="mt-4 pt-3 border-top">
<a href="${event.url}" class="btn btn-main-action btn-sm">
<i class="fas fa-external-link-alt me-1"></i> {% trans "View Full Application" %}
</a>
</div>
`;
detailsContainer.style.display = 'block';
// Scroll to details
detailsContainer.scrollIntoView({ behavior: 'smooth' });
detailsContainer.scrollIntoView({ behavior: 'smooth', block: 'start' });
}
// Close details button
document.getElementById('close-details').addEventListener('click', function() {
document.getElementById('interview-details').style.display = 'none';
});

View File

@ -1,8 +1,8 @@
<!-- templates/recruitment/interview_detail.html -->
{% extends "base.html" %}
{% load static %}
{% load static i18n %}
{% block extra_css %}
{% block customCSS %}
<style>
:root {
--calendar-color: #00636e;
@ -52,7 +52,7 @@
<div class="container mt-4">
<div class="detail-header">
<div class="d-flex justify-content-between align-items-center">
<h1 class="h3 mb-0">Interview Details</h1>
<h1 class="h3 mb-0">{% trans "Interview Details" %}</h1>
<div>
<span class="h5">{{ job.title }}</span>
</div>
@ -63,35 +63,35 @@
<div class="card-body">
<div class="row">
<div class="col-md-6">
<h5>Candidate Information</h5>
<h5>{% trans "Applicant Information"%}</h5>
<table class="table table-borderless">
<tr>
<td><strong>Name:</strong></td>
<td><strong>{% trans "Name:" %}</strong></td>
<td>{{ interview.candidate.name }}</td>
</tr>
<tr>
<td><strong>Email:</strong></td>
<td><strong>{% trans "Email:" %}</strong></td>
<td>{{ interview.candidate.email }}</td>
</tr>
<tr>
<td><strong>Phone:</strong></td>
<td><strong>{% trans "Phone:" %}</strong></td>
<td>{{ interview.candidate.phone|default:"Not provided" }}</td>
</tr>
</table>
</div>
<div class="col-md-6">
<h5>Interview Details</h5>
<h5>{% trans "Interview Details" %}</h5>
<table class="table table-borderless">
<tr>
<td><strong>Date:</strong></td>
<td><strong>{% trans "Date:" %}</strong></td>
<td>{{ interview.interview_date|date:"l, F j, Y" }}</td>
</tr>
<tr>
<td><strong>Time:</strong></td>
<td><strong>{% trans "Time:" %}</strong></td>
<td>{{ interview.interview_time|time:"g:i A" }}</td>
</tr>
<tr>
<td><strong>Status:</strong></td>
<td><strong>{% trans "Status:" %}</strong></td>
<td>
<span class="status-badge status-{{ interview.status }}">
{{ interview.status|title }}
@ -104,22 +104,22 @@
{% if interview.zoom_meeting %}
<div class="mt-4">
<h5>Meeting Information</h5>
<h5>{% trans "Meeting Information" %}</h5>
<table class="table table-borderless">
<tr>
<td><strong>Meeting ID:</strong></td>
<td><strong>{% trans "Meeting ID:" %}</strong></td>
<td>{{ interview.zoom_meeting.meeting_id }}</td>
</tr>
<tr>
<td><strong>Topic:</strong></td>
<td><strong>{% trans "Topic:" %}</strong></td>
<td>{{ interview.zoom_meeting.topic }}</td>
</tr>
<tr>
<td><strong>Duration:</strong></td>
<td>{{ interview.zoom_meeting.duration }} minutes</td>
<td><strong>{% trans "Duration:" %}</strong></td>
<td>{{ interview.zoom_meeting.duration }} {% trans "minutes" %}</td>
</tr>
<tr>
<td><strong>Join URL:</strong></td>
<td><strong>{% trans "Join URL:" %}</strong></td>
<td><a href="{{ interview.zoom_meeting.join_url }}" target="_blank">{{ interview.zoom_meeting.join_url }}</a></td>
</tr>
</table>
@ -129,16 +129,16 @@
<div class="mt-4">
<div class="d-flex gap-2">
<a href="{% url 'interview_calendar' slug=job.slug %}" class="btn btn-secondary">
<i class="fas fa-calendar"></i> Back to Calendar
<i class="fas fa-calendar"></i> {% trans "Back to Calendar" %}
</a>
{% if interview.status == 'scheduled' %}
<button class="btn btn-success">
<i class="fas fa-check"></i> Confirm Interview
<i class="fas fa-check"></i> {% trans "Confirm Interview" %}
</button>
{% endif %}
{% if interview.status != 'cancelled' and interview.status != 'completed' %}
<button class="btn btn-danger">
<i class="fas fa-times"></i> Cancel Interview
<i class="fas fa-times"></i> {% trans "Cancel Interview" %}
</button>
{% endif %}
</div>

View File

@ -1,3 +1,4 @@
{% load i18n %}
<span id="stageDisplay" class="badge bg-{% if candidate.stage == 'Applied' %}primary{% elif candidate.stage == 'Exam' %}info{% elif candidate.stage == 'Interview' %}warning{% elif candidate.stage == 'Offer' %}success{% else %}secondary{% endif %}">
Stage: {{ candidate.get_stage_display }}
{% trans "Stage:" %} {{ candidate.get_stage_display }}
</span>

View File

@ -1,11 +1,12 @@
<!-- Stage Update Form with Errors -->
{% load i18n %}
<form id="stageUpdateForm" hx-post="{% url 'application_update_stage' candidate.slug %}">
{% csrf_token %}
<!-- Stage Selection -->
<div class="mb-3">
<label for="id_stage" class="form-label">
<i class="fas fa-arrow-right me-1"></i>Move to Stage
<i class="fas fa-arrow-right me-1"></i>{% trans "Move to Stage" %}
</label>
<select name="stage" id="id_stage" class="form-select {% if form.stage.errors %}is-invalid{% endif %}">
{% for value, label in form.stage.field.choices %}
@ -30,10 +31,10 @@
<!-- Form Actions -->
<div class="d-flex justify-content-between mt-4">
<button type="button" class="btn btn-secondary">
<i class="fas fa-arrow-left me-1"></i>Cancel
<i class="fas fa-arrow-left me-1"></i>{% trans "Cancel" %}
</button>
<button type="submit" class="btn btn-primary" id="stageUpdateSubmit">
<i class="fas fa-save me-1"></i>Update Stage
<i class="fas fa-save me-1"></i>{% trans "Update Stage" %}
</button>
</div>
</form>

View File

@ -1,7 +1,7 @@
<div id="availableStagesInfo" class="alert alert-success">
<div class="d-flex align-items-center">
<i class="fas fa-check-circle me-2"></i>
<strong>Success:</strong>
<strong>{% trans "Success:" %}</strong>
<span class="ms-2">{{ message }}</span>
</div>
</div>

View File

@ -1,4 +1,4 @@
{%load i18n %}
{% load i18n %}
{# -------------------------------------------------------------------------- #}
{# STATS CARDS SECTION (12 KPIs) #}
{# -------------------------------------------------------------------------- #}
@ -40,14 +40,14 @@
<div class="stat-caption">{% trans "Total Slots to be Filled " %}</div>
</div>
{# GLOBAL - 5. Total Participants #}
{% comment %} {# GLOBAL - 5. Total Participants #}
<div class="card">
<div class="card-header">
<h3><i class="fas fa-address-book stat-icon"></i> {% trans "Total Participants" %}</h3>
</div>
<div class="stat-value">{{ total_participants }}</div>
<div class="stat-caption">{% trans "Total Recruiters/Interviewers" %}</div>
</div>
</div> {% endcomment %}
{# GLOBAL - 6. Total LinkedIn Posts #}
{% comment %} <div class="card">

View File

@ -1,17 +1,18 @@
{% extends "base.html" %}
{% load i18n %}
{% load widget_tweaks %}
{% block title %}{{ title }}{% endblock %}
{% block content %}
<div class="container-fluid py-4">
<div class="row">
<div class="col-12">
<!-- Breadcrumb Navigation -->
<nav aria-label="breadcrumb">
<ol class="breadcrumb">
<li class="breadcrumb-item">
<a href="{% url 'settings_list' %}" class="text-decoration-none text-secondary">
<i class="fas fa-cog me-1"></i> Settings
<i class="fas fa-cog me-1"></i> {% trans "Settings" %}
</a>
</li>
<li class="breadcrumb-item">
@ -20,62 +21,58 @@
</a>
</li>
<li class="breadcrumb-item active" aria-current="page"
style="color: #F43B5E; font-weight: 600;">Delete</li>
style="color: #F43B5E; font-weight: 600;">{% trans "Delete" %}</li>
</ol>
</nav>
<!-- Header -->
<div class="d-flex justify-content-between align-items-center mb-4">
<h1 class="h3 mb-0 text-primary-theme">
<i class="fas fa-exclamation-triangle me-2"></i>
{{ title }}
</h1>
<a href="{{ cancel_url }}" class="btn btn-outline-secondary">
<i class="fas fa-arrow-left me-1"></i> Cancel
<i class="fas fa-arrow-left me-1"></i> {% trans "Cancel" %}
</a>
</div>
<!-- Warning Card -->
<div class="card shadow-sm border-danger">
<div class="card-body">
{% if messages %}
{% for message in messages %}
<div class="alert alert-{{ message.tags }} alert-dismissible fade show" role="alert">
{{ message }}
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="{% trans 'Close' %}"></button>
</div>
{% endfor %}
{% endif %}
<!-- Warning Alert -->
<div class="alert alert-warning d-flex align-items-center" role="alert">
<i class="fas fa-exclamation-triangle me-3 fa-lg"></i>
<div>
<strong>Warning:</strong> This action cannot be undone. Deleting this setting will permanently remove it from the system.
<strong>{% trans "Warning:" %}</strong> {% trans "This action cannot be undone. Deleting this setting will permanently remove it from the system." %}
</div>
</div>
<!-- Setting Details -->
<div class="row">
<div class="col-12">
<h5 class="mb-3">Setting Details:</h5>
<h5 class="mb-3">{% trans "Setting Details:" %}</h5>
<div class="table-responsive">
<table class="table table-hover">
<tbody>
<tr>
<th style="width: 150px;" class="text-primary-theme">Key:</th>
<th style="width: 150px;" class="text-primary-theme">{% trans "Key:" %}</th>
<td><code class="text-primary-theme">{{ setting.key }}</code></td>
</tr>
<tr>
<th class="text-primary-theme">Value:</th>
<th class="text-primary-theme">{% trans "Value:" %}</th>
<td>{{ setting.value|default:"-" }}</td>
</tr>
<tr>
<th class="text-primary-theme">Description:</th>
<td>{{ setting.description|default:"No description" }}</td>
<th class="text-primary-theme">{% trans "Description:" %}</th>
<td>{{ setting.description|default:_("No description") }}</td>
</tr>
<tr>
<th class="text-primary-theme">Type:</th>
<th class="text-primary-theme">{% trans "Type:" %}</th>
<td>
<span class="badge bg-primary-theme text-white">
{{ setting.get_type_display }}
@ -83,12 +80,12 @@
</td>
</tr>
<tr>
<th class="text-primary-theme">Status:</th>
<th class="text-primary-theme">{% trans "Status:" %}</th>
<td>
{% if setting.is_active %}
<span class="badge bg-primary-theme text-white">Active</span>
<span class="badge bg-primary-theme text-white">{% trans "Active" %}</span>
{% else %}
<span class="badge bg-secondary text-white">Inactive</span>
<span class="badge bg-secondary text-white">{% trans "Inactive" %}</span>
{% endif %}
</td>
</tr>
@ -98,17 +95,16 @@
</div>
</div>
<!-- Action Buttons -->
<div class="row mt-4">
<div class="col-12">
<form method="post" onsubmit="return confirm('Are you absolutely sure you want to delete this setting? This action cannot be undone.');">
<form method="post" onsubmit="return confirm('{% trans "Are you absolutely sure you want to delete this setting? This action cannot be undone." %}');">
{% csrf_token %}
<div class="d-flex gap-2">
<button type="submit" class="btn btn-danger">
<i class="fas fa-trash me-1"></i> Yes, Delete Setting
<i class="fas fa-trash me-1"></i> {% trans "Yes, Delete Setting" %}
</button>
<a href="{{ cancel_url }}" class="btn btn-outline-secondary">
<i class="fas fa-times me-1"></i> Cancel
<i class="fas fa-times me-1"></i> {% trans "Cancel" %}
</a>
</div>
</form>
@ -116,8 +112,7 @@
</div>
</div>
</div>
</div>
</div>
</div>
</div>
{% endblock %}
{% endblock %}

View File

@ -1,4 +1,5 @@
{% extends "base.html" %}
{% load i18n %}
{% load crispy_forms_tags %}
{% block title %}{{ title }}{% endblock %}
@ -122,7 +123,7 @@
<ol class="breadcrumb">
<li class="breadcrumb-item">
<a href="{% url 'settings_list' %}" class="text-decoration-none text-secondary">
<i class="fas fa-cog me-1"></i> Settings
<i class="fas fa-cog me-1"></i>{% trans "Settings" %}
</a>
</li>
{% if setting %}
@ -132,10 +133,10 @@
</a>
</li>
<li class="breadcrumb-item active" aria-current="page"
style="color: #F43B5E; font-weight: 600;">Update</li>
style="color: #F43B5E; font-weight: 600;">{% trans "Update" %}</li>
{% else %}
<li class="breadcrumb-item active" aria-current="page"
style="color: #F43B5E; font-weight: 600;">Create</li>
style="color: #F43B5E; font-weight: 600;">{% trans "Create" %}</li>
{% endif %}
</ol>
</nav>
@ -148,14 +149,14 @@
<div class="d-flex gap-2">
{% if setting %}
<a href="{% url 'settings_detail' setting.pk %}" class="btn btn-outline-secondary">
<i class="fas fa-eye me-1"></i> View Details
<i class="fas fa-eye me-1"></i> {% trans "View Details" %}
</a>
<a href="{% url 'settings_delete' setting.pk %}" class="btn btn-danger">
<i class="fas fa-trash me-1"></i> Delete
<i class="fas fa-trash me-1"></i> {% trans "Delete" %}
</a>
{% endif %}
<a href="{% url 'settings_list' %}" class="btn btn-outline-secondary">
<i class="fas fa-arrow-left me-1"></i> Back to List
<i class="fas fa-arrow-left me-1"></i> {% trans "Back to List" %}
</a>
</div>
</div>
@ -166,7 +167,7 @@
{% if form.non_field_errors %}
<div class="alert alert-danger" role="alert">
<h5 class="alert-heading">
<i class="fas fa-exclamation-triangle me-2"></i>Error
<i class="fas fa-exclamation-triangle me-2"></i>{% trans "Error" %}
</h5>
{% for error in form.non_field_errors %}
<p class="mb-0">{{ error }}</p>
@ -191,7 +192,7 @@
<i class="fas fa-save me-1"></i> {{ button_text }}
</button>
<a href="{% url 'settings_list' %}" class="btn btn-outline-secondary">
<i class="fas fa-times me-1"></i> Cancel
<i class="fas fa-times me-1"></i> {% trans "Cancel" %}
</a>
</div>
</form>

View File

@ -1,5 +1,5 @@
{% extends "base.html" %}
{% load static %}
{% load static i18n %}
{% block title %}{{ title }}{% endblock %}
@ -10,7 +10,7 @@
<div class="d-flex justify-content-between align-items-center mb-4">
<h4 class="h3 mb-0">{{ title }}</h4>
<a href="{% url 'source_detail' source.pk %}" class="btn btn-outline-secondary">
<i class="fas fa-arrow-left"></i> Back to Source
<i class="fas fa-arrow-left"></i> {% trans "Back to Source" %}
</a>
</div>
@ -21,40 +21,40 @@
<div class="alert alert-warning d-flex align-items-center">
<i class="fas fa-exclamation-triangle fa-2x me-3"></i>
<div>
<strong>Warning:</strong> This action cannot be undone.
Deleting this source will also remove all associated integration logs and API credentials.
<strong>{% trans "Warning:" %}</strong> {% trans "This action cannot be undone.
Deleting this source will also remove all associated integration logs and API credentials." %}
</div>
</div>
<div class="mb-4">
<h5>Source to be deleted:</h5>
<h5>{% trans "Source to be deleted:" %}</h5>
<div class="card bg-light">
<div class="card-body">
<div class="row">
<div class="col-md-6">
<strong>Name:</strong><br>
<strong>{% trans "Name:" %}</strong><br>
{{ source.name }}
</div>
<div class="col-md-6">
<strong>Type:</strong><br>
<strong>{% trans "Type:" %}</strong><br>
<span class="badge bg-info">{{ source.get_source_type_display }}</span>
</div>
</div>
{% if source.description %}
<hr>
<div>
<strong>Description:</strong><br>
<strong>{% trans "Description:" %}</strong><br>
{{ source.description|linebreaks }}
</div>
{% endif %}
<hr>
<div class="row">
<div class="col-md-6">
<strong>Created:</strong><br>
<strong>{% trans "Created:" %}</strong><br>
{{ source.created_at|date:"M d, Y H:i" }}
</div>
<div class="col-md-6">
<strong>Total API Calls:</strong><br>
<strong>{% trans "Total API Calls:" %}</strong><br>
{{ source.integration_logs.count }}
</div>
</div>
@ -66,10 +66,10 @@
{% csrf_token %}
<div class="d-flex justify-content-between">
<a href="{{ cancel_url }}" class="btn btn-outline-secondary">
<i class="fas fa-times"></i> Cancel
<i class="fas fa-times"></i> {% trans "Cancel" %}
</a>
<button type="submit" class="btn btn-danger">
<i class="fas fa-trash"></i> Delete Source
<i class="fas fa-trash"></i>{% trans "Delete Source" %}
</button>
</div>
</form>
@ -80,25 +80,25 @@
<div class="col-md-4">
<div class="card">
<div class="card-header">
<h6 class="mb-0">Impact Summary</h6>
<h6 class="mb-0">{% trans "Impact Summary" %}</h6>
</div>
<div class="card-body">
<div class="mb-3">
<label class="form-label text-muted">Integration Logs</label>
<label class="form-label text-muted">{% trans "Integration Logs" %}</label>
<div class="h5 mb-0 text-danger">
{{ source.integration_logs.count }} will be deleted
{{ source.integration_logs.count }} {% trans "will be deleted" %}
</div>
</div>
<div class="mb-3">
<label class="form-label text-muted">API Credentials</label>
<label class="form-label text-muted">{% trans "API Credentials" %}</label>
<div class="h5 mb-0 text-danger">
API Key & Secret will be permanently lost
{% trans "API Key & Secret will be permanently lost" %}
</div>
</div>
<div class="mb-3">
<label class="form-label text-muted">Active Integrations</label>
<label class="form-label text-muted">{% trans "Active Integrations" %}</label>
<div class="h5 mb-0 text-warning">
Any systems using this API will lose access
{% trans "Any systems using this API will lose access" %}
</div>
</div>
</div>

View File

@ -4,6 +4,7 @@
{% block title %}{% trans "Sources" %}{% endblock %}
{% block content %}
<div class="container-fluid">
<nav aria-label="breadcrumb">
<ol class="breadcrumb">
@ -16,7 +17,7 @@
</nav>
<div class="row">
<div class="col-12">
<div class="d-flex justify-content-between align-items-center mb-4">
<div class="d-flex justify-content-between align-items-center mb-4">
<h1 class="h3 mb-0">{% trans "Integration Sources" %}</h1>
<a href="{% url 'source_create' %}" class="btn btn-main-action">
{% trans "Create Source for Integration" %} <i class="fas fa-plus"></i>
@ -74,6 +75,7 @@
</tr>
</thead>
<tbody>
{% for source in page_obj %}
<tr>
<td>
@ -129,7 +131,7 @@
<!-- Pagination -->
{% include "includes/paginator.html" %}
{% include "includes/paginator.html" %}
{% else %}
<div class="text-center py-5">
<i class="fas fa-database fa-3x text-muted mb-3"></i>
@ -153,7 +155,7 @@
</div>
{% endblock %}
{% block extra_js %}
{% block customJS %}
<script>
// Auto-refresh after status toggle
document.body.addEventListener('htmx:afterRequest', function(evt) {

View File

@ -1,143 +0,0 @@
{% extends "base.html" %}
{% load static i18n crispy_forms_tags %}
{% block title %}Create Training Material - {{ block.super }}{% endblock %}
{% block customCSS %}
<style>
/* ================================================= */
/* THEME VARIABLES AND GLOBAL STYLES */
/* (Your existing CSS is kept here) */
/* ================================================= */
:root {
--kaauh-teal: #00636e;
--kaauh-teal-dark: #004a53;
--kaauh-border: #eaeff3;
--kaauh-primary-text: #343a40;
}
/* Primary Color Overrides */
.text-primary { color: var(--kaauh-teal) !important; }
/* Main Action Button Style */
.btn-main-action{
background-color: gray; /* Changed to primary teal for main actions */
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); /* Darker on hover */
border-color: var(--kaauh-teal-dark);
transform: translateY(-1px);
box-shadow: 0 4px 8px rgba(0,0,0,0.15);
}
/* Outlined Button Styles */
.btn-secondary, .btn-outline-secondary {
background-color: #f8f9fa;
color: var(--kaauh-teal-dark);
border: 1px solid var(--kaauh-teal);
font-weight: 500;
}
.btn-secondary:hover, .btn-outline-secondary:hover {
background-color: var(--kaauh-teal-dark);
color: white;
border-color: var(--kaauh-teal-dark);
}
/* Card enhancements */
.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;
}
/* Colored Header Card */
.list-header-card {
background: linear-gradient(135deg, var(--kaauh-teal), #004d57);
color: white;
border-radius: 0.75rem 0.75rem 0 0;
padding: 1.5rem;
box-shadow: 0 4px 10px rgba(0,0,0,0.15);
}
.list-header-card h1 {
font-weight: 700;
margin: 0;
font-size: 1.8rem;
}
.heroicon {
width: 1.25rem;
height: 1.25rem;
vertical-align: text-bottom;
stroke: currentColor;
margin-right: 0.5rem;
}
/* ================================================= */
/* CLEANUP: CRISPY FORMS STYLES (Kept to ensure good defaults) */
/* ================================================= */
.card-body .form-group .form-label {
font-weight: 600;
color: var(--kaauh-primary-text);
margin-bottom: 0.5rem;
}
</style>
{% endblock %}
{% block content %}
<div class="container-fluid py-4">
<div class="card mb-4">
<div class="list-header-card">
<div class="d-flex justify-content-between align-items-start flex-wrap">
<div class="flex-grow-1">
<h1 class="h3 mb-1">
<i class="fas fa-file-upload"></i>
{% trans "Create New Training Material" %}
</h1>
<p class="text-white opacity-75 mb-0">{% trans "Upload a new document or guide for your team." %}</p>
</div>
<div class="d-flex gap-2 mt-1">
<a href="{% url 'training_list' %}" class="btn btn-outline-light btn-sm" title="{% trans 'Back to List' %}">
<i class="fas fa-arrow-left"></i>
<span class="d-none d-sm-inline">{% trans "Back to List" %}</span>
</a>
</div>
</div>
</div>
</div>
<div class="card shadow-sm">
<div class="card-header bg-white border-bottom">
<h2 class="h5 mb-0 text-primary">
<i class="fas fa-book me-1"></i>
{% trans "Material Details" %}
</h2>
</div>
<div class="card-body">
<form method="post" enctype="multipart/form-data" class="row">
{% csrf_token %}
{% crispy form %}
{# Add the main action button here for consistency #}
</form>
</div>
</div>
</div>
{% endblock %}

View File

@ -1,33 +0,0 @@
{% extends "base.html" %}
{% load static %}
{% block title %}Delete Training Material - {{ block.super }}{% endblock %}
{% block content %}
<div class="card">
<div class="card-header">
<h1>
<svg class="heroicon" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"></path>
</svg>
Delete Training Material: {{ object.title }}
</h1>
<a href="{% url 'training_list' %}" class="btn btn-secondary">Back to List</a>
</div>
<p>Are you sure you want to delete the training material "{{ object.title }}" created on {{ object.created_at|date:"M d, Y" }}? This action cannot be undone.</p>
{% if object.file %}
<p><strong>File:</strong> {{ object.file.name }}</p>
{% endif %}
{% if object.video_link %}
<p><strong>Video Link:</strong> {{ object.video_link }}</p>
{% endif %}
<form method="post">
{% csrf_token %}
<svg class="heroicon" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"></path>
</svg>
Yes, Delete Material
</button>
</form>
</div>
{% endblock %}

View File

@ -1,285 +0,0 @@
{% extends "base.html" %}
{% load static i18n %}
{% block title %}{% trans "Training Materials" %} - {{ block.super }}{% endblock %}
{% block customCSS %}
<style>
/* UI Variables for the KAAT-S Theme (Consistent with Reference) */
:root {
--kaauh-teal: #00636e;
--kaauh-teal-dark: #004a53;
--kaauh-border: #eaeff3;
--kaauh-primary-text: #343a40;
--kaauh-gray-light: #f8f9fa; /* Added for hover/background consistency */
}
/* Enhanced Card Styling (Consistent) */
.card {
border: 1px solid var(--kaauh-border);
border-radius: 0.75rem;
box-shadow: 0 4px 12px rgba(0,0,0,0.06);
transition: transform 0.2s, box-shadow 0.2s;
background-color: white;
}
/* Standard card hover effect for list items */
.card:not(.no-hover):hover {
transform: translateY(-2px);
box-shadow: 0 6px 16px rgba(0,0,0,0.1);
}
.card.no-hover:hover {
transform: none;
box-shadow: 0 4px 12px rgba(0,0,0,0.06);
}
/* Main Action Button Style (Teal Theme) */
.btn-main-action {
background-color: var(--kaauh-teal);
border-color: var(--kaauh-teal);
color: white;
font-weight: 600;
transition: all 0.2s ease;
display: inline-flex;
align-items: center;
gap: 0.4rem;
padding: 0.5rem 1rem;
}
.btn-main-action:hover {
background-color: var(--kaauh-teal-dark);
border-color: var(--kaauh-teal-dark);
box-shadow: 0 4px 8px rgba(0,0,0,0.15);
}
/* Secondary Button Style (For Edit/Outline - Consistent) */
.btn-outline-secondary {
color: var(--kaauh-teal-dark);
border-color: var(--kaauh-teal);
}
.btn-outline-secondary:hover {
background-color: var(--kaauh-teal-dark);
color: white;
border-color: var(--kaauh-teal-dark);
}
.btn-outline-primary {
color: var(--kaauh-teal);
border-color: var(--kaauh-teal);
}
.btn-outline-primary:hover {
background-color: var(--kaauh-teal);
color: white;
}
/* Training Card Specifics */
.training-card .card-title {
color: var(--kaauh-teal-dark);
font-weight: 600;
font-size: 1.15rem;
}
/* Table Styling (Consistent with Reference) */
.table-view .table thead th {
background-color: var(--kaauh-teal-dark); /* Dark header background */
color: white;
font-weight: 600;
border-color: var(--kaauh-border);
text-transform: uppercase;
font-size: 0.8rem;
letter-spacing: 0.5px;
padding: 1rem;
}
.table-view .table tbody td {
vertical-align: middle;
padding: 1rem;
border-color: var(--kaauh-border);
}
.table-view .table tbody tr:hover {
background-color: var(--kaauh-gray-light);
}
/* Pagination Link Styling (Consistent) */
.pagination .page-item .page-link {
color: var(--kaauh-teal-dark);
border-color: var(--kaauh-border);
}
.pagination .page-item.active .page-link {
background-color: var(--kaauh-teal);
border-color: var(--kaauh-teal);
color: white;
}
.pagination .page-item:hover .page-link:not(.active) {
background-color: #e9ecef;
}
/* Filter & Search Layout Adjustments */
.filter-buttons {
display: flex;
gap: 0.5rem;
}
/* Override for the primary text color being used for card-titles in table */
.text-primary { color: var(--kaauh-teal) !important; }
</style>
{% endblock %}
{% block content %}
<div class="container-fluid py-4">
<div class="d-flex justify-content-between align-items-center mb-4">
<h1 style="color: var(--kaauh-teal-dark); font-weight: 700;">
<i class="fas fa-graduation-cap me-2"></i> {% trans "Training Materials" %}
</h1>
{% if user.is_authenticated %}
<a href="{% url 'training_create' %}" class="btn btn-main-action">
<i class="fas fa-plus me-1"></i> {% trans "Add New Material" %}
</a>
{% endif %}
</div>
<div class="card mb-4 shadow-sm no-hover">
<div class="card-body">
<div class="row">
<div class="col-md-6">
<label for="search" class="form-label small text-muted">{% trans "Search by Title or Creator" %}</label>
<div class="input-group input-group-lg mb-3">
<form method="get" action="" class="w-100">
{% include "includes/search_form.html" with search_query=search_query %}
</form>
</div>
</div>
<div class="col-md-6 d-flex align-items-end">
{# Additional Filters can go here if needed #}
</div>
</div>
</div>
</div>
{% if materials %}
<div id="training-materials-list">
{# View Switcher - list_id must match the container ID #}
{% include "includes/_list_view_switcher.html" with list_id="training-materials-list" %}
{# Card View (Default) - Must have 'row' class for grid layout #}
<div class="card-view active row">
{% for material in materials %}
<div class="col-md-6 col-lg-4 mb-4">
<div class="card training-card h-100 shadow-sm">
<div class="card-body d-flex flex-column">
<div class="d-flex justify-content-between align-items-start mb-2">
<h5 class="card-title flex-grow-1 me-3">{{ material.title }}</h5>
</div>
<p class="card-text text-muted small">
<i class="fas fa-user-edit"></i> {% trans "Created By" %}: {{ material.created_by.username|default:"Anonymous" }}<br>
<i class="fas fa-calendar-alt"></i> {% trans "Created On" %}: {{ material.created_at|date:"M d, Y" }}
</p>
<div class="mt-auto pt-2 border-top">
<div class="d-flex gap-2">
<a href="{% url 'training_detail' material.pk %}" class="btn btn-sm btn-main-action">
<i class="fas fa-eye"></i> {% trans "View" %}
</a>
{% if user.is_authenticated and material.created_by == user %}
<a href="{% url 'training_update' material.pk %}" class="btn btn-sm btn-outline-secondary">
<i class="fas fa-edit"></i> {% trans "Edit" %}
</a>
<button type="button" class="btn btn-outline-danger btn-sm" title="{% trans 'Delete' %}"
data-bs-toggle="modal" data-bs-target="#deleteModal" {# Assuming standard Bootstrap modal trigger #}
data-delete-url="{% url 'training_delete' material.pk %}"
data-item-name="{{ material.title }}">
<i class="fas fa-trash-alt"></i>
</button>
{% endif %}
</div>
</div>
</div>
</div>
</div>
{% endfor %}
</div>
{# Table View #}
<div class="table-view">
<div class="table-responsive">
<table class="table table-hover align-middle mb-0">
<thead>
<tr>
<th scope="col" style="width: 40%;">{% trans "Title" %}</th>
<th scope="col" style="width: 30%;">{% trans "Created By" %}</th>
<th scope="col" style="width: 20%;">{% trans "Created" %}</th>
<th scope="col" style="width: 10%;" class="text-end">{% trans "Actions" %}</th>
</tr>
</thead>
<tbody>
{% for material in materials %}
<tr>
<td><strong class="text-primary">{{ material.title }}</strong></td>
<td>{{ material.created_by.username|default:"Anonymous" }}</td>
<td>{{ material.created_at|date:"M d, Y" }}</td>
<td class="text-end">
<div class="btn-group btn-group-sm" role="group">
<a href="{% url 'training_detail' material.pk %}" class="btn btn-outline-primary" title="{% trans 'View' %}">
<i class="fas fa-eye"></i>
</a>
{% if user.is_authenticated and material.created_by == user %}
<a href="{% url 'training_update' material.pk %}" class="btn btn-outline-secondary" title="{% trans 'Edit' %}">
<i class="fas fa-edit"></i>
</a>
<button type="button" class="btn btn-outline-danger" title="{% trans 'Delete' %}"
data-bs-toggle="modal" data-bs-target="#deleteModal"
data-delete-url="{% url 'training_delete' material.pk %}"
data-item-name="{{ material.title }}">
<i class="fas fa-trash-alt"></i>
</button>
{% endif %}
</div>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
</div>
{# Pagination (Standardized to Reference) #}
{% if is_paginated %}
<nav aria-label="Page navigation" class="mt-4">
<ul class="pagination justify-content-center">
{% if page_obj.has_previous %}
<li class="page-item">
<a class="page-link" href="?page=1{% if search_query %}&q={{ search_query }}{% endif %}">First</a>
</li>
<li class="page-item">
<a class="page-link" href="?page={{ page_obj.previous_page_number }}{% if search_query %}&q={{ search_query }}{% endif %}">Previous</a>
</li>
{% endif %}
<li class="page-item active">
<span class="page-link">{{ page_obj.number }} of {{ page_obj.paginator.num_pages }}</span>
</li>
{% if page_obj.has_next %}
<li class="page-item">
<a class="page-link" href="?page={{ page_obj.next_page_number }}{% if search_query %}&q={{ search_query }}{% endif %}">Next</a>
</li>
<li class="page-item">
<a class="page-link" href="?page={{ page_obj.paginator.num_pages }}{% if search_query %}&q={{ search_query }}{% endif %}">Last</a>
</li>
{% endif %}
</ul>
</nav>
{% endif %}
{% else %}
<div class="text-center py-5 card shadow-sm">
<div class="card-body">
<i class="fas fa-graduation-cap fa-3x mb-3" style="color: var(--kaauh-teal-dark);"></i>
<h3>{% trans "No training materials found" %}</h3>
<p class="text-muted">{% trans "It looks like there are no materials yet. Start by adding one!" %}</p>
{% if user.is_authenticated %}
<a href="{% url 'training_create' %}" class="btn btn-main-action mt-3">
<i class="fas fa-plus me-1"></i> {% trans "Create Your First Material" %}
</a>
{% endif %}
</div>
</div>
{% endif %}
</div>
{% endblock %}

View File

@ -1,191 +0,0 @@
{% extends "base.html" %}
{% load static i18n crispy_forms_tags %}
{% block title %}Update Training Material - {{ block.super }}{% endblock %}
{% block customCSS %}
<style>
/* ================================================= */
/* THEME VARIABLES AND GLOBAL STYLES */
/* ================================================= */
:root {
--kaauh-teal: #00636e;
--kaauh-teal-dark: #004a53;
--kaauh-border: #eaeff3;
--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);
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);
}
/* Outlined Button Styles */
.btn-secondary, .btn-outline-secondary {
background-color: #f8f9fa;
color: var(--kaauh-teal-dark);
border: 1px solid var(--kaauh-teal);
font-weight: 500;
}
.btn-secondary:hover, .btn-outline-secondary:hover {
background-color: var(--kaauh-teal-dark);
color: white;
border-color: var(--kaauh-teal-dark);
}
/* Card enhancements */
.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;
}
/* Colored Header Card */
.list-header-card {
background: linear-gradient(135deg, var(--kaauh-teal), #004d57);
color: white;
border-radius: 0.75rem 0.75rem 0 0;
padding: 1.5rem;
box-shadow: 0 4px 10px rgba(0,0,0,0.15);
}
.list-header-card h1 {
font-weight: 700;
margin: 0;
font-size: 1.8rem;
}
.heroicon {
width: 1.25rem;
height: 1.25rem;
vertical-align: text-bottom;
stroke: currentColor;
margin-right: 0.5rem;
}
/* Form Fixes for consistency with manual rendering */
.form-label {
font-weight: 600;
color: var(--kaauh-primary-text);
margin-bottom: 0.3rem; /* Small space under label */
display: block; /* Ensure full width for label */
}
/* Ensure form fields use Bootstrap's standard look */
.form-control {
border-radius: 0.5rem;
}
</style>
{% endblock %}
{% block content %}
<div class="container-fluid py-4">
<div class="card mb-4">
<div class="list-header-card">
<div class="d-flex justify-content-between align-items-start flex-wrap">
<div class="flex-grow-1">
<h1 class="h3 mb-1">
<i class="fas fa-edit"></i>
{% trans "Update Training Material:" %} {{ object.title }}
</h1>
<p class="text-white opacity-75 mb-0">{% trans "Edit the details of this training document or guide." %}</p>
</div>
<div class="d-flex gap-2 mt-1">
<a href="{% url 'training_list' %}" class="btn btn-outline-light btn-sm" title="{% trans 'Back to List' %}">
<i class="fas fa-arrow-left"></i>
<span class="d-none d-sm-inline">{% trans "Back to List" %}</span>
</a>
{% if object.pk %}
<a href="{% url 'training_detail' object.pk %}" class="btn btn-outline-light btn-sm" title="{% trans 'View Material' %}">
<i class="fas fa-eye"></i>
<span class="d-none d-sm-inline">{% trans "View" %}</span>
</a>
{% endif %}
</div>
</div>
</div>
</div>
<div class="card shadow-sm">
<div class="card-header bg-white border-bottom">
<h2 class="h5 mb-0 text-primary">
<i class="fas fa-file-alt me-1"></i>
{% trans "Material Details" %}
</h2>
</div>
<form method="post" enctype="multipart/form-data">
{% csrf_token %}
<div class="card-body">
<div class="row g-4">
{# Title and Video Link on a single row #}
<div class="col-md-6">
<div class="mb-0">
<label for="{{ form.title.id_for_label }}" class="form-label">{% trans "Title" %}</label>
{{ form.title|attr:"class:form-control" }}
</div>
</div>
<div class="col-md-6">
<div class="mb-0">
<label for="{{ form.video_link.id_for_label }}" class="form-label">{% trans "Video Link" %}</label>
{{ form.video_link|attr:"class:form-control" }}
</div>
</div>
{# Content (Full width) #}
<div class="col-12">
<div class="mb-0">
<label for="{{ form.content.id_for_label }}" class="form-label">{% trans "Content" %}</label>
{{ form.content|attr:"class:form-control"|attr:"rows:6" }}
</div>
</div>
{# File Upload (Full width as it's often complex) #}
<div class="col-12">
<div class="mb-0">
<label for="{{ form.file.id_for_label }}" class="form-label">{% trans "File" %}</label>
{{ form.file }}
{% if form.file.help_text %}
<div class="form-text">{{ form.file.help_text }}</div>
{% endif %}
</div>
</div>
</div>
</div>
<div class="card-footer bg-light border-top">
<div class="d-flex justify-content-end gap-3">
<button type="submit" class="btn btn-main-action">
<i class="fas fa-save me-1"></i>
{% trans "Update Material" %}
</button>
<a href="{% url 'training_delete' object.pk %}" class="btn btn-danger" onclick="return confirm('{% trans "Are you sure you want to delete this material?" %}')">
<i class="fas fa-trash-alt me-1"></i>
{% trans "Delete" %}
</a>
</div>
</div>
</form>
</div>
</div>
{% endblock %}

View File

@ -262,6 +262,7 @@
</tbody>
</table>
</div>
{% include "includes/paginator.html" %}
{# NOTE: Add Pagination links here if the `staffs` object is a Paginator object #}

View File

@ -180,7 +180,7 @@
<div class="mb-3">
<div class="info-label">{% trans "Last Login" %}</div>
<div class="info-value">
{% if user.last_login %}{{ user.last_login|date:"F d, Y P" }}{% else %}N/A{% endif %}
{% if user.last_login %}{{ user.last_login|date:"F d, Y P" }}{% else %}{% endif %}
</div>
</div>
@ -221,7 +221,7 @@
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="myModalLabel">Upload Profile image</h5>
<h5 class="modal-title" id="myModalLabel">{% trans "Upload Profile image" %}</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
@ -229,17 +229,17 @@
{% csrf_token %}
<div class="mb-3">
<label for="{{ profile_form.profile_image.id_for_label }}" class="form-label">Profile Image</label>
<label for="{{ profile_form.profile_image.id_for_label }}" class="form-label">{% trans "Profile Image" %}</label>
{# 1. Check if an image currently exists on the bound instance #}
{% if profile_form.instance.profile_image %}
<div class="mb-2">
<small class="text-muted d-block">Current Image:</small>
<small class="text-muted d-block">{% trans "Current Image:" %}</small>
{# Display Link to View Current Image #}
<a href="{{ profile_form.instance.profile_image.url }}" target="_blank" class="d-inline-block me-3 text-info fw-bold">
View/Download ({{ profile_form.instance.profile_image.name }})
{% trans "View/Download" %} ({{ profile_form.instance.profile_image.name }})
</a>
{# Image Preview #}
@ -271,8 +271,8 @@
</div>
<div class="modal-footer mt-4">
<button type="button" class="btn btn-lg btn-secondary" data-bs-dismiss="modal">Close</button>
<button type="submit" class="btn btn-primary">Save changes</button>
<button type="button" class="btn btn-lg btn-secondary" data-bs-dismiss="modal">{% trans "Close" %}</button>
<button type="submit" class="btn btn-primary">{% trans "Save changes" %}</button>
</div>
</form>
</div>

Some files were not shown because too many files have changed in this diff Show More