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_NAME=haikal_db
DB_USER=norahuniversity DB_USER=faheed
DB_PASSWORD=norahuniversity DB_PASSWORD=Faheed@215

View File

@ -209,23 +209,32 @@ ACCOUNT_LOGIN_ON_EMAIL_CONFIRMATION = True
ACCOUNT_FORMS = {"signup": "recruitment.forms.StaffSignupForm"} ACCOUNT_FORMS = {"signup": "recruitment.forms.StaffSignupForm"}
# MAIL_BACKEND = "django.core.mail.backends.console.EmailBackend" MAIL_BACKEND = "django.core.mail.backends.console.EmailBackend"
# EMAIL_HOST = "10.10.1.110" EMAIL_HOST = "10.10.1.110"
# EMAIL_PORT = 2225 EMAIL_PORT = 2225
# EMAIL_BACKEND = "django.core.mail.backends.console.EmailBackend" # EMAIL_BACKEND = "django.core.mail.backends.console.EmailBackend"
EMAIL_HOST_PASSWORD = os.getenv("EMAIL_PASSWORD", "mssp.0Q0rSwb.zr6ke4n2k3e4on12.aHwJqnI") # EMAIL_HOST_PASSWORD = os.getenv("EMAIL_PASSWORD", "mssp.0Q0rSwb.zr6ke4n2k3e4on12.aHwJqnI")
EMAIL_HOST = "smtp.mailersend.net" # EMAIL_HOST = "smtp.mailersend.net"
EMAIL_PORT = 2525 # EMAIL_PORT = 2525
EMAIL_HOST_USER = "MS_lhygCJ@test-65qngkd8nx3lwr12.mlsender.net" # EMAIL_HOST_USER = "MS_lhygCJ@test-65qngkd8nx3lwr12.mlsender.net"
EMAIL_HOST_PASSWORD = "mssp.0Q0rSwb.zr6ke4n2k3e4on12.aHwJqnI" # EMAIL_HOST_PASSWORD = "mssp.0Q0rSwb.zr6ke4n2k3e4on12.aHwJqnI"
EMAIL_USE_TLS = True # EMAIL_USE_TLS = True
EMAIL_HOST = 'sandbox.smtp.mailtrap.io' # EMAIL_HOST = 'sandbox.smtp.mailtrap.io'
EMAIL_HOST_USER = '38e5179debe69a' # EMAIL_HOST_USER = '38e5179debe69a'
EMAIL_HOST_PASSWORD = 'ffa75647d01ecb' # EMAIL_HOST_PASSWORD = 'ffa75647d01ecb'
EMAIL_PORT = '2525' # 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 Forms Configuration
CRISPY_ALLOWED_TEMPLATE_PACKS = "bootstrap5" CRISPY_ALLOWED_TEMPLATE_PACKS = "bootstrap5"
CRISPY_TEMPLATE_PACK = "bootstrap5" CRISPY_TEMPLATE_PACK = "bootstrap5"

View File

@ -23,7 +23,7 @@ urlpatterns = [
path("ckeditor5/", include('django_ckeditor_5.urls')), path("ckeditor5/", include('django_ckeditor_5.urls')),
path('application/<slug:slug>/', views.application_submit_form, name='application_submit_form'), 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>/apply/', views.job_application_detail, name='job_application_detail'),
path('application/<slug:slug>/signup/', views.application_signup, name='application_signup'), path('application/<slug:slug>/signup/', views.application_signup, name='application_signup'),
path('application/<slug:slug>/success/', views.application_success, name='application_success'), 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/', 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/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/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'), 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/<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"), path("forms/template/<int:template_id>/all-submissions/", views.form_template_all_submissions, name="form_template_all_submissions"),
# Application Forms (Public) # # Application Forms (Public)
path("application/signup/<slug:template_slug>/", views.application_signup, name="application_signup"), # 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>/", views.application_submit_form, name="application_submit_form"),
path("application/<slug: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>/apply/", views.job_application_detail, name="job_application_detail"),
path("application/<slug:slug>/success/", views.application_success, name="application_success"), # path("application/<slug:slug>/success/", views.application_success, name="application_success"),
# ======================================================================== # ========================================================================
# INTEGRATION & EXTERNAL SERVICES # INTEGRATION & EXTERNAL SERVICES

View File

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

View File

@ -1,32 +1,45 @@
/* /* * KAAT-S Theme Styles (V3.0 - Integrated Enterprise Suite)
* KAAT-S Theme Styles (V2.0 - Consolidated Global, Nav, and Components) * Consolidated Global, Sidebar CRM, Nav, and Components
* This file contains all variables, global layout styles, navigation, and component-specific styles.
*/ */
/* ---------------------------------- */ /* ---------------------------------- */
/* 1. UI Variables and Global Reset */ /* 1. VARIABLES & RESET */
/* ---------------------------------- */ /* ---------------------------------- */
:root { :root {
/* Brand Colors */
--kaauh-teal: #00636e; --kaauh-teal: #00636e;
--kaauh-teal-dark: #004a53; --kaauh-teal-dark: #004a53;
--kaauh-light-bg: #f9fbfd; --kaauh-light-bg: #f9fbfd;
--kaauh-border: #eaeff3; --kaauh-border: #eaeff3;
--kaauh-primary-text: #343a40; --kaauh-primary-text: #343a40;
/* Functional Colors */
--kaauh-success: #28a745; --kaauh-success: #28a745;
--kaauh-info: #17a2b8; --kaauh-info: #17a2b8;
--kaauh-danger: #dc3545; --kaauh-danger: #dc3545;
--kaauh-warning: #ffc107; --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-success { color: var(--kaauh-success) !important; }
.text-info { color: var(--kaauh-info) !important; } .text-info { color: var(--kaauh-info) !important; }
.text-danger { color: var(--kaauh-danger) !important; } .text-danger { color: var(--kaauh-danger) !important; }
.text-warning { color: var(--kaauh-warning) !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; } .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-1600 {
max-width: 1600px; max-width: 1600px;
margin-right: auto; margin-right: auto;
@ -35,118 +48,151 @@
padding-left: var(--bs-gutter-x, 0.75rem); 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 { main.container-fluid {
min-height: calc(100vh - 200px); min-height: calc(100vh - 200px);
padding: 1.5rem 0; 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 { .top-bar {
background-color: white; background-color: white;
border-bottom: 1px solid var(--kaauh-border); border-bottom: 1px solid var(--kaauh-border);
font-size: 0.825rem; font-size: 0.825rem;
padding: 0.4rem 0; 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 { .navbar-dark {
background-color: var(--kaauh-teal) !important; background-color: var(--kaauh-teal) !important;
box-shadow: 0 2px 6px rgba(0,0,0,0.12); 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 { .nav-link {
font-weight: 500; font-weight: 500;
transition: all 0.2s ease; 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; color: white !important;
background: rgba(255,255,255,0.12) !important; background: rgba(255,255,255,0.12) !important;
border-radius: 4px; border-radius: 4px;
} }
/* Dropdown */
.dropdown-menu { .dropdown-menu {
backdrop-filter: blur(4px); backdrop-filter: blur(4px);
background-color: rgba(255, 255, 255, 0.98); background-color: rgba(255, 255, 255, 0.98);
border: 1px solid var(--kaauh-border); border: 1px solid var(--kaauh-border);
box-shadow: 0 6px 20px rgba(0,0,0,0.12); box-shadow: 0 6px 20px rgba(0,0,0,0.12);
border-radius: 8px; 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 { .profile-avatar {
width: 36px; width: 36px;
height: 36px; height: 36px;
@ -157,42 +203,28 @@ main.container-fluid {
justify-content: center; justify-content: center;
color: white; color: white;
font-weight: bold; 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: 1px solid var(--kaauh-border);
border-radius: 0.75rem; border-radius: 0.75rem;
box-shadow: 0 4px 12px rgba(0,0,0,0.06); box-shadow: 0 4px 12px rgba(0,0,0,0.06);
background-color: white; background-color: white;
overflow: hidden;
} }
/* NEW: Filter Controls Container Style */ .filter-controls, .tier-controls {
.filter-controls {
border: 1px solid var(--kaauh-border); border: 1px solid var(--kaauh-border);
border-radius: 0.75rem; border-radius: 0.75rem;
box-shadow: 0 4px 12px rgba(0,0,0,0.06);
background-color: white; background-color: white;
padding: 1.5rem; /* Consistent internal padding */ padding: 1.5rem;
margin-bottom: 1.5rem; /* Space below filter */ 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 { .section-header {
color: var(--kaauh-primary-text); color: var(--kaauh-primary-text);
font-weight: 600; font-weight: 600;
@ -200,14 +232,7 @@ main.container-fluid {
padding-bottom: 0.5rem; padding-bottom: 0.5rem;
margin-bottom: 1rem; 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 { .form-control, .form-select {
border-radius: 0.5rem; border-radius: 0.5rem;
border: 1px solid #ced4da; border: 1px solid #ced4da;
@ -215,71 +240,13 @@ label {
font-size: 0.9rem; font-size: 0.9rem;
} }
/* Forms - Compact Size (for modals/tables) */ .form-control-sm, .form-select-sm, .btn-sm {
.form-control-sm, padding: 0.25rem 0.5rem !important;
.form-select-sm,
.btn-sm {
padding: 0.25rem 0.5rem !important; /* Adjusted padding */
font-size: 0.8rem !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 { .btn-main-action {
background-color: var(--kaauh-teal); background-color: var(--kaauh-teal);
@ -295,6 +262,15 @@ label {
} }
.btn-main-action.btn-sm { font-weight: 600 !important; } .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 { .btn-outline-secondary {
color: var(--kaauh-teal-dark); color: var(--kaauh-teal-dark);
border-color: var(--kaauh-teal); border-color: var(--kaauh-teal);
@ -305,6 +281,7 @@ label {
border-color: var(--kaauh-teal-dark); border-color: var(--kaauh-teal-dark);
} }
.btn-bulk-pass { .btn-bulk-pass {
background-color: var(--kaauh-success); background-color: var(--kaauh-success);
border-color: var(--kaauh-success); border-color: var(--kaauh-success);
@ -344,394 +321,143 @@ label {
} }
/* ---------------------------------- */ /* ---------------------------------- */
/* 5. Table & Footer Styles */ /* 7. TABLES & PAGINATION */
/* ---------------------------------- */ /* ---------------------------------- */
.candidate-table { .candidate-table, .job-table {
table-layout: fixed;
width: 100%; width: 100%;
border-collapse: separate; border-collapse: separate;
border-spacing: 0;
background-color: white; background-color: white;
border-radius: 0.5rem; border-radius: 0.5rem;
overflow: hidden; 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 */ .candidate-table th {
.job-table-wrapper { background-color: var(--kaauh-border);
background: white; color: var(--kaauh-teal-dark);
border-radius: 10px; font-weight: 600;
overflow: hidden; padding: 0.75rem;
box-shadow: 0 4px 16px rgba(0,0,0,0.06); border-bottom: 2px solid var(--kaauh-teal);
margin-bottom: 2rem;
} }
.job-table thead th { .job-table thead th {
background: var(--kaauh-teal); background: var(--kaauh-teal);
color: white; color: white;
font-weight: 600;
padding: 1rem; 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 */ .pagination .page-link {
@media (max-width: 575.98px) { color: var(--kaauh-teal);
.table-responsive { border-color: var(--kaauh-border);
overflow-x: auto;
-webkit-overflow-scrolling: touch;
}
.job-table th,
.job-table td {
white-space: nowrap;
font-size: 0.875rem;
}
} }
/* Bulk Action Bar (Interview Management) */ .pagination .page-item.active .page-link {
.bulk-action-bar { background-color: var(--kaauh-teal);
display: flex; border-color: var(--kaauh-teal);
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;
color: white; 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; font-size: 0.75rem;
padding: 0.125rem 0.5rem; padding: 0.25rem 0.6rem;
border-radius: 0.5rem; border-radius: 0.4rem;
font-weight: 600; font-weight: 600;
margin-left: 0.5rem;
display: inline-block; display: inline-block;
} }
.tier-1-badge { background-color: var(--kaauh-success); color: white; } .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-Interview { background-color: #fff3cd; color: #856404; }
.stage-Offer { background-color: #d4edda; color: #155724; }
/* NEW: Applicant Status Badges */ .timeline { position: relative; padding-left: 2rem; }
.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::before { .timeline::before {
content: ''; content: ''; position: absolute; top: 0; bottom: 0; left: 1.25rem;
position: absolute; width: 2px; background-color: var(--kaauh-border);
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;
} }
.timeline-icon { .timeline-icon {
position: absolute; position: absolute; left: 0; top: 0; width: 32px; height: 32px;
left: 0; border-radius: 50%; display: flex; align-items: center; justify-content: center;
top: 0; color: white; border: 4px solid white; z-index: 10;
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;
} }
/* Custom Timeline Background Classes for Stages (Using Bootstrap color palette) */ /* ---------------------------------- */
.timeline-bg-applied { background-color: var(--kaauh-teal) !important; } /* 9. RTL SUPPORT */
.timeline-bg-exam { background-color: #17a2b8 !important; } /* ---------------------------------- */
.timeline-bg-interview { background-color: #ffc107 !important; } html[dir="rtl"] { text-align: right; direction: rtl; }
.timeline-bg-offer { background-color: #28a745 !important; }
.timeline-bg-rejected { background-color: #dc3545 !important; } html[dir="rtl"] .sidebar {
.loading { left: auto; right: 0;
background: linear-gradient(135deg, var(--kaauh-teal), #004d57); box-shadow: -4px 0 10px rgba(0,0,0,0.1);
color: white;
border-radius: 0.75rem 0.75rem 0 0;
padding: 1.5rem;
box-shadow: 0 4px 10px rgba(0,0,0,0.15);
} }
html[dir="rtl"] .main-wrapper {
margin-left: 0;
margin-right: var(--sidebar-width);
}
/* Breadcrumb Styling */ html[dir="rtl"] .sidebar.collapsed + .main-wrapper {
.breadcrumb { margin-right: var(--sidebar-collapsed-width);
background-color: transparent; }
padding: 0;
margin-bottom: 1rem;
}
.breadcrumb-item + .breadcrumb-item::before { html[dir="rtl"] .nav-link-custom {
content: ">"; border-left: none;
color: var(--kaauh-teal); 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 account %}
{% load crispy_forms_tags %} {% load crispy_forms_tags %}
{% block title %}{% translate "Email Addresses" %}{% endblock %} {% block title %}{% trans "Email Addresses" %}{% endblock %}
{% block content %} {% block content %}
<div class="container my-5"> <div class="container my-5">

View File

@ -6,7 +6,7 @@
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>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"> <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 i18n %}
{% load account %} {% load account %}
{% block title %}{% translate "Sign Out" %}{% endblock %} {% block title %}{% trans "Sign Out" %}{% endblock %}
{% block content %} {% block content %}
<div class="container my-5"> <div class="container my-5">
<div class="row mb-4"> <div class="row mb-4">
<div class="col"> <div class="col">
<h3 class="fw-bold">{% translate "Account Settings" %}</h3> <h3 class="fw-bold">{% trans "Account Settings" %}</h3>
<p class="text-muted">{% translate "Manage your personal details and security." %}</p> <p class="text-muted">{% trans "Manage your personal details and security." %}</p>
</div> </div>
</div> </div>
@ -23,18 +23,18 @@
<div class="list-group list-group-flush rounded-4"> <div class="list-group list-group-flush rounded-4">
{# Assuming a main 'Profile' or 'Personal Information' page exists #} {# 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"> <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>
<a href="{% url 'account_email' %}" class="list-group-item list-group-item-action border-0 py-3"> <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>
<a href="{% url 'account_change_password' %}" class="list-group-item list-group-item-action border-0 py-3"> <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> </a>
{# Highlight the current page (Sign Out) as active #} {# 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"> <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> </a>
</div> </div>
</div> </div>
@ -47,9 +47,9 @@
<div class="card-body p-4 text-center"> <div class="card-body p-4 text-center">
<i class="fas fa-sign-out-alt text-danger mb-4" style="font-size: 3rem;"></i> <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' %}"> <form method="post" action="{% url 'account_logout' %}">
{% csrf_token %} {% csrf_token %}
@ -63,12 +63,12 @@
<div class="d-flex justify-content-center gap-3 mt-4"> <div class="d-flex justify-content-center gap-3 mt-4">
{# Sign Out button in danger color #} {# Sign Out button in danger color #}
<button class="btn btn-danger btn-lg px-5" type="submit"> <button class="btn btn-danger btn-lg px-5" type="submit">
{% translate "Sign Out" %} {% trans "Sign Out" %}
</button> </button>
{# Cancel/Go Back button with outline #} {# Cancel/Go Back button with outline #}
<a class="btn btn-outline-secondary btn-lg px-5" href="{% url 'account_email' %}"> <a class="btn btn-outline-secondary btn-lg px-5" href="{% url 'account_email' %}">
{% translate "Cancel" %} {% trans "Cancel" %}
</a> </a>
</div> </div>
</form> </form>

View File

@ -4,7 +4,7 @@
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>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"> <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> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>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"> <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'%} {% extends 'applicant/partials/candidate_facing_base.html'%}
{% load static i18n %} {% load static i18n %}
{% block content %} {% block content %}
<style> <style>
@ -542,6 +543,7 @@
const state = { const state = {
templateId: '{{ template_slug }}', templateId: '{{ template_slug }}',
job_slug: '{{ job_slug }}',
stages: [], stages: [],
currentStage: 0, currentStage: 0,
formData: {}, formData: {},
@ -765,7 +767,7 @@
// API Functions // API Functions
async function loadFormTemplate() { async function loadFormTemplate() {
try { 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(); const result = await response.json();
if (result.success) { if (result.success) {
@ -831,7 +833,7 @@
}); });
try { try {
const response = await fetch(`/application/${state.templateId}/submit/`, { const response = await fetch(`/application/${state.job_slug}/submit/`, {
method: 'POST', method: 'POST',
body: formData body: formData
// IMPORTANT: Do NOT set Content-Type header when using FormData // IMPORTANT: Do NOT set Content-Type header when using FormData

View File

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

View File

@ -9,6 +9,11 @@
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{% trans "Careers" %} - {% block title %}{% trans "Application Form" %}{% endblock %}</title> <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 %} {% comment %} Load the correct Bootstrap CSS file for RTL/LTR {% endcomment %}
{% if LANGUAGE_CODE == 'ar' %} {% if LANGUAGE_CODE == 'ar' %}

View File

@ -6,812 +6,192 @@
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="description" content="{% trans 'King Abdullah Academic University Hospital - Applicant Tracking System' %}">
<title>{% block title %}{% trans 'University ATS' %}{% endblock %}</title> <title>{% block title %}{% trans 'University ATS' %}{% endblock %}</title>
{% comment %} Load correct Bootstrap CSS file for RTL/LTR {% endcomment %}
{% if LANGUAGE_CODE == 'ar' %} {% if LANGUAGE_CODE == 'ar' %}
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.rtl.min.css" rel="stylesheet"> <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.rtl.min.css" rel="stylesheet">
{% else %} {% else %}
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet"> <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
{% endif %} {% 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" />
<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="{% static 'css/main.css' %}"> <link rel="stylesheet" href="{% static 'css/main.css' %}">
<script src="{% static 'js/main.js' %}"></script>
<script src="{% static 'js/typo.js' %}"></script> <script src="{% static 'js/typo.js' %}"></script>
{% block customCSS %}{% endblock %} {% block customCSS %}{% endblock %}
</head> </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"> <aside class="sidebar d-flex flex-column" id="sidebar">
<div class="container-fluid"> <div class="sidebar-brand text-center">
<div class="d-flex justify-content-between align-items-center gap-2 max-width-1600"> <img src="{% static 'image/kaauh_green1.png' %}" alt="Logo" style="width: 40px;">
<div class="logo-container d-flex gap-2"> <div class="text-white small mt-2 fw-bold brand-subtitle">KAAUH ATS</div>
</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>
</div> </div>
</div>
<nav class="sidebar-nav">
<nav class="navbar navbar-expand-lg navbar-dark sticky-top">
<div class="container-fluid max-width-1600"> <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="navbar-brand text-white d-none d-lg-block me-4 pe-4" href="{% url 'dashboard' %}" aria-label="Home"> <a class="nav-link-custom {% if 'message' in request.path %}active{% endif %}" href="{% url 'message_list' %}">
<img src="{% static 'image/kaauh_green1.png' %}" alt="{% trans 'kaauh logo green bg' %}" style="width: 60px; height: 60px;"> <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> </a>
{# Toggler: order-lg-0 ensures it's before navigation links on desktop, but it stays where it is on mobile #} <a class="nav-link-custom {% if 'job' in request.path and 'bank' not in request.path %}active{% endif %}" href="{% url 'job_list' %}">
<button class="navbar-toggler order-lg-0" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav" <i class="fas fa-briefcase me-2"></i> <span>{% trans "Jobs" %}</span>
aria-controls="navbarNav" aria-expanded="false" aria-label="{% trans 'Toggle navigation' %}"> </a>
<span class="navbar-toggler-icon"></span>
</button>
{# Language and Profile Controls (Keep outside collapse for mobile access) #} <a class="nav-link-custom {% if 'job_bank' in request.resolver_match.url_name %}active{% endif %}" href="{% url 'job_bank' %}">
<div class="d-flex align-items-center order-lg-3"> <i class="fas fa-university me-2"></i> <span>{% trans "Job Bank" %}</span>
{% comment %} <ul class="navbar-nav flex-row"> </a>
<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 %}
<ul class="navbar-nav ms-2 ms-lg-4"> <a class="nav-link-custom {% if 'application' in request.path %}active{% endif %}" href="{% url 'application_list' %}">
<!-- Notification Bell for Admin Users --> <i class="fas fa-user-tie me-2"></i> <span>{% trans "Applications" %}</span>
{% comment %} {% if request.user.is_authenticated and request.user.is_staff %} </a>
<li class="nav-item dropdown me-2"> <a class="nav-link-custom {% if 'person' in request.path %}active{% endif %}" href="{% url 'person_list' %}">
<a class="nav-link position-relative" href="#" role="button" id="notificationDropdown" data-bs-toggle="dropdown" aria-expanded="false"> <i class="fas fa-user me-2"></i> <span>{% trans "Applicants" %}</span>
<i class="fas fa-bell"></i> </a>
<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;"> <a class="nav-link-custom {% if 'agency' in request.path %}active{% endif %}" href="{% url 'agency_list' %}">
0 <i class="fas fa-building me-2"></i> <span>{% trans "Agencies" %}</span>
</span> </a>
</a> <a class="nav-link-custom {% if 'interview' in request.path %}active{% endif %}" href="{% url 'interview_list' %}">
<ul class="dropdown-menu dropdown-menu-end" style="min-width: 300px;" aria-labelledby="notificationDropdown"> <i class="fas fa-calendar-alt me-2"></i> <span>{% trans "Interviews" %}</span>
<li class="dropdown-header d-flex justify-content-between align-items-center"> </a>
<span>{% trans "Messages" %}</span> <a class="nav-link-custom {% if request.resolver_match.url_name == 'kaauh_career' %}active{% endif %}" href="{% url 'kaauh_career' %}">
<a href="#" class="text-decoration-none">{% trans "View All" %}</a> <i class="fas fa-globe me-2"></i> <span>{% trans "Career Page" %}</span>
</li> </a>
<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 %}
<li class="nav-item me-2 d-none d-lg-block"> {% if request.user.is_authenticated and request.user.is_superuser %}
{% if LANGUAGE_CODE == 'en' %} <div class="mt-auto">
<form action="{% url 'set_language' %}" method="post" class="d-inline"> <div class="mt-4 pt-3 border-top border-secondary mx-3 uppercase">
{% csrf_token %} {% comment %} <small class="text-white-50 px-2">{% trans "System" %}</small> {% endcomment %}
<input name="next" type="hidden" value="{{ request.get_full_path }}"> </div>
<button name="language" value="ar" class="btn bg-primary-theme text-white" type="submit"> <a class="nav-link-custom {% if 'settings' in request.path %}active{% endif %}" href="{% url 'settings' %}">
<span class="me-2">🇸🇦</span> العربية <i class="fas fa-cog me-2"></i> <span>{% trans "Settings" %}</span>
</button> </a>
</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>
</div> </div>
{# End Language and Profile Controls #} {% endif %}
</nav>
{# Main Navigation Links (This collapses on mobile) - order-lg-1 ensures it is centered on desktop #} {% if request.user.is_authenticated %}
<div class="collapse navbar-collapse order-lg-1" id="navbarNav"> <div class="logout-section">
<ul class="navbar-nav me-auto mb-2 mb-lg-0"> <form method="post" action="{% url 'account_logout'%}">
<li class="nav-item me-lg-4"> {% csrf_token %}
<a class="nav-link {% if request.resolver_match.url_name == 'job_list' %}active{% endif %}" href="{% url 'job_list' %}"> <button type="submit" class="btn btn-link text-decoration-none d-flex align-items-center">
<span class="d-flex align-items-center gap-2"> <i class="fas fa-sign-out-alt me-2"></i> <span>{% trans "Sign Out" %}</span>
<i class="fas fa-briefcase me-2"></i> </button>
{% trans "Jobs" %} </form>
</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>
</div> </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> </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;"> <div class="d-flex align-items-center gap-3">
{% if messages %} <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 %} {% 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 }} {{ 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> </div>
{% endfor %} {% endfor %}
{% endif %} {% endif %}
{% block content %} {% block content %}{% endblock %}
{% endblock %}
</main> </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> </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/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 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 %} {% block customJS %}{% endblock %}
</body> </body>
</html> </html>

View File

@ -1,10 +1,10 @@
{% load static i18n %} {% load static %}
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en"> <html lang="en">
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>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"> <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 = { const djangoConfig = {
csrfToken: "{{ csrf_token }}", csrfToken: "{{ csrf_token }}",
saveUrl: "{% url 'save_form_template' %}", 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 %}, templateId: {% if template_slug %}'{{ template_slug }}'{% else %}null{% endif %},
jobId: {% if job_id %}{{ job_id }}{% else %}null{% endif %} // Add this if you need it jobId: {% if job_id %}{{ job_id }}{% else %}null{% endif %} // Add this if you need it
}; };

View File

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

View File

@ -2,7 +2,7 @@
{% load static i18n crispy_forms_tags %} {% 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 %} {% block customCSS %}
<style> <style>

View File

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

View File

@ -1,10 +1,11 @@
{% load i18n %}
<div class="d-flex justify-content-between align-items-center mb-3"> <div class="d-flex justify-content-between align-items-center mb-3">
<div class="btn-group" role="group"> <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 }}"> <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>
<button type="button" class="btn btn-outline-primary btn-sm view-toggle" data-view="card" data-list-id="{{ list_id }}"> <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> </button>
</div> </div>
</div> </div>

View File

@ -1,6 +1,6 @@
<div class="card mt-4"> <div class="card mt-4">
<div class="card-header text-primary-theme"> <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>
<div class="card-body"> <div class="card-body">
<form method="post" action="{% url 'add_meeting_comment' meeting.slug %}"> <form method="post" action="{% url 'add_meeting_comment' meeting.slug %}">
@ -15,7 +15,7 @@
</div> </div>
{% endif %} {% endif %}
</div> </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 %} {% 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">Cancel</button>
{% endif %} {% endif %}

View File

@ -1,10 +1,10 @@
<div id="comment-section"> <div id="comment-section">
<div class="card mt-4"> <div class="card mt-4">
<div class="card-header text-primary-theme d-flex justify-content-between align-items-center"> <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 %} {% 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"> <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> </button>
{% endif %} {% endif %}
</div> </div>
@ -18,7 +18,7 @@
<div> <div>
<strong>{{ comment.author.get_full_name|default:comment.author.username }}</strong> <strong>{{ comment.author.get_full_name|default:comment.author.username }}</strong>
{% if comment.author != user %} {% if comment.author != user %}
<span class="badge bg-secondary ms-2">Comment</span> <span class="badge bg-secondary ms-2">{% trans "Comment" %}</span>
{% endif %} {% endif %}
</div> </div>
<small class="text-muted">{{ comment.created_at|date:"M d, Y P" }}</small> <small class="text-muted">{{ comment.created_at|date:"M d, Y P" }}</small>
@ -49,7 +49,7 @@
{% endfor %} {% endfor %}
</div> </div>
{% else %} {% 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 %} {% endif %}
</div> </div>
</div> </div>

View File

@ -1,15 +1,15 @@
<div class="card mt-4"> <div class="card mt-4">
<div class="card-header text-primary-theme"> <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>
<div class="card-body"> <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> <p><small>{{ comment.created_at|date:"F d, Y P" }}</small></p>
<form method="post" action="{{ delete_url }}"> <form method="post" action="{{ delete_url }}">
{% csrf_token %} {% 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 %} {% 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 %} {% endif %}
</form> </form>
</div> </div>

View File

@ -1,6 +1,6 @@
<div class="card mt-4"> <div class="card mt-4">
<div class="card-header text-primary-theme"> <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>
<div class="card-body"> <div class="card-body">
<form method="post" action="{% url 'edit_meeting_comment' meeting.slug comment.id %}"> <form method="post" action="{% url 'edit_meeting_comment' meeting.slug comment.id %}">
@ -15,9 +15,9 @@
</div> </div>
{% endif %} {% endif %}
</div> </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 %} {% 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 %} {% endif %}
</form> </form>
</div> </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"> <nav aria-label="Page navigation" class="mt-4">
<ul class="pagination justify-content-center"> <ul class="pagination justify-content-center">
{# Helper to build the query string while excluding the 'page' parameter #} {# Get the filter parameters (e.g., "q=searchterm&job=dev") #}
{% load url_extras %}
{# Build a string of all current filters (e.g., &department=IT&type=FULL_TIME) #}
{% add_get_params request.GET as filter_params %} {% 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 & Previous --- #}
{% if page_obj.has_previous %}
{# First Page Link #} <li class="page-item">
<li class="page-item"> <a class="page-link text-primary-theme" href="?page=1{% if filter_params %}&{{ filter_params }}{% endif %}">
<a class="page-link text-primary-theme" <i class="fas fa-angle-double-left"></i>
href="?page=1{{ filter_params }}"> </a>
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>
</li> </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 %} {# --- Page Numbers --- #}
{# --- Page Numbers --- #}
{# Next Page Link #} {% for num in page_obj.paginator.page_range %}
<li class="page-item"> {% if page_obj.number == num %}
<a class="page-link text-primary-theme" <li class="page-item active"><span class="page-link bg-kaauh-teal text-white border-kaauh-teal">{{ num }}</span></li>
href="?page={{ page_obj.next_page_number }}{{ filter_params }}"> {% elif num > page_obj.number|add:'-3' and num < page_obj.number|add:'3' %}
Next <li class="page-item"><a class="page-link text-primary-theme" href="?page={{ num }}{% if filter_params %}&{{ filter_params }}{% endif %}">{{ num }}</a></li>
</a> {% elif num == 1 or num == page_obj.paginator.num_pages %}
</li> {# 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>
{# Last Page Link #} {% elif num == page_obj.number|add:'-3' or num == page_obj.number|add:'3' %}
<li class="page-item"> {# Show the dots #}
<a class="page-link text-primary-theme" <li class="page-item disabled"><span class="page-link">...</span></li>
href="?page={{ page_obj.paginator.num_pages }}{{ filter_params }}">
Last
</a>
</li>
{% endif %} {% 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> </ul>
</nav> </nav>

View File

@ -7,41 +7,41 @@
{% csrf_token %} {% csrf_token %}
<div class="row"> <div class="row">
<div class="col-md-6"> <div class="col-md-6">
<h5>Select Candidates</h5> <h5>{% trans "Select Candidates" %}</h5>
<div class="form-group"> <div class="form-group">
{{ form.candidates }} {{ form.candidates }}
</div> </div>
</div> </div>
<div class="col-md-6"> <div class="col-md-6">
<h5>Schedule Details</h5> <h5>{% trans "Schedule Details" %}</h5>
<div class="form-group mb-3"> <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 }} {{ form.start_date }}
</div> </div>
<div class="form-group mb-3"> <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 }} {{ form.end_date }}
</div> </div>
<div class="form-group mb-3"> <div class="form-group mb-3">
<label>Working Days</label> <label>{% trans "Working Days" %}</label>
{{ form.working_days }} {{ form.working_days }}
</div> </div>
<div class="row"> <div class="row">
<div class="col-md-6"> <div class="col-md-6">
<div class="form-group mb-3"> <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 }} {{ form.start_time }}
</div> </div>
</div> </div>
<div class="col-md-6"> <div class="col-md-6">
<div class="form-group mb-3"> <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 }} {{ form.end_time }}
</div> </div>
</div> </div>
@ -50,14 +50,14 @@
<div class="row"> <div class="row">
<div class="col-md-6"> <div class="col-md-6">
<div class="form-group mb-3"> <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 }} {{ form.interview_duration }}
</div> </div>
</div> </div>
<div class="col-md-6"> <div class="col-md-6">
<div class="form-group mb-3"> <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 }} {{ form.buffer_time }}
</div> </div>
</div> </div>
@ -73,28 +73,28 @@
{% for form in break_formset %} {% for form in break_formset %}
<div class="break-time-form row mb-2"> <div class="break-time-form row mb-2">
<div class="col-md-5"> <div class="col-md-5">
<label>Start Time</label> <label>{% trans "Start Time" %}</label>
{{ form.start_time }} {{ form.start_time }}
</div> </div>
<div class="col-md-5"> <div class="col-md-5">
<label>End Time</label> <label>{% trans "End Time" %}</label>
{{ form.end_time }} {{ form.end_time }}
</div> </div>
<div class="col-md-2"> <div class="col-md-2">
<label>&nbsp;</label><br> <label>&nbsp;</label><br>
{{ form.DELETE }} {{ 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>
</div> </div>
{% endfor %} {% endfor %}
</div> </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> </div>
<div class="mt-4"> <div class="mt-4">
<button type="submit" class="btn btn-primary">Preview Schedule</button> <button type="submit" class="btn btn-primary">{% trans "Preview Schedule" %}</button>
<a href="{% url 'job_detail' slug=job.slug %}" class="btn btn-secondary">Cancel</a> <a href="{% url 'job_detail' slug=job.slug %}" class="btn btn-secondary">{% trans "Cancel" %}</a>
</div> </div>
</form> </form>
</div> </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"> <div class="card-header">
<h4 class="mb-0"> <h4 class="mb-0">
<i class="fas fa-building me-2"></i> <i class="fas fa-building me-2"></i>
Create Onsite Interview for {{ application.name }} {% trans "Create Onsite Interview for" %} {{ application.name }}
</h4> </h4>
<a href="{% url 'interview_create_type_selection' application.slug %}" <a href="{% url 'interview_create_type_selection' application.slug %}"
class="btn btn-outline-primary"> class="btn btn-outline-primary">
@ -21,8 +21,8 @@
</div> </div>
<div class="card-body"> <div class="card-body">
<p class="text-muted mb-3"> <p class="text-muted mb-3">
Schedule an onsite interview for <strong>{{ application.name }}</strong> {% trans "Schedule an onsite interview for" %} <strong>{{ application.name }}</strong>
for the position of <strong>{{ job.title }}</strong>. {% trans "for the position of" %} <strong>{{ job.title }}</strong>.
</p> </p>
{% if messages %} {% if messages %}

View File

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

View File

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

View File

@ -518,20 +518,35 @@
<!-- AI Generated Questions Section --> <!-- AI Generated Questions Section -->
<div class="kaauh-card shadow-sm p-4 mb-4"> <div class="kaauh-card shadow-sm p-4 mb-4">
<div class="d-flex align-items-center justify-content-between mb-3"> <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;"> <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" %} <i class="fas fa-brain me-2"></i> {% trans "AI Generated Questions" %}
</h5> </h5>
<div class="d-flex gap-2">
<form action="{% url 'generate_ai_questions' schedule.slug %}" method="post"> <div class="d-flex gap-2">
{% csrf_token %} <div class="dropdown">
<button type="submit" class="btn btn-main-action btn-sm"> <button class="btn btn-main-action btn-sm dropdown-toggle" type="button" data-bs-toggle="dropdown" aria-expanded="false">
<span id="button-text-content"> <i class="fas fa-magic me-1"></i> {% trans "AI Actions" %}
<i class="fas fa-magic me-1"></i> {% trans "Generate Interview Questions" %}
</span>
</button> </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> </div>
<div class="accordion" id="aiQuestionsAccordion"> <div class="accordion" id="aiQuestionsAccordion">

View File

@ -1,7 +1,7 @@
{% extends 'base.html' %} {% extends 'base.html' %}
{% load static i18n %} {% load static i18n %}
{% block title %}{% trans "Interview Management" %} - ATS{% endblock %} {% block title %}{% trans "Interviews" %} - ATS{% endblock %}
{% block customCSS %} {% block customCSS %}
<style> <style>
@ -138,19 +138,7 @@
font-size: 0.8rem !important; 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> </style>
{% endblock %} {% endblock %}

View File

@ -6,7 +6,7 @@
<div class="col-lg-6 col-md-8"> <div class="col-lg-6 col-md-8">
<div class="card shadow-lg border-0 rounded-lg mt-5"> <div class="card shadow-lg border-0 rounded-lg mt-5">
<div class="card-header bg-main-action text-white"> <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>
<div class="card-body"> <div class="card-body">
<form method="post" action="{% url 'confirm_schedule_interviews_view' job.slug %}" enctype="multipart/form-data"> <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"> <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"> <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> </a>
<button type="submit" class="btn btn-primary"> <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> </button>
</div> </div>
</form> </form>

View File

@ -28,7 +28,7 @@
</span> </span>
</td> </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> </tr>
{% empty %} {% empty %}
<tr> <tr>

View File

@ -1,7 +1,7 @@
{% extends "base.html" %} {% extends "base.html" %}
{% load static crispy_forms_tags %} {% load static crispy_forms_tags %}
{%load i18n %} {%load i18n %}
{% block title%} {% trans "Preview Interview Schedule" %}{%endblock%}
{% block customCSS %} {% block customCSS %}
<style> <style>
/* Custom Teal Theme Variables (Adapt these if defined globally) */ /* 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"> <div class="d-flex justify-content-between align-items-center mb-5">
<h1 class="h3 page-header"> <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> </h1>
</div> </div>
@ -100,14 +100,14 @@
<div class="row g-4"> <div class="row g-4">
<div class="col-md-6"> <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-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> Interview Duration:</strong> {{ interview_duration }} minutes</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> Buffer Time:</strong> {{ buffer_time }} 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>
<div class="col-md-6"> <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-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> Active Days:</strong> <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 %} {% for day_id in working_days %}
{% if day_id == 0 %}Mon{% endif %} {% if day_id == 0 %}Mon{% endif %}
{% if day_id == 1 %}Tue{% endif %} {% if day_id == 1 %}Tue{% endif %}
@ -119,7 +119,7 @@
{% if not forloop.last %}, {% endif %} {% if not forloop.last %}, {% endif %}
{% endfor %} {% endfor %}
</p> </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>
</div> </div>
@ -134,7 +134,7 @@
{% endfor %} {% endfor %}
</div> </div>
{% else %} {% 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 %} {% endif %}
</div> </div>
</div> </div>
@ -152,10 +152,10 @@
<table class="table table-hover table-striped"> <table class="table table-hover table-striped">
<thead class="bg-primary-theme-light"> <thead class="bg-primary-theme-light">
<tr> <tr>
<th scope="col">Date</th> <th scope="col">{% trans "Date" %}</th>
<th scope="col">Time</th> <th scope="col">{% trans "Time" %}</th>
<th scope="col">Candidate</th> <th scope="col">{% trans "Applicant" %}</th>
<th scope="col">Email</th> <th scope="col">{% trans "Email" %}</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
@ -202,10 +202,10 @@
<div class="modal-footer"> <div class="modal-footer">
<div class="d-flex align-items-center justify-content-between mt-4 mb-0"> <div class="d-flex align-items-center justify-content-between mt-4 mb-0">
<a href="#" class="btn btn-secondary me-2"> <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> </a>
<button type="submit" class="btn btn-primary" form="onsite-form"> <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> </button>
</div> </div>
</div> </div>

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,15 +1,12 @@
{% extends "base.html" %} {% extends "base.html" %}
{% load static %} {% load static %}
{% load i18n %}
{% block title %}Job Bank - All Opportunities{% endblock %} {% block title %}{% trans "Job Bank - All Opportunities" %}{% endblock %}
{% block customCSS %} {% block customCSS %}
<style> <style>
.job-bank-container {
max-width: 1400px;
margin: 0 auto;
padding: 20px;
}
.job-bank-header { .job-bank-header {
background: linear-gradient(135deg, #00636e 0%, #004a53 100%); background: linear-gradient(135deg, #00636e 0%, #004a53 100%);
@ -303,36 +300,7 @@
color: white; 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 { .no-results {
text-align: center; text-align: center;
@ -387,8 +355,8 @@
<div class="job-bank-container"> <div class="job-bank-container">
<!-- Header Section --> <!-- Header Section -->
<div class="job-bank-header"> <div class="job-bank-header">
<h1>🏦 Job Bank</h1> <h1>🏦 {% trans "Job Bank" %}</h1>
<p>Explore all available opportunities across departments and find your perfect role</p> <p>{% trans "Explore all available opportunities across departments and find your perfect role" %}</p>
</div> </div>
<!-- Filters Section --> <!-- Filters Section -->
@ -397,21 +365,21 @@
<div class="filters-grid"> <div class="filters-grid">
<!-- Search Box --> <!-- Search Box -->
<div class="filter-group search-box"> <div class="filter-group search-box">
<label for="search">🔍 Search Jobs</label> <label for="search">🔍 {% trans "Search Jobs" %}</label>
<input <input
type="text" type="text"
id="search" id="search"
name="q" name="q"
value="{{ search_query }}" value="{{ search_query }}"
placeholder="Search by title, department, or keywords..." placeholder="{% trans "Search by title, department, or keywords..." %}"
> >
</div> </div>
<!-- Department Filter --> <!-- Department Filter -->
<div class="filter-group"> <div class="filter-group">
<label for="department">📁 Department</label> <label for="department">📁 {% trans "Department" %}</label>
<select id="department" name="department"> <select id="department" name="department">
<option value="">All Departments</option> <option value="">{% trans "All Departments" %}</option>
{% for dept in departments %} {% for dept in departments %}
<option value="{{ dept }}" {% if department_filter == dept %}selected{% endif %}> <option value="{{ dept }}" {% if department_filter == dept %}selected{% endif %}>
{{ dept }} {{ dept }}
@ -422,9 +390,9 @@
<!-- Job Type Filter --> <!-- Job Type Filter -->
<div class="filter-group"> <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"> <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 %} {% for key, value in job_types.items %}
<option value="{{ key }}" {% if job_type_filter == key %}selected{% endif %}> <option value="{{ key }}" {% if job_type_filter == key %}selected{% endif %}>
{{ value }} {{ value }}
@ -435,9 +403,9 @@
<!-- Workplace Type Filter --> <!-- Workplace Type Filter -->
<div class="filter-group"> <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"> <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 %} {% for key, value in workplace_types.items %}
<option value="{{ key }}" {% if workplace_type_filter == key %}selected{% endif %}> <option value="{{ key }}" {% if workplace_type_filter == key %}selected{% endif %}>
{{ value }} {{ value }}
@ -448,9 +416,9 @@
<!-- Status Filter --> <!-- Status Filter -->
<div class="filter-group"> <div class="filter-group">
<label for="status">📊 Status</label> <label for="status">📊 {% trans "Status" %}</label>
<select id="status" name="status"> <select id="status" name="status">
<option value="">All Statuses</option> <option value="">{% trans "All Statuses" %}</option>
{% for key, value in status_choices.items %} {% for key, value in status_choices.items %}
<option value="{{ key }}" {% if status_filter == key %}selected{% endif %}> <option value="{{ key }}" {% if status_filter == key %}selected{% endif %}>
{{ value }} {{ value }}
@ -461,25 +429,25 @@
<!-- Date Filter --> <!-- Date Filter -->
<div class="filter-group"> <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"> <select id="date_filter" name="date_filter">
<option value="">Any Time</option> <option value="">{% trans "Any Time" %}</option>
<option value="week" {% if date_filter == 'week' %}selected{% endif %}>Last Week</option> <option value="week" {% if date_filter == 'week' %}selected{% endif %}>{% trans "Last Week" %}</option>
<option value="month" {% if date_filter == 'month' %}selected{% endif %}>Last Month</option> <option value="month" {% if date_filter == 'month' %}selected{% endif %}>{% trans "Last Month" %}</option>
<option value="quarter" {% if date_filter == 'quarter' %}selected{% endif %}>Last 3 Months</option> <option value="quarter" {% if date_filter == 'quarter' %}selected{% endif %}>{% trans "Last 3 Months" %}</option>
</select> </select>
</div> </div>
<!-- Sort By --> <!-- Sort By -->
<div class="filter-group"> <div class="filter-group">
<label for="sort">🔄 Sort By</label> <label for="sort">🔄 {% trans "Sort By" %}</label>
<select id="sort" name="sort"> <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 %}>{% trans "Newest First" %}</option>
<option value="created_at" {% if sort_by == 'created_at' %}selected{% endif %}>Oldest 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 %}>Title (A-Z)</option> <option value="title" {% if sort_by == 'title' %}selected{% endif %}>{% trans Title %} (A-Z)</option>
<option value="-title" {% if sort_by == '-title' %}selected{% endif %}>Title (Z-A)</option> <option value="-title" {% if sort_by == '-title' %}selected{% endif %}>{% trans "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 %}>{% trans "Department" %} (A-Z)</option>
<option value="-department" {% if sort_by == '-department' %}selected{% endif %}>Department (Z-A)</option> <option value="-department" {% if sort_by == '-department' %}selected{% endif %}>{% trans "Department" %} (Z-A)</option>
</select> </select>
</div> </div>
</div> </div>
@ -487,10 +455,10 @@
<!-- Filter Actions --> <!-- Filter Actions -->
<div class="filters-actions"> <div class="filters-actions">
<button type="submit" class="btn-filter btn-primary"> <button type="submit" class="btn-filter btn-primary">
🔍 Apply Filters 🔍 {% trans "Apply Filters" %}
</button> </button>
<a href="{% url 'job_bank' %}" class="btn-filter btn-secondary"> <a href="{% url 'job_bank' %}" class="btn-filter btn-secondary">
🔄 Clear All 🔄 {% trans "Clear All" %}
</a> </a>
</div> </div>
</form> </form>
@ -499,9 +467,9 @@
<!-- Results Header --> <!-- Results Header -->
<div class="results-header"> <div class="results-header">
<div class="results-count"> <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 %} {% 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 %} {% endif %}
</div> </div>
</div> </div>
@ -535,7 +503,7 @@
{% if job.max_applications %} {% if job.max_applications %}
<div class="meta-item"> <div class="meta-item">
<span class="meta-icon">👥</span> <span class="meta-icon">👥</span>
<span>{{ job.max_applications }} positions</span> <span>{{ job.max_applications }} {% trans "positions" %}</span>
</div> </div>
{% endif %} {% endif %}
</div> </div>
@ -549,11 +517,11 @@
<div class="job-actions"> <div class="job-actions">
{% if job.status == 'ACTIVE' %} {% if job.status == 'ACTIVE' %}
<a href="{% url 'job_applicants' job.slug %}" class="btn-apply"> <a href="{% url 'job_applicants' job.slug %}" class="btn-apply">
<EFBFBD> View Applicants {% trans "View Applicants" %}
</a> </a>
{% endif %} {% endif %}
<a href="{% url 'job_detail' job.slug %}" class="btn-view"> <a href="{% url 'job_detail' job.slug %}" class="btn-view">
👁️ View Details {% trans "View Details" %}
</a> </a>
</div> </div>
</div> </div>
@ -562,35 +530,21 @@
<!-- Pagination --> <!-- Pagination -->
{% if page_obj.has_other_pages %} {% if page_obj.has_other_pages %}
<div class="pagination"> {% include "includes/paginator.html" %}
{% 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>
{% endif %} {% endif %}
{% else %} {% else %}
<!-- No Results --> <!-- No Results -->
<div class="no-results"> <div class="no-results">
<h3>😔 No Jobs Found</h3> <h3> {% trans "No Jobs Found" %}</h3>
<p> <p>
{% if search_query or department_filter or job_type_filter or workplace_type_filter or status_filter or date_filter %} {% 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 %} {% 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 %} {% endif %}
</p> </p>
<a href="{% url 'job_bank' %}" class="btn-filter btn-primary"> <a href="{% url 'job_bank' %}" class="btn-filter btn-primary">
🔄 Clear Filters {% trans "Clear Filters" %}
</a> </a>
</div> </div>
{% endif %} {% endif %}

View File

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

View File

@ -1,30 +1,30 @@
{% extends "base.html" %} {% extends "base.html" %}
{% load i18n %}
{% block title %}Delete Job - University ATS{% endblock %} {% block title %}{% trans "Delete Job - University ATS" %}{% endblock %}
{% block content %} {% block content %}
<div class="row justify-content-center"> <div class="row justify-content-center">
<div class="col-lg-6"> <div class="col-lg-6">
<div class="card"> <div class="card">
<div class="card-header bg-danger text-white"> <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>
<div class="card-body"> <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"> <div class="alert alert-warning">
<i class="fas fa-exclamation-triangle"></i> <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> </div>
<form method="post"> <form method="post">
{% csrf_token %} {% csrf_token %}
<div class="d-flex justify-content-between"> <div class="d-flex justify-content-between">
<a href="{% url 'job_list' %}" class="btn btn-secondary"> <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> </a>
<button type="submit" class="btn btn-danger"> <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> </button>
</div> </div>
</form> </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 fade mt-4" id="myModalForm" tabindex="-1" aria-labelledby="myModalLabel" aria-hidden="true">
<div class="modal-dialog"> <div class="modal-dialog">
<div class="modal-content"> <div class="modal-content">
<div class="modal-header"> <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> <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div> </div>
<div class="modal-body"> <div class="modal-body">
@ -11,12 +12,12 @@
{{ image_upload_form.as_p}} {{ image_upload_form.as_p}}
{% if image_upload_form.instance.post_image %} {% 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;"> <img src="{{ image_upload_form.instance.post_image.url }}" alt="Post Image" style="max-width: 200px;">
{% endif %} {% endif %}
<div class="modal-footer mt-2"> <div class="modal-footer mt-2">
<button type="button" class="btn btn-lg btn-secondary" data-bs-dismiss="modal">Close</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 ">Save changes</button> <button type="submit" class="btn btn-main-action ">{% trans "Save changes" %}</button>
</div> </div>
</form> </form>
</div> </div>

View File

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

View File

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

View File

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

View File

@ -182,39 +182,8 @@
</table> </table>
</div> </div>
{% if page_obj.has_other_pages %}
<nav aria-label="Message pagination"> {% include "includes/paginator.html" %}
<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 %}
{% else %} {% else %}
<div class="text-center text-muted py-5"> <div class="text-center text-muted py-5">
<i class="fas fa-inbox fa-3x mb-3 text-primary-theme"></i> <i class="fas fa-inbox fa-3x mb-3 text-primary-theme"></i>

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,7 +1,7 @@
{% extends "base.html" %} {% extends "base.html" %}
{% load static i18n %} {% load static i18n %}
{% block title %}People - {{ block.super }}{% endblock %} {% block title %}{% trans "Applicants" %} - {{ block.super }}{% endblock %}
{% block customCSS %} {% block customCSS %}
<style> <style>
@ -108,19 +108,7 @@
background-color: var(--kaauh-gray-light); 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 Styling */
.profile-image-small { .profile-image-small {
@ -395,10 +383,10 @@
<div class="card-body"> <div class="card-body">
<i class="fas fa-user-friends fa-3x mb-3" style="color: var(--kaauh-teal-dark);"></i> <i class="fas fa-user-friends fa-3x mb-3" style="color: var(--kaauh-teal-dark);"></i>
<h3>{% trans "No people found" %}</h3> <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 %} {% if user.is_staff %}
<a href="{% url 'person_create' %}" class="btn btn-main-action mt-3"> <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> </a>
{% endif %} {% endif %}
</div> </div>

View File

@ -1,7 +1,7 @@
{% extends "base.html" %} {% extends "base.html" %}
{% load static i18n crispy_forms_tags %} {% 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 %} {% block customCSS %}
<style> <style>

View File

@ -6,292 +6,188 @@
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="description" content="{% trans 'King Abdullah Academic University Hospital - Agency Portal' %}"> <meta name="description" content="{% trans 'King Abdullah Academic University Hospital - Portal' %}">
<title>{% block title %}{% trans 'KAAUH Agency Portal' %}{% endblock %}</title> <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' %} {% if LANGUAGE_CODE == 'ar' %}
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.rtl.min.css" rel="stylesheet"> <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.rtl.min.css" rel="stylesheet">
{% else %} {% else %}
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet"> <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
{% endif %} {% 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' %}"> <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 %} {% block customCSS %}{% endblock %}
</head> </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;"> <aside class="sidebar d-flex flex-column" id="sidebar">
<div class="container-fluid"> <div class="sidebar-brand text-center">
<div class="d-flex justify-content-between align-items-center gap-2" style="max-width: 1600px; margin: 0 auto; padding: 0.5rem 0;"> <img src="{% static 'image/kaauh_green1.png' %}" alt="Logo" style="width: 40px;">
<div class="logo-group d-flex gap-3 align-items-center"> <div class="text-white small mt-2 fw-bold brand-subtitle">
<img src="{% static 'image/vision.svg' %}" alt="{% trans 'Saudi Vision 2030' %}" loading="lazy" style="height: 35px; object-fit: contain;"> {% if request.user.user_type == 'agency' %}{% trans "Agency Portal" %}{% else %}{% trans "Candidate Portal" %}{% endif %}
</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>
</div> </div>
</div> </div>
</div>
<nav class="sidebar-nav">
{# Using inline style for nav background color - replace with a dedicated CSS class (e.g., .bg-kaauh-nav) if defined in main.css #} {% if request.user.user_type == 'agency' %}
<div style="background-color: #00636e;"> <a class="nav-link-custom {% if 'dashboard' in request.path %}active{% endif %}" href="{% url 'agency_portal_dashboard' %}">
<nav class="navbar navbar-expand-lg navbar-dark sticky-top"> <i class="fas fa-th-large me-2"></i> <span>{% trans "Dashboard" %}</span>
<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>
</a> </a>
{% elif request.user.user_type == 'agency' %} <a class="nav-link-custom {% if 'persons' in request.path %}active{% endif %}" href="{% url 'agency_portal_persons_list' %}">
<a class="navbar-brand text-white" href="{% url 'agency_portal_dashboard' %}" aria-label="Agency Dashboard"> <i class="fas fa-users me-2"></i> <span>{% trans "Applicants" %}</span>
<img src="{% static 'image/kaauh_green1.png' %}" alt="{% trans 'kaauh logo green bg' %}" style="width: 40px; height: 40px;"> </a>
<span class="ms-3 d-none d-md-inline fw-semibold">{% trans "Agency Portal" %}</span> {% 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> </a>
{% endif %} {% 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" <div class="mt-auto">
aria-controls="agencyNavbar" aria-expanded="false" aria-label="{% trans 'Toggle navigation' %}"> <div class="mt-4 pt-3 border-top border-secondary mx-3 brand-subtitle">
<span class="navbar-toggler-icon"></span> <small class="text-white-50 px-2 uppercase">{% trans "Account" %}</small>
</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> </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> </div>
</nav> </nav>
</div>
{% if request.user.is_authenticated %}
<main id="message-container" class="container-fluid flex-grow-1" style="max-width: 1600px; margin: 0 auto;"> <div class="logout-section">
{# Messages Block (Correct) #} <form method="post" action="{% url 'account_logout'%}">
{% if messages %} {% csrf_token %}
{% for message in messages %} <button type="submit" class="btn btn-link text-decoration-none d-flex align-items-center">
<div class="alert alert-{{ message.tags }} alert-dismissible fade show mt-2" role="alert"> <i class="fas fa-sign-out-alt me-2"></i> <span>{% trans "Sign Out" %}</span>
{{ message }} </button>
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="{% trans 'Close' %}"></button> </form>
</div> </div>
{% endfor %}
{% endif %} {% endif %}
{% block content %}
{% endblock %}
</main>
{# Footer (Correct) #} <div class="mt-auto p-3 border-top border-secondary text-center">
<footer class="mt-auto"> <a href="https://tenhal.sa/" class="text-decoration-none" target="_blank">
<div class="footer-bottom py-3 small text-muted" style="background-color: #00363a;"> <div class="text-white-50 brand-subtitle" style="font-size: 0.7rem; letter-spacing: 0.5px;">
<div class="container-fluid"> {% trans "POWERED BY" %} <span class="text-white fw-bold">TENHAL</span>
<div class="d-flex justify-content-between align-items-center flex-wrap" style="max-width: 1600px; margin: 0 auto;"> </div>
<p class="mb-0 text-white-50"> </a>
&copy; {% now "Y" %} {% trans "King Abdullah Academic University Hospital (KAAUH)." %} </div>
{% trans "All rights reserved." %} </aside>
</p>
<p class="mb-0 text-white-50"> <div class="main-wrapper">
{% if request.user.user_type == 'candidate' %} <header class="content-header justify-content-between">
{% trans "Candidate Portal" %} <div class="d-flex align-items-center">
{% elif request.user.user_type == 'agency' %} <button class="btn me-2" id="sidebarCollapse">
{% trans "Agency Portal" %} <i class="fas fa-bars"></i>
{% endif %} </button>
</p>
<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> </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' %} {% 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/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 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 %} {% block customJS %}{% endblock %}
</body> </body>
</html> </html>

View File

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

View File

@ -192,47 +192,7 @@
<!-- Pagination --> <!-- Pagination -->
{% if page_obj.has_other_pages %} {% if page_obj.has_other_pages %}
<nav aria-label="{% trans 'Assignments pagination' %}"> {% include "includes/paginator.html" %}
<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>
{% endif %} {% endif %}
{% else %} {% else %}
<div class="text-center py-5"> <div class="text-center py-5">

View File

@ -1,7 +1,7 @@
{% extends 'portal_base.html' %} {% extends 'portal_base.html' %}
{% load static i18n %} {% 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 %} {% block customCSS %}
<style> <style>

View File

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

View File

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

View File

@ -1,7 +1,7 @@
{% extends 'portal_base.html' %} {% extends 'portal_base.html' %}
{% load static i18n crispy_forms_tags %} {% 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 %} {% block customCSS %}
<style> <style>

View File

@ -292,7 +292,7 @@
{# Header: Larger, more dynamic on large screens. Stacks cleanly on mobile. #} {# 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"> <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);"> <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> </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"> {% 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" %} <i class="fas fa-edit me-2"></i> {% trans "Update Profile" %}

View File

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

View File

@ -9,7 +9,7 @@
{% if LANGUAGE_CODE == 'ar' %} {% if LANGUAGE_CODE == 'ar' %}
<title>{{ application.resume_data_ar.full_name|default:"Application" }} - Application Profile</title> <title>{{ application.resume_data_ar.full_name|default:"Application" }} - Application Profile</title>
{% else %} {% 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 %} {% endif %}
<!-- Use a modern icon set --> <!-- Use a modern icon set -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css"> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
@ -915,7 +915,7 @@
{% if LANGUAGE_CODE == 'ar' %} {% if LANGUAGE_CODE == 'ar' %}
{% with data_source=application.resume_data_ar analysis_source=application.analysis_data_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' %} {% include 'recruitment/partials/ai_overview_breadcromb.html' %}
@ -1264,8 +1264,7 @@
{% else %} {% else %}
{% with data_source=application.resume_data_en analysis_source=application.analysis_data_en %} {% 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' %} {% include 'recruitment/partials/ai_overview_breadcromb.html' %}
<header class="header-box"> <header class="header-box">
<div class="header-info"> <div class="header-info">

View File

@ -1,7 +1,7 @@
{% extends "base.html" %} {% extends "base.html" %}
{% load static i18n crispy_forms_tags %} {% 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 %} {% block customCSS %}
<style> <style>

View File

@ -1,7 +1,7 @@
{% extends 'base.html' %} {% extends 'base.html' %}
{% load static i18n %} {% 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 %} {% block customCSS %}
<style> <style>
/* KAAT-S UI Variables */ /* KAAT-S UI Variables */

View File

@ -1,7 +1,7 @@
{% extends 'base.html' %} {% extends 'base.html' %}
{% load static i18n %} {% 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 %} {% block customCSS %}
<style> <style>
/* KAAT-S UI Variables */ /* KAAT-S UI Variables */

View File

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

View File

@ -1,7 +1,7 @@
{% extends 'base.html' %} {% extends 'base.html' %}
{% load static i18n %} {% 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 %} {% block customCSS %}
<style> <style>
/* KAAT-S UI Variables */ /* KAAT-S UI Variables */

View File

@ -1,7 +1,7 @@
{% extends "base.html" %} {% extends "base.html" %}
{% load static i18n %} {% load static i18n %}
{% block title %}Applications - {{ block.super }}{% endblock %} {% block title %}{% trans "Applications" %} - {{ block.super }}{% endblock %}
{% block customCSS %} {% block customCSS %}
<style> <style>
@ -120,20 +120,7 @@
background-color: var(--kaauh-gray-light); 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 & Search Layout Adjustments */
.filter-buttons { .filter-buttons {
display: flex; display: flex;

View File

@ -1,6 +1,6 @@
{% extends 'base.html' %} {% extends 'base.html' %}
{% load static i18n %} {% 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 %} {% block customCSS %}
<style> <style>
/* KAAT-S UI Variables */ /* KAAT-S UI Variables */

View File

@ -1,7 +1,7 @@
{% extends 'base.html' %} {% extends 'base.html' %}
{% load static i18n %} {% 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 %} {% block customCSS %}
<style> <style>
/* KAAT-S UI Variables */ /* KAAT-S UI Variables */

View File

@ -1,7 +1,7 @@
{% extends "base.html" %} {% extends "base.html" %}
{% load static i18n %} {% load static i18n %}
{% block title %}{% trans "Delete Candidate" %} - {{ block.super }}{% endblock %} {% block title %}{% trans "Delete Applicant" %} - {{ block.super }}{% endblock %}
{% block customCSS %} {% block customCSS %}
<style> <style>
@ -176,14 +176,14 @@
<div> <div>
<h1 class="h3 mb-1" style="color: var(--kaauh-teal-dark); font-weight: 700;"> <h1 class="h3 mb-1" style="color: var(--kaauh-teal-dark); font-weight: 700;">
<i class="fas fa-exclamation-triangle me-2"></i> <i class="fas fa-exclamation-triangle me-2"></i>
{% trans "Delete Candidate" %} {% trans "Delete Applicant" %}
</h1> </h1>
<p class="text-muted mb-0"> <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> </p>
</div> </div>
<a href="{% url 'candidate_detail' object.slug %}" class="btn btn-secondary"> <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> </a>
</div> </div>
@ -196,7 +196,7 @@
</div> </div>
<h3 class="warning-title">{% trans "Warning: This action cannot be undone!" %}</h3> <h3 class="warning-title">{% trans "Warning: This action cannot be undone!" %}</h3>
<p class="warning-text"> <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> </p>
</div> </div>
@ -205,7 +205,7 @@
<div class="card-header bg-white border-bottom"> <div class="card-header bg-white border-bottom">
<h5 class="mb-0" style="color: var(--kaauh-teal-dark);"> <h5 class="mb-0" style="color: var(--kaauh-teal-dark);">
<i class="fas fa-user me-2"></i> <i class="fas fa-user me-2"></i>
{% trans "Candidate to be Deleted" %} {% trans "Applicant to be Deleted" %}
</h5> </h5>
</div> </div>
<div class="card-body"> <div class="card-body">
@ -288,14 +288,14 @@
<div class="card-header bg-white border-bottom"> <div class="card-header bg-white border-bottom">
<h5 class="mb-0" style="color: var(--kaauh-teal-dark);"> <h5 class="mb-0" style="color: var(--kaauh-teal-dark);">
<i class="fas fa-list me-2"></i> <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> </h5>
</div> </div>
<div class="card-body"> <div class="card-body">
<ul class="consequence-list"> <ul class="consequence-list">
<li> <li>
<i class="fas fa-times-circle"></i> <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>
<li> <li>
<i class="fas fa-times-circle"></i> <i class="fas fa-times-circle"></i>
@ -327,7 +327,7 @@
<div class="form-check"> <div class="form-check">
<input class="form-check-input" type="checkbox" id="confirm_delete" name="confirm_delete" required> <input class="form-check-input" type="checkbox" id="confirm_delete" name="confirm_delete" required>
<label class="form-check-label" for="confirm_delete"> <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> </label>
</div> </div>
</div> </div>
@ -342,7 +342,7 @@
id="deleteButton" id="deleteButton"
disabled> disabled>
<i class="fas fa-trash me-2"></i> <i class="fas fa-trash me-2"></i>
{% trans "Delete Candidate Permanently" %} {% trans "Delete Applicant Permanently" %}
</button> </button>
</div> </div>
</form> </form>

View File

@ -1,15 +1,15 @@
{% extends "portal_base.html" %} {% extends "portal_base.html" %}
{% load static %} {% load static %}
{% load i18n %}
{% block title %}Document Management{% endblock %} {% block title %}{% trans "Document Management" %}{% endblock %}
{% block content %} {% block content %}
<div class="container mx-auto px-4 py-8"> <div class="container mx-auto px-4 py-8">
<div class="flex justify-between items-center mb-6"> <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"> <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> <i class="fas fa-upload mr-2"></i>
Upload Document {% trans "Upload Document" %}
</button> </button>
</div> </div>
@ -21,22 +21,22 @@
<thead class="bg-gray-50"> <thead class="bg-gray-50">
<tr> <tr>
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"> <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>
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"> <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>
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"> <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>
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"> <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>
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"> <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>
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"> <th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
Actions {% trans "Actions" %}
</th> </th>
</tr> </tr>
</thead> </thead>
@ -88,7 +88,7 @@
{% empty %} {% empty %}
<tr> <tr>
<td colspan="6" class="px-6 py-4 text-center text-gray-500"> <td colspan="6" class="px-6 py-4 text-center text-gray-500">
No documents uploaded yet. {% trans "No documents uploaded yet." %}
</td> </td>
</tr> </tr>
{% endfor %} {% endfor %}
@ -105,7 +105,7 @@
<div class="bg-white px-4 pt-5 pb-4 sm:p-6 sm:pb-4"> <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"> <div class="flex justify-between items-start mb-4">
<h3 class="text-lg leading-6 font-medium text-gray-900"> <h3 class="text-lg leading-6 font-medium text-gray-900">
Upload New Document {% trans "Upload New Document" %}
</h3> </h3>
<button onclick="hideUploadModal()" class="text-gray-400 hover:text-gray-600"> <button onclick="hideUploadModal()" class="text-gray-400 hover:text-gray-600">
<i class="fas fa-times"></i> <i class="fas fa-times"></i>
@ -115,26 +115,26 @@
{% csrf_token %} {% csrf_token %}
<div class="space-y-4"> <div class="space-y-4">
<div> <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 <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"> 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="resume">{% trans "Resume" %}</option>
<option value="cover_letter">Cover Letter</option> <option value="cover_letter">{% trans "Cover Letter" %}</option>
<option value="transcript">Transcript</option> <option value="transcript">{% trans "Transcript" %}</option>
<option value="certificate">Certificate</option> <option value="certificate">{% trans "Certificate" %}</option>
<option value="id_document">ID Document</option> <option value="id_document">{% trans "ID Document" %}</option>
<option value="portfolio">Portfolio</option> <option value="portfolio">{% trans "Portfolio" %}</option>
<option value="other">Other</option> <option value="other">{% trans "Other" %}</option>
</select> </select>
</div> </div>
<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" <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" 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> placeholder="Optional description of the document"></textarea>
</div> </div>
<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 <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" 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"> 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"> <div class="bg-gray-50 px-4 py-3 sm:px-6 sm:flex sm:flex-row-reverse">
<button type="button" onclick="hideUploadModal()" <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"> 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>
<button type="submit" <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"> 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> </button>
</div> </div>
</form> </form>
@ -167,7 +167,7 @@ function hideUploadModal() {
} }
function deleteDocument(documentId) { 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/`, { fetch(`/candidate/documents/${documentId}/delete/`, {
method: 'POST', method: 'POST',
headers: { headers: {
@ -184,14 +184,14 @@ function deleteDocument(documentId) {
row.remove(); row.remove();
// Show success message // Show success message
showNotification('Document deleted successfully!', 'success'); showNotification('{% trans "Document deleted successfully!" %}', 'success');
} else { } else {
showNotification(data.error || 'Failed to delete document', 'error'); showNotification(data.error || '{% trans "Failed to delete document" %}', 'error');
} }
}) })
.catch(error => { .catch(error => {
console.error('Error:', 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 // Reload the page to show the new document
window.location.reload(); window.location.reload();
} else { } else {
showNotification(data.error || 'Failed to upload document', 'error'); showNotification(data.error || '{% trans "Failed to upload document" %}', 'error');
} }
}) })
.catch(error => { .catch(error => {
console.error('Error:', 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' %} {% extends 'portal_base.html' %}
{% load static i18n %} {% load static i18n %}
{% block title %}{% trans "Candidate Dashboard" %} - ATS{% endblock %} {% block title %}{% trans "Applicant Dashboard" %} - ATS{% endblock %}
{% block content %} {% block content %}
<div class="container-fluid"> <div class="container-fluid">

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,17 +1,18 @@
{% extends "base.html" %} {% extends "base.html" %}
{% load i18n %}
{% load widget_tweaks %} {% load widget_tweaks %}
{% block title %}{{ title }}{% endblock %} {% block title %}{{ title }}{% endblock %}
{% block content %} {% block content %}
<div class="container-fluid py-4"> <div class="container-fluid py-4">
<div class="row"> <div class="row">
<div class="col-12"> <div class="col-12">
<!-- Breadcrumb Navigation -->
<nav aria-label="breadcrumb"> <nav aria-label="breadcrumb">
<ol class="breadcrumb"> <ol class="breadcrumb">
<li class="breadcrumb-item"> <li class="breadcrumb-item">
<a href="{% url 'settings_list' %}" class="text-decoration-none text-secondary"> <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> </a>
</li> </li>
<li class="breadcrumb-item"> <li class="breadcrumb-item">
@ -20,62 +21,58 @@
</a> </a>
</li> </li>
<li class="breadcrumb-item active" aria-current="page" <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> </ol>
</nav> </nav>
<!-- Header -->
<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 text-primary-theme"> <h1 class="h3 mb-0 text-primary-theme">
<i class="fas fa-exclamation-triangle me-2"></i> <i class="fas fa-exclamation-triangle me-2"></i>
{{ title }} {{ title }}
</h1> </h1>
<a href="{{ cancel_url }}" class="btn btn-outline-secondary"> <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> </a>
</div> </div>
<!-- Warning Card -->
<div class="card shadow-sm border-danger"> <div class="card shadow-sm border-danger">
<div class="card-body"> <div class="card-body">
{% if messages %} {% if messages %}
{% for message in messages %} {% for message in messages %}
<div class="alert alert-{{ message.tags }} alert-dismissible fade show" role="alert"> <div class="alert alert-{{ message.tags }} alert-dismissible fade show" role="alert">
{{ message }} {{ 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> </div>
{% endfor %} {% endfor %}
{% endif %} {% endif %}
<!-- Warning Alert -->
<div class="alert alert-warning d-flex align-items-center" role="alert"> <div class="alert alert-warning d-flex align-items-center" role="alert">
<i class="fas fa-exclamation-triangle me-3 fa-lg"></i> <i class="fas fa-exclamation-triangle me-3 fa-lg"></i>
<div> <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>
</div> </div>
<!-- Setting Details -->
<div class="row"> <div class="row">
<div class="col-12"> <div class="col-12">
<h5 class="mb-3">Setting Details:</h5> <h5 class="mb-3">{% trans "Setting Details:" %}</h5>
<div class="table-responsive"> <div class="table-responsive">
<table class="table table-hover"> <table class="table table-hover">
<tbody> <tbody>
<tr> <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> <td><code class="text-primary-theme">{{ setting.key }}</code></td>
</tr> </tr>
<tr> <tr>
<th class="text-primary-theme">Value:</th> <th class="text-primary-theme">{% trans "Value:" %}</th>
<td>{{ setting.value|default:"-" }}</td> <td>{{ setting.value|default:"-" }}</td>
</tr> </tr>
<tr> <tr>
<th class="text-primary-theme">Description:</th> <th class="text-primary-theme">{% trans "Description:" %}</th>
<td>{{ setting.description|default:"No description" }}</td> <td>{{ setting.description|default:_("No description") }}</td>
</tr> </tr>
<tr> <tr>
<th class="text-primary-theme">Type:</th> <th class="text-primary-theme">{% trans "Type:" %}</th>
<td> <td>
<span class="badge bg-primary-theme text-white"> <span class="badge bg-primary-theme text-white">
{{ setting.get_type_display }} {{ setting.get_type_display }}
@ -83,12 +80,12 @@
</td> </td>
</tr> </tr>
<tr> <tr>
<th class="text-primary-theme">Status:</th> <th class="text-primary-theme">{% trans "Status:" %}</th>
<td> <td>
{% if setting.is_active %} {% 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 %} {% else %}
<span class="badge bg-secondary text-white">Inactive</span> <span class="badge bg-secondary text-white">{% trans "Inactive" %}</span>
{% endif %} {% endif %}
</td> </td>
</tr> </tr>
@ -98,17 +95,16 @@
</div> </div>
</div> </div>
<!-- Action Buttons -->
<div class="row mt-4"> <div class="row mt-4">
<div class="col-12"> <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 %} {% csrf_token %}
<div class="d-flex gap-2"> <div class="d-flex gap-2">
<button type="submit" class="btn btn-danger"> <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> </button>
<a href="{{ cancel_url }}" class="btn btn-outline-secondary"> <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> </a>
</div> </div>
</form> </form>
@ -116,8 +112,7 @@
</div> </div>
</div> </div>
</div> </div>
</div>
</div> </div>
</div> </div>
</div> </div>
{% endblock %} {% endblock %}

View File

@ -1,4 +1,5 @@
{% extends "base.html" %} {% extends "base.html" %}
{% load i18n %}
{% load crispy_forms_tags %} {% load crispy_forms_tags %}
{% block title %}{{ title }}{% endblock %} {% block title %}{{ title }}{% endblock %}
@ -122,7 +123,7 @@
<ol class="breadcrumb"> <ol class="breadcrumb">
<li class="breadcrumb-item"> <li class="breadcrumb-item">
<a href="{% url 'settings_list' %}" class="text-decoration-none text-secondary"> <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> </a>
</li> </li>
{% if setting %} {% if setting %}
@ -132,10 +133,10 @@
</a> </a>
</li> </li>
<li class="breadcrumb-item active" aria-current="page" <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 %} {% else %}
<li class="breadcrumb-item active" aria-current="page" <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 %} {% endif %}
</ol> </ol>
</nav> </nav>
@ -148,14 +149,14 @@
<div class="d-flex gap-2"> <div class="d-flex gap-2">
{% if setting %} {% if setting %}
<a href="{% url 'settings_detail' setting.pk %}" class="btn btn-outline-secondary"> <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>
<a href="{% url 'settings_delete' setting.pk %}" class="btn btn-danger"> <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> </a>
{% endif %} {% endif %}
<a href="{% url 'settings_list' %}" class="btn btn-outline-secondary"> <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> </a>
</div> </div>
</div> </div>
@ -166,7 +167,7 @@
{% if form.non_field_errors %} {% if form.non_field_errors %}
<div class="alert alert-danger" role="alert"> <div class="alert alert-danger" role="alert">
<h5 class="alert-heading"> <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> </h5>
{% for error in form.non_field_errors %} {% for error in form.non_field_errors %}
<p class="mb-0">{{ error }}</p> <p class="mb-0">{{ error }}</p>
@ -191,7 +192,7 @@
<i class="fas fa-save me-1"></i> {{ button_text }} <i class="fas fa-save me-1"></i> {{ button_text }}
</button> </button>
<a href="{% url 'settings_list' %}" class="btn btn-outline-secondary"> <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> </a>
</div> </div>
</form> </form>

View File

@ -1,5 +1,5 @@
{% extends "base.html" %} {% extends "base.html" %}
{% load static %} {% load static i18n %}
{% block title %}{{ title }}{% endblock %} {% block title %}{{ title }}{% endblock %}
@ -10,7 +10,7 @@
<div class="d-flex justify-content-between align-items-center mb-4"> <div class="d-flex justify-content-between align-items-center mb-4">
<h4 class="h3 mb-0">{{ title }}</h4> <h4 class="h3 mb-0">{{ title }}</h4>
<a href="{% url 'source_detail' source.pk %}" class="btn btn-outline-secondary"> <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> </a>
</div> </div>
@ -21,40 +21,40 @@
<div class="alert alert-warning d-flex align-items-center"> <div class="alert alert-warning d-flex align-items-center">
<i class="fas fa-exclamation-triangle fa-2x me-3"></i> <i class="fas fa-exclamation-triangle fa-2x me-3"></i>
<div> <div>
<strong>Warning:</strong> This action cannot be undone. <strong>{% trans "Warning:" %}</strong> {% trans "This action cannot be undone.
Deleting this source will also remove all associated integration logs and API credentials. Deleting this source will also remove all associated integration logs and API credentials." %}
</div> </div>
</div> </div>
<div class="mb-4"> <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 bg-light">
<div class="card-body"> <div class="card-body">
<div class="row"> <div class="row">
<div class="col-md-6"> <div class="col-md-6">
<strong>Name:</strong><br> <strong>{% trans "Name:" %}</strong><br>
{{ source.name }} {{ source.name }}
</div> </div>
<div class="col-md-6"> <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> <span class="badge bg-info">{{ source.get_source_type_display }}</span>
</div> </div>
</div> </div>
{% if source.description %} {% if source.description %}
<hr> <hr>
<div> <div>
<strong>Description:</strong><br> <strong>{% trans "Description:" %}</strong><br>
{{ source.description|linebreaks }} {{ source.description|linebreaks }}
</div> </div>
{% endif %} {% endif %}
<hr> <hr>
<div class="row"> <div class="row">
<div class="col-md-6"> <div class="col-md-6">
<strong>Created:</strong><br> <strong>{% trans "Created:" %}</strong><br>
{{ source.created_at|date:"M d, Y H:i" }} {{ source.created_at|date:"M d, Y H:i" }}
</div> </div>
<div class="col-md-6"> <div class="col-md-6">
<strong>Total API Calls:</strong><br> <strong>{% trans "Total API Calls:" %}</strong><br>
{{ source.integration_logs.count }} {{ source.integration_logs.count }}
</div> </div>
</div> </div>
@ -66,10 +66,10 @@
{% csrf_token %} {% csrf_token %}
<div class="d-flex justify-content-between"> <div class="d-flex justify-content-between">
<a href="{{ cancel_url }}" class="btn btn-outline-secondary"> <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> </a>
<button type="submit" class="btn btn-danger"> <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> </button>
</div> </div>
</form> </form>
@ -80,25 +80,25 @@
<div class="col-md-4"> <div class="col-md-4">
<div class="card"> <div class="card">
<div class="card-header"> <div class="card-header">
<h6 class="mb-0">Impact Summary</h6> <h6 class="mb-0">{% trans "Impact Summary" %}</h6>
</div> </div>
<div class="card-body"> <div class="card-body">
<div class="mb-3"> <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"> <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> </div>
<div class="mb-3"> <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"> <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> </div>
<div class="mb-3"> <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"> <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> </div>
</div> </div>

View File

@ -4,6 +4,7 @@
{% block title %}{% trans "Sources" %}{% endblock %} {% block title %}{% trans "Sources" %}{% endblock %}
{% block content %} {% block content %}
<div class="container-fluid"> <div class="container-fluid">
<nav aria-label="breadcrumb"> <nav aria-label="breadcrumb">
<ol class="breadcrumb"> <ol class="breadcrumb">
@ -16,7 +17,7 @@
</nav> </nav>
<div class="row"> <div class="row">
<div class="col-12"> <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> <h1 class="h3 mb-0">{% trans "Integration Sources" %}</h1>
<a href="{% url 'source_create' %}" class="btn btn-main-action"> <a href="{% url 'source_create' %}" class="btn btn-main-action">
{% trans "Create Source for Integration" %} <i class="fas fa-plus"></i> {% trans "Create Source for Integration" %} <i class="fas fa-plus"></i>
@ -74,6 +75,7 @@
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
{% for source in page_obj %} {% for source in page_obj %}
<tr> <tr>
<td> <td>
@ -129,7 +131,7 @@
<!-- Pagination --> <!-- Pagination -->
{% include "includes/paginator.html" %} {% include "includes/paginator.html" %}
{% else %} {% else %}
<div class="text-center py-5"> <div class="text-center py-5">
<i class="fas fa-database fa-3x text-muted mb-3"></i> <i class="fas fa-database fa-3x text-muted mb-3"></i>
@ -153,7 +155,7 @@
</div> </div>
{% endblock %} {% endblock %}
{% block extra_js %} {% block customJS %}
<script> <script>
// Auto-refresh after status toggle // Auto-refresh after status toggle
document.body.addEventListener('htmx:afterRequest', function(evt) { 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> </tbody>
</table> </table>
</div> </div>
{% include "includes/paginator.html" %}
{# NOTE: Add Pagination links here if the `staffs` object is a Paginator object #} {# 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="mb-3">
<div class="info-label">{% trans "Last Login" %}</div> <div class="info-label">{% trans "Last Login" %}</div>
<div class="info-value"> <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>
</div> </div>
@ -221,7 +221,7 @@
<div class="modal-dialog"> <div class="modal-dialog">
<div class="modal-content"> <div class="modal-content">
<div class="modal-header"> <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> <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div> </div>
<div class="modal-body"> <div class="modal-body">
@ -229,17 +229,17 @@
{% csrf_token %} {% csrf_token %}
<div class="mb-3"> <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 #} {# 1. Check if an image currently exists on the bound instance #}
{% if profile_form.instance.profile_image %} {% if profile_form.instance.profile_image %}
<div class="mb-2"> <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 #} {# 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"> <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> </a>
{# Image Preview #} {# Image Preview #}
@ -271,8 +271,8 @@
</div> </div>
<div class="modal-footer mt-4"> <div class="modal-footer mt-4">
<button type="button" class="btn btn-lg btn-secondary" data-bs-dismiss="modal">Close</button> <button type="button" class="btn btn-lg btn-secondary" data-bs-dismiss="modal">{% trans "Close" %}</button>
<button type="submit" class="btn btn-primary">Save changes</button> <button type="submit" class="btn btn-primary">{% trans "Save changes" %}</button>
</div> </div>
</form> </form>
</div> </div>

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