diff --git a/recruitment/__pycache__/forms.cpython-312.pyc b/recruitment/__pycache__/forms.cpython-312.pyc index 8e6cf04..5cd8f96 100644 Binary files a/recruitment/__pycache__/forms.cpython-312.pyc and b/recruitment/__pycache__/forms.cpython-312.pyc differ diff --git a/recruitment/__pycache__/models.cpython-312.pyc b/recruitment/__pycache__/models.cpython-312.pyc index e14024e..d2c2570 100644 Binary files a/recruitment/__pycache__/models.cpython-312.pyc and b/recruitment/__pycache__/models.cpython-312.pyc differ diff --git a/recruitment/__pycache__/urls.cpython-312.pyc b/recruitment/__pycache__/urls.cpython-312.pyc index 30fcc8d..543d155 100644 Binary files a/recruitment/__pycache__/urls.cpython-312.pyc and b/recruitment/__pycache__/urls.cpython-312.pyc differ diff --git a/recruitment/__pycache__/views.cpython-312.pyc b/recruitment/__pycache__/views.cpython-312.pyc index b69104f..603b919 100644 Binary files a/recruitment/__pycache__/views.cpython-312.pyc and b/recruitment/__pycache__/views.cpython-312.pyc differ 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