From f1499f7be0419f44a355d2599f45850b55cd1a86 Mon Sep 17 00:00:00 2001 From: Faheed Date: Tue, 18 Nov 2025 13:13:16 +0300 Subject: [PATCH] littele ui fix --- recruitment/__pycache__/forms.cpython-312.pyc | Bin 84205 -> 84207 bytes .../__pycache__/models.cpython-312.pyc | Bin 107404 -> 107404 bytes recruitment/__pycache__/urls.cpython-312.pyc | Bin 23134 -> 22793 bytes recruitment/__pycache__/views.cpython-312.pyc | Bin 195534 -> 193529 bytes recruitment/forms.py | 2 +- recruitment/models.py | 2 +- recruitment/urls.py | 43 +-- recruitment/views.py | 291 ++++------------- templates/applicant/career.html | 20 +- templates/recruitment/agency_detail.html | 301 ++++++++++++------ .../recruitment/candidate_interview_view.html | 4 +- 11 files changed, 299 insertions(+), 364 deletions(-) diff --git a/recruitment/__pycache__/forms.cpython-312.pyc b/recruitment/__pycache__/forms.cpython-312.pyc index 8e6cf04afb98379d8fd9937a98070d36091482b4..5cd8f9672e233cc441d1a80c73271b86bbc21678 100644 GIT binary patch delta 83 zcmaDmk@fvVR^HRRyj%=Gkj*Ha`MZ&KD=%X;2d9vKUU6ngs!#spcLw5<_0l+}Z{T2D g%r6cTPEIVz%+K4N$jK;S&nh}!ey05NIuFKt08_&no&W#< delta 92 zcmaDqk@f9FR^HRRyj%=GVE0Zc^H(G9R$j(x4k1DRyyDD~RNvIplFYnxxBQ~q$%<)W k)04OvXHNIxWaQ!#Ls76jnUhh%o|Sbz|4jbr4IYg70BuJdKmY&$ diff --git a/recruitment/__pycache__/models.cpython-312.pyc b/recruitment/__pycache__/models.cpython-312.pyc index e14024eb18ada80bc4f2e1818f1e1def2e1add50..d2c25700baa7b1d8440bb8eeb42cb7475de1f2d2 100644 GIT binary patch delta 104 zcmeA<&en6BjrTM!FBbz4{9%^PG}_2pa$)l1TO!=-DXbvK#E{BAdBFuyMuBM)7$q46 zCoAp|pZxqn6C?lRtc!+>{F~=obY&B?2Wl@8W&#pDAi|Du@~7)xnDr*=ZGL%U@hJd^ Caw0we delta 106 zcmeA<&en6BjrTM!FBbz4T>dVVX}poQewHrW6S?0f`=_$)B!&Vb-6hzxm~j G#iszq3nHTc diff --git a/recruitment/__pycache__/urls.cpython-312.pyc b/recruitment/__pycache__/urls.cpython-312.pyc index 30fcc8db63409eda72dd32b4dfa730a3ac9246ee..543d155672ba2bee9e9f9c9a4b4081e3e946f47a 100644 GIT binary patch delta 4839 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 Q96CClhmsfGCjX`RKaIH~od5s; delta 5362 zcmaKvdrVu`9mjFFz`cMm58GhO(?I+H41`b!5Wwb5Y=e1+@C})MA;fBv00XSgBP$R56 zP8rR3dT9I4wB1YFX)NjQM) zD9SB!4Z34I!_X6MT|*J%FeXRB8ECssMvxhNXn{VJTHWG zg_^DxBC2{3SG5&JqF0i#aJ-kpyZ%7Cz7yW1S5)BbKqnPO^5-k`Px@;S(O-ARdfuRW zD=L)Hv7R?+Tb8Vp#UgdG5!)tWcRa~XnfKkW1fizu-H4FCzy-f|0Qp`7`93CZA3)xY zAn#!E1LR6g8~7kX`z6*sJRr)45#+C)MDDznIweD;$^aTtn$RQVRlLJjRZ8Sjsqj*N z5*8f&T}8I;FJWDwrt7Z}`98(@J`E$$MK#&FzlC?*QD@-;b5%nw5BjzyCD!%89qoBQ zwjuqqh=>od_KWXQOegR+V#rl8ekr!_G6>dKvm-)+AZu&A1ejvVw**(UeCH z7+6X55~6F49-vnd9bJ!BX5e5?g##m7*JYd4hMJvcvd3dI+`QYpHn%XdT6RxJ+Ca(D0R8lrsmGBmesGGU7x8XrlIVfBBV})RCKO(e&-XNJ6+a-?$qQV ze_a~7T$j3ItlK=a^UiU*O!m^wqlSOT8q-D=7w7FzfemLLoO7SQrwZ$AUU5&YxoNjL zth)uOK-Dcr&}U7J=!9ilz0!`)?MeqRoy2qza{|3-IhyMr&2D0Ph;b6rOH3az{peqo zx6se5&#G58iGP8Zi^N3oRP`$}0pUkKsMVV^d%GE|Fd(=6mRkmWc!bH)Z&DwONUh zS~E1Zc*UVzhP{@0ZCsR_c)7`^6`12vv<)@3>hQ

DB^lze?>7sQoRq4_b9vGWNbQ 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 delta 24639 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 diff --git a/recruitment/forms.py b/recruitment/forms.py index 90f5aaa..677bdcb 100644 --- a/recruitment/forms.py +++ b/recruitment/forms.py @@ -1962,7 +1962,7 @@ Hi Team, # } #during bulk schedule -class OnsiteMeetingForm(forms.ModelForm): +class OnsiteLocationForm(forms.ModelForm): class Meta: model = OnsiteLocationDetails # Include 'room_number' and update the field list diff --git a/recruitment/models.py b/recruitment/models.py index 84210f0..f00cb80 100644 --- a/recruitment/models.py +++ b/recruitment/models.py @@ -1241,7 +1241,7 @@ class ScheduledInterview(Base): # Links to the specific, individual location/meeting details for THIS interview interview_location = models.OneToOneField( InterviewLocation, - on_delete=models.SET_NULL, + on_delete=models.CASCADE, related_name="scheduled_interview", null=True, blank=True, diff --git a/recruitment/urls.py b/recruitment/urls.py index 7e9fbf5..0e57def 100644 --- a/recruitment/urls.py +++ b/recruitment/urls.py @@ -108,27 +108,8 @@ urlpatterns = [ name="training_delete", ), # Meeting URLs - path("meetings/", views.ZoomMeetingListView.as_view(), name="list_meetings"), - path( - "meetings/create-meeting/", - views.ZoomMeetingCreateView.as_view(), - name="create_meeting", - ), - path( - "meetings/meeting-details//", - views.ZoomMeetingDetailsView.as_view(), - name="meeting_details", - ), - path( - "meetings/update-meeting//", - views.ZoomMeetingUpdateView.as_view(), - name="update_meeting", - ), - path( - "meetings/delete-meeting//", - views.ZoomMeetingDeleteView, - name="delete_meeting", - ), + # path("meetings/", views.ZoomMeetingListView.as_view(), name="list_meetings"), + # JobPosting functional views URLs (keeping for compatibility) path("api/create/", views.create_job, name="create_job_api"), path("api//edit/", views.edit_job, name="edit_job_api"), @@ -596,6 +577,26 @@ urlpatterns = [ name="confirm_schedule_interviews_view", ), + path( + "meetings/create-meeting/", + views.ZoomMeetingCreateView.as_view(), + name="create_meeting", + ), + # path( + # "meetings/meeting-details//", + # views.ZoomMeetingDetailsView.as_view(), + # name="meeting_details", + # ), + path( + "meetings/update-meeting//", + views.ZoomMeetingUpdateView.as_view(), + name="update_meeting", + ), + path( + "meetings/delete-meeting//", + views.ZoomMeetingDeleteView, + name="delete_meeting", + ), # Candidate Meeting Scheduling/Rescheduling URLs path( "jobs//candidates//schedule-meeting/", diff --git a/recruitment/views.py b/recruitment/views.py index 63289b0..8201e30 100644 --- a/recruitment/views.py +++ b/recruitment/views.py @@ -75,7 +75,7 @@ from .forms import ( PortalLoginForm, MessageForm, PersonForm, - OnsiteMeetingForm, + OnsiteLocationForm, OnsiteReshuduleForm, OnsiteScheduleForm, InterviewEmailForm @@ -250,56 +250,56 @@ class ZoomMeetingCreateView(StaffRequiredMixin, CreateView): return redirect(reverse("create_meeting", kwargs={"slug": instance.slug})) -class ZoomMeetingListView(StaffRequiredMixin, ListView): - model = ZoomMeetingDetails - template_name = "meetings/list_meetings.html" - context_object_name = "meetings" - paginate_by = 10 +# class ZoomMeetingListView(StaffRequiredMixin, ListView): +# model = ZoomMeetingDetails +# template_name = "meetings/list_meetings.html" +# context_object_name = "meetings" +# paginate_by = 10 - def get_queryset(self): - queryset = super().get_queryset().order_by("-start_time") +# def get_queryset(self): +# queryset = super().get_queryset().order_by("-start_time") - # Prefetch related interview data efficiently +# # Prefetch related interview data efficiently - queryset = queryset.prefetch_related( - Prefetch( - "interview", # related_name from ZoomMeeting to ScheduledInterview - queryset=ScheduledInterview.objects.select_related("application", "job"), - to_attr="interview_details", # Changed to not start with underscore - ) - ) +# queryset = queryset.prefetch_related( +# Prefetch( +# "interview", # related_name from ZoomMeeting to ScheduledInterview +# queryset=ScheduledInterview.objects.select_related("application", "job"), +# to_attr="interview_details", # Changed to not start with underscore +# ) +# ) - # Handle search by topic or meeting_id - search_query = self.request.GET.get( - "q", "" - ) # Renamed from 'search' to 'q' for consistency - if search_query: - queryset = queryset.filter( - Q(topic__icontains=search_query) | Q(meeting_id__icontains=search_query) - ) +# # Handle search by topic or meeting_id +# search_query = self.request.GET.get( +# "q", "" +# ) # Renamed from 'search' to 'q' for consistency +# if search_query: +# queryset = queryset.filter( +# Q(topic__icontains=search_query) | Q(meeting_id__icontains=search_query) +# ) - # Handle filter by status - status_filter = self.request.GET.get("status", "") - if status_filter: - queryset = queryset.filter(status=status_filter) +# # Handle filter by status +# status_filter = self.request.GET.get("status", "") +# if status_filter: +# queryset = queryset.filter(status=status_filter) - # Handle search by candidate name - candidate_name = self.request.GET.get("candidate_name", "") - if candidate_name: - # Filter based on the name of the candidate associated with the meeting's interview - queryset = queryset.filter( - Q(interview__application__first_name__icontains=candidate_name) - | Q(interview__application__last_name__icontains=candidate_name) - ) +# # Handle search by candidate name +# candidate_name = self.request.GET.get("candidate_name", "") +# if candidate_name: +# # Filter based on the name of the candidate associated with the meeting's interview +# queryset = queryset.filter( +# Q(interview__application__first_name__icontains=candidate_name) +# | Q(interview__application__last_name__icontains=candidate_name) +# ) - return queryset +# return queryset - def get_context_data(self, **kwargs): - context = super().get_context_data(**kwargs) - context["search_query"] = self.request.GET.get("q", "") - context["status_filter"] = self.request.GET.get("status", "") - context["candidate_name_filter"] = self.request.GET.get("candidate_name", "") - return context +# def get_context_data(self, **kwargs): +# context = super().get_context_data(**kwargs) +# context["search_query"] = self.request.GET.get("q", "") +# context["status_filter"] = self.request.GET.get("status", "") +# context["candidate_name_filter"] = self.request.GET.get("candidate_name", "") +# return context @@ -857,13 +857,15 @@ def kaauh_career(request): if selected_department and selected_department in department_type_keys: active_jobs = active_jobs.filter(department=selected_department) selected_workplace_type = request.GET.get("workplace_type", "") - print(selected_workplace_type) + selected_job_type = request.GET.get("employment_type", "") - job_type_keys = active_jobs.values_list("job_type", flat=True).distinct() - workplace_type_keys = active_jobs.values_list( + job_type_keys = active_jobs.order_by("job_type").distinct("job_type").values_list("job_type", flat=True) + print(job_type_keys) + workplace_type_keys = active_jobs.order_by("workplace_type").distinct("workplace_type").values_list( "workplace_type", flat=True ).distinct() + print(workplace_type_keys) if selected_job_type and selected_job_type in job_type_keys: active_jobs = active_jobs.filter(job_type=selected_job_type) if selected_workplace_type and selected_workplace_type in workplace_type_keys: @@ -1613,7 +1615,7 @@ def _handle_preview_submission(request, slug, job): "interview_duration": interview_duration, "buffer_time": buffer_time, "schedule_interview_type":schedule_interview_type, - "form":OnsiteMeetingForm() + "form":OnsiteLocationForm() }, ) else: @@ -1625,174 +1627,6 @@ def _handle_preview_submission(request, slug, job): ) -# def _handle_confirm_schedule(request, slug, job): -# """ -# Handles the final POST request (Confirm Schedule). -# Creates the main schedule record and queues individual interviews asynchronously. -# """ - -# SESSION_DATA_KEY = "interview_schedule_data" -# SESSION_ID_KEY = f"schedule_candidate_ids_{slug}" - -# # 1. Get schedule data from session -# schedule_data = request.session.get(SESSION_DATA_KEY) - -# if not schedule_data: -# messages.error(request, "Session expired. Please try again.") -# return redirect("schedule_interviews", slug=slug) - -# # 2. Create the Interview Schedule (Parent Record) -# # NOTE: You MUST convert the time strings back to Python time objects here. -# try: -# schedule = InterviewSchedule.objects.create( -# job=job, -# created_by=request.user, -# start_date=datetime.fromisoformat(schedule_data["start_date"]).date(), -# end_date=datetime.fromisoformat(schedule_data["end_date"]).date(), -# working_days=schedule_data["working_days"], -# start_time=time.fromisoformat(schedule_data["start_time"]), -# end_time=time.fromisoformat(schedule_data["end_time"]), -# interview_duration=schedule_data["interview_duration"], -# buffer_time=schedule_data["buffer_time"], -# # Use the simple break times saved in the session -# # If the value is None (because required=False in form), handle it gracefully -# break_start_time=schedule_data.get("break_start_time"), -# break_end_time=schedule_data.get("break_end_time"), -# schedule_interview_type=schedule_data.get("schedule_interview_type") -# ) -# except Exception as e: -# # Handle database creation error -# messages.error(request, f"Error creating schedule: {e}") -# if SESSION_ID_KEY in request.session: -# del request.session[SESSION_ID_KEY] -# return redirect("schedule_interviews", slug=slug) - -# # 3. Setup candidates and get slots -# candidates = Application.objects.filter(id__in=schedule_data["candidate_ids"]) -# print(candidates) -# schedule.applications.set(candidates) -# available_slots = get_available_time_slots( -# schedule -# ) # This should still be synchronous and fast - -# # 4. Queue scheduled interviews asynchronously (FAST RESPONSE) -# if schedule_data.get("schedule_interview_type")=='Remote': -# print('....remote..') -# queued_count = 0 -# for i, candidate in enumerate(candidates): -# if i < len(available_slots): -# slot = available_slots[i] - -# # Dispatch the individual creation task to the background queue -# async_task( -# "recruitment.tasks.create_interview_and_meeting", -# candidate.pk, -# job.pk, -# schedule.pk, -# slot["date"], -# slot["time"], -# schedule.interview_duration, -# ) -# queued_count += 1 - -# messages.success( -# request, -# f"Schedule successfully created. Queued {queued_count} interviews to be booked asynchronously. Check back in a moment!", -# ) - -# # Clear both session data keys upon successful completion -# if SESSION_DATA_KEY in request.session: -# del request.session[SESSION_DATA_KEY] -# if SESSION_ID_KEY in request.session: -# del request.session[SESSION_ID_KEY] - -# return redirect("job_detail", slug=slug) - -# elif schedule_data.get("schedule_interview_type") == 'Onsite': -# # The form submission for Onsite details should happen here. -# # This block assumes the OnsiteMeetingForm is being submitted NOW. - -# # NOTE: start_time and duration must be passed through the form -# # for OnsiteLocationDetails creation. - -# if request.method == 'POST': - -# if available_slots: -# first_slot = available_slots[0] -# # Combine the first slot's date and the schedule's start time -# location_start_dt = datetime.combine(first_slot['date'], schedule.start_time) -# else: -# # Fallback if no slots (should not happen if candidates > 0) -# location_start_dt = datetime.now() - -# # Create a form using the submitted POST data -# form = OnsiteMeetingForm(request.POST) - -# if form.is_valid(): -# # 1. Extract location-specific data from the form -# topic = form.cleaned_data['topic'] -# physical_address = form.cleaned_data['physical_address'] -# room_number = form.cleaned_data['room_number'] - -# # 2. Create the OnsiteLocationDetails instance (The Location Template) -# # The duration comes from the parent InterviewSchedule -# try: -# onsite_location = OnsiteLocationDetails.create( -# start_time=location_start_dt, # Uses datetime derived from first slot date -# duration=schedule.interview_duration, # Uses duration from parent schedule -# physical_address=physical_address, -# room_number=room_number, -# ) -# onsite_location.save() - -# # 3. Create the ScheduledInterview entries, linking the location -# for i, candidate in enumerate(candidates): -# if i < len(available_slots): -# slot = available_slots[i] - -# # Combine date and time from the slot for the ScheduledInterview creation - -# ScheduledInterview.objects.create( -# application=candidate, -# job=job, -# schedule=schedule, -# interview_date=slot['date'], -# interview_time=slot['time'], - -# # CRITICAL: Link the location object -# interview_location=onsite_location, -# # Assuming 'topic' is stored on the ScheduledInterview model -# # topic=topic -# ) - -# messages.success( -# request, -# f"Onsite schedule Interview Create succesfully" -# ) - -# # Clear session data keys upon successful completion -# if SESSION_DATA_KEY in request.session: del request.session[SESSION_DATA_KEY] -# if SESSION_ID_KEY in request.session: del request.session[SESSION_ID_KEY] - -# # Redirect to a confirmation or job details page -# return redirect('job_detail', slug=job.slug) - -# except Exception as e: -# # Handle database creation error -# messages.error(request, f"Error creating onsite location/interviews: {e}") -# # Keep the form data for re-submission if possible, or redirect -# return render(request, 'interviews/onsite_location_form.html', {'form': form, 'schedule': schedule}) - -# else: -# # Form is invalid, re-render with errors -# return render(request, 'interviews/onsite_location_form.html', {'form': form, 'schedule': schedule,'job':job}) - -# else: -# # For a GET request (First time after InterviewSchedule is created) -# # Render the form to collect location details -# form = OnsiteMeetingForm() -# print(f"job:{job}") -# return render(request,'interviews/onsite_location_form.html',{'form': form, 'schedule': schedule,'job':job}) def _handle_confirm_schedule(request, slug, job): """ @@ -1871,7 +1705,7 @@ def _handle_confirm_schedule(request, slug, job): print("inside...") if request.method == 'POST': - form = OnsiteMeetingForm(request.POST) + form = OnsiteLocationForm(request.POST) if form.is_valid(): @@ -1882,6 +1716,8 @@ def _handle_confirm_schedule(request, slug, job): # Extract common location data from the form physical_address = form.cleaned_data['physical_address'] room_number = form.cleaned_data['room_number'] + topic=form.cleaned_data['topic'] + try: # 1. Iterate over candidates and create a NEW Location object for EACH @@ -1898,7 +1734,8 @@ def _handle_confirm_schedule(request, slug, job): duration=schedule.interview_duration, physical_address=physical_address, room_number=room_number, - location_type="Onsite" + location_type="Onsite", + topic=topic ) @@ -1935,7 +1772,7 @@ def _handle_confirm_schedule(request, slug, job): else: # For a GET request - form = OnsiteMeetingForm() + form = OnsiteLocationForm() return render(request, 'interviews/onsite_location_form.html', {'form': form, 'schedule': schedule, 'job': job}) @@ -3495,7 +3332,7 @@ def agency_detail(request, slug): candidates = Application.objects.filter(hiring_agency=agency).order_by( "-created_at" ) - + # Statistics total_candidates = candidates.count() active_candidates = candidates.filter( @@ -6085,18 +5922,4 @@ def schedule_onsite_meeting_for_candidate(request, slug, candidate_pk): } return render(request, "meetings/schedule_onsite_meeting_form.html", context) -# def meeting_list_view(request): -# queryset = ScheduledInterview.filter(interview_location__isnull=False).select_related( -# 'interview_location', -# 'job', -# 'application__person', -# 'application', -# ).prefetch_related( -# 'interview_location__zoommeetingdetails', -# 'interview_location__onsitelocationdetails', -# ) -# print(queryset) -# return render(request,) -# ========================================================================= -# 2. Simple Meeting Creation Views (Placeholders) -# ========================================================================= + diff --git a/templates/applicant/career.html b/templates/applicant/career.html index c18ab7f..7d103f8 100644 --- a/templates/applicant/career.html +++ b/templates/applicant/career.html @@ -143,12 +143,12 @@ {% trans "Type" %}: {# Map the key back to its human-readable translation #} - {% if selected_job_type == 'FULL_TIME' %}{% trans "Full-time" %} - {% elif selected_job_type == 'PART_TIME' %}{% trans "Part-time" %} - {% elif selected_job_type == 'CONTRACT' %}{% trans "Contract" %} - {% elif selected_job_type == 'INTERNSHIP' %}{% trans "Internship" %} - {% elif selected_job_type == 'FACULTY' %}{% trans "Faculty" %} - {% elif selected_job_type == 'TEMPORARY' %}{% trans "Temporary" %} + {% if selected_job_type == 'Full-time' %}{% trans "Full-time" %} + {% elif selected_job_type == 'Part-time' %}{% trans "Part-time" %} + {% elif selected_job_type == 'Contract' %}{% trans "Contract" %} + {% elif selected_job_type == 'Internship' %}{% trans "Internship" %} + {% elif selected_job_type == 'Faculty' %}{% trans "Faculty" %} + {% elif selected_job_type == 'Temporary' %}{% trans "Temporary" %} {% endif %} {# Link to clear this specific filter: use current URL but remove `employment_type` parameter #} @@ -159,15 +159,15 @@ {% endif %} - {# --- Active Workplace Type Filter Chip --- #} + {# --- Active Workplace Type Filter Chip --- #} {% if selected_workplace_type %} {% trans "Workplace" %}: {# Map the key back to its human-readable translation #} - {% if selected_workplace_type == 'ON_SITE' %}{% trans "On-site" %} - {% elif selected_workplace_type == 'REMOTE' %}{% trans "Remote" %} - {% elif selected_workplace_type == 'HYBRID' %}{% trans "Hybrid" %} + {% if selected_workplace_type == 'On-site' %}{% trans "On-site" %} + {% elif selected_workplace_type == 'Remote' %}{% trans "Remote" %} + {% elif selected_workplace_type == 'Hybrid' %}{% trans "Hybrid" %} {% endif %} {# Link to clear this specific filter: use current URL but remove `workplace_type` parameter #} diff --git a/templates/recruitment/agency_detail.html b/templates/recruitment/agency_detail.html index 83d5f3d..94882fa 100644 --- a/templates/recruitment/agency_detail.html +++ b/templates/recruitment/agency_detail.html @@ -175,6 +175,38 @@ color: #6c757d; } + /* Job List - Consistent with Candidate List */ + .job-item { + background-color: white; + border: 1px solid var(--kaauh-border); + border-radius: 0.5rem; + padding: 1rem; + margin-bottom: 0.75rem; + transition: all 0.2s ease; + } + .job-item:hover { + background-color: #f8f9fa; + border-color: var(--kaauh-teal); + } + .job-title { + font-weight: 600; + color: var(--kaauh-primary-text); + margin-bottom: 0.25rem; + } + .job-details { + font-size: 0.875rem; + color: #6c757d; + } + .job-status-badge { + font-size: 0.75rem; + padding: 0.25rem 0.6rem; + border-radius: 0.3rem; + font-weight: 600; + display: inline-block; + background-color: #e9ecef; + color: #495057; + } + /* Stage Badge */ .stage-badge { font-size: 0.75rem; @@ -200,7 +232,7 @@ } .empty-state i { - font-size: 3rem; + font-size: 1.5rem; margin-bottom: 1rem; opacity: 0.5; } @@ -235,12 +267,57 @@ .password-value:hover { background-color: #f8f9fa; } + + /* --- TAB OVERRIDES FOR TEAL THEME CONSISTENCY AND VISIBILITY --- */ + + /* Ensure card-header-tabs sit correctly, use kaauh-border */ + .card-header-tabs { + border-bottom: 1px solid var(--kaauh-border); /* Consistent thin bottom border for the entire row */ + } + + /* Default tab link styling */ + .nav-tabs .nav-link { + color: var(--kaauh-primary-text); /* Default text color */ + border: 1px solid var(--kaauh-border); /* Add border to all sides */ + border-bottom: none; /* Remove tab's own bottom border */ + border-radius: 0.5rem 0.5rem 0 0; /* Slightly smaller radius for tabs */ + margin-right: 0.25rem; + padding: 0.75rem 1.25rem; + transition: all 0.2s ease-in-out; + background-color: #f8f9fa; /* Visible light background for inactive tabs */ + } + + /* Tab link hover state */ + .nav-tabs .nav-link:hover:not(.active) { + color: var(--kaauh-teal); + background-color: #e9ecef; /* Slightly darker on hover */ + border-color: var(--kaauh-teal); /* Use teal border on hover */ + border-bottom: none; /* Keep the bottom flat */ + } + + /* Active tab link styling */ + .nav-tabs .nav-link.active { + color: var(--kaauh-teal-dark); + background-color: white; /* White background for active */ + border-color: var(--kaauh-border); /* Use border color for all three sides */ + border-bottom: 2px solid white; /* Override the tab strip border with white to lift the tab */ + margin-bottom: -1px; /* Overlap slightly with the card border for a cleaner look */ + font-weight: 600; + } + + /* Tab pane styling for border consistency */ + .tab-content .tab-pane { + border: 1px solid var(--kaauh-border); /* Consistent border for the content */ + border-top: none; /* The nav tabs handle the top border */ + border-radius: 0 0 0.75rem 0.75rem; + background-color: white; + } + {% endblock %} {% block content %}

-

@@ -252,6 +329,9 @@

-
-
@@ -302,7 +380,6 @@
-
@@ -310,7 +387,6 @@ {% trans "Contact Information" %} - {% if agency.phone %}
@@ -322,7 +398,6 @@
{% endif %} - {% if agency.email %}
@@ -334,7 +409,6 @@
{% endif %} - {% if agency.website %}
@@ -360,7 +434,6 @@ {% trans "Location Information" %} - {% if agency.address %}
@@ -372,7 +445,6 @@
{% endif %} - {% if agency.city %}
@@ -384,7 +456,6 @@
{% endif %} - {% if agency.country %}
@@ -400,7 +471,6 @@
- {% if agency.description %}
@@ -411,14 +481,12 @@
{% endif %} - {% if generated_password and request.user.is_staff %}
{% trans "Agency Login Information" %}
- -
@@ -437,9 +504,8 @@
{% trans "Username" %}
{{ agency.user.username }}
-
+
-
@@ -461,59 +527,138 @@
- -
-
-
-
- - {% trans "Recent Candidates" %} -
- {% comment %} - {% trans "View All Candidates" %} - - {% endcomment %} -
+
+ +
+
-
- {% if candidates %} - {% for candidate in candidates %} -
-
-
-
{{ candidate.name }}
-
- {{ candidate.email }} - {% if candidate.phone %} - {{ candidate.phone }} - {% endif %} + +
+ +
+ {% if candidates %} + {% for candidate in candidates %} +
+
+
+
{{ candidate.name }}
+
+ {{ candidate.email }} + {% if candidate.phone %} + {{ candidate.phone }} + {% endif %} +
-
-
- - {{ candidate.get_stage_display }} - -
- {{ candidate.created_at|date:"M d, Y" }} +
+ + {{ candidate.get_stage_display }} + +
+ {{ candidate.created_at|date:"M d, Y" }} +
+ {% endfor %} + {% else %} +
+ +
{% trans "No candidates yet" %}
+

{% trans "This agency hasn't submitted any candidates yet." %}

- {% endfor %} - {% else %} -
- -
{% trans "No candidates yet" %}
-

{% trans "This agency hasn't submitted any candidates yet." %}

-
- {% endif %} + {% endif %} +
+ +
+ {% comment %} + NOTE: You will need to pass an 'assigned_jobs' list + from your Django view context to populate this section. + {% endcomment %} + + {% if assigned_jobs %} + {% for assignment in assigned_jobs %} +
+
+
+ +
+ {{ assignment.job.location }} + {{ assignment.job.department.name }} +
+
+
+ + {% trans "Assigned" %} + +
+ {% trans "Assigned On:" %} {{ assignment.created_at|date:"M d, Y" }} +
+
+
+
+ {% endfor %} + {% else %} +
+ +
{% trans "No jobs assigned" %}
+

{% trans "There are no open job assignments for this agency." %}

+ + {% trans "Assign New Job" %} + +
+ {% endif %} +
+
-
+
-
-
@@ -551,40 +696,6 @@
- - {% comment %}
-
-
- - {% trans "Quick Actions" %} -
-
- -
{% endcomment %} - -
@@ -634,4 +745,4 @@ function copyPassword() { } -{% endblock %} +{% endblock %} \ No newline at end of file diff --git a/templates/recruitment/candidate_interview_view.html b/templates/recruitment/candidate_interview_view.html index fb6a4fa..e291a73 100644 --- a/templates/recruitment/candidate_interview_view.html +++ b/templates/recruitment/candidate_interview_view.html @@ -328,8 +328,8 @@ {% with latest_meeting=candidate.get_latest_meeting %} - {% if latest_meeting and latest_meeting.join_url %} - join