Compare commits
3 Commits
8e1e3452e9
...
4467a56d67
| Author | SHA1 | Date | |
|---|---|---|---|
| 4467a56d67 | |||
| 6b74990791 | |||
| 51583371db |
Binary file not shown.
Binary file not shown.
@ -57,6 +57,7 @@ INSTALLED_APPS = [
|
||||
'django_countries',
|
||||
'django_celery_results',
|
||||
'django_q',
|
||||
'widget_tweaks',
|
||||
'easyaudit'
|
||||
|
||||
]
|
||||
@ -69,13 +70,13 @@ SITE_ID = 1
|
||||
LOGIN_REDIRECT_URL = '/dashboard/'
|
||||
|
||||
|
||||
ACCOUNT_LOGOUT_REDIRECT_URL = '/'
|
||||
ACCOUNT_LOGOUT_REDIRECT_URL = '/'
|
||||
|
||||
|
||||
ACCOUNT_SIGNUP_REDIRECT_URL = '/dashboard/'
|
||||
ACCOUNT_SIGNUP_REDIRECT_URL = '/dashboard/'
|
||||
|
||||
|
||||
LOGIN_URL = '/accounts/login/'
|
||||
LOGIN_URL = '/accounts/login/'
|
||||
|
||||
|
||||
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -5,9 +5,11 @@ from django.forms.formsets import formset_factory
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from crispy_forms.helper import FormHelper
|
||||
from crispy_forms.layout import Layout, Submit, Row, Column, Field, Div
|
||||
from django.contrib.auth.models import User
|
||||
from .models import (
|
||||
ZoomMeeting, Candidate,TrainingMaterial,JobPosting,
|
||||
FormTemplate,InterviewSchedule,BreakTime,JobPostingImage
|
||||
FormTemplate,InterviewSchedule,BreakTime,JobPostingImage,
|
||||
Profile
|
||||
)
|
||||
# from django_summernote.widgets import SummernoteWidget
|
||||
from django_ckeditor_5.widgets import CKEditor5Widget
|
||||
@ -505,4 +507,16 @@ class ScheduleInterviewForCandiateForm(forms.ModelForm):
|
||||
'end_time': forms.TimeInput(attrs={'type': 'time', 'class': 'form-control'}),
|
||||
'interview_duration': forms.NumberInput(attrs={'class': 'form-control'}),
|
||||
'buffer_time': forms.NumberInput(attrs={'class': 'form-control'}),
|
||||
}
|
||||
}
|
||||
|
||||
class ProfileImageUploadForm(forms.ModelForm):
|
||||
class Meta:
|
||||
model=Profile
|
||||
fields=['profile_image']
|
||||
|
||||
|
||||
|
||||
# class UserEditForms(forms.ModelForm):
|
||||
# class Meta:
|
||||
# model = User
|
||||
# fields = ['first_name', 'last_name']
|
||||
@ -10,6 +10,7 @@ from django_countries.fields import CountryField
|
||||
from django.urls import reverse
|
||||
# from ckeditor.fields import RichTextField
|
||||
from django_ckeditor_5.fields import CKEditor5Field
|
||||
from django.utils.html import strip_tags
|
||||
|
||||
|
||||
|
||||
@ -246,6 +247,43 @@ class JobPosting(Base):
|
||||
"form_wizard", kwargs={"slug": self.form_template.slug}
|
||||
)
|
||||
self.save()
|
||||
|
||||
def _check_content(self, field_value):
|
||||
"""Helper to check if a field contains meaningful content."""
|
||||
if not field_value:
|
||||
return False
|
||||
|
||||
# 1. Replace the common HTML non-breaking space entity with a standard space.
|
||||
content = field_value.replace(' ', ' ')
|
||||
|
||||
# 2. Remove all HTML tags (leaving only text and remaining spaces).
|
||||
stripped = strip_tags(content)
|
||||
|
||||
# 3. Use .strip() to remove ALL leading/trailing whitespace, including the ones from step 1.
|
||||
final_content = stripped.strip()
|
||||
|
||||
# Return True if any content remains after stripping tags and spaces.
|
||||
return bool(final_content)
|
||||
|
||||
|
||||
@property
|
||||
def has_description_content(self):
|
||||
"""Returns True if the description field has meaningful content."""
|
||||
return self._check_content(self.description)
|
||||
|
||||
@property
|
||||
def has_qualifications_content(self):
|
||||
"""Returns True if the qualifications field has meaningful content."""
|
||||
return self._check_content(self.qualifications)
|
||||
|
||||
# Add similar properties for benefits and application_instructions
|
||||
@property
|
||||
def has_benefits_content(self):
|
||||
return self._check_content(self.benefits)
|
||||
|
||||
@property
|
||||
def has_application_instructions_content(self):
|
||||
return self._check_content(self.application_instructions)
|
||||
|
||||
|
||||
class JobPostingImage(models.Model):
|
||||
@ -273,12 +311,12 @@ class Candidate(Base):
|
||||
CANDIDATE = "Candidate", _("Candidate")
|
||||
|
||||
# Stage transition validation constants
|
||||
# STAGE_SEQUENCE = {
|
||||
# "Applied": ["Exam", "Interview", "Offer"],
|
||||
# "Exam": ["Interview", "Offer"],
|
||||
# "Interview": ["Offer"],
|
||||
# "Offer": [], # Final stage - no further transitions
|
||||
# }
|
||||
STAGE_SEQUENCE = {
|
||||
"Applied": ["Exam", "Interview", "Offer"],
|
||||
"Exam": ["Interview", "Offer"],
|
||||
"Interview": ["Offer"],
|
||||
"Offer": [], # Final stage - no further transitions
|
||||
}
|
||||
|
||||
job = models.ForeignKey(
|
||||
JobPosting,
|
||||
@ -412,13 +450,13 @@ class Candidate(Base):
|
||||
# allowed_next_stages = self.STAGE_SEQUENCE.get(old_stage, [])
|
||||
# return new_stage in allowed_next_stages
|
||||
|
||||
# def get_available_stages(self):
|
||||
# """Get list of stages this candidate can transition to"""
|
||||
# if not self.pk: # New record
|
||||
# return ["Applied"]
|
||||
def get_available_stages(self):
|
||||
"""Get list of stages this candidate can transition to"""
|
||||
if not self.pk: # New record
|
||||
return ["Applied"]
|
||||
|
||||
# old_stage = self.__class__.objects.get(pk=self.pk).stage
|
||||
# return self.STAGE_SEQUENCE.get(old_stage, [])
|
||||
old_stage = self.__class__.objects.get(pk=self.pk).stage
|
||||
return self.STAGE_SEQUENCE.get(old_stage, [])
|
||||
|
||||
@property
|
||||
def submission(self):
|
||||
|
||||
@ -68,6 +68,8 @@ urlpatterns = [
|
||||
path('jobs/<slug:slug>/candidate_exam_view/', views.candidate_exam_view, name='candidate_exam_view'),
|
||||
path('jobs/<slug:slug>/candidate_interview_view/', views.candidate_interview_view, name='candidate_interview_view'),
|
||||
|
||||
path('jobs/<slug:slug>/<int:candidate_id>/reschedule_meeting_for_candidate/<int:meeting_id>/', views.reschedule_meeting_for_candidate, name='reschedule_meeting_for_candidate'),
|
||||
|
||||
path('jobs/<slug:slug>/update_candidate_exam_status/', views.update_candidate_exam_status, name='update_candidate_exam_status'),
|
||||
path('jobs/<slug:slug>/bulk_update_candidate_exam_status/', views.bulk_update_candidate_exam_status, name='bulk_update_candidate_exam_status'),
|
||||
|
||||
@ -100,9 +102,12 @@ urlpatterns = [
|
||||
path('jobs/<slug:job_slug>/candidates/<int:candidate_pk>/reschedule-meeting/<int:interview_pk>/', views.reschedule_candidate_meeting, name='reschedule_candidate_meeting'),
|
||||
path('api/jobs/<slug:job_slug>/candidates/<int:candidate_pk>/reschedule-meeting/<int:interview_pk>/', views.api_reschedule_candidate_meeting, name='api_reschedule_candidate_meeting'),
|
||||
# New URL for simple page-based meeting scheduling
|
||||
path('jobs/<slug:job_slug>/candidates/<int:candidate_pk>/schedule-meeting-page/', views.schedule_meeting_for_candidate, name='schedule_meeting_for_candidate'),
|
||||
path('jobs/<slug:slug>/candidates/<int:candidate_pk>/schedule-meeting-page/', views.schedule_meeting_for_candidate, name='schedule_meeting_for_candidate'),
|
||||
path('jobs/<slug:slug>/candidates/<int:candidate_pk>/delete_meeting_for_candidate/<int:meeting_id>/', views.delete_meeting_for_candidate, name='delete_meeting_for_candidate'),
|
||||
|
||||
|
||||
# users urls
|
||||
path('user/<int:pk>',views.user_detail,name='user_detail')
|
||||
path('user/<int:pk>',views.user_detail,name='user_detail'),
|
||||
path('user/user_profile_image_update/<int:pk>',views.user_profile_image_update,name='user_profile_image_update'),
|
||||
|
||||
]
|
||||
|
||||
@ -579,3 +579,38 @@ def get_candidates_from_request(request):
|
||||
except Exception as e:
|
||||
logger.error(e)
|
||||
yield None
|
||||
|
||||
|
||||
|
||||
def update_meeting(instance, updated_data):
|
||||
result = update_zoom_meeting(instance.meeting_id, updated_data)
|
||||
if result["status"] == "success":
|
||||
# Fetch the latest details from Zoom after successful update
|
||||
details_result = get_zoom_meeting_details(instance.meeting_id)
|
||||
|
||||
if details_result["status"] == "success":
|
||||
zoom_details = details_result["meeting_details"]
|
||||
# Update instance with fetched details
|
||||
|
||||
instance.topic = zoom_details.get("topic", instance.topic)
|
||||
|
||||
instance.duration = zoom_details.get("duration", instance.duration)
|
||||
instance.join_url = zoom_details.get("join_url", instance.join_url)
|
||||
instance.password = zoom_details.get("password", instance.password)
|
||||
# Corrected status assignment: instance.status, not instance.password
|
||||
instance.status = zoom_details.get("status")
|
||||
|
||||
instance.zoom_gateway_response = details_result.get("meeting_details") # Store full response
|
||||
instance.save()
|
||||
logger.info(f"Successfully updated Zoom meeting {instance.meeting_id}.")
|
||||
return {"status": "success", "message": "Zoom meeting updated successfully."}
|
||||
elif details_result["status"] == "error":
|
||||
# If fetching details fails, save with form data and log a warning
|
||||
logger.warning(
|
||||
f"Successfully updated Zoom meeting {instance.meeting_id}, but failed to fetch updated details. "
|
||||
f"Error: {details_result.get('message', 'Unknown error')}"
|
||||
)
|
||||
return {"status": "success", "message": "Zoom meeting updated successfully."}
|
||||
|
||||
logger.warning(f"Failed to update Zoom meeting {instance.meeting_id}. Error: {result.get('message', 'Unknown error')}")
|
||||
return {"status": "error", "message": result.get("message", "Zoom meeting update failed.")}
|
||||
@ -19,7 +19,9 @@ from .forms import (
|
||||
FormTemplateForm,
|
||||
InterviewScheduleForm,JobPostingStatusForm,
|
||||
BreakTimeFormSet,
|
||||
JobPostingImageForm
|
||||
JobPostingImageForm,
|
||||
ProfileImageUploadForm,
|
||||
|
||||
)
|
||||
from rest_framework import viewsets
|
||||
from django.contrib import messages
|
||||
@ -32,6 +34,7 @@ from .utils import (
|
||||
create_zoom_meeting,
|
||||
delete_zoom_meeting,
|
||||
get_candidates_from_request,
|
||||
update_meeting,
|
||||
update_zoom_meeting,
|
||||
get_zoom_meeting_details,
|
||||
schedule_interviews,
|
||||
@ -50,7 +53,8 @@ from .models import (
|
||||
ZoomMeeting,
|
||||
Candidate,
|
||||
JobPosting,
|
||||
ScheduledInterview
|
||||
ScheduledInterview,
|
||||
JobPostingImage
|
||||
)
|
||||
import logging
|
||||
from datastar_py.django import (
|
||||
@ -171,39 +175,14 @@ class ZoomMeetingUpdateView(UpdateView):
|
||||
if instance.start_time < timezone.now():
|
||||
messages.error(self.request, "Start time must be in the future.")
|
||||
return redirect(f"/update-meeting/{instance.pk}/", status=400)
|
||||
result = update_zoom_meeting(instance.meeting_id, updated_data)
|
||||
|
||||
result = update_meeting(instance, updated_data)
|
||||
|
||||
if result["status"] == "success":
|
||||
# Fetch the latest details from Zoom after successful update
|
||||
details_result = get_zoom_meeting_details(instance.meeting_id)
|
||||
|
||||
if details_result["status"] == "success":
|
||||
zoom_details = details_result["meeting_details"]
|
||||
# Update instance with fetched details
|
||||
|
||||
instance.topic = zoom_details.get("topic", instance.topic)
|
||||
|
||||
instance.duration = zoom_details.get("duration", instance.duration)
|
||||
instance.join_url = zoom_details.get("join_url", instance.join_url)
|
||||
instance.password = zoom_details.get("password", instance.password)
|
||||
# Corrected status assignment: instance.status, not instance.password
|
||||
instance.status = zoom_details.get("status")
|
||||
|
||||
instance.zoom_gateway_response = details_result.get("meeting_details") # Store full response
|
||||
instance.save()
|
||||
messages.success(self.request, result["message"] + " Local data updated from Zoom.")
|
||||
else:
|
||||
# If fetching details fails, save with form data and log a warning
|
||||
logger.warning(
|
||||
f"Successfully updated Zoom meeting {instance.meeting_id}, but failed to fetch updated details. "
|
||||
f"Error: {details_result.get('message', 'Unknown error')}"
|
||||
)
|
||||
instance.save() # Save with data from the form
|
||||
messages.success(self.request, result["message"] + " (Note: Could not refresh local data from Zoom.)")
|
||||
return redirect(reverse("meeting_details", kwargs={"slug": instance.slug}))
|
||||
messages.success(self.request, result["message"])
|
||||
else:
|
||||
messages.error(self.request, result["message"])
|
||||
return redirect(reverse("meeting_details", kwargs={"slug": instance.slug}))
|
||||
return redirect(reverse("meeting_details", kwargs={"slug": instance.slug}))
|
||||
|
||||
|
||||
def ZoomMeetingDeleteView(request, pk):
|
||||
@ -340,7 +319,7 @@ def job_detail(request, slug):
|
||||
|
||||
|
||||
status_form = JobPostingStatusForm(instance=job)
|
||||
image_upload_form=JobPostingImageForm(instance=job)
|
||||
image_upload_form=JobPostingImageForm(instance=job.post_images)
|
||||
|
||||
|
||||
|
||||
@ -358,6 +337,7 @@ def job_detail(request, slug):
|
||||
|
||||
return redirect('job_detail', slug=slug)
|
||||
else:
|
||||
|
||||
|
||||
messages.error(request, "Failed to update status due to validation errors.")
|
||||
|
||||
@ -379,19 +359,32 @@ def job_detail(request, slug):
|
||||
def job_image_upload(request, slug):
|
||||
#only for handling the post request
|
||||
job=get_object_or_404(JobPosting,slug=slug)
|
||||
if request.method=='POST':
|
||||
image_upload_form=JobPostingImageForm(request.POST,request.FILES)
|
||||
try:
|
||||
instance = JobPostingImage.objects.get(job=job)
|
||||
except JobPostingImage.DoesNotExist:
|
||||
# If it doesn't exist, create a new instance placeholder
|
||||
instance = None
|
||||
|
||||
if request.method == 'POST':
|
||||
# Pass the existing instance to the form if it exists
|
||||
image_upload_form = JobPostingImageForm(request.POST, request.FILES, instance=instance)
|
||||
|
||||
if image_upload_form.is_valid():
|
||||
image_upload_form = image_upload_form.save(commit=False)
|
||||
|
||||
image_upload_form.job = job
|
||||
image_upload_form.save()
|
||||
messages.success(request, f"Image uploaded successfully for {job.title}.")
|
||||
return redirect('job_detail', slug=job.slug)
|
||||
|
||||
# If creating a new one (instance is None), set the job link manually
|
||||
if instance is None:
|
||||
image_instance = image_upload_form.save(commit=False)
|
||||
image_instance.job = job
|
||||
image_instance.save()
|
||||
messages.success(request, f"Image uploaded successfully for {job.title}.")
|
||||
else:
|
||||
# If updating, the form will update the instance passed to it
|
||||
image_upload_form.save()
|
||||
messages.success(request, f"Image updated successfully for {job.title}.")
|
||||
|
||||
else:
|
||||
|
||||
|
||||
messages.error(request, "Image upload failed: Please ensure a valid image file was selected.")
|
||||
|
||||
return redirect('job_detail', slug=job.slug)
|
||||
return redirect('job_detail', slug=job.slug)
|
||||
|
||||
@ -411,7 +404,7 @@ def kaauh_career(request):
|
||||
# job detail facing the candidate:
|
||||
def job_detail_candidate(request, slug):
|
||||
job = get_object_or_404(JobPosting, slug=slug)
|
||||
return render(request, "jobs/job_detail_candidate.html", {"job": job})
|
||||
return render(request, "forms/job_detail_candidate.html", {"job": job})
|
||||
|
||||
|
||||
def post_to_linkedin(request, slug):
|
||||
@ -1020,7 +1013,7 @@ def submit_form(request, template_id):
|
||||
address=address.display_value,
|
||||
resume=resume.get_file if resume.is_file else None,
|
||||
job=submission.template.job,
|
||||
|
||||
|
||||
)
|
||||
return redirect('application_success')
|
||||
|
||||
@ -1125,17 +1118,6 @@ def form_submission_details(request, template_id, slug):
|
||||
|
||||
def schedule_interviews_view(request, slug):
|
||||
job = get_object_or_404(JobPosting, slug=slug)
|
||||
|
||||
# if request.method == "POST" and "Datastar-Request" in request.headers:
|
||||
# form = InterviewScheduleForm(slug=slug)
|
||||
# break_formset = BreakTimeFormSet()
|
||||
# form.initial["candidates"] = get_candidates_from_request(request)
|
||||
# def response():
|
||||
# html = render_to_string("includes/schedule_interview_div.html",{"form": form, "break_formset": break_formset, "job": job})
|
||||
# yield SSE.patch_elements(html,"#candidateviewModalBody")
|
||||
# return DatastarResponse(response())
|
||||
|
||||
|
||||
if request.method == "POST":
|
||||
form = InterviewScheduleForm(slug, request.POST)
|
||||
break_formset = BreakTimeFormSet(request.POST)
|
||||
@ -1193,16 +1175,15 @@ def schedule_interviews_view(request, slug):
|
||||
# Create Zoom meeting
|
||||
meeting_topic = f"Interview for {job.title} - {candidate.name}"
|
||||
|
||||
start_time = interview_datetime.isoformat() + "Z"
|
||||
start_time = interview_datetime
|
||||
|
||||
zoom_meeting = create_zoom_meeting(
|
||||
topic=meeting_topic,
|
||||
start_time=start_time,
|
||||
duration=schedule.interview_duration
|
||||
)
|
||||
# zoom_meeting = create_zoom_meeting(
|
||||
# topic=meeting_topic,
|
||||
# start_time=start_time,
|
||||
# duration=schedule.interview_duration
|
||||
# )
|
||||
|
||||
result = create_zoom_meeting(meeting_topic, start_time, schedule.interview_duration)
|
||||
|
||||
if result["status"] == "success":
|
||||
zoom_meeting = ZoomMeeting.objects.create(
|
||||
topic=meeting_topic,
|
||||
@ -1212,16 +1193,19 @@ def schedule_interviews_view(request, slug):
|
||||
join_url=result["meeting_details"]["join_url"],
|
||||
zoom_gateway_response=result["zoom_gateway_response"],
|
||||
)
|
||||
|
||||
# Create scheduled interview record
|
||||
scheduled_interview = ScheduledInterview.objects.create(
|
||||
candidate=candidate,
|
||||
job=job,
|
||||
zoom_meeting=zoom_meeting,
|
||||
schedule=schedule,
|
||||
interview_date=slot['date'],
|
||||
interview_time=slot['time']
|
||||
)
|
||||
# Create scheduled interview record
|
||||
ScheduledInterview.objects.create(
|
||||
candidate=candidate,
|
||||
job=job,
|
||||
zoom_meeting=zoom_meeting,
|
||||
schedule=schedule,
|
||||
interview_date=slot['date'],
|
||||
interview_time=slot['time']
|
||||
)
|
||||
else:
|
||||
messages.error(request, result["message"])
|
||||
schedule.delete()
|
||||
return redirect("candidate_interview_view", slug=slug)
|
||||
|
||||
# Send email to candidate
|
||||
# try:
|
||||
@ -1354,9 +1338,9 @@ def schedule_interviews_view(request, slug):
|
||||
else:
|
||||
form = InterviewScheduleForm(slug=slug)
|
||||
break_formset = BreakTimeFormSet()
|
||||
print(request.headers)
|
||||
if "Hx-Request" in request.headers:
|
||||
form.initial["candidates"] = [Candidate.objects.get(pk=c[0]) for c in request.GET.items()]
|
||||
if "HX-Request" in request.headers:
|
||||
candidate_ids = request.GET.getlist("candidate_ids")
|
||||
form.initial["candidates"] = Candidate.objects.filter(pk__in = candidate_ids)
|
||||
|
||||
return render(
|
||||
request,
|
||||
@ -1675,7 +1659,6 @@ def candidate_screening_view(request, slug):
|
||||
return render(request, "recruitment/candidate_screening_view.html", context)
|
||||
|
||||
|
||||
|
||||
def candidate_exam_view(request, slug):
|
||||
"""
|
||||
Manage candidate tiers and stage transitions
|
||||
@ -1728,7 +1711,6 @@ def candidate_update_status(request, slug):
|
||||
job = get_object_or_404(JobPosting, slug=slug)
|
||||
mark_as = request.POST.get('mark_as')
|
||||
candidate_ids = request.POST.getlist("candidate_ids")
|
||||
|
||||
if c := Candidate.objects.filter(pk__in = candidate_ids):
|
||||
c.update(stage=mark_as,exam_date=timezone.now(),applicant_status="Candidate" if mark_as in ["Exam","Interview","Offer"] else "Applicant")
|
||||
|
||||
@ -1739,14 +1721,55 @@ def candidate_update_status(request, slug):
|
||||
|
||||
def candidate_interview_view(request,slug):
|
||||
job = get_object_or_404(JobPosting,slug=slug)
|
||||
if "Datastar-Request" in request.headers:
|
||||
for candidate in get_candidates_from_request(request):
|
||||
print(candidate)
|
||||
context = {"job":job,"candidates":job.candidates.all()}
|
||||
context = {"job":job,"candidates":job.candidates.filter(stage="Interview").order_by("-match_score")}
|
||||
return render(request,"recruitment/candidate_interview_view.html",context)
|
||||
|
||||
def reschedule_meeting_for_candidate(request,slug,candidate_id,meeting_id):
|
||||
job = get_object_or_404(JobPosting,slug=slug)
|
||||
candidate = get_object_or_404(Candidate,pk=candidate_id)
|
||||
meeting = get_object_or_404(ZoomMeeting,pk=meeting_id)
|
||||
form = ZoomMeetingForm(instance=meeting)
|
||||
|
||||
if request.method == "POST":
|
||||
form = ZoomMeetingForm(request.POST,instance=meeting)
|
||||
if form.is_valid():
|
||||
instance = form.save(commit=False)
|
||||
updated_data = {
|
||||
"topic": instance.topic,
|
||||
"start_time": instance.start_time.isoformat() + "Z",
|
||||
"duration": instance.duration,
|
||||
}
|
||||
if instance.start_time < timezone.now():
|
||||
messages.error(request, "Start time must be in the future.")
|
||||
return redirect("reschedule_meeting_for_candidate",slug=job.slug,candidate_id=candidate_id,meeting_id=meeting_id)
|
||||
|
||||
result = update_meeting(instance, updated_data)
|
||||
|
||||
if result["status"] == "success":
|
||||
messages.success(request, result["message"])
|
||||
else:
|
||||
messages.error(request, result["message"])
|
||||
return redirect(reverse("candidate_interview_view", kwargs={"slug": job.slug}))
|
||||
|
||||
context = {"job":job,"candidate":candidate,"meeting":meeting,"form":form}
|
||||
return render(request,"meetings/reschedule_meeting.html",context)
|
||||
|
||||
|
||||
def delete_meeting_for_candidate(request,slug,candidate_pk,meeting_id):
|
||||
job = get_object_or_404(JobPosting,slug=slug)
|
||||
candidate = get_object_or_404(Candidate,pk=candidate_pk)
|
||||
meeting = get_object_or_404(ZoomMeeting,pk=meeting_id)
|
||||
if request.method == "POST":
|
||||
result = delete_zoom_meeting(meeting.meeting_id)
|
||||
if result["status"] == "success":
|
||||
meeting.delete()
|
||||
messages.success(request, result["message"])
|
||||
else:
|
||||
messages.error(request, result["message"])
|
||||
return redirect(reverse("candidate_interview_view", kwargs={"slug": job.slug}))
|
||||
|
||||
context = {"job":job,"candidate":candidate,"meeting":meeting}
|
||||
return render(request,"meetings/delete_meeting_form.html",context)
|
||||
|
||||
def interview_calendar_view(request, slug):
|
||||
job = get_object_or_404(JobPosting, slug=slug)
|
||||
@ -2230,12 +2253,12 @@ def reschedule_candidate_meeting(request, job_slug, candidate_pk, interview_pk):
|
||||
})
|
||||
|
||||
|
||||
def schedule_meeting_for_candidate(request, job_slug, candidate_pk):
|
||||
def schedule_meeting_for_candidate(request, slug, candidate_pk):
|
||||
"""
|
||||
Handles GET to display a simple form for scheduling a meeting for a candidate.
|
||||
Handles POST to process the form, create the meeting, and redirect back.
|
||||
"""
|
||||
job = get_object_or_404(JobPosting, slug=job_slug)
|
||||
job = get_object_or_404(JobPosting, slug=slug)
|
||||
candidate = get_object_or_404(Candidate, pk=candidate_pk, job=job)
|
||||
|
||||
if request.method == "POST":
|
||||
@ -2253,14 +2276,15 @@ def schedule_meeting_for_candidate(request, job_slug, candidate_pk):
|
||||
if start_time_val <= timezone.now():
|
||||
messages.error(request, "Start time must be in the future.")
|
||||
# Re-render form with error and initial data
|
||||
return render(request, "recruitment/schedule_meeting_form.html", {
|
||||
'form': form,
|
||||
'job': job,
|
||||
'candidate': candidate,
|
||||
'initial_topic': topic_val,
|
||||
'initial_start_time': start_time_val.strftime('%Y-%m-%dT%H:%M') if start_time_val else '',
|
||||
'initial_duration': duration_val
|
||||
})
|
||||
return redirect('candidate_interview_view', slug=job.slug)
|
||||
# return render(request, "recruitment/schedule_meeting_form.html", {
|
||||
# 'form': form,
|
||||
# 'job': job,
|
||||
# 'candidate': candidate,
|
||||
# 'initial_topic': topic_val,
|
||||
# 'initial_start_time': start_time_val.strftime('%Y-%m-%dT%H:%M') if start_time_val else '',
|
||||
# 'initial_duration': duration_val
|
||||
# })
|
||||
|
||||
# Create Zoom meeting using utility function
|
||||
# The create_zoom_meeting expects start_time as a datetime object
|
||||
@ -2307,7 +2331,7 @@ def schedule_meeting_for_candidate(request, job_slug, candidate_pk):
|
||||
})
|
||||
else:
|
||||
# Form validation errors
|
||||
return render(request, "recruitment/schedule_meeting_form.html", {
|
||||
return render(request, "meetings/schedule_meeting_form.html", {
|
||||
'form': form,
|
||||
'job': job,
|
||||
'candidate': candidate,
|
||||
@ -2322,7 +2346,7 @@ def schedule_meeting_for_candidate(request, job_slug, candidate_pk):
|
||||
'duration': 60, # Default duration
|
||||
}
|
||||
form = ZoomMeetingForm(initial=initial_data)
|
||||
return render(request, "recruitment/schedule_meeting_form.html", {
|
||||
return render(request, "meetings/schedule_meeting_form.html", {
|
||||
'form': form,
|
||||
'job': job,
|
||||
'candidate': candidate
|
||||
@ -2330,6 +2354,43 @@ def schedule_meeting_for_candidate(request, job_slug, candidate_pk):
|
||||
|
||||
|
||||
|
||||
def user_detail(requests,pk):
|
||||
user=get_object_or_404(User,pk=pk)
|
||||
return render(requests,'user/profile.html')
|
||||
def user_profile_image_update(request, pk):
|
||||
user = get_object_or_404(User, pk=pk)
|
||||
|
||||
if request.method == 'POST':
|
||||
profile_form = ProfileImageUploadForm(request.POST, request.FILES, instance=user.profile)
|
||||
if profile_form.is_valid():
|
||||
profile_form.save()
|
||||
messages.success(request, 'Image uploaded successfully')
|
||||
return redirect('user_detail', pk=user.pk)
|
||||
else:
|
||||
messages.error(request, 'An error occurred while uploading the image')
|
||||
else:
|
||||
profile_form = ProfileImageUploadForm(instance=user.profile)
|
||||
|
||||
context = {
|
||||
'profile_form': profile_form,
|
||||
'user': user,
|
||||
}
|
||||
return render(request, 'user/profile.html', context)
|
||||
|
||||
|
||||
|
||||
def user_detail(request, pk):
|
||||
user = get_object_or_404(User, pk=pk)
|
||||
profile_form = ProfileImageUploadForm(instance=user.profile)
|
||||
if request.method == 'POST':
|
||||
first_name=request.POST.get('first_name')
|
||||
last_name=request.POST.get('last_name')
|
||||
if first_name:
|
||||
user.first_name=first_name
|
||||
if last_name:
|
||||
user.last_name=last_name
|
||||
user.save()
|
||||
context = {
|
||||
|
||||
'user': user,
|
||||
'profile_form':profile_form
|
||||
|
||||
}
|
||||
return render(request, 'user/profile.html', context)
|
||||
|
||||
@ -1,947 +0,0 @@
|
||||
|
||||
/* Custom CSS for NorahUniversity ATS */
|
||||
/* Keep only essential custom styles that Bootstrap doesn't handle */
|
||||
|
||||
/* Primary Brand Color */
|
||||
:root {
|
||||
--primary-color: #1b8354;
|
||||
--primary-hover: #155f3e;
|
||||
}
|
||||
|
||||
/* Header and Navigation */
|
||||
.header {
|
||||
background-color: white !important;
|
||||
box-shadow: 0 2px 4px rgba(0,0,0,0.1) !important;
|
||||
border-bottom: 1px solid #e0e0e0 !important;
|
||||
}
|
||||
|
||||
.navbar {
|
||||
background-color: white !important;
|
||||
border-bottom: 1px solid #e0e0e0 !important;
|
||||
}
|
||||
|
||||
.nav-link.active {
|
||||
background-color: var(--primary-color) !important;
|
||||
color: white !important;
|
||||
}
|
||||
|
||||
.nav-link:hover {
|
||||
background-color: var(--primary-color) !important;
|
||||
color: white !important;
|
||||
}
|
||||
|
||||
.logo {
|
||||
font-size: 1.5rem;
|
||||
font-weight: 700;
|
||||
color: var(--primary-color) !important;
|
||||
}
|
||||
|
||||
/* Buttons - Override Bootstrap primary color */
|
||||
.btn-primary {
|
||||
background-color: var(--primary-color) !important;
|
||||
border-color: var(--primary-color) !important;
|
||||
}
|
||||
|
||||
.btn-primary:hover {
|
||||
background-color: var(--primary-hover) !important;
|
||||
border-color: var(--primary-hover) !important;
|
||||
}
|
||||
|
||||
.btn-outline-primary {
|
||||
border-color: var(--primary-color) !important;
|
||||
color: var(--primary-color) !important;
|
||||
}
|
||||
|
||||
.btn-outline-primary:hover {
|
||||
background-color: var(--primary-color) !important;
|
||||
border-color: var(--primary-color) !important;
|
||||
color: white !important;
|
||||
}
|
||||
|
||||
/* Cards */
|
||||
.card {
|
||||
border: 1px solid #e0e0e0 !important;
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.05) !important;
|
||||
}
|
||||
|
||||
.card-header {
|
||||
border-bottom: 1px solid #e0e0e0 !important;
|
||||
background-color: white !important;
|
||||
}
|
||||
|
||||
/* Table Improvements */
|
||||
.table-hover tbody tr:hover {
|
||||
background-color: rgba(27, 131, 84, 0.05) !important;
|
||||
}
|
||||
|
||||
/* Custom Badge Colors */
|
||||
.badge.bg-success {
|
||||
background-color: #28a745 !important;
|
||||
}
|
||||
|
||||
.badge.bg-warning {
|
||||
background-color: #ffc107 !important;
|
||||
}
|
||||
|
||||
/* Form Improvements */
|
||||
.form-control:focus {
|
||||
border-color: var(--primary-color) !important;
|
||||
box-shadow: 0 0 0 0.2rem rgba(27, 131, 84, 0.25) !important;
|
||||
}
|
||||
|
||||
/* Responsive adjustments */
|
||||
@media (max-width: 768px) {
|
||||
.nav-list {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.nav-item {
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Utility classes */
|
||||
.text-primary-custom {
|
||||
color: var(--primary-color) !important;
|
||||
}
|
||||
|
||||
.bg-primary-custom {
|
||||
background-color: var(--primary-color) !important;
|
||||
}
|
||||
|
||||
.border-primary-custom {
|
||||
border-color: var(--primary-color) !important;
|
||||
}
|
||||
|
||||
/* Loading states */
|
||||
.loading {
|
||||
opacity: 0.6;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
/* Icon Styling */
|
||||
.heroicon {
|
||||
width: 1rem;
|
||||
height: 1rem;
|
||||
margin-right: 0.5rem;
|
||||
vertical-align: middle;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.size-6 {
|
||||
width: 1.5rem !important;
|
||||
height: 1.5rem !important;
|
||||
}
|
||||
|
||||
/* Responsive icon sizing */
|
||||
.icon-sm {
|
||||
width: 0.875rem !important;
|
||||
height: 0.875rem !important;
|
||||
margin-right: 0.375rem !important;
|
||||
}
|
||||
|
||||
.icon-md {
|
||||
width: 1.125rem !important;
|
||||
height: 1.125rem !important;
|
||||
margin-right: 0.625rem !important;
|
||||
}
|
||||
|
||||
.icon-lg {
|
||||
width: 1.5rem !important;
|
||||
height: 1.5rem !important;
|
||||
margin-right: 0.75rem !important;
|
||||
}
|
||||
|
||||
.icon-xl {
|
||||
width: 2rem !important;
|
||||
height: 2rem !important;
|
||||
margin-right: 1rem !important;
|
||||
}
|
||||
|
||||
/* Context-specific icon adjustments */
|
||||
.btn-sm .heroicon,
|
||||
.btn-sm .size-6,
|
||||
.btn-sm .icon-md {
|
||||
width: 0.875rem !important;
|
||||
height: 0.875rem !important;
|
||||
margin-right: 0.375rem !important;
|
||||
}
|
||||
|
||||
.nav-link .heroicon,
|
||||
.nav-link .size-6 {
|
||||
width: 1.25rem !important;
|
||||
height: 1.25rem !important;
|
||||
margin-right: 0.5rem !important;
|
||||
}
|
||||
|
||||
.card-header .heroicon,
|
||||
.card-header .size-6 {
|
||||
width: 1.375rem !important;
|
||||
height: 1.375rem !important;
|
||||
margin-right: 0.625rem !important;
|
||||
}
|
||||
|
||||
/* Print styles */
|
||||
@media print {
|
||||
.navbar,
|
||||
.header,
|
||||
.btn,
|
||||
.pagination {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
.card {
|
||||
box-shadow: none !important;
|
||||
border: 1px solid #ccc !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* Responsive adjustments for icons */
|
||||
@media (max-width: 768px) {
|
||||
.nav-link .heroicon,
|
||||
.nav-link .size-6 {
|
||||
width: 1rem !important;
|
||||
height: 1rem !important;
|
||||
margin-right: 0.375rem !important;
|
||||
}
|
||||
|
||||
.card-header .heroicon,
|
||||
.card-header .size-6 {
|
||||
width: 1.125rem !important;
|
||||
height: 1.125rem !important;
|
||||
margin-right: 0.5rem !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* Header and Search Enhancements */
|
||||
.card-header {
|
||||
background-color: white !important;
|
||||
border-bottom: 1px solid #e0e0e0 !important;
|
||||
padding: 1.25rem 1.5rem !important;
|
||||
}
|
||||
|
||||
.card-header h1,
|
||||
.card-header h2,
|
||||
.card-header h3 {
|
||||
margin-bottom: 0 !important;
|
||||
font-weight: 600 !important;
|
||||
}
|
||||
|
||||
.card-header h1.h3,
|
||||
.card-header h2.h3,
|
||||
.card-header h3.h3 {
|
||||
font-size: 1.25rem !important;
|
||||
}
|
||||
|
||||
/* Search Form Enhancements */
|
||||
.search-form-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.75rem;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.input-group {
|
||||
flex: 1;
|
||||
min-width: 250px;
|
||||
}
|
||||
|
||||
.input-group-text {
|
||||
background-color: #f8f9fa !important;
|
||||
border: 1px solid #ced4da !important;
|
||||
/* border-right: none !important; */
|
||||
color: #495057 !important;
|
||||
transition: all 0.2s ease !important;
|
||||
}
|
||||
|
||||
.input-group-text:hover {
|
||||
background-color: #e9ecef !important;
|
||||
}
|
||||
|
||||
.input-group-text .heroicon {
|
||||
width: 1rem !important;
|
||||
height: 1rem !important;
|
||||
margin-right: 0 !important;
|
||||
}
|
||||
|
||||
|
||||
.form-control:focus {
|
||||
border-color: var(--primary-color) !important;
|
||||
box-shadow: 0 0 0 0.2rem rgba(27, 131, 84, 0.15) !important;
|
||||
}
|
||||
|
||||
.form-control:focus + .input-group-text {
|
||||
border-color: var(--primary-color) !important;
|
||||
background-color: rgba(27, 131, 84, 0.05) !important;
|
||||
}
|
||||
|
||||
/* Button Group Enhancements */
|
||||
.d-flex.gap-2 .btn {
|
||||
white-space: nowrap !important;
|
||||
transition: all 0.2s ease !important;
|
||||
}
|
||||
|
||||
.d-flex.gap-2 .btn:hover {
|
||||
transform: translateY(-1px) !important;
|
||||
}
|
||||
|
||||
.d-flex.gap-2 .btn svg {
|
||||
margin-right: 0.375rem !important;
|
||||
}
|
||||
|
||||
/* Responsive Header Adjustments */
|
||||
@media (max-width: 768px) {
|
||||
.card-header {
|
||||
padding: 1rem 1.25rem !important;
|
||||
}
|
||||
|
||||
.card-header h1.h3,
|
||||
.card-header h2.h3,
|
||||
.card-header h3.h3 {
|
||||
font-size: 1.125rem !important;
|
||||
}
|
||||
|
||||
.input-group {
|
||||
min-width: 200px !important;
|
||||
max-width: 100% !important;
|
||||
}
|
||||
|
||||
.d-flex.gap-2 {
|
||||
flex-wrap: wrap !important;
|
||||
gap: 0.5rem !important;
|
||||
}
|
||||
|
||||
.d-flex.gap-2 .btn {
|
||||
flex: 1 !important;
|
||||
min-width: 120px !important;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 576px) {
|
||||
.card-header {
|
||||
padding: 0.875rem 1rem !important;
|
||||
}
|
||||
|
||||
.input-group {
|
||||
min-width: 100% !important;
|
||||
}
|
||||
|
||||
.d-flex.gap-2 .btn {
|
||||
font-size: 0.875rem !important;
|
||||
padding: 0.375rem 0.75rem !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* Search Input Placeholder */
|
||||
.form-control::placeholder {
|
||||
color: #6c757d !important;
|
||||
opacity: 0.7 !important;
|
||||
}
|
||||
|
||||
/* Enhanced Focus States */
|
||||
.form-control:focus::placeholder {
|
||||
opacity: 1 !important;
|
||||
}
|
||||
|
||||
/* Detail Page Enhancements */
|
||||
.detail-page-header {
|
||||
background: linear-gradient(135deg, var(--primary-color) 0%, rgba(27, 131, 84, 0.1) 100%);
|
||||
border-bottom: 3px solid var(--primary-color) !important;
|
||||
}
|
||||
|
||||
.detail-page-header h1 {
|
||||
color: var(--primary-color) !important;
|
||||
}
|
||||
|
||||
/* Information Cards Enhancement */
|
||||
.info-card {
|
||||
background: #f8f9fa;
|
||||
border-left: 4px solid var(--primary-color);
|
||||
border-radius: 0.375rem;
|
||||
padding: 1rem;
|
||||
margin-bottom: 1rem;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.info-card:hover {
|
||||
background: #e9ecef;
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.info-card .info-label {
|
||||
font-size: 0.875rem;
|
||||
color: #6c757d;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.5px;
|
||||
font-weight: 600;
|
||||
margin-bottom: 0.25rem;
|
||||
}
|
||||
|
||||
.info-card .info-value {
|
||||
font-size: 1rem;
|
||||
color: #212529;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
/* Action Cards Enhancement */
|
||||
.action-card {
|
||||
text-align: center;
|
||||
padding: 2rem;
|
||||
background: linear-gradient(135deg, #f8f9fa 0%, #e9ecef 100%);
|
||||
border-radius: 0.75rem;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.action-card:hover {
|
||||
transform: translateY(-3px);
|
||||
box-shadow: 0 6px 20px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
/* Resume File Display */
|
||||
.resume-file {
|
||||
background: #f8f9fa;
|
||||
border: 1px solid #dee2e6;
|
||||
border-radius: 0.5rem;
|
||||
padding: 1rem;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.resume-file:hover {
|
||||
background: #e9ecef;
|
||||
border-color: var(--primary-color);
|
||||
}
|
||||
|
||||
.resume-file .file-name {
|
||||
font-weight: 600;
|
||||
color: #495057;
|
||||
margin-bottom: 0.25rem;
|
||||
}
|
||||
|
||||
.resume-file .file-info {
|
||||
font-size: 0.875rem;
|
||||
color: #6c757d;
|
||||
}
|
||||
|
||||
/* Parsed Data Grid Enhancement */
|
||||
.parsed-data-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
|
||||
gap: 1rem;
|
||||
margin-top: 1rem;
|
||||
}
|
||||
|
||||
.parsed-data-item {
|
||||
background: white;
|
||||
border: 1px solid #e0e0e0;
|
||||
border-radius: 0.5rem;
|
||||
padding: 1rem;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.parsed-data-item:hover {
|
||||
border-color: var(--primary-color);
|
||||
box-shadow: 0 2px 8px rgba(27, 131, 84, 0.1);
|
||||
}
|
||||
|
||||
.parsed-data-item .data-key {
|
||||
font-size: 0.75rem;
|
||||
text-transform: uppercase;
|
||||
color: var(--primary-color);
|
||||
font-weight: 600;
|
||||
letter-spacing: 0.5px;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.parsed-data-item .data-value {
|
||||
font-size: 0.875rem;
|
||||
color: #495057;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
/* Status Badge Enhancement */
|
||||
.status-badge {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 0.375rem;
|
||||
padding: 0.375rem 0.75rem;
|
||||
border-radius: 2rem;
|
||||
font-size: 0.875rem;
|
||||
font-weight: 600;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.status-badge .heroicon {
|
||||
width: 1rem !important;
|
||||
height: 1rem !important;
|
||||
}
|
||||
|
||||
/* Contact Information Enhancement */
|
||||
.contact-info-item {
|
||||
padding: 0.75rem 1rem;
|
||||
margin-bottom: 0.5rem;
|
||||
background: white;
|
||||
border-radius: 0.5rem;
|
||||
border: 1px solid #e0e0e0;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.contact-info-item:hover {
|
||||
background: #f8f9fa;
|
||||
border-color: var(--primary-color);
|
||||
box-shadow: 0 2px 8px rgba(27, 131, 84, 0.1);
|
||||
}
|
||||
|
||||
.contact-info-item .contact-label {
|
||||
font-size: 0.75rem;
|
||||
text-transform: uppercase;
|
||||
color: #6c757d;
|
||||
font-weight: 600;
|
||||
letter-spacing: 0.5px;
|
||||
}
|
||||
|
||||
.contact-info-item .contact-value {
|
||||
font-size: 1rem;
|
||||
color: #212529;
|
||||
font-weight: 500;
|
||||
margin-top: 0.125rem;
|
||||
}
|
||||
|
||||
/* Responsive Detail Page Adjustments */
|
||||
@media (max-width: 768px) {
|
||||
.detail-page-header {
|
||||
background: linear-gradient(135deg, var(--primary-color) 0%, rgba(27, 131, 84, 0.05) 100%);
|
||||
border-bottom: 2px solid var(--primary-color);
|
||||
}
|
||||
|
||||
.detail-page-header h1 {
|
||||
font-size: 1.5rem !important;
|
||||
}
|
||||
|
||||
.info-card {
|
||||
padding: 0.75rem;
|
||||
margin-bottom: 0.75rem;
|
||||
}
|
||||
|
||||
.parsed-data-grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.contact-info-item {
|
||||
padding: 0.5rem 0.75rem;
|
||||
}
|
||||
|
||||
.action-card {
|
||||
padding: 1.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 576px) {
|
||||
.detail-page-header h1 {
|
||||
font-size: 1.25rem !important;
|
||||
}
|
||||
|
||||
.info-card .info-label {
|
||||
font-size: 0.75rem;
|
||||
}
|
||||
|
||||
.info-card .info-value {
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
|
||||
.status-badge {
|
||||
font-size: 0.75rem;
|
||||
padding: 0.25rem 0.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
/* Loading Animation for Detail Pages */
|
||||
.detail-loading {
|
||||
display: inline-block;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
border: 3px solid rgba(255, 255, 255, 0.3);
|
||||
border-radius: 50%;
|
||||
border-top-color: white;
|
||||
animation: spin 1s ease-in-out infinite;
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
to { transform: rotate(360deg); }
|
||||
}
|
||||
|
||||
/* Print Styles for Detail Pages */
|
||||
@media print {
|
||||
.detail-page-header {
|
||||
background: white !important;
|
||||
border: 2px solid #dee2e6 !important;
|
||||
}
|
||||
|
||||
.detail-page-header h1 {
|
||||
color: #212529 !important;
|
||||
}
|
||||
|
||||
.contact-info-item,
|
||||
.info-card,
|
||||
.parsed-data-item {
|
||||
border: 1px solid #dee2e6 !important;
|
||||
break-inside: avoid;
|
||||
}
|
||||
|
||||
.btn,
|
||||
.action-card {
|
||||
display: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* Form and Update Page Enhancements */
|
||||
.form-page-header {
|
||||
background: linear-gradient(135deg, var(--primary-color) 0%, rgba(27, 131, 84, 0.1) 100%);
|
||||
border-bottom: 3px solid var(--primary-color) !important;
|
||||
}
|
||||
|
||||
.form-page-header h1 {
|
||||
color: var(--primary-color) !important;
|
||||
}
|
||||
|
||||
.form-page-header p {
|
||||
color: rgba(27, 131, 84, 0.8) !important;
|
||||
}
|
||||
|
||||
/* Form Section Enhancement */
|
||||
.form-section {
|
||||
background: #f8f9fa;
|
||||
border-left: 4px solid var(--primary-color);
|
||||
border-radius: 0.375rem;
|
||||
padding: 1.5rem;
|
||||
margin-bottom: 2rem;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.form-section:hover {
|
||||
background: #e9ecef;
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.form-section h5 {
|
||||
color: var(--primary-color);
|
||||
font-weight: 600;
|
||||
margin-bottom: 1rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.form-section .section-icon {
|
||||
width: 1.25rem !important;
|
||||
height: 1.25rem !important;
|
||||
}
|
||||
|
||||
/* Form Field Enhancement */
|
||||
.form-field-wrapper {
|
||||
position: relative;
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
.form-field-wrapper label {
|
||||
font-weight: 600;
|
||||
color: #495057;
|
||||
margin-bottom: 0.5rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.form-field-wrapper .required-indicator {
|
||||
color: #dc3545;
|
||||
font-size: 0.875rem;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.form-field-wrapper .field-icon {
|
||||
position: absolute;
|
||||
right: 1rem;
|
||||
top: 2.5rem;
|
||||
color: #6c757d;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.form-control,
|
||||
.form-select {
|
||||
border: 2px solid #e0e0e0;
|
||||
border-radius: 0.5rem;
|
||||
padding: 0.75rem 1rem;
|
||||
font-size: 1rem;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.form-control:focus,
|
||||
.form-select:focus {
|
||||
border-color: var(--primary-color);
|
||||
box-shadow: 0 0 0 0.2rem rgba(27, 131, 84, 0.15);
|
||||
color: #212529;
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
.form-control.is-invalid,
|
||||
.form-select.is-invalid {
|
||||
border-color: #dc3545;
|
||||
padding-right: 2.5rem;
|
||||
background-image: none;
|
||||
}
|
||||
|
||||
.form-control.is-valid,
|
||||
.form-select.is-valid {
|
||||
border-color: #28a745;
|
||||
padding-right: 2.5rem;
|
||||
background-image: none;
|
||||
}
|
||||
|
||||
.form-text {
|
||||
font-size: 0.875rem;
|
||||
color: #6c757d;
|
||||
margin-top: 0.25rem;
|
||||
}
|
||||
|
||||
.invalid-feedback,
|
||||
.valid-feedback {
|
||||
font-size: 0.875rem;
|
||||
margin-top: 0.25rem;
|
||||
}
|
||||
|
||||
.invalid-feedback {
|
||||
color: #dc3545;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.valid-feedback {
|
||||
color: #28a745;
|
||||
display: block;
|
||||
}
|
||||
|
||||
/* Form Enhancement for Special Fields */
|
||||
.form-check-input {
|
||||
width: 1.25rem;
|
||||
height: 1.25rem;
|
||||
margin-top: 0.25rem;
|
||||
}
|
||||
|
||||
.form-check-input:checked {
|
||||
background-color: var(--primary-color);
|
||||
border-color: var(--primary-color);
|
||||
}
|
||||
|
||||
.form-check-label {
|
||||
font-weight: 500;
|
||||
color: #495057;
|
||||
}
|
||||
|
||||
/* Action Buttons Enhancement */
|
||||
.form-action-buttons {
|
||||
background: white;
|
||||
border: 1px solid #e0e0e0;
|
||||
border-radius: 0.75rem;
|
||||
padding: 1.5rem;
|
||||
margin-top: 2rem;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
|
||||
.form-action-buttons .btn {
|
||||
min-width: 120px;
|
||||
font-weight: 500;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.form-action-buttons .btn:hover {
|
||||
transform: translateY(-1px);
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
|
||||
}
|
||||
|
||||
/* Responsive Form Adjustments */
|
||||
@media (max-width: 768px) {
|
||||
.form-section {
|
||||
padding: 1rem;
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
.form-section h5 {
|
||||
font-size: 1.1rem;
|
||||
margin-bottom: 0.75rem;
|
||||
}
|
||||
|
||||
.form-control,
|
||||
.form-select {
|
||||
font-size: 0.875rem;
|
||||
padding: 0.625rem 0.75rem;
|
||||
}
|
||||
|
||||
.form-field-wrapper label {
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
|
||||
.form-text,
|
||||
.invalid-feedback,
|
||||
.valid-feedback {
|
||||
font-size: 0.75rem;
|
||||
}
|
||||
|
||||
.form-action-buttons {
|
||||
padding: 1rem;
|
||||
margin-top: 1.5rem;
|
||||
}
|
||||
|
||||
.form-action-buttons .btn {
|
||||
min-width: 100px;
|
||||
font-size: 0.875rem;
|
||||
padding: 0.5rem 1rem;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 576px) {
|
||||
.form-section {
|
||||
padding: 0.75rem;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.form-section h5 {
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.form-control,
|
||||
.form-select {
|
||||
font-size: 0.813rem;
|
||||
padding: 0.5rem 0.625rem;
|
||||
}
|
||||
|
||||
.form-field-wrapper {
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.form-action-buttons {
|
||||
flex-direction: column !important;
|
||||
gap: 0.5rem !important;
|
||||
}
|
||||
|
||||
.form-action-buttons .btn {
|
||||
min-width: auto;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
/* Loading State for Forms */
|
||||
.form-loading {
|
||||
position: relative;
|
||||
pointer-events: none;
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
.form-loading::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background: rgba(255, 255, 255, 0.8);
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.form-loading .spinner {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
/* Print Styles for Forms */
|
||||
@media print {
|
||||
.form-page-header,
|
||||
.form-section,
|
||||
.form-action-buttons {
|
||||
border: 1px solid #dee2e6 !important;
|
||||
background: white !important;
|
||||
box-shadow: none !important;
|
||||
}
|
||||
|
||||
.form-section {
|
||||
break-inside: avoid;
|
||||
page-break-inside: avoid;
|
||||
}
|
||||
|
||||
.btn {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
.form-control,
|
||||
.form-select {
|
||||
border: 1px solid #000 !important;
|
||||
background: white !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* File Upload Enhancement */
|
||||
.form-control[type="file"] {
|
||||
padding: 0.5rem;
|
||||
border: 2px dashed #dee2e6;
|
||||
background: #f8f9fa;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.form-control[type="file"]:hover {
|
||||
border-color: var(--primary-color);
|
||||
background: #f0f8f4;
|
||||
}
|
||||
|
||||
.form-control[type="file"]:focus {
|
||||
border-color: var(--primary-color);
|
||||
background: white;
|
||||
box-shadow: 0 0 0 0.2rem rgba(27, 131, 84, 0.15);
|
||||
}
|
||||
|
||||
/* Checkbox and Radio Enhancement */
|
||||
.form-check-input:checked ~ .form-check-label::before {
|
||||
background-color: var(--primary-color);
|
||||
border-color: var(--primary-color);
|
||||
}
|
||||
|
||||
.form-check-input:focus ~ .form-check-label::before {
|
||||
box-shadow: 0 0 0 0.2rem rgba(27, 131, 84, 0.25);
|
||||
}
|
||||
|
||||
/* Help Text Enhancement */
|
||||
.help-text {
|
||||
font-size: 0.813rem;
|
||||
color: #6c757d;
|
||||
margin-top: 0.25rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.25rem;
|
||||
}
|
||||
|
||||
.help-text .help-icon {
|
||||
width: 1rem !important;
|
||||
height: 1rem !important;
|
||||
color: #6c757d;
|
||||
}
|
||||
|
||||
/* Error State Enhancement */
|
||||
.field-error {
|
||||
border-color: #dc3545 !important;
|
||||
background-color: #fff5f5 !important;
|
||||
}
|
||||
|
||||
.field-error:focus {
|
||||
box-shadow: 0 0 0 0.2rem rgba(220, 53, 69, 0.25) !important;
|
||||
}
|
||||
|
||||
/* Success State Enhancement */
|
||||
.field-success {
|
||||
border-color: #28a745 !important;
|
||||
background-color: #f8fff9 !important;
|
||||
}
|
||||
|
||||
.field-success:focus {
|
||||
box-shadow: 0 0 0 0.2rem rgba(40, 167, 69, 0.25) !important;
|
||||
}
|
||||
@ -1,5 +1,6 @@
|
||||
{% load i18n static %}
|
||||
{% load partials %}
|
||||
{% load static i18n %}
|
||||
{% get_current_language as LANGUAGE_CODE %}
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html lang="{{ LANGUAGE_CODE }}" dir="{% if LANGUAGE_CODE == 'ar' %}rtl{% else %}ltr{% endif %}">
|
||||
<head>
|
||||
@ -8,511 +9,237 @@
|
||||
<meta name="description" content="{% trans 'King Abdullah Academic University Hospital - Applicant Tracking System' %}">
|
||||
<title>{% block title %}{% trans 'University ATS' %}{% endblock %}</title>
|
||||
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
{% comment %} Load the correct Bootstrap CSS file for RTL/LTR {% endcomment %}
|
||||
{% if LANGUAGE_CODE == 'ar' %}
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.rtl.min.css" rel="stylesheet">
|
||||
{% else %}
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
{% endif %}
|
||||
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.0/css/all.min.css">
|
||||
{% comment %} <link href="https://unpkg.com/filepond/dist/filepond.css" rel="stylesheet">
|
||||
<link href="https://unpkg.com/filepond-plugin-image-preview/dist/filepond-plugin-image-preview.css" rel="stylesheet"> {% endcomment %}
|
||||
<link rel="stylesheet" href="{% static 'css/style.css' %}">
|
||||
|
||||
<style>
|
||||
:root {
|
||||
--kaauh-teal: #00636e;
|
||||
--kaauh-teal-dark: #004a53;
|
||||
--kaauh-light-bg: #f9fbfd;
|
||||
--kaauh-border: #eaeff3;
|
||||
}
|
||||
|
||||
/* NEW CLASS FOR WIDER CONTENT */
|
||||
.max-width-1600 {
|
||||
max-width: 1600px;
|
||||
margin-right: auto;
|
||||
margin-left: auto;
|
||||
padding-right: var(--bs-gutter-x, 0.75rem); /* Add Bootstrap padding for responsiveness */
|
||||
padding-left: var(--bs-gutter-x, 0.75rem);
|
||||
}
|
||||
|
||||
/* === Top Bar === */
|
||||
.top-bar {
|
||||
background-color: white;
|
||||
border-bottom: 1px solid var(--kaauh-border);
|
||||
font-size: 0.825rem;
|
||||
padding: 0.4rem 0;
|
||||
}
|
||||
.top-bar a { text-decoration: none; }
|
||||
.top-bar .social-icons i {
|
||||
color: var(--kaauh-teal);
|
||||
transition: color 0.2s;
|
||||
}
|
||||
.top-bar .social-icons i:hover {
|
||||
color: var(--kaauh-teal-dark);
|
||||
}
|
||||
.top-bar .contact-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.35rem;
|
||||
padding: 0.25rem 0.5rem;
|
||||
}
|
||||
.top-bar .logo-container img {
|
||||
height: 60px;
|
||||
object-fit: contain;
|
||||
}
|
||||
|
||||
@media (max-width: 767.98px) {
|
||||
.top-bar {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
/* === Navbar === */
|
||||
.navbar-brand {
|
||||
font-weight: 700;
|
||||
letter-spacing: -0.5px;
|
||||
font-size: 1.25rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
.navbar-dark {
|
||||
background-color: var(--kaauh-teal) !important;
|
||||
box-shadow: 0 2px 6px rgba(0,0,0,0.12);
|
||||
}
|
||||
/* Change the outer navbar container to fluid, rely on inner max-width */
|
||||
.navbar-dark > .container {
|
||||
max-width: 100%; /* Override default container width */
|
||||
}
|
||||
.nav-link {
|
||||
font-weight: 500;
|
||||
transition: all 0.2s ease;
|
||||
padding: 0.5rem 0.75rem;
|
||||
}
|
||||
.nav-link:hover,
|
||||
.nav-link.active {
|
||||
color: white !important;
|
||||
background: rgba(255,255,255,0.12) !important;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
/* Dropdown - Enhanced UX */
|
||||
.dropdown-menu {
|
||||
backdrop-filter: blur(4px);
|
||||
background-color: rgba(255, 255, 255, 0.98);
|
||||
border: 1px solid var(--kaauh-border);
|
||||
box-shadow: 0 6px 20px rgba(0,0,0,0.12);
|
||||
border-radius: 8px;
|
||||
padding: 0.5rem 0;
|
||||
min-width: 200px;
|
||||
will-change: transform, opacity;
|
||||
transition: transform 0.2s ease, opacity 0.2s ease;
|
||||
}
|
||||
.dropdown-item {
|
||||
padding: 0.5rem 1.25rem;
|
||||
transition: background-color 0.15s;
|
||||
}
|
||||
.dropdown-item:hover {
|
||||
background-color: var(--kaauh-light-bg);
|
||||
color: var(--kaauh-teal-dark);
|
||||
}
|
||||
|
||||
/* Language Toggle Button Style */
|
||||
.language-toggle-btn {
|
||||
color: white !important;
|
||||
background: none !important;
|
||||
border: none !important;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.3rem;
|
||||
padding: 0.5rem 0.75rem !important;
|
||||
font-weight: 500;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
.language-toggle-btn:hover {
|
||||
background: rgba(255,255,255,0.12) !important;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
/* Profile Avatar - Enhanced Feedback */
|
||||
.profile-avatar {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
border-radius: 50%;
|
||||
background: var(--kaauh-teal);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: white;
|
||||
font-weight: bold;
|
||||
font-size: 0.85rem;
|
||||
transition: transform 0.1s ease;
|
||||
}
|
||||
/* Subtle hover effect for the profile button */
|
||||
.navbar-nav .dropdown-toggle:hover .profile-avatar {
|
||||
transform: scale(1.05);
|
||||
}
|
||||
.navbar-nav .dropdown-toggle.p-0:hover {
|
||||
background: none !important; /* Keep avatar background fixed */
|
||||
}
|
||||
<link rel="stylesheet" href="{% static 'css/main.css' %}">
|
||||
|
||||
|
||||
/* === Job Table and other sections CSS remain the same... === */
|
||||
.job-table-wrapper {
|
||||
background: white;
|
||||
border-radius: 10px;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 4px 16px rgba(0,0,0,0.06);
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
.job-table thead th {
|
||||
background: var(--kaauh-teal);
|
||||
color: white;
|
||||
font-weight: 600;
|
||||
padding: 1rem;
|
||||
text-align: center;
|
||||
}
|
||||
.job-table td {
|
||||
padding: 1rem;
|
||||
vertical-align: middle;
|
||||
text-align: center;
|
||||
}
|
||||
.job-table tr:hover td {
|
||||
background-color: rgba(0, 99, 110, 0.03);
|
||||
}
|
||||
.btn-apply {
|
||||
background: var(--kaauh-teal);
|
||||
border: none;
|
||||
color: white;
|
||||
padding: 0.45rem 1rem;
|
||||
font-weight: 600;
|
||||
border-radius: 6px;
|
||||
transition: all 0.2s;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.btn-apply:hover {
|
||||
background: var(--kaauh-teal-dark);
|
||||
transform: translateY(-1px);
|
||||
box-shadow: 0 2px 6px rgba(0,0,0,0.15);
|
||||
}
|
||||
|
||||
@media (max-width: 575.98px) {
|
||||
.table-responsive {
|
||||
overflow-x: auto;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
}
|
||||
.job-table th,
|
||||
.job-table td {
|
||||
white-space: nowrap;
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
}
|
||||
|
||||
/* === 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);
|
||||
}
|
||||
/* The main content width is already handled by the inline style, but making it explicit here */
|
||||
main.container-fluid {
|
||||
min-height: calc(100vh - 200px);
|
||||
padding: 1.5rem 0;
|
||||
}
|
||||
|
||||
/* === RTL Support === */
|
||||
html[dir="rtl"] {
|
||||
text-align: right;
|
||||
direction: rtl;
|
||||
}
|
||||
html[dir="rtl"] .navbar-brand {
|
||||
flex-direction: row-reverse;
|
||||
}
|
||||
html[dir="rtl"] .dropdown-menu {
|
||||
/* Ensures RTL dropdown menu opens left aligned to its button when dropdown-menu-end isn't used */
|
||||
right: auto;
|
||||
left: 0;
|
||||
}
|
||||
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; }
|
||||
|
||||
|
||||
.form-control-sm,
|
||||
.btn-sm {
|
||||
/* Reduce vertical padding even more than default Bootstrap 'sm' */
|
||||
padding-top: 0.2rem !important;
|
||||
padding-bottom: 0.2rem !important;
|
||||
/* Ensure a consistent, small height for both */
|
||||
height: 35px !important;
|
||||
font-size: 2 rem !important; /* Slightly smaller font */
|
||||
}
|
||||
|
||||
</style>
|
||||
{% block customCSS %}{% endblock %}
|
||||
</head>
|
||||
<body class="d-flex flex-column min-vh-100" hx-headers='{"X-CSRFToken": "{{ csrf_token }}"}'>
|
||||
|
||||
<div class="top-bar d-none d-md-block">
|
||||
{# Changed container to container-fluid and added max-width-1600 to inner div #}
|
||||
<div class="container-fluid">
|
||||
<div class="d-flex justify-content-between align-items-center gap-2 max-width-1600">
|
||||
<div class="d-flex align-items-center gap-3 social-icons">
|
||||
{% comment %} <span class="text-muted">{% trans "Follow Us:" %}</span>
|
||||
<a href="#" aria-label="Facebook"><i class="fab fa-facebook-f"></i></a>
|
||||
<a href="#" aria-label="Twitter"><i class="fab fa-twitter"></i></a>
|
||||
<a href="#" aria-label="Instagram"><i class="fab fa-instagram"></i></a> {% endcomment %}
|
||||
</div>
|
||||
<div class="contact-info d-flex gap-3">
|
||||
{% comment %} <div class="contact-item">
|
||||
<i class="fas fa-envelope text-primary"></i>
|
||||
<span>info@kaauh.edu.sa</span>
|
||||
</div>
|
||||
<div class="contact-item">
|
||||
<i class="fas fa-phone text-primary"></i>
|
||||
<span>+966 11 820 0000</span>
|
||||
</div> {% endcomment %}
|
||||
</div>
|
||||
<div class="logo-container d-flex gap-2">
|
||||
<img src="{% static 'image/vision.svg' %}" alt="{% trans 'Saudi Vision 2030' %}" loading="lazy">
|
||||
<div class="kaauh-logo-container d-flex flex-column flex-md-row align-items-center gap-2 ms-4">
|
||||
<div class="hospital-text text-center text-md-start me-3">
|
||||
<div class="ar small">جامعة الأميرة نورة بنت عبدالرحمن الأكاديمية</div>
|
||||
<div class="ar small">ومستشفى الملك عبدالله بن عبدالعزيز التخصصي</div>
|
||||
<div class="en small">Princess Nourah bint Abdulrahman University</div>
|
||||
<div class="en small">King Abdullah bin Abdulaziz University Hospital</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="ar text-xs">جامعة الأميرة نورة بنت عبدالرحمن الأكاديمية</div>
|
||||
<div class="ar text-xs">ومستشفى الملك عبدالله بن عبدالرحمن التخصصي</div>
|
||||
<div class="en text-xs">Princess Nourah bint Abdulrahman University</div>
|
||||
<div class="en text-xs">King Abdullah bin Abdulaziz University Hospital</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<img src="{% static 'image/kaauh.png' %}" alt="KAAUH Logo" style="max-height: 100px;max-width:100px;">
|
||||
<img src="{% static 'image/kaauh.png' %}" alt="KAAUH Logo" style="max-height: 40px; max-width: 40px;">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<nav class="navbar navbar-expand-lg navbar-dark sticky-top">
|
||||
{# Changed container to container-fluid and added max-width-1600 to inner div #}
|
||||
<div class="container-fluid">
|
||||
<div class="navbar-content-wrapper max-width-1600 d-flex justify-content-between align-items-center" style="width: 100%;">
|
||||
<a class="navbar-brand text-white d-none d-md-block" href="{% url 'dashboard' %}">
|
||||
<img src="{% static 'image/kaauh_green1.png' %}" alt="{% trans 'kaauh logo green bg' %}" style="width: 60px; height: 60px;">
|
||||
</a>
|
||||
<div class="container-fluid max-width-1600">
|
||||
|
||||
{# --- MOBILE BRAND LOGIC: Show small logo on mobile, large on desktop (lg) --- #}
|
||||
<a class="navbar-brand text-white d-block d-lg-none" href="{% url 'dashboard' %}" aria-label="Home">
|
||||
<img src="{% static 'image/kaauh_green1.png' %}" alt="{% trans 'kaauh logo green bg' %}" class="navbar-brand-mobile">
|
||||
</a>
|
||||
|
||||
<a class="navbar-brand text-white d-none d-lg-block" href="{% url 'dashboard' %}" aria-label="Home">
|
||||
<img src="{% static 'image/kaauh_green1.png' %}" alt="{% trans 'kaauh logo green bg' %}" style="width: 60px; height: 60px;">
|
||||
</a>
|
||||
|
||||
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav"
|
||||
aria-controls="navbarNav" aria-expanded="false" aria-label="{% trans 'Toggle navigation' %}">
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
</button>
|
||||
|
||||
<div class="collapse navbar-collapse" id="navbarNav">
|
||||
<ul class="navbar-nav me-auto mb-2 mb-lg-0">
|
||||
{% comment %} <li class="nav-item">
|
||||
<a class="nav-link {% if request.resolver_match.url_name == 'dashboard' %}active{% endif %}" href="{% url 'dashboard' %}">
|
||||
<span class="d-flex align-items-center gap-2">
|
||||
{% include "icons/dashboard.html" %}
|
||||
{% trans "Dashboard" %}
|
||||
</span>
|
||||
</a>
|
||||
</li> {% endcomment %}
|
||||
<li class="nav-item me-4">
|
||||
<a class="nav-link {% if request.resolver_match.url_name == 'job_list' %}active{% endif %}" href="{% url 'job_list' %}">
|
||||
<span class="d-flex align-items-center gap-2">
|
||||
{% include "icons/jobs.html" %}
|
||||
{% trans "Jobs" %}
|
||||
</span>
|
||||
</a>
|
||||
</li>
|
||||
|
||||
{% comment %} <li class="nav-item me-2">
|
||||
<a class="nav-link {% if request.resolver_match.url_name == 'form_templates_list' %}active{% endif %}" href="{% url 'form_templates_list' %}">
|
||||
<span class="d-flex align-items-center gap-2">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-6">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M19.5 14.25v-2.625a3.375 3.375 0 0 0-3.375-3.375h-1.5A1.125 1.125 0 0 1 13.5 7.125v-1.5a3.375 3.375 0 0 0-3.375-3.375H8.25m0 12.75h7.5m-7.5 3H12M10.5 2.25H5.625c-.621 0-1.125.504-1.125 1.125v17.25c0 .621.504 1.125 1.125 1.125h12.75c.621 0 1.125-.504 1.125-1.125V11.25a9 9 0 0 0-9-9Z" />
|
||||
</svg>
|
||||
|
||||
{% trans "Form Templates" %}
|
||||
</span>
|
||||
|
||||
</a>
|
||||
</li> {% endcomment %}
|
||||
|
||||
<li class="nav-item me-4">
|
||||
<a class="nav-link {% if request.resolver_match.url_name == 'candidate_list' %}active{% endif %}" href="{% url 'candidate_list' %}">
|
||||
<span class="d-flex align-items-center gap-2">
|
||||
{% include "icons/users.html" %}
|
||||
{% trans "Applicants" %}
|
||||
</span>
|
||||
</a>
|
||||
</li>
|
||||
|
||||
<li class="nav-item me-4">
|
||||
<a class="nav-link {% if request.resolver_match.url_name == 'list_meetings' %}active{% endif %}" href="{% url 'list_meetings' %}">
|
||||
<span class="d-flex align-items-center gap-2">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-6">
|
||||
<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 "Meetings" %}
|
||||
</span>
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
<li class="nav-item me-4">
|
||||
<a class="nav-link {% if request.resolver_match.url_name == 'training_list' %}active{% endif %}" href="{% url 'training_list' %}">
|
||||
<span class="d-flex align-items-center gap-2">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-6">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M4.26 10.147a60.438 60.438 0 0 0-.491 6.347A48.62 48.62 0 0 1 12 20.904a48.62 48.62 0 0 1 8.232-4.41 60.46 60.46 0 0 0-.491-6.347m-15.482 0a50.636 50.636 0 0 0-2.658-.813A59.906 59.906 0 0 1 12 3.493a59.903 59.903 0 0 1 10.399 5.84c-.896.248-1.783.52-2.658.814m-15.482 0A50.717 50.717 0 0 1 12 13.489a50.702 50.702 0 0 1 7.74-3.342M6.75 15a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5Zm0 0v-3.675A55.378 55.378 0 0 1 12 8.443m-7.007 11.55A5.981 5.981 0 0 0 6.75 15.75v-1.5" />
|
||||
</svg>
|
||||
|
||||
{% trans "Training" %}
|
||||
</span>
|
||||
</a>
|
||||
</li>
|
||||
|
||||
<li class="nav-item dropdown ms-2">
|
||||
<a class="nav-link dropdown-toggle" href="#" role="button" data-bs-toggle="dropdown"
|
||||
data-bs-offset="0, 8" data-bs-auto-close="outside">
|
||||
{% trans "More" %}
|
||||
</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>
|
||||
</ul>
|
||||
|
||||
|
||||
<ul class="navbar-nav me-2">
|
||||
<li class="nav-item dropdown">
|
||||
<a class="language-toggle-btn dropdown-toggle" href="#" role="button" data-bs-toggle="dropdown"
|
||||
data-bs-offset="0, 8" aria-expanded="false" aria-label="{% trans 'Toggle language menu' %}">
|
||||
<i class="fas fa-globe"></i>
|
||||
<span class="d-none d-lg-inline">{{ LANGUAGE_CODE|upper }}</span>
|
||||
</a>
|
||||
<ul class="dropdown-menu dropdown-menu-end" data-bs-popper="static">
|
||||
|
||||
{% get_current_language as LANGUAGE_CODE %}
|
||||
|
||||
<li>
|
||||
<form action="{% url 'set_language' %}" method="post" class="d-inline">{% csrf_token %}
|
||||
<input name="next" type="hidden" value="{{ request.get_full_path }}">
|
||||
<button name="language" value="en" class="dropdown-item {% if LANGUAGE_CODE == 'en' %}active bg-light-subtle{% endif %}" type="submit">
|
||||
<span class="me-2">🇺🇸</span> English
|
||||
</button>
|
||||
</form>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<form action="{% 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>
|
||||
{# Toggler: order-lg-0 ensures it's before navigation links on desktop, but it stays where it is on mobile #}
|
||||
<button class="navbar-toggler order-lg-0" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav"
|
||||
aria-controls="navbarNav" aria-expanded="false" aria-label="{% trans 'Toggle navigation' %}">
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
</button>
|
||||
|
||||
{# Language and Profile Controls (Keep outside collapse for mobile access) #}
|
||||
<div class="d-flex align-items-center order-lg-3">
|
||||
<ul class="navbar-nav flex-row">
|
||||
<li class="nav-item dropdown">
|
||||
<a class="language-toggle-btn dropdown-toggle" href="#" role="button" data-bs-toggle="dropdown"
|
||||
data-bs-offset="0, 8" aria-expanded="false" aria-label="{% trans 'Toggle language menu' %}">
|
||||
<i class="fas fa-globe"></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="{% url 'set_language' %}" method="post" class="d-inline">{% csrf_token %}
|
||||
<input name="next" type="hidden" value="{{ request.get_full_path }}">
|
||||
<button name="language" value="en" class="dropdown-item {% if LANGUAGE_CODE == 'en' %}active bg-light-subtle{% endif %}" type="submit">
|
||||
<span class="me-2">🇺🇸</span> English
|
||||
</button>
|
||||
</form>
|
||||
</li>
|
||||
<li>
|
||||
<form action="{% 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>
|
||||
|
||||
<ul class="navbar-nav ms-4">
|
||||
<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, 8"
|
||||
>
|
||||
{% if user.profile.profile_image %}
|
||||
<img src="{{ user.profile.profile_image.url }}" alt="{{ user.username }}" class="profile-avatar"
|
||||
style="width: 36px; height: 36px; object-fit: cover; background-color: var(--kaauh-teal); display: inline-block; vertical-align: middle;"
|
||||
title="{% trans 'Your account' %}">
|
||||
{% else %}
|
||||
<div class="profile-avatar" title="{% trans 'Your account' %}">
|
||||
{{ user.username|first|upper }}
|
||||
<ul class="navbar-nav ms-2 ms-lg-4">
|
||||
<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" {# Vertical offset remains 16px to prevent clipping #}
|
||||
>
|
||||
{% if user.profile.profile_image %}
|
||||
<img src="{{ user.profile.profile_image.url }}" alt="{{ user.username }}" class="profile-avatar"
|
||||
style="width: 36px; height: 36px; object-fit: cover; background-color: var(--kaauh-teal); display: inline-block; vertical-align: middle;"
|
||||
title="{% trans 'Your account' %}">
|
||||
{% else %}
|
||||
<div class="profile-avatar" title="{% trans 'Your account' %}">
|
||||
{{ user.username|first|upper }}
|
||||
</div>
|
||||
{% endif %}
|
||||
</button>
|
||||
<ul
|
||||
{# FINAL FIX: Always use dropdown-menu-end. In LTR (English), this aligns right. In RTL (Arabic), this aligns left. #}
|
||||
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.profile_image %}
|
||||
<img src="{{ user.profile.profile_image.url }}" alt="{{ user.username }}" class="profile-avatar shadow-sm border"
|
||||
style="width: 44px; height: 44px; object-fit: cover; background-color: var(--kaauh-teal); display: block;"
|
||||
title="{% trans 'Your account' %}">
|
||||
{% else %}
|
||||
<div class="profile-avatar shadow-sm border d-flex align-items-center justify-content-center"
|
||||
style="width: 44px; height: 44px; background-color: var(--kaauh-teal); font-size: 1.2rem;">
|
||||
{{ user.username|first|upper }}
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endif %}
|
||||
{% comment %} <span class="ms-2 d-none d-lg-inline fw-semibold">{{ user.username }}</span> {% endcomment %}
|
||||
</button>
|
||||
<ul
|
||||
class="dropdown-menu dropdown-menu-end py-0 shadow border-0 rounded-3"
|
||||
data-bs-popper="static"
|
||||
style="min-width: 240px;"
|
||||
>
|
||||
<li class="px-4 py-3 ">
|
||||
<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.profile_image %}
|
||||
<img src="{{ user.profile.profile_image.url }}" alt="{{ user.username }}" class="profile-avatar shadow-sm border"
|
||||
style="width: 44px; height: 44px; object-fit: cover; background-color: var(--kaauh-teal); display: block;"
|
||||
title="{% trans 'Your account' %}">
|
||||
{% else %}
|
||||
<div class="profile-avatar shadow-sm border d-flex align-items-center justify-content-center"
|
||||
style="width: 44px; height: 44px; background-color: var(--kaauh-teal); font-size: 1.2rem;">
|
||||
{{ user.username|first|upper }}
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div>
|
||||
<div class="fw-semibold text-dark">{{ user.get_full_name|default:user.username }}</div>
|
||||
<div class="text-muted small">{{ user.email|truncatechars:24 }}</div>
|
||||
</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>
|
||||
</li>
|
||||
<li><hr class="dropdown-divider my-1"></li>
|
||||
<li><a class="dropdown-item py-2 px-4 d-flex align-items-center text-decoration-none" href="{% url 'user_detail' request.user.pk %}"><i class="fas fa-user-circle me-3 text-primary fs-5"></i> <span>{% trans "My Profile" %}</span></a></li>
|
||||
<li><a class="dropdown-item py-2 px-4 d-flex align-items-center text-decoration-none" href="#"><i class="fas fa-cog me-3 text-primary fs-5"></i> <span>{% trans "Settings" %}</span></a></li>
|
||||
<li><a class="dropdown-item py-2 px-4 d-flex align-items-center text-decoration-none" href="#"><i class="fas fa-history me-3 text-primary fs-5"></i> <span>{% trans "Activity Log" %}</span></a></li>
|
||||
<li><a class="dropdown-item py-2 px-4 d-flex align-items-center text-decoration-none" href="#"><i class="fas fa-question-circle me-3 text-primary fs-5"></i> <span>{% trans "Help & Support" %}</span></a></li>
|
||||
<li><a class="dropdown-item py-2 px-4 d-flex align-items-center text-decoration-none" href="#">
|
||||
{% if not request.session.linkedin_authenticated %}
|
||||
<ul class="navbar-nav ms-auto">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="{% url 'linkedin_login' %}">
|
||||
<i class="fab fa-linkedin me-1"></i> {% trans "Connect LinkedIn" %}
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
{% else %}
|
||||
<i class="fab fa-linkedin text-primary me-1"></i>
|
||||
<span class="text-primary d-none d-lg-inline ms-auto me-3">
|
||||
{% trans "LinkedIn Connected" %}
|
||||
</span>
|
||||
{% endif %}
|
||||
</a></li>
|
||||
|
||||
<li><hr class="dropdown-divider my-1"></li>
|
||||
</div>
|
||||
</li>
|
||||
<li><hr class="dropdown-divider my-1"></li>
|
||||
<li><a class="dropdown-item py-2 px-4 d-flex align-items-center text-decoration-none" href="{% url 'user_detail' request.user.pk %}"><i class="fas fa-user-circle me-3 text-primary fs-5"></i> <span>{% trans "My Profile" %}</span></a></li>
|
||||
<li><a class="dropdown-item py-2 px-4 d-flex align-items-center text-decoration-none" href="#"><i class="fas fa-cog me-3 text-primary fs-5"></i> <span>{% trans "Settings" %}</span></a></li>
|
||||
<li><a class="dropdown-item py-2 px-4 d-flex align-items-center text-decoration-none" href="#"><i class="fas fa-history me-3 text-primary fs-5"></i> <span>{% trans "Activity Log" %}</span></a></li>
|
||||
<li><a class="dropdown-item py-2 px-4 d-flex align-items-center text-decoration-none" href="#"><i class="fas fa-question-circle me-3 text-primary fs-5"></i> <span>{% trans "Help & Support" %}</span></a></li>
|
||||
|
||||
{% comment %} CORRECTED LINKEDIN BLOCK {% endcomment %}
|
||||
{% if not request.session.linkedin_authenticated %}
|
||||
<li>
|
||||
<form method="post" action="{% url 'account_logout'%}" class="d-inline">
|
||||
{% csrf_token %}
|
||||
<button
|
||||
type="submit"
|
||||
class="dropdown-item py-2 px-4 text-danger d-flex align-items-center border-0 bg-transparent text-start"
|
||||
aria-label="{% trans 'Sign out' %}"
|
||||
>
|
||||
<i class="fas fa-sign-out-alt me-3 fs-5"></i>
|
||||
<span>{% trans "Sign Out" %}</span>
|
||||
</button>
|
||||
</form>
|
||||
<a class="dropdown-item py-2 px-4 d-flex align-items-center text-decoration-none" href="{% url 'linkedin_login' %}">
|
||||
<i class="fab fa-linkedin me-3 text-primary fs-5"></i>
|
||||
<span>{% trans "Connect LinkedIn" %}</span>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
{% else %}
|
||||
<li class="px-4 py-2 text-muted small">
|
||||
<i class="fab fa-linkedin text-primary me-2"></i>
|
||||
{% trans "LinkedIn Connected" %}
|
||||
</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 text-danger d-flex align-items-center border-0 bg-transparent text-start"
|
||||
aria-label="{% trans 'Sign out' %}"
|
||||
>
|
||||
<i class="fas fa-sign-out-alt me-3 fs-5"></i>
|
||||
<span>{% trans "Sign Out" %}</span>
|
||||
</button>
|
||||
</form>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
{# End Language and Profile Controls #}
|
||||
|
||||
{# Main Navigation Links (This collapses on mobile) - order-lg-1 ensures it is centered on desktop #}
|
||||
<div class="collapse navbar-collapse order-lg-1" id="navbarNav">
|
||||
<ul class="navbar-nav me-auto mb-2 mb-lg-0">
|
||||
{# Changed me-4 to me-lg-4 so they stack tightly on mobile #}
|
||||
<li class="nav-item me-lg-4">
|
||||
<a class="nav-link {% if request.resolver_match.url_name == 'job_list' %}active{% endif %}" href="{% url 'job_list' %}">
|
||||
<span class="d-flex align-items-center gap-2">
|
||||
{% include "icons/jobs.html" %}
|
||||
{% trans "Jobs" %}
|
||||
</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item me-lg-4">
|
||||
<a class="nav-link {% if request.resolver_match.url_name == 'candidate_list' %}active{% endif %}" href="{% url 'candidate_list' %}">
|
||||
<span class="d-flex align-items-center gap-2">
|
||||
{% include "icons/users.html" %}
|
||||
{% trans "Applicants" %}
|
||||
</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item me-lg-4">
|
||||
<a class="nav-link {% if request.resolver_match.url_name == 'list_meetings' %}active{% endif %}" href="{% url 'list_meetings' %}">
|
||||
<span class="d-flex align-items-center gap-2">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-6">
|
||||
<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 "Meetings" %}
|
||||
</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item me-lg-4">
|
||||
<a class="nav-link {% if request.resolver_match.url_name == 'training_list' %}active{% endif %}" href="{% url 'training_list' %}">
|
||||
<span class="d-flex align-items-center gap-2">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-6">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M4.26 10.147a60.438 60.438 0 0 0-.491 6.347A48.62 48.62 0 0 1 12 20.904a48.62 48.62 0 0 1 8.232-4.41 60.46 60.46 0 0 0-.491-6.347m-15.482 0a50.636 50.636 0 0 0-2.658-.813A59.906 59.906 0 0 1 12 3.493a59.903 59.903 0 0 1 10.399 5.84c-.896.248-1.783.52-2.658.814m-15.482 0A50.717 50.717 0 0 1 12 13.489a50.702 50.702 0 0 1 7.74-3.342M6.75 15a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5Zm0 0v-3.675A55.378 55.378 0 0 1 12 8.443m-7.007 11.55A5.981 5.981 0 0 0 6.75 15.75v-1.5" />
|
||||
</svg>
|
||||
{% trans "Training" %}
|
||||
</span>
|
||||
</a>
|
||||
</li>
|
||||
<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>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
@ -552,19 +279,24 @@
|
||||
{% include 'includes/delete_modal.html' %}
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/htmx.org@2.0.7/dist/htmx.min.js"></script>
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
// Navbar collapse auto-close on link click (Standard Mobile UX)
|
||||
const navbarCollapse = document.getElementById('navbarNav');
|
||||
if (navbarCollapse) {
|
||||
const navLinks = navbarCollapse.querySelectorAll('.nav-link:not(.dropdown-toggle)');
|
||||
// Select all links, including those inside the "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 the nav is actually shown (i.e., on mobile)
|
||||
if (navbarCollapse.classList.contains('show')) {
|
||||
bsCollapse.hide();
|
||||
// Check if the click was on a non-dropdown-toggle or a dropdown item (which navigate away)
|
||||
if (!link.classList.contains('dropdown-toggle')) {
|
||||
bsCollapse.hide();
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
@ -574,34 +306,15 @@
|
||||
const logoutButton = document.querySelector('form[action$="/logout/"] button');
|
||||
if (logoutButton) {
|
||||
logoutButton.addEventListener('click', (e) => {
|
||||
// Check if screen is small (e.g., Bootstrap large breakpoint is 992px)
|
||||
if (window.innerWidth <= 992) {
|
||||
// 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();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Handle language form submission: Manually trigger click on button inside form
|
||||
document.querySelectorAll('.language-toggle-btn').forEach(toggle => {
|
||||
const menu = toggle.nextElementSibling;
|
||||
if (menu) {
|
||||
menu.querySelectorAll('.dropdown-item').forEach(item => {
|
||||
item.addEventListener('click', (e) => {
|
||||
// Find the containing form and submit it
|
||||
const form = item.closest('form');
|
||||
if (form) {
|
||||
form.submit();
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/htmx.org@2.0.7/dist/htmx.min.js"></script>
|
||||
{% comment %} <script type="module" src="https://cdn.jsdelivr.net/gh/starfederation/datastar@1.0.0-RC.5/bundles/datastar.js"></script> {% endcomment %}
|
||||
|
||||
{% block customJS %}{% endblock %}
|
||||
|
||||
</body>
|
||||
|
||||
@ -1,40 +1,6 @@
|
||||
{% extends 'forms/partials/candidate_facing_base.html'%}
|
||||
{% load static i18n %}
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>{% translate "Application Form" %}</title>
|
||||
<link
|
||||
href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css"
|
||||
rel="stylesheet"
|
||||
/>
|
||||
<link
|
||||
rel="stylesheet"
|
||||
href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css"
|
||||
/>
|
||||
<style>
|
||||
/* KAAT-S Theme Variables */
|
||||
:root {
|
||||
--kaauh-teal: #00636e; /* Main Primary Color */
|
||||
--kaauh-teal-dark: #004a53; /* Dark Primary Color */
|
||||
|
||||
/* Mapping wizard defaults to theme colors */
|
||||
--primary: var(--kaauh-teal);
|
||||
--primary-light: #007c89; /* Slightly lighter shade for subtle hover/border */
|
||||
--secondary: var(--kaauh-teal-dark);
|
||||
--success: #198754; /* Keeping a standard success green for Submit */
|
||||
--error: #dc3545; /* Standard danger red */
|
||||
|
||||
--light: #f8f9fa;
|
||||
--dark: #212529;
|
||||
--gray: #6c757d;
|
||||
--light-gray: #e9ecef;
|
||||
--border: #dee2e6;
|
||||
--shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
|
||||
--radius: 16px; /* Increased radius for a softer look */
|
||||
--transition: all 0.3s ease;
|
||||
}
|
||||
{% block content %}
|
||||
<style>
|
||||
/* KAAT-S Theme Variables */
|
||||
:root {
|
||||
@ -501,57 +467,7 @@
|
||||
/* The z-index is already 1030 in the inline style, which is correct */
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<nav
|
||||
id="topNavbar"
|
||||
class="navbar navbar-expand-lg"
|
||||
style="background-color: white; z-index: 1030"
|
||||
>
|
||||
<div class="container-fluid">
|
||||
<a class="navbar-brand text-white fw-bold" href="/">
|
||||
<img
|
||||
src="{% static 'image/kaauh.jpeg' %}"
|
||||
alt="{% translate 'KAAUH IMAGE' %}"
|
||||
style="height: 50px; margin-right: 10px"
|
||||
/>
|
||||
</a>
|
||||
<button
|
||||
class="navbar-toggler"
|
||||
type="button"
|
||||
data-bs-toggle="collapse"
|
||||
data-bs-target="#navbarNav"
|
||||
aria-controls="navbarNav"
|
||||
aria-expanded="false"
|
||||
aria-label="Toggle navigation"
|
||||
>
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
</button>
|
||||
<div class="collapse navbar-collapse" id="navbarNav">
|
||||
<ul class="navbar-nav ms-auto">
|
||||
<li class="nav-item">
|
||||
<a
|
||||
class="nav-link text-secondary"
|
||||
href="/applications/"
|
||||
>{% translate "Applications" %}</a
|
||||
>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link text-secondary" href="/profile/"
|
||||
>{% translate "Profile" %}</a
|
||||
>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a
|
||||
class="nav-link text-secondary"
|
||||
href="https://kaauh.edu.sa/career"
|
||||
>{% translate "Careers" %}</a
|
||||
>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<nav
|
||||
id="bottomNavbar"
|
||||
class="navbar navbar-expand-lg sticky-top"
|
||||
@ -616,7 +532,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
|
||||
|
||||
<script>
|
||||
// Application State
|
||||
const csrfToken = '{{ csrf_token }}';
|
||||
@ -1344,5 +1260,4 @@
|
||||
// Start the application
|
||||
document.addEventListener('DOMContentLoaded', init);
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
{% endblock content %}
|
||||
88
templates/forms/job_detail_candidate.html
Normal file
88
templates/forms/job_detail_candidate.html
Normal file
@ -0,0 +1,88 @@
|
||||
{% extends 'forms/partials/candidate_facing_base.html'%}
|
||||
{% load static i18n %}
|
||||
{% block content %}
|
||||
|
||||
<nav id="bottomNavbar" class="navbar navbar-expand-lg sticky-top" style="background-color: var(--kaauh-teal); z-index: 1030;">
|
||||
<div class="container-fluid">
|
||||
<span class="navbar-text text-white fw-bold">{% trans "Job Overview" %}</span>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<div class="container">
|
||||
<div class="row mb-5 mt-3 main-content-area">
|
||||
|
||||
<div class="col-lg-4 order-lg-2 order-1 d-none d-lg-block">
|
||||
<div class="card shadow-sm sticky-top">
|
||||
<div class="card-header bg-kaauh-teal-dark text-white">
|
||||
<h5 class="mb-0"><i class="fas fa-file-signature me-2"></i>{% trans "Ready to Apply?" %}</h5>
|
||||
</div>
|
||||
<div class="card-body text-center">
|
||||
<p class="text-muted">{% trans "Review the job details, then apply below." %}</p>
|
||||
|
||||
{% if job.form_template %}
|
||||
<a href="{% url 'form_wizard' job.form_template.pk %}" class="btn btn-main-action btn-lg w-100">
|
||||
<i class="fas fa-paper-plane me-2"></i> {% trans "Apply for this Position" %}
|
||||
</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-lg-8 order-lg-1 order-2">
|
||||
<div class="card shadow-sm">
|
||||
<div class="card-header bg-kaauh-teal-dark text-white d-flex justify-content-between align-items-center">
|
||||
<h2 class="h3 mb-0 fw-bold">{{ job.title }}</h2>
|
||||
</div>
|
||||
|
||||
<div class="card-body">
|
||||
|
||||
<h4 class="mb-3" style="color: var(--kaauh-teal-dark);">{% trans "Job Overview" %}</h4>
|
||||
<div class="row row-cols-1 row-cols-md-2 g-3 mb-4 small text-secondary">
|
||||
{% if job.salary_range %}
|
||||
<div class="col">
|
||||
<i class="fas fa-money-bill-wave text-success me-2"></i>
|
||||
<strong>{% trans "Salary:" %}</strong>
|
||||
<span class="fw-bold text-success">{{ job.salary_range }}</span>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<div class="col">
|
||||
<i class="fas fa-calendar-alt text-muted me-2"></i>
|
||||
<strong>{% trans "Deadline:" %}</strong>
|
||||
{% if job.application_deadline %}
|
||||
{{ job.application_deadline|date:"M d, Y" }}
|
||||
{% if job.is_expired %}
|
||||
<span class="badge bg-danger ms-2">{% trans "EXPIRED" %}</span>
|
||||
{% endif %}
|
||||
{% else %}
|
||||
<span class="text-muted">{% trans "Not specified" %}</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<div class="col"> <i class="fas fa-briefcase text-muted me-2"></i> <strong>{% trans "Job Type:" %}</strong> {{ job.get_job_type_display }} </div>
|
||||
<div class="col"> <i class="fas fa-map-marker-alt text-muted me-2"></i> <strong>{% trans "Location:" %}</strong> {{ job.get_location_display }} </div>
|
||||
<div class="col"> <i class="fas fa-building text-muted me-2"></i> <strong>{% trans "Department:" %}</strong> {{ job.department|default:"N/A" }} </div>
|
||||
<div class="col"> <i class="fas fa-hashtag text-muted me-2"></i> <strong>{% trans "JOB ID:" %}</strong> {{ job.internal_job_id|default:"N/A" }} </div>
|
||||
<div class="col"> <i class="fas fa-desktop text-muted me-2"></i> <strong>{% trans "Workplace:" %}</strong> {{ job.get_workplace_type_display }} </div>
|
||||
</div>
|
||||
|
||||
{% if job.has_description_content %}<hr class="my-4"><div class="mb-4"><h5 class="fw-bold" style="color: var(--kaauh-teal-dark);"><i class="fas fa-info-circle me-2"></i>{% trans "Job Description" %}</h5><div class="text-secondary">{{ job.description|safe }}</div></div>{% endif %}
|
||||
{% if job.has_qualifications_content %}<hr class="my-4"><div class="mb-4"><h5 class="fw-bold" style="color: var(--kaauh-teal-dark);"><i class="fas fa-graduation-cap me-2"></i>{% trans "Qualifications" %}</h5><div class="text-secondary">{{ job.qualifications|safe }}</div></div>{% endif %}
|
||||
{% if job.has_benefits_content %}<hr class="my-4"><div class="mb-4"><h5 class="fw-bold" style="color: var(--kaauh-teal-dark);"><i class="fas fa-hand-holding-usd me-2"></i>{% trans "Benefits" %}</h5><div class="text-secondary">{{ job.benefits|safe }}</div></div>{% endif %}
|
||||
{% if job.has_application_instructions_content %}<hr class="my-4"><div class="mb-4"><h5 class="fw-bold" style="color: var(--kaauh-teal-dark);"><i class="fas fa-file-alt me-2"></i>{% trans "Application Instructions" %}</h5><div class="text-secondary">{{ job.application_instructions|safe }}</div></div>{% endif %}
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mobile-fixed-apply-bar d-lg-none">
|
||||
{% if job.form_template %}
|
||||
<a href="{% url 'form_wizard' job.form_template.pk %}" class="btn btn-main-action btn-lg w-100">
|
||||
<i class="fas fa-paper-plane me-2"></i> {% trans "Apply for this Position" %}
|
||||
</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endblock content%}
|
||||
265
templates/forms/partials/candidate_facing_base.html
Normal file
265
templates/forms/partials/candidate_facing_base.html
Normal file
@ -0,0 +1,265 @@
|
||||
{% load static i18n %}
|
||||
{% get_current_language as LANGUAGE_CODE %}
|
||||
{% get_available_languages as LANGUAGES %}
|
||||
{% get_language_info_list for LANGUAGES as language_info_list %}
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html lang="{{ LANGUAGE_CODE }}" dir="{% if LANGUAGE_CODE == 'ar' %}rtl{% else %}ltr{% endif %}">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>{% translate "Application Form" %}</title>
|
||||
|
||||
{% comment %} Load the correct Bootstrap CSS file for RTL/LTR {% endcomment %}
|
||||
{% if LANGUAGE_CODE == 'ar' %}
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.rtl.min.css" rel="stylesheet">
|
||||
{% else %}
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
{% endif %}
|
||||
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
|
||||
<style>
|
||||
/* ---------------------------------------------------------------------- */
|
||||
/* THEME & UTILITY VARIABLES */
|
||||
/* ---------------------------------------------------------------------- */
|
||||
:root {
|
||||
--kaauh-teal: #00636e;
|
||||
--kaauh-teal-dark: #004a53;
|
||||
--success: #198754;
|
||||
--danger: #dc3545;
|
||||
--light-bg: #f8f9fa;
|
||||
--gray-text: #6c757d;
|
||||
--kaauh-border: #eaeff3; /* Added for dropdown styling */
|
||||
|
||||
/* CALCULATED STICKY HEIGHTS */
|
||||
--navbar-height: 56px;
|
||||
--navbar-gap: 16px;
|
||||
--sticky-navbar-total-height: 128px;
|
||||
}
|
||||
|
||||
body {
|
||||
min-height: 100vh;
|
||||
background-color: #f0f0f5; /* Light gray background for contrast */
|
||||
padding-top: 0;
|
||||
}
|
||||
|
||||
.btn-main-action {
|
||||
background-color: var(--kaauh-teal);
|
||||
color: white;
|
||||
border: none;
|
||||
transition: background-color 0.3s ease, transform 0.2s ease;
|
||||
box-shadow: 0 4px 12px rgba(0, 99, 110, 0.3);
|
||||
}
|
||||
|
||||
.btn-main-action:hover {
|
||||
background-color: var(--kaauh-teal-dark);
|
||||
color: white;
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
|
||||
.bg-kaauh-teal-dark {
|
||||
background-color: var(--kaauh-teal-dark) !important;
|
||||
}
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
/* LANGUAGE TOGGLE STYLES (COPIED FROM MAIN LAYOUT) */
|
||||
/* ---------------------------------------------------------------------- */
|
||||
.language-toggle-btn {
|
||||
color: var(--gray-text) !important; /* Use secondary color */
|
||||
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: var(--light-bg) !important;
|
||||
border-radius: 4px;
|
||||
color: var(--kaauh-teal) !important;
|
||||
}
|
||||
|
||||
/* Dropdown Menu styling for language */
|
||||
.dropdown-menu {
|
||||
backdrop-filter: blur(4px);
|
||||
background-color: rgba(255, 255, 255, 0.98);
|
||||
border: 1px solid var(--kaauh-border);
|
||||
box-shadow: 0 6px 20px rgba(0,0,0,0.12);
|
||||
border-radius: 8px;
|
||||
padding: 0.5rem 0;
|
||||
min-width: 150px;
|
||||
}
|
||||
.dropdown-item {
|
||||
padding: 0.5rem 1.25rem;
|
||||
transition: background-color 0.15s;
|
||||
text-align: inherit; /* Ensure text alignment is controlled by dir="rtl" */
|
||||
}
|
||||
|
||||
/* Use button as dropdown-item inside form for full click area */
|
||||
.dropdown-item[type="submit"] {
|
||||
width: 100%;
|
||||
text-align: inherit;
|
||||
border: none;
|
||||
}
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
/* LAYOUT & STICKY POSITIONING FIXES (Desktop/Tablet) */
|
||||
/* ---------------------------------------------------------------------- */
|
||||
|
||||
#topNavbar {
|
||||
z-index: 1040; /* Higher than the bottom bar */
|
||||
}
|
||||
|
||||
/* 1. Position the dark navbar below the white navbar + gap */
|
||||
#bottomNavbar {
|
||||
/* 56px (white nav) + 16px (mb-3) = 72px */
|
||||
top: calc(var(--navbar-height) + var(--navbar-gap));
|
||||
z-index: 1030;
|
||||
}
|
||||
|
||||
/* 2. Pushes the main content down so it's not hidden under the navbars */
|
||||
.main-content-area {
|
||||
/* Total Sticky Height (128px) + Extra Margin (12px) = 140px */
|
||||
margin-top: calc(var(--sticky-navbar-total-height) + 12px);
|
||||
}
|
||||
|
||||
/* 3. Positions the sticky sidebar correctly */
|
||||
.card.sticky-top {
|
||||
/* Start scrolling the sidebar just below the two navbars + a small gap */
|
||||
top: calc(var(--sticky-navbar-total-height) + 15px);
|
||||
}
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
/* RTL / ARABIC SUPPORT - Optimized */
|
||||
/* ---------------------------------------------------------------------- */
|
||||
html[dir="rtl"] {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
/* Flip Margin Utilities (m-end and m-start) */
|
||||
html[dir="rtl"] .ms-auto { margin-right: auto !important; margin-left: 0 !important; }
|
||||
html[dir="rtl"] .me-auto { margin-left: auto !important; margin-right: 0 !important; }
|
||||
html[dir="rtl"] .ms-2 { margin-right: 0.5rem !important; margin-left: 0 !important; }
|
||||
html[dir="rtl"] .me-2 { margin-left: 0.5rem !important; margin-right: 0 !important; }
|
||||
html[dir="rtl"] .me-1 { margin-left: 0.25rem !important; margin-right: 0 !important; } /* For the globe icon */
|
||||
|
||||
/* Flip alignment for text-end/text-start */
|
||||
html[dir="rtl"] .text-end { text-align: left !important; }
|
||||
html[dir="rtl"] .text-start { text-align: right !important; }
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
/* MOBILE RESPONSIVE STYLES (Below 992px) */
|
||||
/* ---------------------------------------------------------------------- */
|
||||
@media (max-width: 991.98px) {
|
||||
|
||||
/* Ensures dropdown items in mobile menu align correctly */
|
||||
html[dir="rtl"] .navbar-collapse .dropdown-menu {
|
||||
text-align: right;
|
||||
left: auto;
|
||||
right: 0;
|
||||
}
|
||||
|
||||
/* On mobile, the top navbar is generally only 56px tall when collapsed. */
|
||||
#bottomNavbar {
|
||||
top: calc(var(--navbar-height) + var(--navbar-gap));
|
||||
}
|
||||
|
||||
.main-content-area {
|
||||
/* Reduced margin-top for smaller screens */
|
||||
margin-top: calc(var(--sticky-navbar-total-height) / 2);
|
||||
}
|
||||
|
||||
/* Mobile Fixed Footer Bar for Application */
|
||||
.mobile-fixed-apply-bar {
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
width: 100%;
|
||||
padding: 15px;
|
||||
background-color: var(--light-bg);
|
||||
border-top: 1px solid #ddd;
|
||||
z-index: 1000;
|
||||
box-shadow: 0 -4px 10px rgba(0, 0, 0, 0.08);
|
||||
}
|
||||
|
||||
/* Add padding to the bottom of the body content to prevent it from hiding under the fixed bar */
|
||||
body {
|
||||
padding-bottom: 90px;
|
||||
}
|
||||
|
||||
/* Fix job overview grid responsiveness (ensures 1 column) */
|
||||
.row-cols-md-2 > .col {
|
||||
flex: 0 0 100%;
|
||||
max-width: 100%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<nav id="topNavbar" class="navbar navbar-expand-lg sticky-top" style="background-color: white; z-index: 1040;">
|
||||
<div class="container-fluid">
|
||||
<a class="navbar-brand text-white fw-bold" href="{% url 'kaauh_career' %}">
|
||||
<img src="{% static 'image/kaauh.jpeg' %}" alt="{% translate 'KAAUH IMAGE' %}" style="height: 50px; margin-right: 10px;">
|
||||
</a>
|
||||
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav"
|
||||
aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
</button>
|
||||
<div class="collapse navbar-collapse" id="navbarNav">
|
||||
<ul class="navbar-nav ms-auto">
|
||||
|
||||
<li class="nav-item">
|
||||
<a class="nav-link text-secondary" href="/applications/">{% translate "Applications" %}</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link text-secondary" href="/profile/">{% translate "Profile" %}</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link text-secondary" href="{% url 'kaauh_career' %}">{% translate "Careers" %}</a>
|
||||
</li>
|
||||
|
||||
<li class="nav-item dropdown">
|
||||
<button class="language-toggle-btn dropdown-toggle" type="button"
|
||||
data-bs-toggle="dropdown" data-bs-offset="0, 8" aria-expanded="false"
|
||||
aria-label="{% trans 'Toggle language menu' %}">
|
||||
<i class="fas fa-globe"></i>
|
||||
<span class="d-inline">{{ LANGUAGE_CODE|upper }}</span>
|
||||
</button>
|
||||
|
||||
<ul class="dropdown-menu {% if LANGUAGE_CODE == 'ar' %}dropdown-menu-start{% else %}dropdown-menu-end{% endif %}" aria-labelledby="navbarLanguageDropdown">
|
||||
|
||||
{% comment %} English Button {% endcomment %}
|
||||
<li>
|
||||
<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>
|
||||
|
||||
{% comment %} Arabic Button {% endcomment %}
|
||||
<li>
|
||||
<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>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
{% block content %}
|
||||
|
||||
{% endblock content %}
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
18
templates/includes/language_options.html
Normal file
18
templates/includes/language_options.html
Normal file
@ -0,0 +1,18 @@
|
||||
{% load i18n %}
|
||||
|
||||
<li>
|
||||
<form action="{% url 'set_language' %}" method="post" class="d-inline">{% csrf_token %}
|
||||
<input name="next" type="hidden" value="{{ next_url }}">
|
||||
<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="{% url 'set_language' %}" method="post" class="d-inline">{% csrf_token %}
|
||||
<input name="next" type="hidden" value="{{ next_url }}">
|
||||
<button name="language" value="ar" class="dropdown-item {% if LANGUAGE_CODE == 'ar' %}active bg-light-subtle{% endif %}" type="submit">
|
||||
<span class="me-2">🇸🇦</span> العربية
|
||||
</button>
|
||||
</form>
|
||||
</li>
|
||||
@ -1,108 +1,247 @@
|
||||
<!-- templates/interviews/schedule_interviews.html -->
|
||||
{% extends "base.html" %}
|
||||
{% extends 'base.html' %}
|
||||
{% load static i18n %}
|
||||
|
||||
{% block title %}Bulk Interview Scheduling - {{ job.title }} - ATS{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="container mt-4 interview-schedule">
|
||||
<h1>Schedule Interviews for {{ job.title }}</h1>
|
||||
<div class="container-fluid py-4">
|
||||
<style>
|
||||
/* KAAT-S UI Variables */
|
||||
:root {
|
||||
--kaauh-teal: #00636e;
|
||||
--kaauh-teal-dark: #004a53;
|
||||
--kaauh-border: #eaeff3;
|
||||
--kaauh-primary-text: #343a40;
|
||||
--kaauh-success: #28a745;
|
||||
--kaauh-info: #17a2b8;
|
||||
--kaauh-danger: #dc3545;
|
||||
--kaauh-warning: #ffc107;
|
||||
}
|
||||
|
||||
<div class="card mt-4">
|
||||
<div class="card-body">
|
||||
<form method="post" id="schedule-form">
|
||||
{% csrf_token %}
|
||||
/* 1. Card & Container Styling */
|
||||
.kaauh-card {
|
||||
border: 1px solid var(--kaauh-border);
|
||||
border-radius: 0.75rem;
|
||||
box-shadow: 0 4px 12px rgba(0,0,0,0.06);
|
||||
background-color: white;
|
||||
padding: 1.5rem;
|
||||
}
|
||||
.container-fluid.py-4 { padding-top: 1.5rem !important; padding-bottom: 1.5rem !important; }
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<h5>Select Candidates</h5>
|
||||
<div class="form-group">
|
||||
{{ form.candidates }}
|
||||
</div>
|
||||
</div>
|
||||
/* 2. Typography & Headers */
|
||||
.page-header {
|
||||
color: var(--kaauh-teal-dark);
|
||||
font-weight: 700;
|
||||
}
|
||||
.section-header {
|
||||
color: var(--kaauh-primary-text);
|
||||
font-weight: 600;
|
||||
border-bottom: 2px solid var(--kaauh-border);
|
||||
padding-bottom: 0.5rem;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
label {
|
||||
font-weight: 500;
|
||||
color: var(--kaauh-primary-text);
|
||||
font-size: 0.9rem;
|
||||
margin-bottom: 0.25rem;
|
||||
}
|
||||
.form-control, .form-select {
|
||||
border-radius: 0.5rem;
|
||||
border: 1px solid #ced4da;
|
||||
padding: 0.5rem 0.75rem;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
.form-group > .form-check {
|
||||
margin-top: 0.5rem;
|
||||
}
|
||||
|
||||
<div class="col-md-6">
|
||||
<h5>Schedule Details</h5>
|
||||
/* --- FIX: SCROLLABLE CANDIDATE LIST --- */
|
||||
.form-group select[multiple] {
|
||||
max-height: 450px;
|
||||
overflow-y: auto;
|
||||
min-height: 250px;
|
||||
padding: 0;
|
||||
}
|
||||
/* ------------------------------------- */
|
||||
|
||||
<div class="form-group mb-3">
|
||||
<label for="{{ form.start_date.id_for_label }}">Start Date</label>
|
||||
{{ form.start_date }}
|
||||
</div>
|
||||
/* 3. Button Styling */
|
||||
.btn-main-action {
|
||||
background-color: var(--kaauh-teal);
|
||||
border-color: var(--kaauh-teal);
|
||||
color: white;
|
||||
font-weight: 600;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
.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);
|
||||
}
|
||||
.btn-secondary {
|
||||
color: var(--kaauh-teal-dark);
|
||||
border-color: var(--kaauh-border);
|
||||
background-color: #f1f3f4;
|
||||
}
|
||||
|
||||
<div class="form-group mb-3">
|
||||
<label for="{{ form.end_date.id_for_label }}">End Date</label>
|
||||
{{ form.end_date }}
|
||||
</div>
|
||||
/* 4. Break Times Section Styling */
|
||||
.break-time-form {
|
||||
background-color: #f8f9fa;
|
||||
padding: 0.75rem;
|
||||
border-radius: 0.5rem;
|
||||
border: 1px solid var(--kaauh-border);
|
||||
align-items: flex-end;
|
||||
}
|
||||
.remove-break {
|
||||
width: 100%;
|
||||
}
|
||||
.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;
|
||||
}
|
||||
</style>
|
||||
<div class="d-flex justify-content-between align-items-center mb-4">
|
||||
<div>
|
||||
<h1 class="h3 page-header">
|
||||
<i class="fas fa-calendar-alt me-2"></i>
|
||||
{% trans "Bulk Interview Scheduling" %}
|
||||
</h1>
|
||||
<h2 class="h5 text-muted mb-0">
|
||||
{% trans "Configure time slots for:" %} **{{ job.title }}**
|
||||
</h2>
|
||||
</div>
|
||||
<a href="{% url 'job_detail' slug=job.slug %}" class="btn btn-outline-secondary">
|
||||
<i class="fas fa-arrow-left me-1"></i> {% trans "Back to Job" %}
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="form-group mb-3">
|
||||
<label>Working Days</label>
|
||||
{{ form.working_days }}
|
||||
</div>
|
||||
<div class="kaauh-card shadow-sm">
|
||||
<form method="post" id="schedule-form">
|
||||
{% csrf_token %}
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="form-group mb-3">
|
||||
<label for="{{ form.start_time.id_for_label }}">Start Time</label>
|
||||
{{ form.start_time }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-4">
|
||||
<h5 class="section-header">{% trans "Select Candidates" %}</h5>
|
||||
|
||||
<div class="col-md-6">
|
||||
<div class="form-group mb-3">
|
||||
<label for="{{ form.end_time.id_for_label }}">End Time</label>
|
||||
{{ form.end_time }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="form-group mb-3">
|
||||
<label for="{{ form.interview_duration.id_for_label }}">Interview Duration (minutes)</label>
|
||||
{{ form.interview_duration }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6">
|
||||
<div class="form-group mb-3">
|
||||
<label for="{{ form.buffer_time.id_for_label }}">Buffer Time (minutes)</label>
|
||||
{{ form.buffer_time }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="{{ form.candidates.id_for_label }}">
|
||||
{% trans "Candidates to Schedule (Hold Ctrl/Cmd to select multiple)" %}
|
||||
</label>
|
||||
{{ form.candidates }}
|
||||
{% if form.candidates.errors %}
|
||||
<div class="text-danger small mt-1">{{ form.candidates.errors }}</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row mt-4">
|
||||
<div class="col-12">
|
||||
<h5>Break Times</h5>
|
||||
<div class="col-md-8">
|
||||
<h5 class="section-header">{% trans "Schedule Details" %}</h5>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="form-group mb-3">
|
||||
<label for="{{ form.start_date.id_for_label }}">{% trans "Start Date" %}</label>
|
||||
{{ form.start_date }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6">
|
||||
<div class="form-group mb-3">
|
||||
<label for="{{ form.end_date.id_for_label }}">{% trans "End Date" %}</label>
|
||||
{{ form.end_date }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group mb-3">
|
||||
<label>{% trans "Working Days" %}</label>
|
||||
<div class="d-flex flex-wrap gap-3 p-2 border rounded" style="background-color: #f8f9fa;">
|
||||
{{ form.working_days }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-3">
|
||||
<div class="form-group mb-3">
|
||||
<label for="{{ form.start_time.id_for_label }}">{% trans "Start Time" %}</label>
|
||||
{{ form.start_time }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-3">
|
||||
<div class="form-group mb-3">
|
||||
<label for="{{ form.end_time.id_for_label }}">{% trans "End Time" %}</label>
|
||||
{{ form.end_time }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-3">
|
||||
<div class="form-group mb-3">
|
||||
<label for="{{ form.interview_duration.id_for_label }}">{% trans "Duration (min)" %}</label>
|
||||
{{ form.interview_duration }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-3">
|
||||
<div class="form-group mb-3">
|
||||
<label for="{{ form.buffer_time.id_for_label }}">{% trans "Buffer (min)" %}</label>
|
||||
{{ form.buffer_time }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mt-4 pt-4 border-top">
|
||||
<h5 class="section-header">{% trans "Daily Break Times" %}</h5>
|
||||
<div id="break-times-container">
|
||||
{{ break_formset.management_form }}
|
||||
{% for hidden in break_formset.management_form.hidden_fields %}
|
||||
{% if "TOTAL_FORMS" in hidden.id_for_label %}
|
||||
<input type="hidden" name="{{ hidden.name }}" id="id_breaks-TOTAL_FORMS" value="{{ hidden.value }}">
|
||||
{% else %}
|
||||
{{ hidden }}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
|
||||
{% for form in break_formset %}
|
||||
<div class="break-time-form row mb-2">
|
||||
<div class="col-md-5">
|
||||
<label>Start Time</label>
|
||||
<div class="break-time-form row mb-2 g-2">
|
||||
<div class="col-5">
|
||||
<label for="{{ form.start_time.id_for_label }}">{% trans "Start Time" %}</label>
|
||||
{{ form.start_time }}
|
||||
</div>
|
||||
<div class="col-md-5">
|
||||
<label>End Time</label>
|
||||
<div class="col-5">
|
||||
<label for="{{ form.end_time.id_for_label }}">{% trans "End Time" %}</label>
|
||||
{{ form.end_time }}
|
||||
</div>
|
||||
<div class="col-md-2">
|
||||
<label> </label><br>
|
||||
<div class="col-2 d-flex align-items-end">
|
||||
{{ 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 w-100">
|
||||
<i class="fas fa-trash-alt"></i> {% trans "Remove" %}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
<button type="button" id="add-break" class="btn btn-secondary btn-sm mt-2">Add Break</button>
|
||||
<button type="button" id="add-break" class="btn btn-secondary btn-sm mt-3">
|
||||
<i class="fas fa-plus me-1"></i> {% trans "Add Break" %}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mt-4">
|
||||
<button type="submit" class="btn btn-primary">Preview Schedule</button>
|
||||
<a href="{% url 'job_detail' slug=job.slug %}" class="btn btn-secondary">Cancel</a>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mt-4 pt-4 border-top d-flex justify-content-end gap-2">
|
||||
<button type="submit" class="btn btn-main-action">
|
||||
<i class="fas fa-calendar-check me-1"></i> {% trans "Preview Schedule" %}
|
||||
</button>
|
||||
<a href="{% url 'job_detail' slug=job.slug %}" class="btn btn-secondary">
|
||||
{% trans "Cancel" %}
|
||||
</a>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -110,43 +249,59 @@
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
const addBreakBtn = document.getElementById('add-break');
|
||||
const breakTimesContainer = document.getElementById('break-times-container');
|
||||
// The ID is now guaranteed to be 'id_breaks-TOTAL_FORMS' thanks to the template fix
|
||||
const totalFormsInput = document.getElementById('id_breaks-TOTAL_FORMS');
|
||||
|
||||
// Safety check added, though the template fix should resolve the core issue
|
||||
if (!totalFormsInput) {
|
||||
console.error("TOTAL_FORMS input not found. Cannot add break dynamically.");
|
||||
return;
|
||||
}
|
||||
|
||||
addBreakBtn.addEventListener('click', function() {
|
||||
const formCount = parseInt(totalFormsInput.value);
|
||||
|
||||
// Template for a new form, ensuring the correct classes are applied
|
||||
const newFormHtml = `
|
||||
<div class="break-time-form row mb-2">
|
||||
<div class="col-md-5">
|
||||
<label>Start Time</label>
|
||||
<div class="break-time-form row mb-2 g-2">
|
||||
<div class="col-5">
|
||||
<label for="id_breaks-${formCount}-start_time">Start Time</label>
|
||||
<input type="time" name="breaks-${formCount}-start_time" class="form-control" id="id_breaks-${formCount}-start_time">
|
||||
</div>
|
||||
<div class="col-md-5">
|
||||
<label>End Time</label>
|
||||
<div class="col-5">
|
||||
<label for="id_breaks-${formCount}-end_time">End Time</label>
|
||||
<input type="time" name="breaks-${formCount}-end_time" class="form-control" id="id_breaks-${formCount}-end_time">
|
||||
</div>
|
||||
<div class="col-md-2">
|
||||
<label> </label><br>
|
||||
<div class="col-2 d-flex align-items-end">
|
||||
<input type="hidden" name="breaks-${formCount}-id" id="id_breaks-${formCount}-id">
|
||||
<input type="hidden" name="breaks-${formCount}-meeting" id="id_breaks-${formCount}-meeting">
|
||||
<input type="checkbox" name="breaks-${formCount}-DELETE" id="id_breaks-${formCount}-DELETE" style="display:none;">
|
||||
<button type="button" class="btn btn-danger btn-sm remove-break">Remove</button>
|
||||
<button type="button" class="btn btn-danger btn-sm remove-break w-100">
|
||||
<i class="fas fa-trash-alt"></i> Remove
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
const tempDiv = document.createElement('div');
|
||||
tempDiv.innerHTML = newFormHtml;
|
||||
tempDiv.innerHTML = newFormHtml.trim();
|
||||
const newForm = tempDiv.firstChild;
|
||||
|
||||
breakTimesContainer.appendChild(newForm);
|
||||
totalFormsInput.value = formCount + 1;
|
||||
});
|
||||
|
||||
// Handle remove button clicks
|
||||
// Handle remove button clicks (both existing and dynamically added)
|
||||
breakTimesContainer.addEventListener('click', function(e) {
|
||||
if (e.target.classList.contains('remove-break')) {
|
||||
if (e.target.closest('.remove-break')) {
|
||||
const form = e.target.closest('.break-time-form');
|
||||
const deleteCheckbox = form.querySelector('input[name$="-DELETE"]');
|
||||
deleteCheckbox.checked = true;
|
||||
form.style.display = 'none';
|
||||
if (form) {
|
||||
const deleteCheckbox = form.querySelector('input[name$="-DELETE"]');
|
||||
if (deleteCheckbox) {
|
||||
deleteCheckbox.checked = true;
|
||||
}
|
||||
form.style.display = 'none';
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@ -1,628 +0,0 @@
|
||||
{% load i18n static %}
|
||||
<!DOCTYPE html>
|
||||
<html lang="{{ LANGUAGE_CODE }}" dir="{% if LANGUAGE_CODE == 'ar' %}rtl{% else %}ltr{% endif %}">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>{% block title %}{% trans 'Careers' %} - KAAUH{% endblock %}</title>
|
||||
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.0/css/all.min.css">
|
||||
|
||||
<style>
|
||||
:root {
|
||||
--kaauh-teal: #00636e;
|
||||
--kaauh-teal-dark: #004a53;
|
||||
--kaauh-light-bg: #f9fbfd;
|
||||
--kaauh-border: #eaeff3;
|
||||
--kaauh-green-dark: #00363a;
|
||||
--kaauh-nav-bg: #004a53;
|
||||
--kaauh-dark-nav-active: #00363a;
|
||||
}
|
||||
|
||||
body {
|
||||
background-color: white;
|
||||
}
|
||||
|
||||
.max-width-container {
|
||||
max-width: 1600px;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
padding-left: 15px;
|
||||
padding-right: 15px;
|
||||
}
|
||||
|
||||
/* === Header Top Info === */
|
||||
.header-top-info {
|
||||
background-color: white;
|
||||
padding: 15px 0;
|
||||
font-size: 0.95rem;
|
||||
color: #333;
|
||||
border-bottom: 1px solid #eee;
|
||||
}
|
||||
|
||||
.header-top-info .section-divider {
|
||||
border-inline-start: 1px solid #ddd;
|
||||
height: 60px;
|
||||
margin: 0 15px;
|
||||
}
|
||||
|
||||
.header-top-info .follow-us-section {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 15px;
|
||||
font-weight: 500;
|
||||
}
|
||||
.header-top-info .social-icon-circle {
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
border-radius: 50%;
|
||||
background-color: var(--kaauh-teal);
|
||||
color: white;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 1rem;
|
||||
text-decoration: none;
|
||||
transition: background-color 0.2s;
|
||||
}
|
||||
.header-top-info .social-icon-circle:hover {
|
||||
background-color: var(--kaauh-teal-dark);
|
||||
}
|
||||
|
||||
.header-top-info .contact-block {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
min-width: 200px;
|
||||
}
|
||||
|
||||
.header-top-info .contact-icon-block {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
text-align: center;
|
||||
padding: 0 15px;
|
||||
min-height: 60px;
|
||||
}
|
||||
.header-top-info .contact-icon {
|
||||
font-size: 2rem;
|
||||
color: var(--kaauh-teal);
|
||||
margin-inline-end: 15px;
|
||||
}
|
||||
.header-top-info .contact-details {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
text-align: start;
|
||||
}
|
||||
.header-top-info .contact-details span:first-child {
|
||||
font-weight: bold;
|
||||
color: var(--kaauh-teal);
|
||||
}
|
||||
.header-top-info .contact-details span:last-child {
|
||||
font-size: 0.85rem;
|
||||
color: #555;
|
||||
}
|
||||
|
||||
.header-top-info .logo-section {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 15px;
|
||||
}
|
||||
.header-top-info .vision-logo-container {
|
||||
border-inline-start: 1px solid #ddd;
|
||||
padding-inline-start: 20px;
|
||||
min-height: 60px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
.header-top-info .vision-logo {
|
||||
max-height: 50px;
|
||||
width: auto;
|
||||
}
|
||||
.header-top-info .kaauh-logo-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
border-inline-start: 1px solid #ddd;
|
||||
padding-inline-start: 20px;
|
||||
}
|
||||
.header-top-info .kaauh-logo-container img {
|
||||
max-height: 80px;
|
||||
width: auto;
|
||||
margin-inline-end: 10px;
|
||||
}
|
||||
.header-top-info .hospital-text {
|
||||
line-height: 1.2;
|
||||
text-align: initial;
|
||||
}
|
||||
.header-top-info .hospital-text .ar {
|
||||
font-size: 1.1rem;
|
||||
font-weight: bold;
|
||||
color: var(--kaauh-teal-dark);
|
||||
}
|
||||
.header-top-info .hospital-text .en {
|
||||
font-size: 0.8rem;
|
||||
color: #555;
|
||||
}
|
||||
|
||||
/* === Main Navbar === */
|
||||
.navbar-main {
|
||||
background-color: var(--kaauh-nav-bg);
|
||||
padding: 0;
|
||||
min-height: 60px;
|
||||
box-shadow: 0 2px 5px rgba(0,0,0,0.1);
|
||||
}
|
||||
.navbar-main .nav-link {
|
||||
color: white !important;
|
||||
font-weight: 500;
|
||||
font-size: 0.95rem;
|
||||
padding: 1.1rem 1.2rem;
|
||||
transition: background-color 0.2s;
|
||||
border-inline-end: 1px solid rgba(255, 255, 255, 0.1);
|
||||
line-height: 1;
|
||||
}
|
||||
.navbar-main .nav-link:hover,
|
||||
.navbar-main .nav-link.active {
|
||||
background-color: var(--kaauh-dark-nav-active);
|
||||
border-inline-end: 1px solid var(--kaauh-dark-nav-active);
|
||||
}
|
||||
.navbar-main .nav-icons-group {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
background-color: var(--kaauh-dark-nav-active);
|
||||
height: 100%;
|
||||
min-height: 60px;
|
||||
}
|
||||
.navbar-main .nav-icon {
|
||||
color: white;
|
||||
padding: 0 15px;
|
||||
font-size: 1.5rem;
|
||||
cursor: pointer;
|
||||
text-decoration: none;
|
||||
transition: opacity 0.2s;
|
||||
}
|
||||
.navbar-main .nav-icon:hover {
|
||||
opacity: 0.8;
|
||||
}
|
||||
.navbar-main .profile-icon {
|
||||
font-size: 1.8rem;
|
||||
}
|
||||
.navbar-main .lang-switch {
|
||||
font-size: 1.2rem;
|
||||
font-weight: bold;
|
||||
text-transform: uppercase;
|
||||
line-height: 1;
|
||||
}
|
||||
.navbar-main .dropdown-menu {
|
||||
border-radius: 0;
|
||||
margin-top: 0;
|
||||
border: none;
|
||||
box-shadow: 0 4px 10px rgba(0,0,0,0.15);
|
||||
}
|
||||
.navbar-main .dropdown-item:hover {
|
||||
background-color: var(--kaauh-light-bg);
|
||||
color: var(--kaauh-teal-dark);
|
||||
}
|
||||
.navbar-toggler {
|
||||
border: none;
|
||||
padding: 0;
|
||||
}
|
||||
.navbar-toggler:focus {
|
||||
box-shadow: none;
|
||||
}
|
||||
.navbar-toggler-icon {
|
||||
filter: invert(1);
|
||||
}
|
||||
.navbar-main .collapse {
|
||||
justify-content: flex-start !important;
|
||||
}
|
||||
|
||||
/* === Footer === */
|
||||
.footer-main {
|
||||
background-color: var(--kaauh-green-dark);
|
||||
color: white;
|
||||
padding-top: 3rem;
|
||||
}
|
||||
.footer-main a {
|
||||
color: white;
|
||||
text-decoration: none;
|
||||
transition: color 0.2s;
|
||||
}
|
||||
.footer-main a:hover {
|
||||
color: var(--kaauh-teal);
|
||||
}
|
||||
.footer-main h5 {
|
||||
color: var(--kaauh-teal);
|
||||
font-weight: 700;
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
.footer-main .social-icons a {
|
||||
font-size: 1.5rem;
|
||||
margin-inline-end: 15px;
|
||||
}
|
||||
.footer-main .contact-info,
|
||||
.footer-main .app-download {
|
||||
font-size: 0.95rem;
|
||||
}
|
||||
.footer-bottom {
|
||||
background-color: #00282b;
|
||||
padding: 1rem 0;
|
||||
font-size: 0.85rem;
|
||||
color: rgba(255, 255, 255, 0.7);
|
||||
}
|
||||
.footer-bottom strong {
|
||||
color: white;
|
||||
}
|
||||
.footer-main .col-lg-3, .footer-main .col-lg-5 {
|
||||
padding-bottom: 2rem;
|
||||
}
|
||||
.footer-main .vision-logo {
|
||||
max-width: 100px;
|
||||
}
|
||||
.footer-main .app-store-badge,
|
||||
.footer-main .play-store-badge {
|
||||
max-width: 150px;
|
||||
height: auto;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
/* === Hero Section === */
|
||||
.hero-section {
|
||||
height: 50vh;
|
||||
background: url('{% static "image/hospital-bg.jpg" %}') no-repeat center center;
|
||||
background-size: cover;
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: white;
|
||||
text-align: center;
|
||||
}
|
||||
.hero-overlay {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background-color: rgba(0, 0, 0, 0.45);
|
||||
}
|
||||
.hero-content {
|
||||
z-index: 10;
|
||||
}
|
||||
.hero-content h1 {
|
||||
font-size: 3rem;
|
||||
font-weight: 700;
|
||||
}
|
||||
.breadcrumb-section {
|
||||
padding: 10px 0;
|
||||
background-color: #f1f1f1;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
.breadcrumb-item a {
|
||||
color: #6c757d;
|
||||
}
|
||||
.breadcrumb-item.active {
|
||||
font-weight: 600;
|
||||
color: var(--kaauh-teal-dark);
|
||||
}
|
||||
|
||||
/* === RTL Adjustments === */
|
||||
html[dir="rtl"] {
|
||||
text-align: right;
|
||||
direction: rtl;
|
||||
}
|
||||
html[dir="rtl"] .max-width-container {
|
||||
padding-left: 15px;
|
||||
padding-right: 15px;
|
||||
}
|
||||
html[dir="rtl"] .header-top-info .section-divider {
|
||||
border-inline-start: none;
|
||||
border-inline-end: 1px solid #ddd;
|
||||
}
|
||||
html[dir="rtl"] .header-top-info .contact-icon {
|
||||
margin-inline-end: 0;
|
||||
margin-inline-start: 15px;
|
||||
}
|
||||
html[dir="rtl"] .header-top-info .contact-details {
|
||||
text-align: right;
|
||||
}
|
||||
html[dir="rtl"] .header-top-info .vision-logo-container {
|
||||
border-inline-start: none;
|
||||
border-inline-end: 1px solid #ddd;
|
||||
padding-inline-start: 0;
|
||||
padding-inline-end: 20px;
|
||||
}
|
||||
html[dir="rtl"] .header-top-info .kaauh-logo-container {
|
||||
border-inline-start: none;
|
||||
border-inline-end: 1px solid #ddd;
|
||||
padding-inline-start: 0;
|
||||
padding-inline-end: 20px;
|
||||
}
|
||||
html[dir="rtl"] .header-top-info .kaauh-logo-container img {
|
||||
margin-inline-end: 0;
|
||||
margin-inline-start: 10px;
|
||||
}
|
||||
html[dir="rtl"] .header-top-info .hospital-text {
|
||||
text-align: right;
|
||||
}
|
||||
html[dir="rtl"] .navbar-main .nav-link {
|
||||
border-inline-end: none;
|
||||
border-inline-start: 1px solid rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
html[dir="rtl"] .navbar-main .nav-link:hover,
|
||||
html[dir="rtl"] .navbar-main .nav-link.active {
|
||||
border-inline-start: 1px solid var(--kaauh-dark-nav-active);
|
||||
}
|
||||
html[dir="rtl"] .nav-icons i {
|
||||
margin-inline-start: 10px;
|
||||
margin-inline-end: 0;
|
||||
}
|
||||
html[dir="rtl"] .top-bar .social-icons a {
|
||||
margin-inline-start: 0;
|
||||
margin-inline-end: 10px;
|
||||
}
|
||||
html[dir="rtl"] .footer-main .social-icons a {
|
||||
margin-inline-start: 15px;
|
||||
margin-inline-end: 0;
|
||||
}
|
||||
|
||||
/* === Responsive Adjustments for Small Screens === */
|
||||
@media (max-width: 991.98px) {
|
||||
.header-top-info .max-width-container {
|
||||
flex-direction: column !important;
|
||||
align-items: flex-start !important;
|
||||
gap: 15px;
|
||||
}
|
||||
|
||||
.header-top-info .follow-us-section,
|
||||
.header-top-info .d-flex.align-items-center.flex-grow-1.justify-content-end {
|
||||
width: 100%;
|
||||
justify-content: space-between !important;
|
||||
}
|
||||
|
||||
.header-top-info .contact-block,
|
||||
.header-top-info .logo-section {
|
||||
flex-wrap: wrap;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.header-top-info .section-divider {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.header-top-info .kaauh-logo-container img {
|
||||
max-height: 60px;
|
||||
}
|
||||
|
||||
.header-top-info .hospital-text .ar,
|
||||
.header-top-info .hospital-text .en {
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.header-top-info .contact-icon {
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
|
||||
.header-top-info .contact-icon-block {
|
||||
min-height: auto;
|
||||
padding: 8px 0;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 575.98px) {
|
||||
.header-top-info .kaauh-logo-container,
|
||||
.header-top-info .vision-logo-container {
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.header-top-info .hospital-text {
|
||||
text-align: center !important;
|
||||
}
|
||||
|
||||
.header-top-info .kaauh-logo-container img {
|
||||
max-height: 50px;
|
||||
}
|
||||
|
||||
.header-top-info .follow-us-section span,
|
||||
.header-top-info .contact-details span {
|
||||
font-size: 0.85rem;
|
||||
}
|
||||
|
||||
.header-top-info .social-icon-circle {
|
||||
width: 26px;
|
||||
height: 26px;
|
||||
font-size: 0.85rem;
|
||||
}
|
||||
|
||||
.hero-content h1 {
|
||||
font-size: 2rem;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
{% block customCSS %}{% endblock %}
|
||||
</head>
|
||||
<body class="d-flex flex-column min-vh-100">
|
||||
|
||||
<!-- Responsive Header Top Info -->
|
||||
<div class="header-top-info">
|
||||
<div class="max-width-container d-flex flex-column flex-md-row justify-content-between align-items-start align-items-md-center gap-3">
|
||||
|
||||
<div class="follow-us-section d-flex flex-wrap align-items-center gap-2">
|
||||
<span>{% trans "Follow Us On:" %}</span>
|
||||
<div class="d-flex gap-2">
|
||||
<a href="#" class="social-icon-circle"><i class="fab fa-facebook-f"></i></a>
|
||||
<a href="#" class="social-icon-circle"><i class="fab fa-twitter"></i></a>
|
||||
<a href="#" class="social-icon-circle"><i class="fab fa-instagram"></i></a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="d-flex flex-column flex-md-row align-items-start align-items-md-center gap-3 w-100 w-md-auto">
|
||||
<div class="d-flex flex-wrap justify-content-between gap-3">
|
||||
<div class="contact-icon-block d-flex align-items-center">
|
||||
<i class="fas fa-headset contact-icon"></i>
|
||||
<div class="contact-details ms-2">
|
||||
<span>24/7 {% trans "Online Support" %}</span>
|
||||
<span class="d-block d-md-inline">info@kaauh.edu.sa</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="contact-icon-block d-flex align-items-center">
|
||||
<i class="fas fa-phone-alt contact-icon"></i>
|
||||
<div class="contact-details ms-2">
|
||||
<span>{% trans "Contact Us Free" %}</span>
|
||||
<span class="d-block d-md-inline">+966118200000</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="logo-section d-flex flex-wrap align-items-center gap-3 mt-2 mt-md-0">
|
||||
|
||||
<div class="kaauh-logo-container d-flex flex-column flex-md-row align-items-center gap-2">
|
||||
<img src="{% static 'image/vision.svg' %}" alt="Vision 2030" class="vision-logo" style="min-height: 70px; min-width:300px;">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="logo-section d-flex flex-wrap align-items-center gap-3 mt-2 mt-md-0 ms-6">
|
||||
|
||||
<div class="kaauh-logo-container d-flex flex-column flex-md-row align-items-center gap-2 ms-4">
|
||||
<div class="hospital-text text-center text-md-start me-3">
|
||||
<div class="ar">جامعة الأميرة نورة بنت عبدالرحمن الأكاديمية</div>
|
||||
<div class="ar">ومستشفى الملك عبدالله بن عبدالعزيز التخصصي</div>
|
||||
<div class="en">Princess Nourah bint Abdulrahman University</div>
|
||||
<div class="en">King Abdullah bin Abdulaziz University Hospital</div>
|
||||
</div>
|
||||
<img src="{% static 'image/kaauh.png' %}" alt="KAAUH Logo" style="max-height: 80px;min-width:80px;">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Main Navigation -->
|
||||
<nav class="navbar navbar-expand-lg navbar-dark navbar-main">
|
||||
<div class="max-width-container d-flex justify-content-between align-items-stretch w-100">
|
||||
<button class="navbar-toggler ms-auto" type="button" data-bs-toggle="collapse" data-bs-target="#publicNavCollapse"
|
||||
aria-controls="publicNavCollapse" aria-expanded="false" aria-label="{% trans 'Toggle navigation' %}">
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
</button>
|
||||
|
||||
<div class="collapse navbar-collapse" id="publicNavCollapse">
|
||||
<ul class="navbar-nav mb-2 mb-lg-0">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="#">{% trans "About KAAUH" %}</a>
|
||||
</li>
|
||||
<li class="nav-item dropdown">
|
||||
<a class="nav-link dropdown-toggle" href="#" role="button" data-bs-toggle="dropdown" aria-expanded="false">
|
||||
{% trans "Patients & Visitor" %}
|
||||
</a>
|
||||
<ul class="dropdown-menu">
|
||||
<li><a class="dropdown-item" href="#">{% trans "Find a Doctor" %}</a></li>
|
||||
<li><a class="dropdown-item" href="#">{% trans "Visiting Hours" %}</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="nav-item dropdown">
|
||||
<a class="nav-link dropdown-toggle" href="#" role="button" data-bs-toggle="dropdown" aria-expanded="false">
|
||||
{% trans "Training & Education" %}
|
||||
</a>
|
||||
<ul class="dropdown-menu">
|
||||
<li><a class="dropdown-item" href="#">{% trans "Courses" %}</a></li>
|
||||
<li><a class="dropdown-item" href="#">{% trans "Residency" %}</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link active" href="{% url 'kaauh_career' %}">{% trans "Careers" %}</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="#">{% trans "Gallery" %}</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="#">{% trans "Connect" %}</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="nav-icons-group d-none d-lg-flex">
|
||||
<a href="#" class="nav-icon profile-icon"><i class="fas fa-user-circle"></i></a>
|
||||
<a href="#" class="nav-icon lang-switch">ع</a>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<!-- Main Content -->
|
||||
<main class="flex-grow-1">
|
||||
{% block content %}{% endblock %}
|
||||
</main>
|
||||
|
||||
<!-- Footer -->
|
||||
<footer class="footer-main mt-auto">
|
||||
<div class="max-width-container">
|
||||
<div class="row">
|
||||
<div class="col-lg-3 col-md-6 app-download">
|
||||
<h5 class="text-white-50">{% trans "Download our mobile app" %}</h5>
|
||||
<p>{% trans "Get the latest updates and services on the go." %}</p>
|
||||
<div class="d-flex flex-column gap-2 mt-3">
|
||||
<a href="#"><img src="{% static 'image/google-play-badge.png' %}" alt="Google Play" class="play-store-badge"></a>
|
||||
<a href="#"><img src="{% static 'image/app-store-badge.png' %}" alt="App Store" class="app-store-badge"></a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-lg-2 col-md-6">
|
||||
<h5>{% trans "Information" %}</h5>
|
||||
<ul class="list-unstyled">
|
||||
<li><a href="#">{% trans "About the Hospital" %}</a></li>
|
||||
<li><a href="{% url 'kaauh_career' %}">{% trans "Careers" %}</a></li>
|
||||
<li><a href="#">{% trans "Today's Clinic Hours" %}</a></li>
|
||||
<li><a href="#">{% trans "Support Us" %}</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="col-lg-3 col-md-6">
|
||||
<h5>{% trans "Need Help" %}</h5>
|
||||
<ul class="list-unstyled">
|
||||
<li><a href="#">{% trans "Support and Services" %}</a></li>
|
||||
<li><a href="#">{% trans "Contact Us" %}</a></li>
|
||||
<li><a href="#">{% trans "FAQ" %}</a></li>
|
||||
<li><a href="#">{% trans "Sitemap" %}</a></li>
|
||||
</ul>
|
||||
<div class="contact-info mt-3">
|
||||
<i class="fas fa-phone-alt me-2 text-white-50"></i> <strong class="text-white">966118200000</strong><br>
|
||||
<i class="fas fa-envelope me-2 text-white-50"></i> <a href="mailto:info@kaauh.edu.sa">info@kaauh.edu.sa</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-lg-4 col-md-6">
|
||||
<h5>{% trans "Contact & Address" %}</h5>
|
||||
<p>{% trans "KAAUH Campus, Riyadh, Saudi Arabia" %}</p>
|
||||
<p class="small text-white-50">{% trans "Postal Code 11564, King Fahd Road, Al-Rabi District." %}</p>
|
||||
|
||||
<div class="d-flex align-items-center justify-content-start gap-3 mt-4">
|
||||
<img src="{% static 'image/vision.svg' %}" alt="Vision 2030" class="vision-logo">
|
||||
<div class="social-icons">
|
||||
<a href="#"><i class="fab fa-facebook-f"></i></a>
|
||||
<a href="#"><i class="fab fa-twitter"></i></a>
|
||||
<a href="#"><i class="fab fa-instagram"></i></a>
|
||||
<a href="#"><i class="fab fa-youtube"></i></a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="footer-bottom mt-5">
|
||||
<div class="max-width-container d-flex justify-content-between align-items-center flex-wrap">
|
||||
<p class="mb-0">
|
||||
© {% now "Y" %} {% trans "King Abdullah Academic University Hospital (KAAUH)." %}
|
||||
{% trans "All rights reserved." %}
|
||||
</p>
|
||||
<p class="mb-0">
|
||||
{% trans "Powered by" %} <strong class="text-white">Tenhal</strong>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
|
||||
{% block customJS %}{% endblock %}
|
||||
</body>
|
||||
</html>
|
||||
@ -1,154 +1,568 @@
|
||||
{% extends "jobs/base_public.html" %}
|
||||
{% load i18n static %}
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
||||
<meta name="author" content="KAAUH" />
|
||||
<meta name="title" content="Regions no.1 University Hospital">
|
||||
<meta name="description" content="King Abdullah bin Abdulaziz University Hospital is among the premier and esteemed institutions. KAAUH is the epitome of education in the healthcare sector." />
|
||||
<meta name="keywords" content="Best hospital in Saudi Arabia,Healthcare providers near me,Top healthcare providers,Health care services in saudi arabia,Best hospital in saudi arabia,Healthcare system in saudi arabia" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>King Abdullah bin Abdulaziz University Hospital</title>
|
||||
<link rel="apple-touch-icon" sizes="57x57" href="https://kaauh.edu.sa/assets/images/apple-icon-57x57.png" />
|
||||
<link rel="apple-touch-icon" sizes="60x60" href="https://kaauh.edu.sa/assets/images/apple-icon-60x60.png" />
|
||||
<link rel="apple-touch-icon" sizes="72x72" href="https://kaauh.edu.sa/assets/images/apple-icon-72x72.png" />
|
||||
<link rel="apple-touch-icon" sizes="76x76" href="https://kaauh.edu.sa/assets/images/apple-icon-76x76.png" />
|
||||
<link rel="apple-touch-icon" sizes="114x114" href="https://kaauh.edu.sa/assets/images/apple-icon-114x114.png" />
|
||||
<link rel="apple-touch-icon" sizes="120x120" href="https://kaauh.edu.sa/assets/images/apple-icon-120x120.png" />
|
||||
<link rel="apple-touch-icon" sizes="144x144" href="https://kaauh.edu.sa/assets/images/apple-icon-144x144.png" />
|
||||
<link rel="apple-touch-icon" sizes="152x152" href="https://kaauh.edu.sa/assets/images/apple-icon-152x152.png" />
|
||||
<link rel="apple-touch-icon" sizes="180x180" href="https://kaauh.edu.sa/assets/images/apple-icon-180x180.png" />
|
||||
<link rel="icon" type="image/png" sizes="192x192" href="https://kaauh.edu.sa/assets/images/android-icon-192x192.png" />
|
||||
<link rel="icon" type="image/png" sizes="32x32" href="https://kaauh.edu.sa/assets/images/favicon-32x32.png" />
|
||||
<link rel="icon" type="image/png" sizes="96x96" href="https://kaauh.edu.sa/assets/images/favicon-96x96.png" />
|
||||
<link rel="icon" type="image/png" sizes="16x16" href="https://kaauh.edu.sa/assets/images/favicon-16x16.png" />
|
||||
<link rel="manifest" href="https://kaauh.edu.sa/assets/images/manifest.json" />
|
||||
<link href="https://kaauh.edu.sa/assets/css/bootstrap.min.css" rel="stylesheet" />
|
||||
<link rel="stylesheet" href="https://kaauh.edu.sa/assets/fonts/css/all.min.css" />
|
||||
<link href="https://kaauh.edu.sa/assets/css/flaticon.css" rel="stylesheet" />
|
||||
<link href="https://kaauh.edu.sa/assets/css/menu.css" rel="stylesheet" />
|
||||
<link id="effect" href="https://kaauh.edu.sa/assets/css/dropdown-effects/fade-down.css" media="all" rel="stylesheet" />
|
||||
<link href="https://kaauh.edu.sa/assets/css/magnific-popup.css" rel="stylesheet" />
|
||||
<link href="https://kaauh.edu.sa/assets/css/owl.carousel.min.css" rel="stylesheet" />
|
||||
<link href="https://kaauh.edu.sa/assets/css/owl.theme.default.min.css" rel="stylesheet" />
|
||||
<link href="https://kaauh.edu.sa/assets/css/animate.css" rel="stylesheet" />
|
||||
<link href="https://kaauh.edu.sa/assets/css/normalize.min.css" rel="stylesheet" />
|
||||
<link href="https://kaauh.edu.sa/assets/css/style.css" rel="stylesheet" />
|
||||
<link href="https://kaauh.edu.sa/assets/css/responsive.css" rel="stylesheet" />
|
||||
<link href="https://kaauh.edu.sa/assets/css/custom.css" rel="stylesheet" />
|
||||
<link href="https://kaauh.edu.sa/assets/css/sweetalert2.min.css" rel="stylesheet" />
|
||||
<link href="https://kaauh.edu.sa/css/toastmin.css" rel="stylesheet" />
|
||||
<link href="https://kaauh.edu.sa/css/select2min.css" rel="stylesheet" />
|
||||
<link href="https://kaauh.edu.sa/assets/css/bootstrap-datepicker.min.css" rel="stylesheet" />
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css" integrity="sha512-DTOQO9RWCH3ppGqcWaEA1BIZOC6xxalwEsw9c2QQeAIftl+Vegovlnee1c9QX4TctnWMn13TZye+giMm8e2LwA==" crossorigin="anonymous" referrerpolicy="no-referrer" />
|
||||
|
||||
<style>
|
||||
b{font-weight: bold;}
|
||||
@font-face {font-family: Poppins-Regular;src: url("https://kaauh.edu.sa/assets/fonts/POPPINS-REGULAR.TTF");}
|
||||
@font-face {font-family: Poppins-Extrabold;src: url("https://kaauh.edu.sa/assets/fonts/POPPINS-EXTRABOLD.TTF");}
|
||||
@font-face {font-family: Rajdhani-Regular;src: url("https://kaauh.edu.sa/assets/fonts/RAJDHANI-REGULAR.TTF");}
|
||||
@font-face {font-family: Rajdhani-Semibold;src: url("https://kaauh.edu.sa/assets/fonts/RAJDHANI-SEMIBOLD.TTF");}
|
||||
@font-face {font-family: Rajdhani-Bold;src: url("https://kaauh.edu.sa/assets/fonts/RAJDHANI-BOLD.TTF");}
|
||||
@font-face {font-family: Serti;src: url("https://kaauh.edu.sa/assets/fonts/SERTI.OTF");}
|
||||
@font-face {font-family: Serti-bold;src: url("https://kaauh.edu.sa/assets/fonts/SERTIBOLD.OTF");}
|
||||
.lang-area span {background: #fff;width: 38px;height: 38px;display: block;border-radius: 50%;font-size: 24px;font-weight: 600;padding: 0px 14px;}
|
||||
body.yf_rtl .lang-area span {background: #fff;width: 38px;height: 38px;display: block;border-radius: 50%;font-size: 17px;font-weight: 600;padding:7px 9px;}
|
||||
@media only screen and (max-width: 767px){/* body .lang-area {display : none !important} */}
|
||||
</style>
|
||||
</head>
|
||||
<body class="" data-spy="scroll" data-target="#myScrollspy" data-offset="1" >
|
||||
<div id="loader-wrapper">
|
||||
<div id="loader">
|
||||
<div class="loader-inner" style="background:url(https://kaauh.edu.sa/assets/images/loader-kaauh.png);background-size: contain; background-position: center !important;"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="page" class="page">
|
||||
<header id="header-2" class="header">
|
||||
<div class="wsmobileheader clearfix">
|
||||
<a id="wsnavtoggle" class="wsanimated-arrow"><span></span></a>
|
||||
<span class="smllogo"><a href="https://kaauh.edu.sa"><img src="https://kaauh.edu.sa/assets/images/logo.png" width="180" height="40" alt="mobile-logo"></a></span>
|
||||
<div class="lang-area" style="float: right;height: 60px;display: flex;justify-content: center;align-items: center">
|
||||
<a title="عربى" href="https://kaauh.edu.sa/changelanguage/ar"><span>ع</span></a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="hero-widget clearfix">
|
||||
<div class="shape-img"><img src="https://kaauh.edu.sa/assets/images/hos.svg" alt=""></div>
|
||||
<div class="container-fluid">
|
||||
<div class="row d-flex align-items-center">
|
||||
<div class="col-md-9 col-xl-9">
|
||||
<div class="row align-items-center">
|
||||
<div class="col-md-4">
|
||||
<div class="header-widget icon-xs">
|
||||
<div class="socials-links">
|
||||
<h3>Follow us on :</h3>
|
||||
<ul class="foo-socials text-center clearfix">
|
||||
<li><a href="javascript:;" target="_blank" class="ico-facebook"><i class="fab fa-facebook-f"></i></a></li>
|
||||
<li><a href="https://twitter.com/KAAUH_PNU?lang=ar" target="_blank" class="ico-twitter"><i class="fab fa-twitter"></i></a></li>
|
||||
<li><a href="https://www.instagram.com/kaauh_pnu/" target="_blank" class="ico-insta"><i class="fab fa-instagram"></i></a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="email-and-call">
|
||||
<div class="header-widget icon-xs online_support">
|
||||
<span class="icon-img"><img src="https://kaauh.edu.sa/assets/images/Customer-care.svg" alt="" /></span>
|
||||
<div class="header-widget-txt">
|
||||
<p>24x7 online Support</p>
|
||||
<p class="header-widget-phone steelblue-color"><a href="mailto:info@kaauh.edu.sa">info@kaauh.edu.sa</a></p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="header-widget icon-xs online_support call-support">
|
||||
<span class="icon-img"><img src="https://kaauh.edu.sa/assets/images/call.svg" alt="" /></span>
|
||||
<div class="header-widget-txt">
|
||||
<p>Contact Us Free</p>
|
||||
<p class="header-widget-phone steelblue-color"><a href="tel:+966118200000">+966118200000</a></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-2">
|
||||
<div class="lang-with-vision-logo">
|
||||
<div class="saudi-log"><img src="https://kaauh.edu.sa/assets/images/vision.svg" alt=""></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-3 col-xl-3 logo-col">
|
||||
<div class="desktoplogo"><a href="https://kaauh.edu.sa"><img src="https://kaauh.edu.sa/assets/images/logo.png" alt="header-logo" /></a></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="wsmainfull menu clearfix original" style="visibility: visible;">
|
||||
<div class="container-fluid">
|
||||
<div class="wsmainwp clearfix">
|
||||
<nav class="wsmenu clearfix">
|
||||
<div class="overlapblackbg"></div>
|
||||
<ul class="wsmenu-list">
|
||||
<li class="fist"><a href="https://kaauh.edu.sa/about-us">About KAAUH</a></li>
|
||||
<li aria-haspopup="true"><span class="wsmenu-click"><i class="wsmenu-arrow"></i></span><a href="javascript:;">Patients & Visitor<span class="wsarrow"></span></a>
|
||||
<ul class="sub-menu">
|
||||
<li aria-haspopup="true"><a href="https://kaauh.edu.sa/patients-visitor/registration-and-appointments">Registration & Appointments</a></li>
|
||||
<li aria-haspopup="true"><a href="https://kaauh.edu.sa/patients-visitor/meet-your-doctor">Meet your doctor</a></li>
|
||||
<li aria-haspopup="true"><a href="https://kaauh.edu.sa/patients-visitor/eligibility">Eligibility</a></li>
|
||||
<li aria-haspopup="true"><a href="https://kaauh.edu.sa/patients-visitor/visiting-hours">Visiting hours</a></li>
|
||||
<li aria-haspopup="true"><a href="https://kaauh.edu.sa/patients-visitor/patient-rights-and-responsibilities">Patient rights & responsibilities</a></li>
|
||||
<li aria-haspopup="true"><a href="https://kaauh.edu.sa/patients-visitor/patient-experience">Patient Experience</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li aria-haspopup="true"><span class="wsmenu-click"><i class="wsmenu-arrow"></i></span><a href="javascript:;">Training & Education<span class="wsarrow"></span></a>
|
||||
<ul class="sub-menu">
|
||||
<li aria-haspopup="true"><a href="https://kaauh.edu.sa/training-education/training-and-development">Training & Development</a></li>
|
||||
<li aria-haspopup="true"><a href="https://kaauh.edu.sa/training-education/student-training-office">Student Training Office</a></li>
|
||||
<li aria-haspopup="true"><a href="https://kaauh.edu.sa/training-education/scholarship-and-postgraduate">Scholarship & Postgraduate</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><a href="{% url 'kaauh_career' %}">Careers</a></li>
|
||||
<li><a href="https://kaauh.edu.sa/gallery">Gallery</a></li>
|
||||
<li aria-haspopup="true"><span class="wsmenu-click"><i class="wsmenu-arrow"></i></span><a href="javascript:;">Connect<span class="wsarrow"></span></a>
|
||||
<ul class="sub-menu">
|
||||
<li aria-haspopup="true"><a href="https://kaauh.edu.sa/need-help">Need Help</a></li>
|
||||
<li aria-haspopup="true"><a href="https://kaauh.edu.sa/contact">Contact Us</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
<div class="lang-area d-none d-sm-none d-md-flex" style="float: right;height: 60px;display: flex;justify-content: center;align-items: center">
|
||||
<a title="عربى" href='https://kaauh.edu.sa/changelanguage/ar'><span>ع</span></a>
|
||||
</div>
|
||||
|
||||
<div class="serch-area appointment-are"><a href="javascript:;" data-toggle="modal" data-target="#appointment-check" data-placement="top" title="Our Appointment"><i class="fa fa-user"></i></a></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="wsmainfull menu clearfix cloned" style="position: fixed; top: 0px; margin-top: 0px; z-index: 500; left: 0px; width: 1519.2px; display: none;">
|
||||
<div class="container-fluid">
|
||||
<div class="wsmainwp clearfix">
|
||||
<nav class="wsmenu clearfix">
|
||||
<div class="overlapblackbg"></div>
|
||||
<ul class="wsmenu-list">
|
||||
<li class="fist"><a href="https://web.archive.org/web/20240215180141/https://www.kaauh.edu.sa/about-us">About KAAUH</a></li>
|
||||
<li aria-haspopup="true"><span class="wsmenu-click"><i class="wsmenu-arrow"></i></span><a href="javascript:;">Patients & Visitor<span class="wsarrow"></span></a>
|
||||
<ul class="sub-menu">
|
||||
<li aria-haspopup="true"><a href="https://kaauh.edu.sa/patients-visitor/registration-and-appointments">Registration & Appointments</a></li>
|
||||
<li aria-haspopup="true"><a href="https://kaauh.edu.sa/patients-visitor/meet-your-doctor">Meet your doctor</a></li>
|
||||
<li aria-haspopup="true"><a href="https://kaauh.edu.sa/patients-visitor/eligibility">Eligibility</a></li>
|
||||
<li aria-haspopup="true"><a href="https://kaauh.edu.sa/patients-visitor/visiting-hours">Visiting hours</a></li>
|
||||
<li aria-haspopup="true"><a href="https://kaauh.edu.sa/patients-visitor/patient-rights-and-responsibilities">Patient rights & responsibilities</a></li>
|
||||
<li aria-haspopup="true"><a href="https://kaauh.edu.sa/patients-visitor/patient-experience">Patient Experience</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li aria-haspopup="true"><span class="wsmenu-click"><i class="wsmenu-arrow"></i></span><a href="javascript:;">Training & Education<span class="wsarrow"></span></a>
|
||||
<ul class="sub-menu">
|
||||
<li aria-haspopup="true"><a href="https://kaauh.edu.sa/training-education/training-and-development">Training & Development</a></li>
|
||||
<li aria-haspopup="true"><a href="https://kaauh.edu.sa/training-education/student-training-office">Student Training Office</a></li>
|
||||
<li aria-haspopup="true"><a href="https://kaauh.edu.sa/training-education/scholarship-and-postgraduate">Scholarship & Postgraduate</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><a href="{% url 'kaauh_career' %}">Careers</a></li>
|
||||
<li><a href="https://kaauh.edu.sa/gallery">Gallery</a></li>
|
||||
<li aria-haspopup="true"><span class="wsmenu-click"><i class="wsmenu-arrow"></i></span><a href="javascript:;">Connect<span class="wsarrow"></span></a>
|
||||
<ul class="sub-menu">
|
||||
<li aria-haspopup="true"><a href="https://kaauh.edu.sa/need-help">Need Help</a></li>
|
||||
<li aria-haspopup="true"><a href="https://kaauh.edu.sa/contact">Contact Us</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
<div class="lang-area d-none d-sm-none d-md-flex" style="float: right;height: 60px;display: flex;justify-content: center;align-items: center">
|
||||
<a title="عربى" href='https://kaauh.edu.sa/changelanguage/ar'><span>ع</span></a>
|
||||
</div>
|
||||
<div class="serch-area"><a href="javascript:;" data-toggle="modal" data-target="#serch"><i class="fa fa-magnifying-glass"></i></a></div>
|
||||
<div class="serch-area appointment-are"><a href="javascript:;" data-toggle="modal" data-target="#appointment-check" data-placement="top" title="Our Appointment"><i class="fa fa-user"></i></a></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
<div class="breadcrumb-area dark-bg" style="background:url(https://kaauh.edu.sa/assets/images/career_page.jpg)">
|
||||
<div class="container">
|
||||
<ul class="page-list">
|
||||
<li class="item-home"><a class="bread-link" href="https://kaauh.edu.sa" title="Home">Home</a></li>
|
||||
</ul>
|
||||
<h1 class="breadcrumb-title">Careers</h1>
|
||||
</div>
|
||||
</div>
|
||||
<div class="career_page_section">
|
||||
<div class="container mb-5 mt-3">
|
||||
<div class="card">
|
||||
{% load i18n %}
|
||||
<div class="table-responsive">
|
||||
<table class="table table-stripped w-100" id="career_table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="col-job-id">{% trans 'Job ID#' %}</th>
|
||||
<th class="col-job-title">{% trans 'Job Title' %}</th>
|
||||
<th class="col-hiring"><i class="fa fa-user" style="color:#00636e"></i> {% trans 'Hiring' %}</th>
|
||||
<th class="col-date d-none d-md-table-cell"><i class="fa fa-calendar" style="color:#00636e"></i> {% trans 'Posting Date' %}</th>
|
||||
<th class="col-date d-none d-sm-table-cell"><i class="fa fa-calendar" style="color:#00636e"></i> {% trans 'Apply Before' %}</th>
|
||||
<th class="col-link"><i class="fa fa-link" style="color:#00636e"></i> {% trans 'Link' %}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for job in active_jobs %}
|
||||
<tr>
|
||||
{# The columns below directly correspond to the headers above #}
|
||||
|
||||
<td class="col-job-id" data-label="{% trans 'Job ID#' %}">{{ job.internal_job_id }}</td>
|
||||
|
||||
<td class="col-job-title" data-label="{% trans 'Job Title' %}">{{ job.title }}</td>
|
||||
|
||||
<td class="col-hiring" data-label="{% trans 'Hiring' %}">{{ job.open_positions }}</td>
|
||||
|
||||
{# Hidden on small screens to prioritize space #}
|
||||
<td class="col-date d-none d-md-table-cell" data-label="{% trans 'Posting Date' %}">{{ job.application_start_date }}</td>
|
||||
|
||||
{# Hidden only on extra-small screens #}
|
||||
<td class="col-date d-none d-sm-table-cell" data-label="{% trans 'Apply Before' %}">{{ job.application_deadline }}</td>
|
||||
|
||||
<td class="col-link" data-label="{% trans 'Link' %}">
|
||||
<a style="background-color : #00636e;color : #FFF; padding : 4px 10px; white-space: nowrap;"
|
||||
href="{% url 'job_detail_candidate' job.slug %}"
|
||||
target="_blank">
|
||||
{% trans 'Apply' %}
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
|
||||
{% block customCSS %}
|
||||
<style>
|
||||
/* Custom style for the job list table */
|
||||
.job-listing-section {
|
||||
padding: 3rem 0;
|
||||
background-color: white;
|
||||
}
|
||||
.job-table {
|
||||
border-collapse: separate;
|
||||
border-spacing: 0;
|
||||
overflow: hidden;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 4px 15px rgba(0,0,0,0.08);
|
||||
width: 100%;
|
||||
}
|
||||
.job-table thead th {
|
||||
background-color: var(--kaauh-teal-dark);
|
||||
color: white;
|
||||
font-weight: 600;
|
||||
padding: 1rem 1.5rem;
|
||||
text-align: start;
|
||||
border-bottom: 2px solid var(--kaauh-teal);
|
||||
}
|
||||
.job-table tbody td {
|
||||
padding: 1rem 1.5rem;
|
||||
vertical-align: middle;
|
||||
border-bottom: 1px solid var(--kaauh-border);
|
||||
font-size: 0.95rem;
|
||||
font-weight: 500;
|
||||
color: var(--kaauh-primary-text);
|
||||
}
|
||||
.job-table tbody tr:hover {
|
||||
background-color: var(--kaauh-light-bg);
|
||||
}
|
||||
.job-link-cell {
|
||||
font-size: 0.85rem;
|
||||
text-align: center; /* Center the button in its cell */
|
||||
}
|
||||
.job-link-cell i {
|
||||
color: var(--kaauh-teal);
|
||||
}
|
||||
|
||||
/* Apply Button */
|
||||
.btn-apply {
|
||||
background-color: var(--kaauh-teal);
|
||||
color: white;
|
||||
font-weight: 600;
|
||||
padding: 0.4rem 1.2rem;
|
||||
border-radius: 5px;
|
||||
transition: background-color 0.2s;
|
||||
min-width: 80px;
|
||||
display: inline-block;
|
||||
text-align: center;
|
||||
text-decoration: none;
|
||||
border: 1px solid var(--kaauh-teal);
|
||||
}
|
||||
.btn-apply:hover {
|
||||
background-color: var(--kaauh-teal-dark);
|
||||
color: white;
|
||||
}
|
||||
.hero-section{
|
||||
background-image: url("{% static 'image/kaauh_banner.png' %}");
|
||||
background-size: cover;
|
||||
background-position: center;
|
||||
}
|
||||
|
||||
/* Hero Text Positioning */
|
||||
.hero-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
.arabic-title {
|
||||
font-size: 4rem;
|
||||
font-weight: 900;
|
||||
line-height: 1;
|
||||
margin-top: 0.5rem;
|
||||
}
|
||||
|
||||
/* RTL specific table alignment */
|
||||
html[dir="rtl"] .job-table thead th {
|
||||
text-align: right;
|
||||
}
|
||||
html[dir="rtl"] .job-link-cell {
|
||||
text-align: center; /* Ensure button remains centered in RTL */
|
||||
/*
|
||||
* Custom CSS for mobile stacking (displaying the table as a list) on screens <= 576px.
|
||||
*/
|
||||
@media (max-width: 576px) {
|
||||
.table thead {
|
||||
display: none; /* Hide header row */
|
||||
}
|
||||
.table tr {
|
||||
display: block;
|
||||
margin-bottom: 1rem;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 4px;
|
||||
}
|
||||
.table td {
|
||||
display: block;
|
||||
text-align: right;
|
||||
padding-left: 50% !important;
|
||||
position: relative;
|
||||
border-top: none;
|
||||
word-wrap: break-word;
|
||||
}
|
||||
.table td::before {
|
||||
/* This is where the magic happens: it pulls the translated header text from the data-label attribute */
|
||||
content: attr(data-label);
|
||||
position: absolute;
|
||||
left: 6px;
|
||||
width: 45%;
|
||||
padding-right: 10px;
|
||||
white-space: nowrap;
|
||||
text-align: left;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/* Ensure the link button is visible and positioned correctly */
|
||||
.col-link a {
|
||||
display: inline-block;
|
||||
margin: 5px 0;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
|
||||
<div class="hero-section">
|
||||
<div class="hero-overlay"></div>
|
||||
<div class="hero-content">
|
||||
<p class="text-uppercase small fw-bold mb-1">
|
||||
{% trans "Home Page" %}
|
||||
</p>
|
||||
<h1 class="arabic-title">
|
||||
{% if LANGUAGE_CODE == 'ar' %}التوظيف{% else %}Careers{% endif %}
|
||||
</h1>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="job-listing-section">
|
||||
<div class="max-width-container">
|
||||
<h2 class="h3 mb-4 text-center" style="color: var(--kaauh-teal-dark); font-weight: 700;">
|
||||
{% trans "Open Positions" %}
|
||||
</h2>
|
||||
|
||||
<div class="table-responsive">
|
||||
<table class="job-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col" style="width: 10%;">{% trans "Job ID" %}</th>
|
||||
<th scope="col">{% trans "Job Title" %}</th>
|
||||
<th scope="col" style="width: 10%;">{% trans "Hiring" %}</th>
|
||||
<th scope="col" style="width: 15%;">{% trans "Posting Date" %}</th>
|
||||
<th scope="col" style="width: 15%;">{% trans "Apply Before" %}</th>
|
||||
<th scope="col" style="width: 10%;">{% trans "Apply" %}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% comment %} Django loop would typically go here: {% for job in jobs %} {% endcomment %}
|
||||
{% for job in active_jobs %}
|
||||
<tr>
|
||||
<td class="text-nowrap">{{job.internal_job_id}}</td>
|
||||
<td class="text-nowrap">{{job.title}}</td>
|
||||
<td>{{job.open_positions}}</td>
|
||||
<td>{{job.application_start_date}}</td>
|
||||
<td>{{job.application_deadline}}</td>
|
||||
<td class="job-link-cell">
|
||||
<a href="{% url 'job_detail_candidate' job.slug %}" class="btn-apply">{% trans "Apply" %}</a>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div class="text-center mt-5">
|
||||
<a href="#" class="btn btn-lg btn-secondary">
|
||||
<i class="fas fa-list-alt me-2"></i> {% trans "View All Openings" %}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<footer id="footer-1" class="bg-image wide-40 footer division">
|
||||
<div class="footer-center-logo"><a href="/"><img src="https://kaauh.edu.sa/assets/images/logo.png" alt="header-logo" /></a></div>
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="col-md-6 col-lg-3">
|
||||
<div class="footer-info mb-30">
|
||||
<div class="lang-with-vision-logo footer-lang"><img src="https://kaauh.edu.sa/assets/images/vision-f.svg" alt="footer-logo" width="180" height="40"></div>
|
||||
<h5 class="h5-xs mb-0">Address</h5>
|
||||
<p class="p-sm mt-10">Airport Road, King Khalid International Airport, Riyadh 11564, Saudi Arabia</p>
|
||||
<h5 class="h5-xs">Follow us on</h5>
|
||||
<div class="footer-socials-links mt-20">
|
||||
<ul class="foo-socials text-center clearfix">
|
||||
<li><a href="javascript:;" target="_blank" class="ico-facebook"><i class="fa-brands fa-facebook-f"></i></a></li>
|
||||
<li><a href="https://twitter.com/KAAUH_PNU?lang=ar" target="_blank" class="ico-twitter"><i class="fa-brands fa-twitter"></i></a></li>
|
||||
<li><a href="https://www.instagram.com/kaauh_pnu/" target="_blank" class="ico-insta"><i class="fab fa-instagram"></i></a></li>
|
||||
<li><a href="https://mail.kaauh.edu.sa/" target="_blank" class="ico-insta"><i class="fa fa-envelope"></i></a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6 col-lg-3">
|
||||
<div class="footer-box mb-30 need-help f-space">
|
||||
<h5 class="h5-xs">Need help</h5>
|
||||
<p class="sales-services">Sales & Service Support</p>
|
||||
<a href="tel:+966118200000">+966118200000</a><br /><br />
|
||||
<p>Monday -Friday: 9:00-20:00<br />Saturday: 9:00-15:00</p>
|
||||
<p class="foo-email mt-20">Email: <a href="mailto:info@kaauh.edu.sa">info@kaauh.edu.sa</a></p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6 col-lg-2">
|
||||
<div class="footer-box mb-30 info-links f-space">
|
||||
<h5 class="h5-xs">Information</h5>
|
||||
<ul>
|
||||
<li><a href="https://kaauh.edu.sa/about-us">About KAAUH</a></li>
|
||||
<li><a href="{% url 'kaauh_career' %}">Careers</a></li>
|
||||
<li><a href="https://kaauh.edu.sa/gallery">Gallery</a></li>
|
||||
<li><a href="https://kaauh.edu.sa/need-help">Need Help</a></li>
|
||||
<li><a href="https://kaauh.edu.sa/contact">Contact Us</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6 col-lg-4">
|
||||
<div class="footer-box mb-30 apps-contact-info f-space">
|
||||
<h5 class="h5-xs">Download Our Mobile App</h5>
|
||||
<ul>
|
||||
<li><a href="https://apps.apple.com/us/app/kaauh-patient/id1502802835" target="_blank"><img src="https://kaauh.edu.sa/assets/images/app1.png" alt="" /></a></li>
|
||||
<li class="adroid"><a href="javascript:;"><img src="https://kaauh.edu.sa/assets/images/app2.png" alt="" /></a></li>
|
||||
</ul>
|
||||
<div class="email-and-call">
|
||||
<div class="header-widget icon-xs online_support">
|
||||
<span class="icon-img"><img src="https://kaauh.edu.sa/assets/images/support-f.svg" alt="" /></span>
|
||||
<div class="header-widget-txt">
|
||||
<p>24x7 online Support</p>
|
||||
<p class="header-widget-phone steelblue-color"><a href="mailto:info@kaauh.edu.sa">info@kaauh.edu.sa</a></p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="header-widget icon-xs online_support call-support">
|
||||
<span class="icon-img"><img src="https://kaauh.edu.sa/assets/images/call-f.svg" alt="" /></span>
|
||||
<div class="header-widget-txt">
|
||||
<p>Contact Us Free</p>
|
||||
<p class="header-widget-phone steelblue-color"><a href="tel:+966118200000">+966118200000</a></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="ads_banner"><img src="https://kaauh.edu.sa/ads.jpg" alt=""></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="bottom-footer">
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<p class="footer-copyright">© 2022 Kaauh. All Rights Reserved</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
<div class="modal fade commom-popup" id="serch">
|
||||
<div class="modal-dialog modal-lg center">
|
||||
<div class="modal-content">
|
||||
<div class="modal-body">
|
||||
<div class="form-area">
|
||||
<div class="search-input">
|
||||
<div class="form-group">
|
||||
<input type="text" name="" class="form-control" placeholder="Seaching......" required="" />
|
||||
<button><i class="fa fa-magnifying-glass"></i></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal fade commom-popup" id="appointment-check">
|
||||
<div class="modal-dialog modal-md center">
|
||||
<div class="modal-content">
|
||||
<div class="modal-body">
|
||||
<div class="form-area">
|
||||
<div class="text-center"><img src="https://kaauh.edu.sa/assets/images/logo-2.png" alt="header-logo" width="15%"/></div>
|
||||
<h2 class="text-center">Login With Patient Mobile</h2>
|
||||
<div class="search-input">
|
||||
<div class="form-group" id="enterMobile">
|
||||
<input type="text" id="mobile_numner" class="form-control" placeholder="Mobile number"/>
|
||||
<button onclick="sendOtpToMobile()">Request OTP <i class="fa fa-location-arrow"></i></button>
|
||||
</div>
|
||||
<div class="form-group d-none" id="enterOTP">
|
||||
<input type="text" id="mobile_otp" class="form-control" placeholder="Enter OTP" />
|
||||
<button onclick="verifyMobileOtp()">Verify <i class="fa fa-location-arrow"></i></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal fade commom-popup" id="referal">
|
||||
<div class="modal-dialog modal-xl">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h4 class="modal-title"><a href="javascript:;" data-dismiss="modal"><i class="fa fa-arrow-left"></i></a> Patient Referral</h4>
|
||||
<img src="https://kaauh.edu.sa/assets/images/logo.png" alt="header-logo" />
|
||||
<button type="button" class="close" data-dismiss="modal">×</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="form-area">
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<div class="form-holder">
|
||||
<form name="contactForm" method="POST" action="https://kaauh.edu.sa/patient-referral-form" class="row contact-form">
|
||||
<input type="hidden" name="_token" value="HnGLM2FHMNifpfgqfmGAiC0GcW9SEHjLa6r7INf7">
|
||||
<ul>
|
||||
<li>Authorize healthcare providers to administer medical treatment, and/or perform diagnostic tests as may be deemed necessary or advisable in the diagnosis and treatment required.</li>
|
||||
<li>I also agree:<div claa="space-l"><ul><li>(a) To have the patient transferred to another facility if medically needed. </li><li>(b) To vacate the room for medical isolation purposes or other treatment reasons.</li></ul></div></li>
|
||||
<li>I am aware that the hospital provide a safe for keeping personal belongings (money, jewelry, glasses, mobile,phone, documents, others). Therefore, the hospital will not be responsible for the loss of any belongings ifnot placed in the safe.</li>
|
||||
<li>I am aware that the hospital is an Academic Institution and it is likely that non-consultant physicians willparticipate in healthcare under the supervision of the Consultant.</li>
|
||||
<li>I have received Patient & Family’s Bill of Rights booklet, and | was briefed on its contents by the assignedemployee upon Admission/Registration.</li>
|
||||
<li>I understand that my discharge from the hospital is recommendedby my attending physician based on my medical condition. Therefore, | agree to leave the hospital on thedate of discharge.</li>
|
||||
</ul>
|
||||
<div class="col-lg-12">
|
||||
<div class="form-check">
|
||||
<label class="form-check-label"><input required class="form-check-input" type="checkbox" id="check1">I have read and agree to all information mentioned above</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg-12 mt-15 form-btn">
|
||||
<button type="submit" class="btn btn-blue blue-hover submit">Submit new referral request</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal fade commom-popup" id="doctorLogin">
|
||||
<div class="modal-dialog modal-md center">
|
||||
<div class="modal-content">
|
||||
<div class="modal-body">
|
||||
<div class="form-area">
|
||||
<div class="search-input">
|
||||
<div class="text-center"><img src="https://kaauh.edu.sa/assets/images/logo-2.png" alt="header-logo" width="15%"/></div>
|
||||
<h2 class="text-center">Login With Doctor Email</h2>
|
||||
<form method="POST" action="https://kaauh.edu.sa/doctorLogin" class="user" autocomplete="off">
|
||||
<input type="hidden" name="_token" value="HnGLM2FHMNifpfgqfmGAiC0GcW9SEHjLa6r7INf7">
|
||||
<div class="form-group">
|
||||
<input type="text" name="email" class="form-control" placeholder="Email" >
|
||||
<input type="password" name="password" class="form-control" placeholder="Password">
|
||||
<button type="submit">Login <i class="fas fa-sign-in-alt"></i></button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="need_lelp_buttn">
|
||||
<ul class="circle">
|
||||
<li><a href="https://kaauh.edu.sa/need-help"><i class="fa fa-headphones"></i> </a> <span>Need Help</span></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
<script src="https://kaauh.edu.sa/assets/js/jquery-3.6.3.min.js"></script>
|
||||
<script src="https://kaauh.edu.sa/assets/js/bootstrap.min.js"></script>
|
||||
<script src="https://kaauh.edu.sa/assets/js/modernizr.custom.js"></script>
|
||||
<script src="https://kaauh.edu.sa/assets/js/menu.js"></script>
|
||||
<script src="https://kaauh.edu.sa/assets/js/sticky.js"></script>
|
||||
<script src="https://kaauh.edu.sa/assets/js/materialize.js"></script>
|
||||
<script src="https://kaauh.edu.sa/assets/js/owl.carousel.min.js"></script>
|
||||
<script src="https://kaauh.edu.sa/assets/js/jquery.magnific-popup.min.js"></script>
|
||||
<script src="https://kaauh.edu.sa/assets/js/imagesloaded.pkgd.min.js"></script>
|
||||
<script src="https://kaauh.edu.sa/assets/js/wow.js"></script>
|
||||
<script src="https://kaauh.edu.sa/assets/js/sweetalert2.min.js"></script>
|
||||
<script src="https://kaauh.edu.sa/assets/js/bootstrap-datepicker.min.js"></script>
|
||||
<script src="https://kaauh.edu.sa/js/toastmin.js"></script>
|
||||
<script src="https://kaauh.edu.sa/js/select2min.js"></script>
|
||||
<script>new WOW().init();</script>
|
||||
<script></script>
|
||||
<script>
|
||||
var preloader = $('#loader-wrapper'),loader = preloader.find('.loader-inner');loader.fadeOut();preloader.delay(400).fadeOut('slow');
|
||||
function sendOtpToMobile(){
|
||||
let mobile_number = $('#mobile_numner').val();
|
||||
mobile_number = mobile_number.replace(/\s/g, '')
|
||||
if(mobile_number == ''){
|
||||
Swal.fire({title: 'Warning',text: 'Mobile Number Required',icon: 'warning',confirmButtonText: 'OK'});
|
||||
return false;
|
||||
}
|
||||
var regex = new RegExp(/^(009665|9665|\+9665|05|5)(5|0|3|6|4|9|1|8|7)([0-9]{7})$/);
|
||||
if(!regex.test(mobile_number)){
|
||||
Swal.fire({title: 'Warning',text: 'Invalid Mobile Number',icon: 'warning',confirmButtonText: 'OK'});
|
||||
return false;
|
||||
}
|
||||
var url ="https://kaauh.edu.sa/otp-request";
|
||||
$.post(url, {_token:"HnGLM2FHMNifpfgqfmGAiC0GcW9SEHjLa6r7INf7",mobile:mobile_number,},
|
||||
function (data) {
|
||||
if (data=='true') {
|
||||
$('#enterOTP').removeClass('d-none');
|
||||
$('#enterMobile').addClass('d-none');
|
||||
}else{
|
||||
console.log(data);
|
||||
}
|
||||
})
|
||||
}
|
||||
function verifyMobileOtp(){
|
||||
let contact_number = $('#mobile_numner').val();
|
||||
contact_number = contact_number.replace(/\s/g, '')
|
||||
var otp=$('#mobile_otp').val();
|
||||
var url ="https://kaauh.edu.sa/otp-request-verify";
|
||||
$.post(url, {_token:"HnGLM2FHMNifpfgqfmGAiC0GcW9SEHjLa6r7INf7",mobile:contact_number,otp:otp},
|
||||
function (data) {
|
||||
if (data=='true') {
|
||||
location.href = 'https://kaauh.edu.sa/appointments'
|
||||
}else{
|
||||
console.log(data);
|
||||
Swal.fire({title: 'Warning',text: 'Invalid OTP',icon: 'warning',confirmButtonText: 'OK'});
|
||||
}
|
||||
})
|
||||
}
|
||||
var search = window.location.search;
|
||||
var hrefname = window.location.href;
|
||||
hrefname = hrefname.replace(search, '');
|
||||
var active = $('[href="'+hrefname+'"]').closest('li');
|
||||
if (active.parents('.sub-menu').length) {
|
||||
active.parents('.sub-menu').parent('li').addClass('active');
|
||||
}else{
|
||||
$('[href="'+hrefname+'"]').closest('li').addClass('active');
|
||||
}
|
||||
$('#datepicker').datepicker({format: 'dd-mm-yyyy',autoclose:true,startDate: new Date()});
|
||||
$('.datepicker_dob').datepicker({format: 'dd-mm-yyyy',autoclose:true,endDate:new Date()});
|
||||
$('#datepicker_ref').datepicker({format: 'yyyy-mm-dd',autoclose:true,endDate: new Date()});
|
||||
$('.image-link').magnificPopup({type: 'image'});
|
||||
$('.video-popup1').magnificPopup({type: 'iframe',iframe: {patterns: {youtube: {index: 'youtube.com',src: 'https://www.youtube.com/embed/SZEflIVnhH8'}}}});
|
||||
$('.datepicker_psd').datepicker({format: 'dd-mm-yyyy',autoclose:true,endDate:new Date()});
|
||||
</script>
|
||||
<script></script>
|
||||
<script>$(".speciality").select2({tags: true});</script>
|
||||
</body>
|
||||
</html>
|
||||
@ -280,10 +280,10 @@
|
||||
<i class="fas fa-edit me-2 text-primary"></i> <strong>{% trans "Updated At:" %}</strong> {{ job.updated_at|default:"N/A" }}
|
||||
</div>
|
||||
|
||||
<div class="col-md-6">
|
||||
<div class="col-md-4">
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-main-action"
|
||||
class="btn btn-main-action btn-sm"
|
||||
id="copyJobLinkButton"
|
||||
data-url="{{ job.application_url }}">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-6">
|
||||
@ -298,7 +298,6 @@
|
||||
</span>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
<h5 class="text-muted mb-3">{% trans "Financial & Timeline" %}</h5>
|
||||
<div class="row g-3">
|
||||
|
||||
@ -1,260 +0,0 @@
|
||||
{% load static i18n %}
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>{% translate "Application Form" %}</title>
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
|
||||
<style>
|
||||
/* ---------------------------------------------------------------------- */
|
||||
/* THEME & UTILITY VARIABLES */
|
||||
/* ---------------------------------------------------------------------- */
|
||||
:root {
|
||||
--kaauh-teal: #00636e;
|
||||
--kaauh-teal-dark: #004a53;
|
||||
--success: #198754;
|
||||
--light-bg: #f8f9fa;
|
||||
|
||||
/* CALCULATED STICKY HEIGHTS */
|
||||
/* Standard Bootstrap Navbar Height (approx 56px) */
|
||||
--navbar-height: 56px;
|
||||
/* Bootstrap mb-3 spacing (1rem or 16px) */
|
||||
--navbar-gap: 16px;
|
||||
|
||||
/* Combined height of the two navbars when collapsed on desktop (56 + 16 + 56 = 128px) */
|
||||
--sticky-navbar-total-height: 128px;
|
||||
}
|
||||
|
||||
.btn-main-action {
|
||||
background-color: var(--kaauh-teal);
|
||||
color: white;
|
||||
border: none;
|
||||
transition: background-color 0.3s ease, transform 0.2s ease;
|
||||
box-shadow: 0 4px 12px rgba(0, 99, 110, 0.3);
|
||||
}
|
||||
|
||||
.btn-main-action:hover {
|
||||
background-color: var(--kaauh-teal-dark);
|
||||
color: white;
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
|
||||
.bg-kaauh-teal-dark {
|
||||
background-color: var(--kaauh-teal-dark) !important;
|
||||
}
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
/* LAYOUT & STICKY POSITIONING FIXES (Desktop/Tablet) */
|
||||
/* ---------------------------------------------------------------------- */
|
||||
|
||||
/* 1. Position the dark navbar below the white navbar + gap */
|
||||
#bottomNavbar {
|
||||
/* 56px (white nav) + 16px (mb-3) = 72px */
|
||||
top: calc(var(--navbar-height) + var(--navbar-gap));
|
||||
z-index: 1030;
|
||||
}
|
||||
|
||||
/* 2. Pushes the main content down so it's not hidden under the navbars */
|
||||
.main-content-area {
|
||||
/* Total Sticky Height (128px) + Extra Margin (12px) = 140px */
|
||||
margin-top: calc(var(--sticky-navbar-total-height) + 12px);
|
||||
}
|
||||
|
||||
/* 3. Positions the sticky sidebar correctly */
|
||||
.card.sticky-top {
|
||||
/* Start scrolling the sidebar just below the two navbars + a small gap */
|
||||
top: calc(var(--sticky-navbar-total-height) + 15px);
|
||||
}
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
/* MOBILE RESPONSIVE STYLES (Below 992px) */
|
||||
/* ---------------------------------------------------------------------- */
|
||||
@media (max-width: 991.98px) {
|
||||
|
||||
/* --- FIX: Spacing for Collapsed Hamburger Menu --- */
|
||||
#navbarNav .nav-item {
|
||||
margin-top: 5px;
|
||||
margin-bottom: 5px;
|
||||
padding: 5px 0;
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
}
|
||||
|
||||
#navbarNav .nav-item:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
#navbarNav .nav-link {
|
||||
padding: 8px 15px;
|
||||
display: block;
|
||||
}
|
||||
/* --- END FIX --- */
|
||||
|
||||
/* On mobile, the top navbar is generally only 56px tall when collapsed.
|
||||
The bottom navbar position remains correct (72px down). */
|
||||
#bottomNavbar {
|
||||
top: calc(var(--navbar-height) + var(--navbar-gap));
|
||||
}
|
||||
|
||||
/* Main content area needs less margin on mobile since the sidebar isn't active
|
||||
and the navs are collapsed by default. */
|
||||
.main-content-area {
|
||||
/* Reduced margin-top for smaller screens */
|
||||
margin-top: calc(var(--sticky-navbar-total-height) / 2);
|
||||
}
|
||||
|
||||
/* Mobile Fixed Footer Bar for Application */
|
||||
.mobile-fixed-apply-bar {
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
width: 100%;
|
||||
padding: 15px;
|
||||
background-color: var(--light-bg);
|
||||
border-top: 1px solid #ddd;
|
||||
z-index: 1000;
|
||||
box-shadow: 0 -4px 10px rgba(0, 0, 0, 0.08);
|
||||
}
|
||||
|
||||
/* Add padding to the bottom of the body content to prevent it from hiding under the fixed bar */
|
||||
body {
|
||||
padding-bottom: 90px;
|
||||
}
|
||||
|
||||
/* Fix job overview grid responsiveness (ensures 1 column) */
|
||||
.row-cols-md-2 > .col {
|
||||
flex: 0 0 100%;
|
||||
max-width: 100%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<nav id="topNavbar" class="navbar navbar-expand-lg sticky-top" style="background-color: white; z-index: 1030;">
|
||||
<div class="container-fluid">
|
||||
<span class="navbar-brand text-white fw-bold">
|
||||
<img src="{% static 'image/kaauh.jpeg' %}" alt="{% translate 'KAAUH IMAGE' %}" style="height: 50px; margin-right: 10px;">
|
||||
</span>
|
||||
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav"
|
||||
aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
</button>
|
||||
<div class="collapse navbar-collapse" id="navbarNav">
|
||||
<ul class="navbar-nav ms-auto">
|
||||
|
||||
<li class="nav-item">
|
||||
<a class="nav-link text-secondary" href="/applications/">{% translate "Applications" %}</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link text-secondary" href="/profile/">{% translate "Profile" %}</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link text-secondary" href="https://kaauh.edu.sa/career">{% translate "Careers" %}</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<nav id="bottomNavbar" class="navbar navbar-expand-lg sticky-top" style="background-color: var(--kaauh-teal); z-index: 1030;">
|
||||
<div class="container-fluid">
|
||||
<span class="navbar-text text-white fw-bold">{% translate "Job Overview" %}</span>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<div class="container">
|
||||
<div class="row mb-5 mt-3 main-content-area">
|
||||
|
||||
<div class="col-lg-4 order-lg-2 order-1 d-none d-lg-block">
|
||||
<div class="card shadow-sm sticky-top" style="top: var(--sticky-navbar-total-height);">
|
||||
<div class="card-header bg-kaauh-teal-dark text-white">
|
||||
<h5 class="mb-0"><i class="fas fa-file-signature me-2"></i>Ready to Apply?</h5>
|
||||
</div>
|
||||
<div class="card-body text-center">
|
||||
<p class="text-muted">Review the job details, then apply below.</p>
|
||||
|
||||
{% if job.form_template %}
|
||||
<a href="{% url 'form_wizard' job.form_template.pk %}" class="btn btn-main-action btn-lg w-100">
|
||||
<i class="fas fa-paper-plane me-2"></i> Apply for this Position
|
||||
</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-lg-8 order-lg-1 order-2">
|
||||
<div class="card shadow-sm">
|
||||
<div class="card-header bg-kaauh-teal-dark text-white d-flex justify-content-between align-items-center">
|
||||
<h2 class="h3 mb-0 fw-bold">{{ job.title }}</h2>
|
||||
|
||||
{% comment %} {% with status_class=job.status|lower %}
|
||||
<span class="badge
|
||||
{% if status_class == 'open' %}bg-success
|
||||
{% elif status_class == 'closed' %}bg-danger
|
||||
{% elif status_class == 'draft' %}bg-secondary
|
||||
{% else %}bg-primary
|
||||
{% endif %}
|
||||
status-badge fw-bold p-2">
|
||||
{{ job.get_status_display }}
|
||||
</span>
|
||||
{% endwith %} {% endcomment %}
|
||||
</div>
|
||||
|
||||
<div class="card-body">
|
||||
|
||||
<h4 class="mb-3" style="color: var(--kaauh-teal-dark);">Job Overview</h4>
|
||||
<div class="row row-cols-1 row-cols-md-2 g-3 mb-4">
|
||||
{% if job.salary_range %}
|
||||
<div class="col">
|
||||
<i class="fas fa-money-bill-wave text-success me-2"></i>
|
||||
<strong>Salary:</strong>
|
||||
<span class="fw-bold text-success">{{ job.salary_range }}</span>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<div class="col">
|
||||
<i class="fas fa-calendar-alt text-muted me-2"></i>
|
||||
<strong>Deadline:</strong>
|
||||
{% if job.application_deadline %}
|
||||
{{ job.application_deadline|date:"M d, Y" }}
|
||||
{% if job.is_expired %}
|
||||
<span class="badge bg-danger ms-2">EXPIRED</span>
|
||||
{% endif %}
|
||||
{% else %}
|
||||
<span class="text-muted">Not specified</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<div class="col"> <i class="fas fa-briefcase text-muted me-2"></i> <strong>Job Type:</strong> {{ job.get_job_type_display }} </div>
|
||||
<div class="col"> <i class="fas fa-map-marker-alt text-muted me-2"></i> <strong>Location:</strong> {{ job.get_location_display }} </div>
|
||||
<div class="col"> <i class="fas fa-building text-muted me-2"></i> <strong>Department:</strong> {{ job.department|default:"Not specified" }} </div>
|
||||
<div class="col"> <i class="fas fa-hashtag text-muted me-2"></i> <strong>JOB ID:</strong> {{ job.internal_job_id|default:"N/A" }} </div>
|
||||
<div class="col"> <i class="fas fa-desktop text-muted me-2"></i> <strong>Workplace:</strong> {{ job.get_workplace_type_display }} </div>
|
||||
{% comment %} <div class="col"> <i class="fas fa-user-tie text-muted me-2"></i> <strong>Created By:</strong> {{ job.created_by|default:"N/A" }} </div> {% endcomment %}
|
||||
</div>
|
||||
|
||||
{% if job.description %}<hr class="my-4"><div class="mb-4"><h5 class="fw-bold" style="color: var(--kaauh-teal-dark);"><i class="fas fa-info-circle me-2"></i>Job Description</h5><div class="text-secondary">{{ job.description|safe }}</div></div>{% endif %}
|
||||
{% if job.qualifications %}<hr class="my-4"><div class="mb-4"><h5 class="fw-bold" style="color: var(--kaauh-teal-dark);"><i class="fas fa-graduation-cap me-2"></i>Qualifications</h5><div class="text-secondary">{{ job.qualifications|safe }}</div></div>{% endif %}
|
||||
{% if job.benefits %}<hr class="my-4"><div class="mb-4"><h5 class="fw-bold" style="color: var(--kaauh-teal-dark);"><i class="fas fa-hand-holding-usd me-2"></i>Benefits</h5><div class="text-secondary">{{ job.benefits|safe }}</div></div>{% endif %}
|
||||
{% if job.application_instructions %}<hr class="my-4"><div class="mb-4"><h5 class="fw-bold" style="color: var(--kaauh-teal-dark);"><i class="fas fa-file-alt me-2"></i>Application Instructions</h5><div class="text-secondary">{{ job.application_instructions|safe }}</div></div>{% endif %}
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mobile-fixed-apply-bar d-lg-none">
|
||||
{% if job.form_template %}
|
||||
<a href="{% url 'form_wizard' job.form_template.pk %}" class="btn btn-main-action btn-lg w-100">
|
||||
<i class="fas fa-paper-plane me-2"></i> Apply for this Position
|
||||
</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
@ -225,14 +225,14 @@
|
||||
<option value="ARCHIVED" {% if status_filter == 'ARCHIVED' %}selected{% endif %}>{% trans "Archived" %}</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-5">
|
||||
<div class="col-md-7">
|
||||
<div class="filter-buttons">
|
||||
|
||||
<button type="submit" class="btn btn-main-action btn-lg">
|
||||
<button type="submit" class="btn btn-main-action">
|
||||
<i class="fas fa-filter me-1"></i> {% trans "Apply Filters" %}
|
||||
</button>
|
||||
{% if job_filter or search_query %}
|
||||
<a href="{% url 'job_list' %}" class="btn btn-outline-secondary btn-lg">
|
||||
<a href="{% url 'job_list' %}" class="btn btn-outline-secondary">
|
||||
<i class="fas fa-times me-1"></i> {% trans "Clear" %}
|
||||
</a>
|
||||
{% endif %}
|
||||
|
||||
7
templates/meetings/delete_meeting_form.html
Normal file
7
templates/meetings/delete_meeting_form.html
Normal file
@ -0,0 +1,7 @@
|
||||
{% load i18n %}
|
||||
<form action="{% url 'delete_meeting_for_candidate' job.slug candidate.pk meeting.pk %}" method="post">
|
||||
{% csrf_token %}
|
||||
<p class="text-danger">{% trans "Are you sure you want to delete this meeting? This action is irreversible." %}</p>
|
||||
<button type="submit" class="btn btn-danger">{% trans "Delete Meeting" %}</button>
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">{% trans "Cancel" %}</button>
|
||||
</form>
|
||||
@ -168,7 +168,7 @@
|
||||
<form method="GET" class="row g-3 align-items-end" >
|
||||
{% if search_query %}<input type="hidden" name="q" value="{{ search_query }}">{% endif %}
|
||||
|
||||
<div class="col-md-3">
|
||||
<div class="col-md-4">
|
||||
<label for="status" class="form-label small text-muted">{% trans "Filter by Status" %}</label>
|
||||
<select name="status" id="status" class="form-select form-select-sm">
|
||||
<option value="">{% trans "All Statuses" %}</option>
|
||||
@ -178,13 +178,13 @@
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="col-md-5">
|
||||
<div class="col-md-7">
|
||||
<div class="filter-buttons">
|
||||
<button type="submit" class="btn btn-main-action btn-lg">
|
||||
<button type="submit" class="btn btn-main-action">
|
||||
<i class="fas fa-filter me-1"></i> {% trans "Apply Filters" %}
|
||||
</button>
|
||||
{% if status_filter or search_query %}
|
||||
<a href="{% url 'meeting_list' %}" class="btn btn-outline-secondary btn-lg">
|
||||
<a href="{% url 'meeting_list' %}" class="btn btn-outline-secondary">
|
||||
<i class="fas fa-times me-1"></i> {% trans "Clear" %}
|
||||
</a>
|
||||
{% endif %}
|
||||
|
||||
70
templates/meetings/reschedule_meeting.html
Normal file
70
templates/meetings/reschedule_meeting.html
Normal file
@ -0,0 +1,70 @@
|
||||
{% load static i18n %}
|
||||
{% load widget_tweaks %}
|
||||
|
||||
<div class="p-3" id="reschedule-meeting-form">
|
||||
<div class="d-flex justify-content-between align-items-center mb-3">
|
||||
<div>
|
||||
<h5 class="mb-1" style="color: var(--kaauh-teal-dark); font-weight: 700;">
|
||||
<i class="fas fa-redo-alt me-1"></i>
|
||||
{% trans "Update Interview" %} for {{ candidate.name }}
|
||||
</h5>
|
||||
<p class="text-muted mb-0 small">{% trans "Job" %}: {{ job.title }}</p>
|
||||
{% if has_future_meeting %}
|
||||
<div class="alert alert-info mt-2 mb-0 p-2 small" role="alert">
|
||||
<i class="fas fa-info-circle me-1"></i>
|
||||
{% trans "You are updating the existing meeting schedule." %}
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<form method="post" id="updateMeeting" action="{% url 'reschedule_meeting_for_candidate' job.slug candidate.pk meeting.pk %}">
|
||||
{% csrf_token %}
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="{{ form.topic.id_for_label }}" class="form-label small">
|
||||
{% trans "Meeting Topic" %}
|
||||
</label>
|
||||
{{ form.topic|add_class:"form-control" }}
|
||||
{% for error in form.topic.errors %}
|
||||
<div class="text-danger small mt-1">{{ error }}</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-7">
|
||||
<div class="mb-3">
|
||||
<label for="{{ form.start_time.id_for_label }}" class="form-label small">
|
||||
{% trans "Start Time" %} (Date & Time)
|
||||
</label>
|
||||
{{ form.start_time|add_class:"form-control" }}
|
||||
{% for error in form.start_time.errors %}
|
||||
<div class="text-danger small mt-1">{{ error }}</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-5">
|
||||
<div class="mb-3">
|
||||
<label for="{{ form.duration.id_for_label }}" class="form-label small">
|
||||
{% trans "Duration (minutes)" %}
|
||||
</label>
|
||||
{{ form.duration|add_class:"form-control" }}
|
||||
{% for error in form.duration.errors %}
|
||||
<div class="text-danger small mt-1">{{ error }}</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr style="border-top: 1px solid var(--kaauh-border); margin-top: 1.5rem; margin-bottom: 1.5rem;">
|
||||
|
||||
<div class="d-flex justify-content-end gap-2">
|
||||
<button type="submit" class="btn btn-main-action btn-sm">
|
||||
<i class="fas fa-save me-1"></i> {% trans "Update Meeting" %}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
90
templates/meetings/schedule_meeting_form.html
Normal file
90
templates/meetings/schedule_meeting_form.html
Normal file
@ -0,0 +1,90 @@
|
||||
{% load i18n %}
|
||||
<div class="p-3" id="schedule-meeting-form">
|
||||
<div class="d-flex justify-content-between align-items-center mb-3">
|
||||
<div>
|
||||
<h5 class="mb-1" style="color: var(--kaauh-teal-dark); font-weight: 700;">
|
||||
{% if has_future_meeting %}
|
||||
{% trans "Update Interview" %} for {{ candidate.name }}
|
||||
{% else %}
|
||||
{% trans "Schedule Interview" %} for {{ candidate.name }}
|
||||
{% endif %}
|
||||
</h5>
|
||||
<p class="text-muted mb-0 small">{% trans "Job" %}: {{ job.title }}</p>
|
||||
{% if has_future_meeting %}
|
||||
<div class="alert alert-info mt-2 mb-0 p-2 small" role="alert">
|
||||
<i class="fas fa-info-circle me-1"></i>
|
||||
{% trans "Candidate has upcoming interviews. Updating existing schedule." %}
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<form method="post" action="{% url 'schedule_meeting_for_candidate' job.slug candidate.pk %}">
|
||||
{% csrf_token %}
|
||||
<div class="mb-3">
|
||||
<label for="{{ form.topic.id_for_label }}" class="form-label small">
|
||||
{% trans "Meeting Topic" %}
|
||||
</label>
|
||||
{{ form.topic }}
|
||||
{% if form.topic.errors %}
|
||||
<div class="text-danger">
|
||||
{% for error in form.topic.errors %}
|
||||
<small>{{ error }}</small>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endif %}
|
||||
<div class="form-text small text-muted">
|
||||
{% trans "e.g., Technical Screening, HR Interview" %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-7">
|
||||
<div class="mb-3">
|
||||
<label for="{{ form.start_time.id_for_label }}" class="form-label small">
|
||||
{% trans "Start Time" %} (Date & Time)
|
||||
</label>
|
||||
{{ form.start_time }}
|
||||
{% if form.start_time.errors %}
|
||||
<div class="text-danger">
|
||||
{% for error in form.start_time.errors %}
|
||||
<small>{{ error }}</small>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-5">
|
||||
<div class="mb-3">
|
||||
<label for="{{ form.duration.id_for_label }}" class="form-label small">
|
||||
{% trans "Duration (minutes)" %}
|
||||
</label>
|
||||
{{ form.duration }}
|
||||
{% if form.duration.errors %}
|
||||
<div class="text-danger">
|
||||
{% for error in form.duration.errors %}
|
||||
<small>{{ error }}</small>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr style="border-top: 1px solid var(--kaauh-border); margin-top: 1.5rem; margin-bottom: 1.5rem;">
|
||||
|
||||
<div class="d-flex justify-content-end gap-2">
|
||||
<button type="submit" class="btn btn-main-action btn-sm">
|
||||
<i class="fas fa-save me-1"></i>
|
||||
{% if has_future_meeting %}
|
||||
{% trans "Update Meeting" %}
|
||||
{% else %}
|
||||
{% trans "Schedule Meeting" %}
|
||||
{% endif %}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
@ -3,169 +3,7 @@
|
||||
{% block title %}{% trans "Update Zoom Meeting" %} - {{ block.super }}{% endblock %}
|
||||
|
||||
{% block customCSS %}
|
||||
<style>
|
||||
/* UI Variables for the KAAT-S Theme */
|
||||
:root {
|
||||
--kaauh-teal: #00636e;
|
||||
--kaauh-teal-dark: #004a53;
|
||||
--kaauh-border: #eaeff3;
|
||||
--kaauh-primary-text: #343a40;
|
||||
--kaauh-gray: #6c757d;
|
||||
|
||||
/* Status Colors for alerts/messages */
|
||||
--kaauh-success: var(--kaauh-teal);
|
||||
--kaauh-danger: #dc3545;
|
||||
--kaauh-info: #17a2b8;
|
||||
}
|
||||
|
||||
/* CONTAINER AND CARD STYLING */
|
||||
.container {
|
||||
padding: 2rem 1rem;
|
||||
}
|
||||
.card {
|
||||
border: 1px solid var(--kaauh-border);
|
||||
border-radius: 0.75rem;
|
||||
box-shadow: 0 4px 12px rgba(0,0,0,0.06);
|
||||
max-width: 600px;
|
||||
margin: 0 auto; /* Center the card */
|
||||
padding: 1.5rem;
|
||||
}
|
||||
|
||||
/* HEADER STYLING (The section outside the card) */
|
||||
.header {
|
||||
text-align: center;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
.header h1 {
|
||||
font-size: 2rem;
|
||||
color: var(--kaauh-teal-dark);
|
||||
font-weight: 700;
|
||||
margin-bottom: 0.25rem;
|
||||
}
|
||||
.header p {
|
||||
color: var(--kaauh-gray);
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
/* CARD TITLE STYLING */
|
||||
.card-title {
|
||||
font-size: 1.25rem;
|
||||
color: var(--kaauh-teal-dark);
|
||||
font-weight: 600;
|
||||
border-bottom: 1px solid var(--kaauh-border);
|
||||
padding-bottom: 0.75rem;
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
/* FORM STYLING */
|
||||
.form-row {
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
.form-label {
|
||||
display: block;
|
||||
font-weight: 600;
|
||||
color: var(--kaauh-gray);
|
||||
margin-bottom: 0.5rem;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
.form-input {
|
||||
display: block;
|
||||
width: 100%;
|
||||
padding: 0.75rem 1rem;
|
||||
font-size: 1rem;
|
||||
line-height: 1.5;
|
||||
color: var(--kaauh-primary-text);
|
||||
background-color: #fff;
|
||||
background-clip: padding-box;
|
||||
border: 1px solid var(--kaauh-border);
|
||||
border-radius: 0.5rem;
|
||||
transition: border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;
|
||||
}
|
||||
.form-input:focus {
|
||||
border-color: var(--kaauh-teal);
|
||||
outline: 0;
|
||||
box-shadow: 0 0 0 0.1rem rgba(0, 99, 110, 0.25);
|
||||
}
|
||||
input[type="datetime-local"] {
|
||||
font-family: inherit;
|
||||
}
|
||||
|
||||
/* MESSAGES/ALERTS STYLING */
|
||||
.messages {
|
||||
max-width: 600px;
|
||||
margin: 0 auto 1.5rem auto;
|
||||
}
|
||||
.alert {
|
||||
padding: 1rem;
|
||||
border-radius: 0.5rem;
|
||||
margin-bottom: 0.5rem;
|
||||
font-weight: 500;
|
||||
}
|
||||
.alert-success {
|
||||
color: white;
|
||||
background-color: var(--kaauh-success);
|
||||
border-color: var(--kaauh-success);
|
||||
}
|
||||
.alert-danger {
|
||||
color: white;
|
||||
background-color: var(--kaauh-danger);
|
||||
border-color: var(--kaauh-danger);
|
||||
}
|
||||
.alert-info {
|
||||
color: white;
|
||||
background-color: var(--kaauh-info);
|
||||
border-color: var(--kaauh-info);
|
||||
}
|
||||
|
||||
/* BUTTON STYLING */
|
||||
.actions {
|
||||
margin-top: 1.5rem;
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
}
|
||||
.btn-base {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
padding: 0.5rem 1rem;
|
||||
font-size: 1rem;
|
||||
line-height: 1.5;
|
||||
border-radius: 0.5rem;
|
||||
font-weight: 600;
|
||||
border: 1px solid transparent;
|
||||
transition: all 0.2s ease;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
/* Primary Action Button (Update) */
|
||||
.btn-main-action {
|
||||
background-color: var(--kaauh-teal);
|
||||
border-color: var(--kaauh-teal);
|
||||
color: white;
|
||||
}
|
||||
.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);
|
||||
color: white;
|
||||
}
|
||||
|
||||
/* Secondary Button (Cancel) */
|
||||
.btn-kaats-outline-secondary {
|
||||
color: var(--kaauh-secondary);
|
||||
border-color: var(--kaauh-secondary);
|
||||
background-color: transparent;
|
||||
}
|
||||
.btn-kaats-outline-secondary:hover {
|
||||
background-color: var(--kaauh-secondary);
|
||||
color: white;
|
||||
border-color: var(--kaauh-secondary);
|
||||
}
|
||||
</style>
|
||||
<link rel="stylesheet" href="{% static 'css/update_meeting.css' %}">
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
@ -190,7 +28,7 @@
|
||||
<div class="card">
|
||||
<h2 class="card-title">{% trans "Meeting Information" %}</h2>
|
||||
|
||||
<form method="post" action="{% url 'update_meeting' meeting.slug %}">
|
||||
<form method="post" id="updateMeeting" action="{% url 'update_meeting' meeting.slug %}">
|
||||
{% csrf_token %}
|
||||
|
||||
<div class="form-row">
|
||||
|
||||
@ -3,206 +3,11 @@
|
||||
|
||||
{% block title %}Candidate Tier Management - {{ job.title }} - ATS{% endblock %}
|
||||
|
||||
{% block customCSS %}
|
||||
<style>
|
||||
/* KAAT-S UI Variables */
|
||||
:root {
|
||||
--kaauh-teal: #00636e;
|
||||
--kaauh-teal-dark: #004a53;
|
||||
--kaauh-border: #eaeff3;
|
||||
--kaauh-primary-text: #343a40;
|
||||
--kaauh-success: #28a745;
|
||||
--kaauh-info: #17a2b8; /* Used for Exam stages (Pending status) */
|
||||
--kaauh-danger: #dc3545;
|
||||
--kaauh-warning: #ffc107;
|
||||
}
|
||||
|
||||
/* Primary Color Overrides */
|
||||
.text-primary-theme { color: var(--kaauh-teal) !important; }
|
||||
.bg-primary-theme { background-color: var(--kaauh-teal) !important; }
|
||||
|
||||
/* 1. Main Container & Card Styling */
|
||||
.kaauh-card {
|
||||
border: 1px solid var(--kaauh-border);
|
||||
border-radius: 0.75rem;
|
||||
box-shadow: 0 4px 12px rgba(0,0,0,0.06);
|
||||
background-color: white;
|
||||
}
|
||||
|
||||
/* Dedicated style for the tier control block (consistent with .filter-controls) */
|
||||
.tier-controls {
|
||||
background-color: var(--kaauh-border); /* Light background for control sections */
|
||||
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;
|
||||
}
|
||||
|
||||
/* 2. Button Styling (Themed for Main Actions) */
|
||||
.btn-main-action {
|
||||
background-color: var(--kaauh-teal);
|
||||
border-color: var(--kaauh-teal);
|
||||
color: white;
|
||||
font-weight: 600;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
.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);
|
||||
}
|
||||
.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);
|
||||
}
|
||||
/* Style for Bulk Pass button */
|
||||
.btn-bulk-pass {
|
||||
background-color: var(--kaauh-success);
|
||||
border-color: var(--kaauh-success);
|
||||
color: white;
|
||||
font-weight: 500;
|
||||
}
|
||||
.btn-bulk-pass:hover {
|
||||
background-color: #1e7e34;
|
||||
border-color: #1e7e34;
|
||||
}
|
||||
/* Style for Bulk Fail button */
|
||||
.btn-bulk-fail {
|
||||
background-color: var(--kaauh-danger);
|
||||
border-color: var(--kaauh-danger);
|
||||
color: white;
|
||||
font-weight: 500;
|
||||
}
|
||||
.btn-bulk-fail:hover {
|
||||
background-color: #bd2130;
|
||||
border-color: #bd2130;
|
||||
}
|
||||
|
||||
/* 3. Input and Button Height Optimization (Thin look) */
|
||||
.form-control-sm,
|
||||
.btn-sm {
|
||||
/* Reduce vertical padding even more than default Bootstrap 'sm' */
|
||||
padding-top: 0.2rem !important;
|
||||
padding-bottom: 0.2rem !important;
|
||||
/* Ensure a consistent, small height for inputs and buttons */
|
||||
height: 28px !important;
|
||||
font-size: 0.8rem !important;
|
||||
}
|
||||
.btn-main-action.btn-sm { font-weight: 600 !important; }
|
||||
|
||||
/* Container for the timeline include */
|
||||
.applicant-tracking-timeline {
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
/* 4. Candidate Table Styling (KAAT-S Look) */
|
||||
.candidate-table {
|
||||
table-layout: fixed;
|
||||
width: 100%;
|
||||
border-collapse: separate;
|
||||
border-spacing: 0;
|
||||
background-color: white;
|
||||
border-radius: 0.5rem;
|
||||
overflow: hidden;
|
||||
}
|
||||
.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;
|
||||
}
|
||||
.candidate-table-responsive {
|
||||
overflow-x: auto;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
/* 5. Badges */
|
||||
.ai-score-badge {
|
||||
background-color: var(--kaauh-teal-dark) !important;
|
||||
color: white;
|
||||
font-weight: 700;
|
||||
padding: 0.4em 0.8em;
|
||||
border-radius: 0.4rem;
|
||||
font-size: 0.8rem;
|
||||
}
|
||||
.status-badge { /* Used for Exam Status (Passed/Failed/Pending) */
|
||||
font-size: 0.75rem;
|
||||
padding: 0.3em 0.7em;
|
||||
border-radius: 0.35rem;
|
||||
font-weight: 700;
|
||||
text-transform: uppercase;
|
||||
display: inline-block;
|
||||
}
|
||||
.bg-success { background-color: var(--kaauh-success) !important; color: white; }
|
||||
.bg-danger { background-color: var(--kaauh-danger) !important; color: white; }
|
||||
.bg-info-pending { background-color: var(--kaauh-info) !important; color: white; }
|
||||
|
||||
.tier-badge { /* Used for Tier labels */
|
||||
font-size: 0.75rem;
|
||||
padding: 0.125rem 0.5rem;
|
||||
border-radius: 0.5rem;
|
||||
font-weight: 600;
|
||||
margin-left: 0.5rem;
|
||||
display: inline-block;
|
||||
}
|
||||
.tier-1-badge { background-color: var(--kaauh-success); color: white; }
|
||||
.tier-2-badge { background-color: var(--kaauh-warning); color: #856404; }
|
||||
.tier-3-badge { background-color: #d1ecf1; color: #0c5460; }
|
||||
|
||||
/* Fix table column widths for better layout */
|
||||
.candidate-table th:nth-child(1) { width: 40px; } /* Checkbox */
|
||||
.candidate-table th:nth-child(4) { width: 10%; } /* AI Score */
|
||||
.candidate-table th:nth-child(5) { width: 12%; } /* Exam Status */
|
||||
.candidate-table th:nth-child(6) { width: 15%; } /* Exam Date */
|
||||
.candidate-table th:nth-child(7) { width: 220px; } /* Actions */
|
||||
|
||||
.cd_exam{
|
||||
color: #00636e;
|
||||
}
|
||||
</style>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="container-fluid py-4">
|
||||
<div class="d-flex justify-content-between align-items-center mb-4">
|
||||
<div>
|
||||
<h1 class="h3 mb-1" style="color: var(--kaauh-teal-dark); font-weight: 700;">
|
||||
<h1 class="h3 mb-1 page-header">
|
||||
<i class="fas fa-edit me-2"></i>
|
||||
{% trans "Exam Management" %} - {{ job.title }}
|
||||
</h1>
|
||||
@ -215,35 +20,9 @@
|
||||
</a>
|
||||
</div>
|
||||
|
||||
{# APPLICANT TRACKING TIMELINE INCLUSION #}
|
||||
<div class="applicant-tracking-timeline">
|
||||
<div class="applicant-tracking-timeline mb-4">
|
||||
{% include 'jobs/partials/applicant_tracking.html' %}
|
||||
</div>
|
||||
{# END APPLICANT TRACKING TIMELINE INCLUSION #}
|
||||
|
||||
{% comment %} <div class="tier-controls kaauh-card shadow-sm">
|
||||
<h4 class="h6 mb-3 fw-bold" style="color: var(--kaauh-teal-dark);">
|
||||
<i class="fas fa-sort-amount-up me-1"></i> {% trans "Define Top Candidates (Tiers)" %}
|
||||
</h4>
|
||||
<form method="post" class="mb-0">
|
||||
{% csrf_token %}
|
||||
<div class="row g-3 align-items-end">
|
||||
<div class="col-md-3 col-sm-6">
|
||||
<label for="tier1_count" class="form-label small text-muted mb-1">
|
||||
{% trans "Number of Tier 1 Candidates (Top N)" %}
|
||||
</label>
|
||||
<input type="number" name="tier1_count" id="tier1_count" class="form-control form-control-sm"
|
||||
value="{{ tier1_count }}" min="1" max="{{ total_candidates }}" placeholder="e.g., 50">
|
||||
</div>
|
||||
<div class="col-md-3 col-sm-6">
|
||||
<button type="submit" name="update_tiers" class="btn btn-main-action btn-sm w-100">
|
||||
<i class="fas fa-sync-alt me-1"></i> {% trans "Update Tiers" %}
|
||||
</button>
|
||||
</div>
|
||||
<div class="col-md-6 d-none d-md-block"></div>
|
||||
</div>
|
||||
</form> {% endcomment %}
|
||||
</div>
|
||||
|
||||
<h2 class="h4 mb-3" style="color: var(--kaauh-primary-text);">
|
||||
{% trans "Candidate List" %}
|
||||
@ -252,27 +31,26 @@
|
||||
</h2>
|
||||
|
||||
<div class="kaauh-card shadow-sm p-3">
|
||||
<div class="candidate-table-responsive" data-signals__ifmissing="{_fetching: false, selections: Array({{ candidates|length }}).fill(false)}">
|
||||
<div class="col-md-3 col-sm-6 mb-3 d-flex gap-2">
|
||||
{% if candidates %}
|
||||
<form hx-boost="true" hx-include="#candidate-form" action="{% url 'candidate_update_status' job.slug %}" method="post">
|
||||
<div class="d-flex align-items-center">
|
||||
<select name="mark_as" id="update_status" class="form-select form-select-sm" style="height: 3rem;">
|
||||
<option value="Applied">
|
||||
<i class="fas fa-arrow-left me-1"></i> {% trans "Apply" %}
|
||||
</option>
|
||||
<option value="Interview">
|
||||
<i class="fas fa-arrow-right me-1"></i> {% trans "Interview" %}
|
||||
</option>
|
||||
</select>
|
||||
<button type="submit" class="btn btn-main-action btn-mds ms-2">
|
||||
<i class="fas fa-arrow-right me-1"></i> {% trans "Update" %}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
{% if candidates %}
|
||||
<div class="bulk-action-bar">
|
||||
<form hx-boost="true" hx-include="#candidate-form" action="{% url 'candidate_update_status' job.slug %}" method="post" class="action-group">
|
||||
{% csrf_token %}
|
||||
<label for="update_status" class="form-label small mb-0 fw-bold">{% trans "Move Selected To:" %}</label>
|
||||
<select name="mark_as" id="update_status" class="form-select form-select-sm" style="width: auto;">
|
||||
<option value="Applied">
|
||||
{% trans "Screening Stage" %}
|
||||
</option>
|
||||
<option value="Interview">
|
||||
{% trans "Interview Stage" %}
|
||||
</option>
|
||||
</select>
|
||||
<button type="submit" class="btn btn-main-action btn-sm">
|
||||
<i class="fas fa-arrow-right me-1"></i> {% trans "Update Status" %}
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
{% endif %}
|
||||
<div class="table-responsive">
|
||||
<form id="candidate-form" method="post">
|
||||
{% csrf_token %}
|
||||
<table class="table candidate-table align-middle">
|
||||
@ -308,7 +86,7 @@
|
||||
<td>
|
||||
<div class="candidate-name">
|
||||
{{ candidate.name }}
|
||||
{# Tier logic updated to be cleaner #}
|
||||
{# Tier badges now use defined stage-badge and tier-X-badge classes #}
|
||||
{% if forloop.counter <= tier1_count %}
|
||||
<span class="stage-badge tier-1-badge">Tier 1</span>
|
||||
{% elif forloop.counter <= tier1_count|default:0|add:tier1_count %}
|
||||
@ -333,7 +111,7 @@
|
||||
{% elif candidate.exam_status == "Failed" %}
|
||||
<span class="status-badge bg-danger">{{ candidate.exam_status }}</span>
|
||||
{% else %}
|
||||
<span class="status-badge bg-info-pending">Pending</span>
|
||||
<span class="status-badge bg-info">Pending</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>
|
||||
@ -370,7 +148,6 @@
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="modal fade modal-lg" id="candidateviewModal" tabindex="-1" aria-labelledby="candidateviewModalLabel" aria-hidden="true">
|
||||
@ -423,36 +200,24 @@
|
||||
selectAllCheckbox.checked = false;
|
||||
selectAllCheckbox.indeterminate = true;
|
||||
}
|
||||
|
||||
// IMPORTANT: We do NOT fire a change event here to prevent the infinite loop.
|
||||
// Your existing data-bind-_all logic should handle the bulk action status.
|
||||
}
|
||||
|
||||
// 1. Logic for the 'Select All' checkbox (Clicking it updates all rows)
|
||||
selectAllCheckbox.addEventListener('change', function () {
|
||||
const isChecked = selectAllCheckbox.checked;
|
||||
|
||||
// Temporarily disable the change listener on rows to prevent cascading events
|
||||
rowCheckboxes.forEach(checkbox => checkbox.removeEventListener('change', updateSelectAllState));
|
||||
|
||||
// Update all row checkboxes
|
||||
rowCheckboxes.forEach(function (checkbox) {
|
||||
checkbox.checked = isChecked;
|
||||
|
||||
// You must still dispatch the event here so your framework's data-bind-selections
|
||||
// picks up the change on individual elements. This should NOT trigger the updateSelectAllState.
|
||||
checkbox.dispatchEvent(new Event('change', { bubbles: true }));
|
||||
});
|
||||
|
||||
// Re-attach the change listeners to the rows
|
||||
rowCheckboxes.forEach(checkbox => checkbox.addEventListener('change', updateSelectAllState));
|
||||
|
||||
// Ensure the header state is correct after forcing all changes
|
||||
updateSelectAllState();
|
||||
});
|
||||
|
||||
// 2. Logic to update 'Select All' state based on row checkboxes
|
||||
// Attach the function to be called whenever a row checkbox changes
|
||||
rowCheckboxes.forEach(function (checkbox) {
|
||||
checkbox.addEventListener('change', updateSelectAllState);
|
||||
});
|
||||
|
||||
@ -1,385 +1,296 @@
|
||||
{% extends 'base.html' %}
|
||||
{% load static i18n %}
|
||||
|
||||
{% block title %}Candidate Tier Management - {{ job.title }} - ATS{% endblock %}
|
||||
|
||||
{% block customCSS %}
|
||||
<style>
|
||||
/* Minimal Tier Management Styles */
|
||||
.tier-controls {
|
||||
background-color: #f8f9fa;
|
||||
padding: 1rem;
|
||||
border-radius: 0.375rem;
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
.tier-controls .form-row {
|
||||
display: flex;
|
||||
align-items: end;
|
||||
gap: 0.75rem;
|
||||
}
|
||||
.tier-controls .form-group {
|
||||
flex: 1;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
.bulk-update-controls {
|
||||
background-color: #f8f9fa;
|
||||
padding: 1rem;
|
||||
border-radius: 0.375rem;
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
.stage-groups {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
|
||||
gap: 1rem;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
.stage-group {
|
||||
border: 1px solid #dee2e6;
|
||||
border-radius: 0.375rem;
|
||||
overflow: hidden;
|
||||
}
|
||||
.stage-group .stage-header {
|
||||
background-color: #495057;
|
||||
color: white;
|
||||
padding: 0.5rem 0.75rem;
|
||||
font-weight: 500;
|
||||
font-size: 0.95rem;
|
||||
}
|
||||
.stage-group .stage-body {
|
||||
padding: 0.75rem;
|
||||
min-height: 80px;
|
||||
}
|
||||
.stage-candidate {
|
||||
padding: 0.375rem;
|
||||
border-bottom: 1px solid #f1f3f4;
|
||||
}
|
||||
.stage-candidate:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
.match-score {
|
||||
font-weight: 600;
|
||||
color: #0056b3;
|
||||
}
|
||||
.btn-sm {
|
||||
font-size: 0.75rem;
|
||||
padding: 0.2rem 0.4rem;
|
||||
}
|
||||
|
||||
/* Tab Styles for Tiers */
|
||||
.nav-tabs {
|
||||
border-bottom: 1px solid #dee2e6;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
.nav-tabs .nav-link {
|
||||
border: none;
|
||||
color: #495057;
|
||||
font-weight: 500;
|
||||
padding: 0.5rem 1rem;
|
||||
transition: all 0.2s;
|
||||
}
|
||||
.nav-tabs .nav-link:hover {
|
||||
border: none;
|
||||
background-color: #f8f9fa;
|
||||
}
|
||||
.nav-tabs .nav-link.active {
|
||||
color: #495057;
|
||||
background-color: #fff;
|
||||
border: none;
|
||||
border-bottom: 2px solid #007bff;
|
||||
font-weight: 600;
|
||||
}
|
||||
.tier-1 .nav-link {
|
||||
color: #155724;
|
||||
}
|
||||
.tier-1 .nav-link.active {
|
||||
border-bottom-color: #28a745;
|
||||
}
|
||||
.tier-2 .nav-link {
|
||||
color: #856404;
|
||||
}
|
||||
.tier-2 .nav-link.active {
|
||||
border-bottom-color: #ffc107;
|
||||
}
|
||||
.tier-3 .nav-link {
|
||||
color: #721c24;
|
||||
}
|
||||
.tier-3 .nav-link.active {
|
||||
border-bottom-color: #dc3545;
|
||||
}
|
||||
|
||||
/* Candidate Table Styles */
|
||||
.candidate-table {
|
||||
width: 100%;
|
||||
border-collapse: separate;
|
||||
border-spacing: 0;
|
||||
background-color: white;
|
||||
border-radius: 0.375rem;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 1px 3px rgba(0,0,0,0.1);
|
||||
}
|
||||
.candidate-table thead {
|
||||
background-color: #f8f9fa;
|
||||
}
|
||||
.candidate-table th {
|
||||
padding: 0.75rem;
|
||||
text-align: left;
|
||||
font-weight: 600;
|
||||
font-size: 0.875rem;
|
||||
color: #495057;
|
||||
border-bottom: 1px solid #dee2e6;
|
||||
}
|
||||
.candidate-table td {
|
||||
padding: 0.75rem;
|
||||
border-bottom: 1px solid #f1f3f4;
|
||||
vertical-align: middle;
|
||||
}
|
||||
.candidate-table tbody tr:hover {
|
||||
background-color: #f8f9fa;
|
||||
}
|
||||
.candidate-table tbody tr:last-child td {
|
||||
border-bottom: none;
|
||||
}
|
||||
.candidate-name {
|
||||
font-weight: 600;
|
||||
font-size: 0.95rem;
|
||||
}
|
||||
.candidate-details {
|
||||
font-size: 0.8rem;
|
||||
color: #6c757d;
|
||||
}
|
||||
.candidate-table-responsive {
|
||||
overflow-x: auto;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
.stage-badge {
|
||||
padding: 0.125rem 0.5rem;
|
||||
border-radius: 0.5rem;
|
||||
font-size: 0.7rem;
|
||||
font-weight: 600;
|
||||
margin-left: 0.375rem;
|
||||
}
|
||||
.stage-Applied {
|
||||
background-color: #e9ecef;
|
||||
color: #495057;
|
||||
}
|
||||
.stage-Exam {
|
||||
background-color: #cce5ff;
|
||||
color: #004085;
|
||||
}
|
||||
.stage-Interview {
|
||||
background-color: #d1ecf1;
|
||||
color: #0c5460;
|
||||
}
|
||||
.stage-Offer {
|
||||
background-color: #d4edda;
|
||||
color: #155724;
|
||||
}
|
||||
.exam-controls {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.375rem;
|
||||
margin-top: 0.375rem;
|
||||
}
|
||||
.exam-controls select,
|
||||
.exam-controls input {
|
||||
font-size: 0.75rem;
|
||||
padding: 0.125rem 0.25rem;
|
||||
}
|
||||
.tier-badge {
|
||||
font-size: 0.7rem;
|
||||
padding: 0.125rem 0.375rem;
|
||||
border-radius: 0.25rem;
|
||||
background-color: rgba(0,0,0,0.1);
|
||||
color: #495057;
|
||||
margin-left: 0.375rem;
|
||||
}
|
||||
</style>
|
||||
{% endblock %}
|
||||
{% block title %}- {{ job.title }} - ATS{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="container py-4">
|
||||
<div class="container-fluid py-4">
|
||||
<div class="d-flex justify-content-between align-items-center mb-4">
|
||||
<div>
|
||||
<h1 class="h3 mb-1">
|
||||
<i class="fas fa-layer-group me-2"></i>
|
||||
{% trans "Interview" %} - {{ job.title }}
|
||||
<h1 class="h3 mb-1" style="color: var(--kaauh-teal-dark); font-weight: 700;">
|
||||
<i class="fas fa-calendar-alt me-2"></i>
|
||||
{% trans "Interview Management" %} - {{ job.title }}
|
||||
</h1>
|
||||
|
||||
<h2 class="h5 text-muted mb-0">
|
||||
{% trans "Candidates in Interview Stage:" %} <span class="fw-bold">{{ candidates|length }}</span>
|
||||
</h2>
|
||||
</div>
|
||||
<a href="{% url 'job_detail' job.slug %}" class="btn btn-outline-secondary">
|
||||
<i class="fas fa-arrow-left me-1"></i> {% trans "Back to Job" %}
|
||||
</a>
|
||||
</div>
|
||||
<div class="applicant-tracking-timeline">
|
||||
{% include 'jobs/partials/applicant_tracking.html' %}
|
||||
</div>
|
||||
|
||||
<!-- Tier Display -->
|
||||
<h2 class="h4 mb-3 mt-5">{% trans "Candidate Tiers" %}</h2>
|
||||
<div class="candidate-table-responsive" data-signals__ifmissing="{_fetching: false, selections: Array({{ candidates|length }}).fill(false)}">
|
||||
{% url "schedule_interviews" job.slug as bulk_update_candidate_exam_status_url %}
|
||||
{% if candidates %}
|
||||
<button class="btn btn-primary"
|
||||
data-bs-toggle="modal"
|
||||
data-bs-target="#candidateviewModal"
|
||||
data-attr="{disabled: !$selections.filter(Boolean).length}"
|
||||
hx-get="{{bulk_update_candidate_exam_status_url}}"
|
||||
hx-target="#candidateviewModalBody"
|
||||
hx-include="#myform"
|
||||
hx-select=".interview-schedule"
|
||||
>Mark as Pass and move to Interview</button>
|
||||
<button class="btn btn-danger"
|
||||
data-attr="{disabled: !$selections.filter(Boolean).length}"
|
||||
data-on-click="@post('{{bulk_update_candidate_exam_status_url}}',{
|
||||
contentType: 'form',
|
||||
selector: '#myform',
|
||||
headers: {'X-CSRFToken': '{{ csrf_token }}','status': 'fail'}
|
||||
})"
|
||||
>Mark as Failed</button>
|
||||
{% endif %}
|
||||
<form id="myform" action="{{bulk_update_candidate_exam_status_url}}" method="post">
|
||||
<table class="candidate-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>
|
||||
{% if candidates %}
|
||||
<div class="form-check">
|
||||
<input
|
||||
data-bind-_all
|
||||
data-on-change="$selections = Array({{ candidates|length }}).fill($_all)"
|
||||
data-effect="$selections; $_all = $selections.every(Boolean)"
|
||||
data-attr-disabled="$_fetching"
|
||||
type="checkbox" class="form-check-input" id="candidate-{{ candidate.id }}">
|
||||
</div>
|
||||
{% endif %}
|
||||
</th>
|
||||
<th>{% trans "Name" %}</th>
|
||||
<th>{% trans "Contact" %}</th>
|
||||
<th>{% trans "Interview Date" %}</th>
|
||||
<th>{% trans "Interview Link" %}</th>
|
||||
<th>{% trans "Actions" %}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for candidate in candidates %}
|
||||
<tr>
|
||||
<td>
|
||||
<div class="form-check">
|
||||
<input
|
||||
data-bind-selections
|
||||
data-attr-disabled="$_fetching"
|
||||
name="{{ candidate.id }}"
|
||||
<div class="kaauh-card shadow-sm p-3">
|
||||
{% if candidates %}
|
||||
<div class="bulk-action-bar">
|
||||
|
||||
type="checkbox" class="form-check-input" id="candidate-{{ candidate.id }}">
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<div class="candidate-name">{{ candidate.name }}</div>
|
||||
</td>
|
||||
<td>
|
||||
<div class="candidate-details">
|
||||
Email: {{ candidate.email }}<br>
|
||||
Phone: {{ candidate.phone }}<br>
|
||||
</div>
|
||||
</td>
|
||||
|
||||
<td>{{candidate.get_latest_meeting.start_time|date:"m-d-Y h:i A"}}</td>
|
||||
<td><a href="{{candidate.get_latest_meeting.join_url}}">{% include "icons/link.html" %}</a></td>
|
||||
<td>
|
||||
<a href="{% url 'schedule_meeting_for_candidate' job.slug candidate.pk %}" class="btn btn-primary btn-sm me-1" title="{% trans 'Schedule Interview' %}">
|
||||
<i class="fas fa-calendar-plus"></i>
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
<form hx-boost="true" hx-include="#candidate-form" action="{% url 'candidate_update_status' job.slug %}" method="post" class="action-group">
|
||||
{% csrf_token %}
|
||||
<select name="mark_as" id="update_status" class="form-select form-select-sm" style="width: 120px;">
|
||||
<option value="Exam">
|
||||
{% trans "To Exam" %}
|
||||
</option>
|
||||
<option value="Offer">
|
||||
{% trans "To Offer" %}
|
||||
</option>
|
||||
</select>
|
||||
<button type="submit" class="btn btn-main-action btn-sm">
|
||||
<i class="fas fa-arrow-right me-1"></i> {% trans "Update" %}
|
||||
</button>
|
||||
</form>
|
||||
|
||||
<div class="vr" style="height: 28px;"></div> <form hx-boost="true" hx-include="#candidate-form" action="{% url 'schedule_interviews' job.slug %}" method="get" class="action-group">
|
||||
<button type="submit" class="btn btn-main-action btn-sm">
|
||||
<i class="fas fa-calendar-plus me-1"></i> {% trans "Schedule Interviews" %}
|
||||
</button>
|
||||
</form>
|
||||
|
||||
</div>
|
||||
<!-- Tab Content -->
|
||||
{% endif %}
|
||||
<form id="candidate-form" action="{% url 'candidate_update_status' job.slug %}" method="get">
|
||||
{% csrf_token %}
|
||||
<table class="table candidate-table align-middle">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>
|
||||
{% if candidates %}
|
||||
<div class="form-check">
|
||||
<input
|
||||
type="checkbox" class="form-check-input" id="selectAllCheckbox">
|
||||
</div>
|
||||
{% endif %}
|
||||
</th>
|
||||
<th>{% trans "Name" %}</th>
|
||||
<th>{% trans "Contact" %}</th>
|
||||
<th>{% trans "Topic" %}</th>
|
||||
<th>{% trans "Duration" %}</th>
|
||||
<th>{% trans "Interview Date" %}</th>
|
||||
<th>{% trans "Interview Link" %}</th>
|
||||
<th>{% trans "Actions" %}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for candidate in candidates %}
|
||||
<tr>
|
||||
<td>
|
||||
<div class="form-check">
|
||||
<input name="candidate_ids" value="{{ candidate.id }}" type="checkbox" class="form-check-input rowCheckbox" id="candidate-{{ candidate.id }}">
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<div class="candidate-name">
|
||||
{{ candidate.name }}
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<div class="candidate-details">
|
||||
<i class="fas fa-envelope me-1"></i> {{ candidate.email }}<br>
|
||||
<i class="fas fa-phone me-1"></i> {{ candidate.phone }}
|
||||
</div>
|
||||
</td>
|
||||
<td class="candidate-details text-muted">{{ candidate.get_latest_meeting.topic }}</td>
|
||||
<td class="candidate-details text-muted"><div class="d-block"><i class="fas fa-clock me-1"></i> {{ candidate.get_latest_meeting.duration }} min</div></td>
|
||||
<td class="candidate-details text-muted">
|
||||
{% with latest_meeting=candidate.get_latest_meeting %}
|
||||
{% if latest_meeting %}
|
||||
{{ latest_meeting.start_time|date:"M d, Y h:i A" }}
|
||||
{% else %}
|
||||
<span class="text-muted">N/A</span>
|
||||
{% endif %}
|
||||
{% endwith %}
|
||||
</td>
|
||||
<td>
|
||||
{% with latest_meeting=candidate.get_latest_meeting %}
|
||||
{% if latest_meeting and latest_meeting.join_url %}
|
||||
<a href="{{ latest_meeting.join_url }}" target="_blank" class="text-primary-theme" title="Join Interview">
|
||||
<i class="fas fa-link"></i>
|
||||
</a>
|
||||
{% else %}
|
||||
<span class="text-muted">--</span>
|
||||
{% endif %}
|
||||
{% endwith %}
|
||||
</td>
|
||||
<td>
|
||||
<button type="button" class="btn btn-outline-secondary btn-sm"
|
||||
data-bs-toggle="modal"
|
||||
data-bs-target="#candidateviewModal"
|
||||
hx-get="{% url 'candidate_criteria_view_htmx' candidate.pk %}"
|
||||
hx-target="#candidateviewModalBody"
|
||||
title="View Profile">
|
||||
<i class="fas fa-eye"></i>
|
||||
</button>
|
||||
{% if candidate.get_latest_meeting %}
|
||||
<button type="button" class="btn btn-outline-secondary btn-sm"
|
||||
data-bs-toggle="modal"
|
||||
data-bs-target="#candidateviewModal"
|
||||
hx-get="{% url 'reschedule_meeting_for_candidate' job.slug candidate.pk candidate.get_latest_meeting.pk %}"
|
||||
hx-target="#candidateviewModalBody"
|
||||
title="Reschedule">
|
||||
<i class="fas fa-redo-alt"></i>
|
||||
</button>
|
||||
<button type="button" class="btn btn-outline-danger btn-sm"
|
||||
data-bs-toggle="modal"
|
||||
data-bs-target="#candidateviewModal"
|
||||
hx-get="{% url 'delete_meeting_for_candidate' job.slug candidate.pk candidate.get_latest_meeting.pk %}"
|
||||
hx-target="#candidateviewModalBody"
|
||||
title="Delete Meeting">
|
||||
<i class="fas fa-trash"></i>
|
||||
</button>
|
||||
|
||||
{% else %}
|
||||
<button type="button" class="btn btn-main-action btn-sm"
|
||||
data-bs-toggle="modal"
|
||||
data-bs-target="#candidateviewModal"
|
||||
hx-get="{% url 'schedule_meeting_for_candidate' job.slug candidate.pk %}"
|
||||
hx-target="#candidateviewModalBody"
|
||||
data-modal-title="{% trans 'Schedule Interview' %}"
|
||||
title="Schedule Interview">
|
||||
<i class="fas fa-calendar-plus"></i>
|
||||
</button>
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{% if not candidates %}
|
||||
<div class="alert alert-info text-center mt-3 mb-0" role="alert">
|
||||
<i class="fas fa-info-circle me-1"></i>
|
||||
{% trans "No candidates are currently in the Interview stage for this job." %}
|
||||
</div>
|
||||
{% endif %}
|
||||
</form>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="modal fade modal-xl" id="candidateviewModal" tabindex="-1" aria-labelledby="candidateviewModalLabel" aria-hidden="true">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title" id="candidateviewModalLabel">Form Settings</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
</div>
|
||||
<div id="candidateviewModalBody" class="modal-body">
|
||||
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Main Meeting Schedule/Reschedule Modal -->
|
||||
<div class="modal fade" id="meetingModal" tabindex="-1" aria-labelledby="meetingModalLabel" aria-hidden="true">
|
||||
<div class="modal-dialog modal-dialog-centered">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title" id="meetingModalLabel">
|
||||
{% trans "Schedule Interview" %} <!-- Default title -->
|
||||
<div class="modal-content kaauh-card"> <div class="modal-header" style="border-bottom: 1px solid var(--kaauh-border);">
|
||||
<h5 class="modal-title" id="candidateviewModalLabel" style="color: var(--kaauh-teal-dark);">
|
||||
{% trans "Candidate Details / Bulk Action Form" %}
|
||||
</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
</div>
|
||||
<div id="meetingModalBody" class="modal-body">
|
||||
<!-- HTMX will load the form here -->
|
||||
<p class="text-center text-muted">{% trans "Loading form..." %}</p>
|
||||
<div id="candidateviewModalBody" class="modal-body">
|
||||
<div class="text-center py-5 text-muted">
|
||||
<i class="fas fa-spinner fa-spin fa-2x"></i><br>
|
||||
{% trans "Loading content..." %}
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<!-- HTMX might update the button text or add specific actions here if needed -->
|
||||
<!-- For now, the form itself has its own submit and cancel buttons -->
|
||||
<!-- The cancel button inside the form will close the modal -->
|
||||
<div class="modal-footer" style="border-top: 1px solid var(--kaauh-border);">
|
||||
<button type="button" class="btn btn-outline-secondary" data-bs-dismiss="modal">
|
||||
{% trans "Cancel" %}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
|
||||
{% block customJS %}
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
const meetingModalElement = document.getElementById('meetingModal');
|
||||
const meetingModalTitle = meetingModalElement.querySelector('#meetingModalLabel');
|
||||
// The submit button is now inside the form snippet loaded by HTMX.
|
||||
// We will pass title info via data-* attributes or rely on the snippet's initial rendering.
|
||||
document.addEventListener('DOMContentLoaded', function () {
|
||||
const selectAllCheckbox = document.getElementById('selectAllCheckbox');
|
||||
const rowCheckboxes = document.querySelectorAll('.rowCheckbox');
|
||||
|
||||
// Add event listeners to buttons that trigger this modal
|
||||
// Using event delegation for dynamically added buttons
|
||||
document.addEventListener('click', function(event) {
|
||||
const button = event.target.closest('button[data-bs-toggle="modal"][data-bs-target="#meetingModal"]');
|
||||
if (button) {
|
||||
event.preventDefault(); // Prevent default if button has an href or type="submit"
|
||||
if (selectAllCheckbox) {
|
||||
|
||||
const modalTitle = button.getAttribute('data-modal-title');
|
||||
const modalSubmitText = button.getAttribute('data-modal-submit-text');
|
||||
// Function to safely update the header checkbox state
|
||||
function updateSelectAllState() {
|
||||
const checkedCount = Array.from(rowCheckboxes).filter(cb => cb.checked).length;
|
||||
const totalCount = rowCheckboxes.length;
|
||||
|
||||
if (meetingModalTitle) {
|
||||
meetingModalTitle.textContent = modalTitle || "{% trans 'Schedule Interview' %}";
|
||||
if (checkedCount === 0) {
|
||||
selectAllCheckbox.checked = false;
|
||||
selectAllCheckbox.indeterminate = false;
|
||||
} else if (checkedCount === totalCount) {
|
||||
selectAllCheckbox.checked = true;
|
||||
selectAllCheckbox.indeterminate = false;
|
||||
} else {
|
||||
// Set to indeterminate state (partially checked)
|
||||
selectAllCheckbox.checked = false;
|
||||
selectAllCheckbox.indeterminate = true;
|
||||
}
|
||||
}
|
||||
// The submit button text is now handled by the meeting_form.html snippet itself,
|
||||
// based on whether 'scheduled_interview' context is passed.
|
||||
|
||||
// Show the modal first, then HTMX will load the content into #meetingModalBody
|
||||
const modal = new bootstrap.Modal(meetingModalElement);
|
||||
modal.show();
|
||||
// 1. Logic for the 'Select All' checkbox (Clicking it updates all rows)
|
||||
selectAllCheckbox.addEventListener('change', function () {
|
||||
const isChecked = selectAllCheckbox.checked;
|
||||
|
||||
// HTMX attributes (hx-get, hx-target, hx-swap) on the button will trigger the fetch
|
||||
// after the modal is shown. HTMX handles this automatically.
|
||||
// Temporarily disable the change listener on rows to prevent cascading events
|
||||
rowCheckboxes.forEach(checkbox => checkbox.removeEventListener('change', updateSelectAllState));
|
||||
|
||||
// Update all row checkboxes
|
||||
rowCheckboxes.forEach(function (checkbox) {
|
||||
checkbox.checked = isChecked;
|
||||
|
||||
// Dispatch event for the framework (data-bind-selections)
|
||||
checkbox.dispatchEvent(new Event('change', { bubbles: true }));
|
||||
});
|
||||
|
||||
// Re-attach the change listeners to the rows
|
||||
rowCheckboxes.forEach(checkbox => checkbox.addEventListener('change', updateSelectAllState));
|
||||
|
||||
// Ensure the header state is correct after forcing all changes
|
||||
updateSelectAllState();
|
||||
});
|
||||
|
||||
// 2. Logic to update 'Select All' state based on row checkboxes
|
||||
// Attach the function to be called whenever a row checkbox changes
|
||||
rowCheckboxes.forEach(function (checkbox) {
|
||||
checkbox.addEventListener('change', updateSelectAllState);
|
||||
});
|
||||
|
||||
// Initial check to set the correct state on load (in case items are pre-checked)
|
||||
updateSelectAllState();
|
||||
}
|
||||
});
|
||||
|
||||
// Optional: Clear HTMX target content if modal is hidden without submission
|
||||
meetingModalElement.addEventListener('hidden.bs.modal', function () {
|
||||
const modalBody = meetingModalElement.querySelector('#meetingModalBody');
|
||||
if (modalBody) {
|
||||
// Reset to a loading message or clear, so next open fetches fresh
|
||||
modalBody.innerHTML = '<p class="text-center text-muted">{% trans "Loading form..." %}</p>';
|
||||
}
|
||||
});
|
||||
});
|
||||
// Handle Meeting Modal Opening (Rescheduling/Scheduling)
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
// Renamed to candidateviewModal as that's the ID in the template
|
||||
const candidateviewModalElement = document.getElementById('candidateviewModal');
|
||||
const candidateviewModalLabel = candidateviewModalElement.querySelector('#candidateviewModalLabel');
|
||||
|
||||
// The old JS functions (loadScheduleMeetingForm, loadRescheduleMeetingForm) are no longer needed
|
||||
// as HTMX handles fetching the form. They can be removed if not used elsewhere.
|
||||
// Event listener for dynamic buttons using delegation
|
||||
document.addEventListener('click', function(event) {
|
||||
// Targetting buttons with hx-get that also targets the modal body
|
||||
const button = event.target.closest('button[data-bs-toggle="modal"][data-bs-target="#candidateviewModal"]');
|
||||
if (button) {
|
||||
// Do NOT preventDefault here as hx-get needs to fire.
|
||||
// hx-get will load the content into #candidateviewModalBody.
|
||||
|
||||
const modalTitle = button.getAttribute('data-modal-title');
|
||||
|
||||
// Update the modal title if a custom one is provided (used for scheduling)
|
||||
if (modalTitle && candidateviewModalLabel) {
|
||||
candidateviewModalLabel.textContent = modalTitle;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Clear HTMX target content when modal is hidden
|
||||
candidateviewModalElement.addEventListener('hidden.bs.modal', function () {
|
||||
const modalBody = candidateviewModalElement.querySelector('#candidateviewModalBody');
|
||||
if (modalBody) {
|
||||
// Reset to the loading state message, matching the initial content
|
||||
modalBody.innerHTML = `
|
||||
<div class="text-center py-5 text-muted">
|
||||
<i class="fas fa-spinner fa-spin fa-2x"></i><br>
|
||||
{% trans "Loading content..." %}
|
||||
</div>
|
||||
`;
|
||||
// Reset the modal title to its default state for next use
|
||||
const defaultTitle = "{% trans "Candidate Details / Bulk Action Form" %}";
|
||||
candidateviewModalLabel.textContent = defaultTitle;
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
{% endblock %}
|
||||
{% endblock %}
|
||||
@ -165,7 +165,7 @@
|
||||
<form method="GET" class="row g-3 align-items-end" >
|
||||
{% if search_query %}<input type="hidden" name="q" value="{{ search_query }}">{% endif %}
|
||||
|
||||
<div class="col-md-3">
|
||||
<div class="col-md-4">
|
||||
<label for="job_filter" class="form-label small text-muted">{% trans "Filter by Job" %}</label>
|
||||
<select name="job" id="job_filter" class="form-select form-select-sm">
|
||||
<option value="">{% trans "All Jobs" %}</option>
|
||||
@ -175,13 +175,13 @@
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="col-md-5">
|
||||
<div class="col-md-8">
|
||||
<div class="filter-buttons">
|
||||
<button type="submit" class="btn btn-main-action btn-lg">
|
||||
<button type="submit" class="btn btn-main-action">
|
||||
<i class="fas fa-filter me-1"></i> {% trans "Apply Filters" %}
|
||||
</button>
|
||||
{% if job_filter or search_query %}
|
||||
<a href="{% url 'candidate_list' %}" class="btn btn-outline-secondary btn-lg">
|
||||
<a href="{% url 'candidate_list' %}" class="btn btn-outline-secondary">
|
||||
<i class="fas fa-times me-1"></i> {% trans "Clear" %}
|
||||
</a>
|
||||
{% endif %}
|
||||
|
||||
@ -3,184 +3,12 @@
|
||||
|
||||
{% block title %}Candidate Management - {{ job.title }} - University ATS{% endblock %}
|
||||
|
||||
{% block customCSS %}
|
||||
<style>
|
||||
/* KAAT-S UI Variables */
|
||||
:root {
|
||||
--kaauh-teal: #00636e;
|
||||
--kaauh-teal-dark: #004a53;
|
||||
--kaauh-border: #eaeff3;
|
||||
--kaauh-primary-text: #343a40;
|
||||
--kaauh-success: #28a745;
|
||||
--kaauh-info: #17a2b8;
|
||||
--kaauh-danger: #dc3545;
|
||||
--kaauh-warning: #ffc107;
|
||||
}
|
||||
|
||||
/* Primary Color Overrides */
|
||||
.text-primary-theme { color: var(--kaauh-teal) !important; }
|
||||
.bg-primary-theme { background-color: var(--kaauh-teal) !important; }
|
||||
|
||||
/* 1. Main Container & Card Styling */
|
||||
.kaauh-card {
|
||||
border: 1px solid var(--kaauh-border);
|
||||
border-radius: 0.75rem;
|
||||
box-shadow: 0 4px 12px rgba(0,0,0,0.06);
|
||||
background-color: white;
|
||||
}
|
||||
|
||||
/* Dedicated style for the filter block */
|
||||
.filter-controls {
|
||||
background-color: #f8f9fa;
|
||||
border-radius: 0.75rem;
|
||||
padding: 1.5rem;
|
||||
margin-bottom: 2rem;
|
||||
border: 1px solid var(--kaauh-border);
|
||||
}
|
||||
|
||||
/* 2. Button Styling (Themed for Main Actions) */
|
||||
.btn-main-action {
|
||||
background-color: var(--kaauh-teal);
|
||||
border-color: var(--kaauh-teal);
|
||||
color: white;
|
||||
font-weight: 600;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
.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);
|
||||
}
|
||||
.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);
|
||||
}
|
||||
/* Style for the Bulk Move button */
|
||||
.btn-bulk-action {
|
||||
background-color: var(--kaauh-teal-dark);
|
||||
border-color: var(--kaauh-teal-dark);
|
||||
color: white;
|
||||
font-weight: 500;
|
||||
}
|
||||
.btn-bulk-action:hover {
|
||||
background-color: #00363e;
|
||||
border-color: #00363e;
|
||||
}
|
||||
|
||||
/* 3. Candidate Table Styling (Aligned with KAAT-S) */
|
||||
.candidate-table {
|
||||
table-layout: fixed;
|
||||
width: 100%;
|
||||
border-collapse: separate;
|
||||
border-spacing: 0;
|
||||
background-color: white;
|
||||
border-radius: 0.5rem;
|
||||
overflow: hidden;
|
||||
}
|
||||
.candidate-table thead {
|
||||
background-color: var(--kaauh-border);
|
||||
}
|
||||
.candidate-table th {
|
||||
padding: 0.75rem 1rem;
|
||||
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 1rem;
|
||||
border-bottom: 1px solid var(--kaauh-border);
|
||||
vertical-align: middle;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
.candidate-table tbody tr:hover {
|
||||
background-color: #f1f3f4;
|
||||
}
|
||||
.candidate-table thead th:nth-child(1) { width: 40px; }
|
||||
.candidate-table thead th:nth-child(4) { width: 10%; }
|
||||
.candidate-table thead th:nth-child(7) { width: 100px; }
|
||||
|
||||
.candidate-name {
|
||||
font-weight: 600;
|
||||
color: var(--kaauh-primary-text);
|
||||
}
|
||||
.candidate-details {
|
||||
font-size: 0.8rem;
|
||||
color: #6c757d;
|
||||
}
|
||||
|
||||
/* 4. Badges and Statuses */
|
||||
.ai-score-badge {
|
||||
background-color: var(--kaauh-teal-dark) !important;
|
||||
color: white;
|
||||
font-weight: 700;
|
||||
padding: 0.4em 0.8em;
|
||||
border-radius: 0.4rem;
|
||||
}
|
||||
.status-badge {
|
||||
font-size: 0.75rem;
|
||||
padding: 0.3em 0.7em;
|
||||
border-radius: 0.35rem;
|
||||
font-weight: 700;
|
||||
}
|
||||
.bg-applicant { background-color: #6c757d !important; color: white; }
|
||||
.bg-candidate { background-color: var(--kaauh-success) !important; color: white; }
|
||||
|
||||
/* Stage Badges */
|
||||
.stage-badge {
|
||||
font-size: 0.75rem;
|
||||
padding: 0.25rem 0.6rem;
|
||||
border-radius: 0.3rem;
|
||||
font-weight: 600;
|
||||
display: inline-block;
|
||||
margin-bottom: 0.2rem;
|
||||
}
|
||||
.stage-Applied { background-color: #e9ecef; color: #495057; }
|
||||
.stage-Screening { background-color: var(--kaauh-info); color: white; }
|
||||
.stage-Exam { background-color: var(--kaauh-warning); color: #856404; }
|
||||
.stage-Interview { background-color: #17a2b8; color: white; }
|
||||
.stage-Offer { background-color: var(--kaauh-success); color: white; }
|
||||
|
||||
/* Timeline specific container */
|
||||
.applicant-tracking-timeline {
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
/* --- CUSTOM HEIGHT OPTIMIZATION (MAKING INPUTS/BUTTONS SMALLER) --- */
|
||||
.form-control-sm,
|
||||
.btn-sm {
|
||||
/* Reduce vertical padding even more than default Bootstrap 'sm' */
|
||||
padding-top: 0.2rem !important;
|
||||
padding-bottom: 0.2rem !important;
|
||||
/* Ensure a consistent, small height for both */
|
||||
height: 28px !important;
|
||||
font-size: 0.8rem !important; /* Slightly smaller font */
|
||||
}
|
||||
|
||||
.cd_screening{
|
||||
color: #00636e;
|
||||
|
||||
}
|
||||
|
||||
</style>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
<div class="container-fluid py-4">
|
||||
<div class="applicant-tracking-timeline">
|
||||
{% include 'jobs/partials/applicant_tracking.html' %}
|
||||
</div>
|
||||
|
||||
<div class="d-flex justify-content-between align-items-center mb-4">
|
||||
<div>
|
||||
<h1 class="h3 mb-1" style="color: var(--kaauh-teal-dark); font-weight: 700;">
|
||||
<h1 class="h3 mb-1 page-header">
|
||||
<i class="fas fa-layer-group me-2"></i>
|
||||
{% trans "Applicant Screening" %}
|
||||
</h1>
|
||||
@ -194,40 +22,42 @@
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="filter-controls shadow-sm">
|
||||
<h4 class="h6 mb-3 fw-bold" style="color: var(--kaauh-primary-text);">
|
||||
<div class="applicant-tracking-timeline mb-4">
|
||||
{% include 'jobs/partials/applicant_tracking.html' %}
|
||||
</div>
|
||||
|
||||
<div class="filter-controls">
|
||||
<h4 class="h6 mb-3 fw-bold">
|
||||
<i class="fas fa-sort-numeric-up me-1"></i> {% trans "AI Scoring & Top Candidate Filter" %}
|
||||
</h4>
|
||||
|
||||
<form method="GET" class="mb-0 pb-3">
|
||||
{% csrf_token %}
|
||||
<div class="d-flex flex-nowrap g-3 align-items-end" style="overflow-x: auto;">
|
||||
<form method="GET" class="mb-0">
|
||||
<div class="row g-3 align-items-end">
|
||||
|
||||
<div class="p-2">
|
||||
<label for="min_ai_score" class="form-label small text-muted mb-1">
|
||||
{% trans "Min AI Score" %}
|
||||
</label>
|
||||
<input type="number" name="min_ai_score" id="min_ai_score" class="form-control form-control-sm"
|
||||
value="{{ min_ai_score}}" min="0" max="100" step="1"
|
||||
placeholder="e.g., 75" style="min-width: 120px;">
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<label for="min_ai_score" class="form-label small text-muted mb-1">
|
||||
{% trans "Min AI Score" %}
|
||||
</label>
|
||||
<input type="number" name="min_ai_score" id="min_ai_score" class="form-control form-control-sm"
|
||||
value="{{ min_ai_score}}" min="0" max="100" step="1"
|
||||
placeholder="e.g., 75" style="width: 120px;">
|
||||
</div>
|
||||
|
||||
<div class="p-2">
|
||||
<label for="tier1_count" class="form-label small text-muted mb-1">
|
||||
{% trans "Top N" %}
|
||||
</label>
|
||||
<input type="number" name="tier1_count" id="tier1_count" class="form-control form-control-sm"
|
||||
value="{{ tier1_count }}" min="1" max="{{ total_candidates }}" style="min-width: 100px;">
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<label for="tier1_count" class="form-label small text-muted mb-1">
|
||||
{% trans "Top N Candidates" %}
|
||||
</label>
|
||||
<input type="number" name="tier1_count" id="tier1_count" class="form-control form-control-sm"
|
||||
value="{{ tier1_count }}" min="1" max="{{ total_candidates }}" style="width: 120px;">
|
||||
</div>
|
||||
|
||||
<div class="p-2">
|
||||
<label class="form-label small text-muted mb-1 d-block"> </label>
|
||||
<button type="submit" name="update_tiers" class="btn btn-main-action btn-sm w-100" style="min-width: 150px;">
|
||||
<i class="fas fa-sync-alt me-1"></i> {% trans "Update Filters" %}
|
||||
</button>
|
||||
<div class="col-auto">
|
||||
<button type="submit" name="update_tiers" class="btn btn-main-action btn-sm">
|
||||
<i class="fas fa-sync-alt me-1"></i> {% trans "Update Filters" %}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<h2 class="h4 mb-3" style="color: var(--kaauh-primary-text);">
|
||||
@ -235,20 +65,22 @@
|
||||
<span class="badge bg-primary-theme ms-2">{{ candidates|length }} / {{ total_candidates }} Total</span>
|
||||
</h2>
|
||||
|
||||
<div class="kaauh-card shadow-sm p-3">
|
||||
<div class="kaauh-card p-3">
|
||||
{% if candidates %}
|
||||
<form hx-boost="true" hx-include="#candidate-form" action="{% url 'candidate_update_status' job.slug %}" method="post">
|
||||
<div class="d-flex align-items-center">
|
||||
<select name="mark_as" id="update_status" class="form-select form-select-sm" style="height: 3rem;">
|
||||
<option value="Exam">
|
||||
<i class="fas fa-arrow-right me-1"></i> {% trans "Exam" %}
|
||||
</option>
|
||||
<div class="bulk-action-bar">
|
||||
<form hx-boost="true" hx-include="#candidate-form" action="{% url 'candidate_update_status' job.slug %}" method="post" class="action-group">
|
||||
{% csrf_token %}
|
||||
<label for="update_status" class="form-label small mb-0 fw-bold">{% trans "Move Selected To:" %}</label>
|
||||
<select name="mark_as" id="update_status" class="form-select form-select-sm" style="width: auto;">
|
||||
<option value="Exam">
|
||||
{% trans "Exam Stage" %}
|
||||
</option>
|
||||
</select>
|
||||
<button type="submit" class="btn btn-main-action btn-mds ms-2">
|
||||
<i class="fas fa-arrow-right me-1"></i> {% trans "Update" %}
|
||||
</button>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-main-action btn-sm">
|
||||
<i class="fas fa-arrow-right me-1"></i> {% trans "Update Status" %}
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<div class="table-responsive">
|
||||
@ -375,7 +207,6 @@
|
||||
const rowCheckboxes = document.querySelectorAll('.rowCheckbox');
|
||||
|
||||
if (selectAllCheckbox) {
|
||||
|
||||
// Function to safely update the header checkbox state
|
||||
function updateSelectAllState() {
|
||||
const checkedCount = Array.from(rowCheckboxes).filter(cb => cb.checked).length;
|
||||
@ -388,40 +219,27 @@
|
||||
selectAllCheckbox.checked = true;
|
||||
selectAllCheckbox.indeterminate = false;
|
||||
} else {
|
||||
// Set to indeterminate state (partially checked)
|
||||
selectAllCheckbox.checked = false;
|
||||
selectAllCheckbox.indeterminate = true;
|
||||
}
|
||||
|
||||
// IMPORTANT: We do NOT fire a change event here to prevent the infinite loop.
|
||||
// Your existing data-bind-_all logic should handle the bulk action status.
|
||||
}
|
||||
|
||||
// 1. Logic for the 'Select All' checkbox (Clicking it updates all rows)
|
||||
selectAllCheckbox.addEventListener('change', function () {
|
||||
const isChecked = selectAllCheckbox.checked;
|
||||
|
||||
// Temporarily disable the change listener on rows to prevent cascading events
|
||||
rowCheckboxes.forEach(checkbox => checkbox.removeEventListener('change', updateSelectAllState));
|
||||
|
||||
// Update all row checkboxes
|
||||
rowCheckboxes.forEach(function (checkbox) {
|
||||
checkbox.checked = isChecked;
|
||||
|
||||
// You must still dispatch the event here so your framework's data-bind-selections
|
||||
// picks up the change on individual elements. This should NOT trigger the updateSelectAllState.
|
||||
checkbox.dispatchEvent(new Event('change', { bubbles: true }));
|
||||
});
|
||||
|
||||
// Re-attach the change listeners to the rows
|
||||
rowCheckboxes.forEach(checkbox => checkbox.addEventListener('change', updateSelectAllState));
|
||||
|
||||
// Ensure the header state is correct after forcing all changes
|
||||
updateSelectAllState();
|
||||
});
|
||||
|
||||
// 2. Logic to update 'Select All' state based on row checkboxes
|
||||
// Attach the function to be called whenever a row checkbox changes
|
||||
rowCheckboxes.forEach(function (checkbox) {
|
||||
checkbox.addEventListener('change', updateSelectAllState);
|
||||
});
|
||||
|
||||
@ -104,7 +104,7 @@
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="container" style="max-width: 900px;">
|
||||
<div class="container mt-4" style="max-width: 900px;">
|
||||
|
||||
<div class="profile-header d-flex align-items-center justify-content-between">
|
||||
<div>
|
||||
@ -122,7 +122,7 @@
|
||||
<div class="card p-5">
|
||||
<h5 class="fw-bold mb-4 text-accent">{% trans "Personal Information" %}</h5>
|
||||
|
||||
<form method="post" action="#">
|
||||
<form method="post" action="{% url 'user_detail' user.pk %}">
|
||||
{% csrf_token %}
|
||||
|
||||
<div class="row g-4"> <div class="col-md-6">
|
||||
@ -139,7 +139,7 @@
|
||||
<label for="id_email" class="form-label">{% trans "Email Address" %}</label>
|
||||
<input type="email" class="form-control" id="id_email" value="{{ user.email }}" disabled>
|
||||
<div class="form-text mt-2">
|
||||
<a href="#" class="small text-accent">{% trans "Manage email addresses" %}</a>
|
||||
<a href="{% url 'account_email' %}" class="small text-accent">{% trans "Manage email addresses" %}</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -160,9 +160,9 @@
|
||||
<a href="{% url 'account_change_password' %}" class="btn btn-outline-danger w-100 rounded-pill py-2">
|
||||
<i class="fas fa-lock me-2"></i> {% trans "Change Password" %}
|
||||
</a>
|
||||
{% comment %} <a href="#" class="btn btn-outline-secondary w-100 rounded-pill py-2">
|
||||
<i class="fas fa-shield-alt me-2"></i> {% trans "Two-Factor Auth" %}
|
||||
</a> {% endcomment %}
|
||||
<button type="button" class="btn btn-outline-secondary" data-bs-toggle="modal" data-bs-target="#myModalForm">
|
||||
<i class="fas fa-image me-1"></i> {% trans "Change Profile Image" %}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -192,4 +192,32 @@
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<!--modal for image upload-->
|
||||
<div class="modal fade mt-4" id="myModalForm" tabindex="-1" aria-labelledby="myModalLabel" aria-hidden="true">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title" id="myModalLabel">Upload Profile image</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<form method="post" action="{% url 'user_profile_image_update' user.pk %}" enctype="multipart/form-data" >
|
||||
{% csrf_token %}
|
||||
{{ profile_form.as_p}}
|
||||
|
||||
{% if image_upload_form.instance.post_image %}
|
||||
<p>Current Image:</p>
|
||||
<img src="{{ image_upload_form.instance.post_image.url }}" alt="Post Image" style="max-width: 200px;">
|
||||
{% endif %}
|
||||
<div class="modal-footer mt-2">
|
||||
<button type="button" class="btn btn-lg btn-secondary" data-bs-dismiss="modal">Close</button>
|
||||
<button type="submit" class="btn btn-primary">Save changes</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
Loading…
x
Reference in New Issue
Block a user