677 lines
40 KiB
HTML
677 lines
40 KiB
HTML
{% extends "base.html" %}
|
|
{% load i18n static crispy_forms_tags %}
|
|
|
|
{% block title %}{{ job.title }} - KAAUH ATS{% endblock %}
|
|
|
|
{% block customCSS %}
|
|
<style>
|
|
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;600;700&display=swap');
|
|
body { font-family: 'Inter', sans-serif; }
|
|
</style>
|
|
{% endblock %}
|
|
|
|
{% block content %}
|
|
<div class="container-fluid py-4">
|
|
|
|
<!-- Modal for Staff Assignment -->
|
|
<div id="staffAssignmentModal" class="fixed inset-0 bg-black/50 backdrop-blur-sm z-50 hidden flex items-center justify-center p-4">
|
|
<div class="bg-white rounded-2xl shadow-xl max-w-lg w-full">
|
|
<div class="flex items-center justify-between p-4 border-b border-gray-200">
|
|
<h5 class="text-lg font-bold text-gray-900 flex items-center gap-2">
|
|
<i data-lucide="user-plus" class="w-5 h-5"></i>
|
|
{% trans "Assign Staff Member" %}
|
|
</h5>
|
|
<button type="button" onclick="document.getElementById('staffAssignmentModal').classList.add('hidden')" class="text-gray-400 hover:text-gray-600 transition">
|
|
<i data-lucide="x" class="w-5 h-5"></i>
|
|
</button>
|
|
</div>
|
|
<div class="p-4">
|
|
<form method="post" action="{% url 'staff_assignment_view' job.slug %}">
|
|
{% csrf_token %}
|
|
{{staff_form|crispy}}
|
|
<div class="flex justify-end mt-3">
|
|
<button type="submit" class="inline-flex items-center gap-2 bg-kaauh-blue hover:bg-[#004f57] text-white font-medium px-4 py-2.5 rounded-xl text-sm transition shadow-sm hover:shadow-md">
|
|
<i data-lucide="save" class="w-4 h-4"></i>
|
|
{% trans "Save Assignment" %}
|
|
</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Breadcrumb -->
|
|
<nav class="mb-6" aria-label="breadcrumb">
|
|
<ol class="flex items-center gap-2 text-sm flex-wrap">
|
|
<li><a href="{% url 'dashboard' %}" class="text-gray-500 hover:text-kaauh-blue transition flex items-center gap-1">
|
|
<i data-lucide="home" class="w-4 h-4"></i> {% trans "Home" %}
|
|
</a></li>
|
|
<li class="text-gray-400">/</li>
|
|
<li><a href="{% url 'job_list' %}" class="text-gray-500 hover:text-kaauh-blue transition">{% trans "Jobs" %}</a></li>
|
|
<li class="text-gray-400">/</li>
|
|
<li class="text-temple-red font-semibold">{% trans "Job Detail" %}</li>
|
|
</ol>
|
|
</nav>
|
|
|
|
<div class="grid grid-cols-1 lg:grid-cols-12 gap-6">
|
|
|
|
<!-- LEFT COLUMN: JOB DETAILS -->
|
|
<div class="lg:col-span-7">
|
|
<div class="bg-white rounded-2xl shadow-sm border border-gray-100 overflow-hidden">
|
|
|
|
<!-- Header Section -->
|
|
<div class="bg-gradient-to-br from-temple-red to-[#7a1a29] text-white p-6">
|
|
<div class="flex flex-col md:flex-row md:items-start md:justify-between gap-4">
|
|
<div class="flex-1">
|
|
<h1 class="text-2xl font-bold mb-1">{{ job.title }}</h1>
|
|
<p class="text-sm text-white/80 mb-2">{% trans "JOB ID:" %} {{ job.internal_job_id }}</p>
|
|
|
|
{% if job.application_deadline %}
|
|
<div class="flex items-center gap-2 text-sm">
|
|
<i data-lucide="calendar-x" class="w-4 h-4"></i>
|
|
<span class="text-white font-semibold">{% trans "Deadline:" %}</span>
|
|
<span class="bg-white/20 px-2 py-0.5 rounded">{{ job.application_deadline }}</span>
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
|
|
<div class="flex flex-col items-start md:items-end gap-3">
|
|
<!-- Status Badge -->
|
|
<div class="flex items-center gap-2">
|
|
<span class="inline-flex items-center px-3 py-1.5 rounded-full text-xs font-bold uppercase tracking-wide
|
|
{% if job.status == "ACTIVE" %}bg-emerald-500 text-white
|
|
{% elif job.status == "DRAFT" %}bg-gray-500 text-white
|
|
{% elif job.status == "CLOSED" %}bg-amber-500 text-white
|
|
{% elif job.status == "CANCELLED" %}bg-red-500 text-white
|
|
{% elif job.status == "ARCHIVED" %}bg-gray-600 text-white
|
|
{% else %}bg-gray-500 text-white{% endif %}">
|
|
{{ job.get_status_display }}
|
|
</span>
|
|
{% if user.is_superuser or user.is_staff and user == job.assigned_to %}
|
|
<button type="button" onclick="document.getElementById('editStatusModal').classList.remove('hidden')"
|
|
class="p-2 bg-white/10 hover:bg-white/20 rounded-lg text-white transition">
|
|
<i data-lucide="edit-2" class="w-4 h-4"></i>
|
|
</button>
|
|
{% endif %}
|
|
</div>
|
|
|
|
<!-- Share Link Button -->
|
|
<button type="button" id="copyJobLinkButton" data-url="{{ job.application_url }}"
|
|
class="inline-flex items-center gap-2 bg-yellow-400 hover:bg-yellow-500 text-gray-900 font-medium px-3 py-2 rounded-xl text-sm transition shadow-lg">
|
|
<i data-lucide="link" class="w-4 h-4"></i>
|
|
{% trans "Share Link" %}
|
|
</button>
|
|
<span id="copyFeedback" class="text-sm text-emerald-400 hidden">{% trans "Copied!" %}</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Content Section -->
|
|
<div class="p-6">
|
|
|
|
<!-- Administrative Header -->
|
|
<div class="flex items-center justify-between mb-4 pb-4 border-b border-gray-200">
|
|
<h5 class="text-sm font-bold text-gray-600 flex items-center gap-2">
|
|
<i data-lucide="settings" class="w-5 h-5"></i>
|
|
{% trans "Administrative & Location" %}
|
|
</h5>
|
|
{% if job.status == 'DRAFT' and user.is_superuser %}
|
|
<a href="{% url 'job_update' job.slug %}" class="inline-flex items-center gap-2 bg-gray-100 hover:bg-gray-200 text-gray-700 font-medium px-3 py-2 rounded-xl text-sm transition">
|
|
<i data-lucide="edit" class="w-4 h-4"></i>
|
|
{% trans "Edit Job" %}
|
|
</a>
|
|
{% endif %}
|
|
</div>
|
|
|
|
<!-- Staff Assignment -->
|
|
<div class="mb-4">
|
|
{% if job.assigned_to %}
|
|
<div class="flex items-center gap-2 text-sm">
|
|
<i data-lucide="user-tie" class="w-5 h-5 text-kaauh-blue"></i>
|
|
<span class="text-gray-600">{% trans "Assigned to:" %}</span>
|
|
<span class="font-semibold text-gray-900">{{ job.assigned_to }}</span>
|
|
</div>
|
|
{% else %}
|
|
<button type="button" onclick="document.getElementById('staffAssignmentModal').classList.remove('hidden')"
|
|
class="inline-flex items-center gap-2 bg-temple-red hover:bg-[#7a1a29] text-white font-medium px-3 py-2 rounded-xl text-sm transition shadow-sm hover:shadow-md">
|
|
<i data-lucide="user-plus" class="w-4 h-4"></i>
|
|
{% trans "Click To Assign" %}
|
|
</button>
|
|
{% endif %}
|
|
</div>
|
|
|
|
<!-- Job Details Grid -->
|
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-3 mb-6 pb-6 border-b border-gray-200 text-sm">
|
|
<div class="flex items-start gap-2">
|
|
<i data-lucide="building-2" class="w-5 h-5 text-gray-400 flex-shrink-0 mt-0.5"></i>
|
|
<span class="text-gray-700"><strong>{% trans "Department:" %}</strong> {{ job.department|default:"-" }}</span>
|
|
</div>
|
|
<div class="flex items-start gap-2">
|
|
<i data-lucide="hash" class="w-5 h-5 text-gray-400 flex-shrink-0 mt-0.5"></i>
|
|
<span class="text-gray-700"><strong>{% trans "Position No:" %}</strong> {{ job.position_number|default:"-" }}</span>
|
|
</div>
|
|
<div class="flex items-start gap-2">
|
|
<i data-lucide="briefcase" class="w-5 h-5 text-gray-400 flex-shrink-0 mt-0.5"></i>
|
|
<span class="text-gray-700"><strong>{% trans "Job Type:" %}</strong> {{ job.get_job_type_display }}</span>
|
|
</div>
|
|
<div class="flex items-start gap-2">
|
|
<i data-lucide="map-pin" class="w-5 h-5 text-gray-400 flex-shrink-0 mt-0.5"></i>
|
|
<span class="text-gray-700"><strong>{% trans "Workplace:" %}</strong> {{ job.get_workplace_type_display }}</span>
|
|
</div>
|
|
<div class="flex items-start gap-2">
|
|
<i data-lucide="globe" class="w-5 h-5 text-gray-400 flex-shrink-0 mt-0.5"></i>
|
|
<span class="text-gray-700"><strong>{% trans "Location:" %}</strong> {{ job.get_location_display }}</span>
|
|
</div>
|
|
<div class="flex items-start gap-2">
|
|
<i data-lucide="banknote" class="w-5 h-5 text-gray-400 flex-shrink-0 mt-0.5"></i>
|
|
<span class="text-gray-700"><strong>{% trans "Salary:" %}</strong> {{ job.salary_range|default:"-" }}</span>
|
|
</div>
|
|
<div class="flex items-start gap-2">
|
|
<i data-lucide="user-tie" class="w-5 h-5 text-gray-400 flex-shrink-0 mt-0.5"></i>
|
|
<span class="text-gray-700"><strong>{% trans "Created By:" %}</strong> {{ job.created_by|default:"-" }}</span>
|
|
</div>
|
|
<div class="flex items-start gap-2">
|
|
<i data-lucide="calendar" class="w-5 h-5 text-gray-400 flex-shrink-0 mt-0.5"></i>
|
|
<span class="text-gray-700"><strong>{% trans "Created At:" %}</strong> {{ job.created_at|date:"M d, Y"|default:"-" }}</span>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Description Blocks -->
|
|
{% if job.has_description_content %}
|
|
<div class="mb-4">
|
|
<h5 class="text-base font-bold text-gray-900 mb-3 flex items-center gap-2">
|
|
<i data-lucide="file-text" class="w-5 h-5 text-temple-red"></i>
|
|
{% trans "Job Description" %}
|
|
</h5>
|
|
<div class="text-gray-600 text-sm leading-relaxed">{{ job.description|safe }}</div>
|
|
</div>
|
|
{% endif %}
|
|
|
|
{% if job.has_qualifications_content %}
|
|
<div class="mb-4">
|
|
<h5 class="text-base font-bold text-gray-900 mb-3 flex items-center gap-2">
|
|
<i data-lucide="award" class="w-5 h-5 text-temple-red"></i>
|
|
{% trans "Required Qualifications" %}
|
|
</h5>
|
|
<div class="text-gray-600 text-sm leading-relaxed">{{ job.qualifications|safe }}</div>
|
|
</div>
|
|
{% endif %}
|
|
|
|
{% if job.has_benefits_content %}
|
|
<div class="mb-4">
|
|
<h5 class="text-base font-bold text-gray-900 mb-3 flex items-center gap-2">
|
|
<i data-lucide="gift" class="w-5 h-5 text-temple-red"></i>
|
|
{% trans "Benefits" %}
|
|
</h5>
|
|
<div class="text-gray-600 text-sm leading-relaxed">{{ job.benefits|safe }}</div>
|
|
</div>
|
|
{% endif %}
|
|
|
|
{% if job.has_application_instructions_content %}
|
|
<div class="mb-4">
|
|
<h5 class="text-base font-bold text-gray-900 mb-3 flex items-center gap-2">
|
|
<i data-lucide="info" class="w-5 h-5 text-temple-red"></i>
|
|
{% trans "Application Instructions" %}
|
|
</h5>
|
|
<div class="text-gray-600 text-sm leading-relaxed">{{ job.application_instructions|safe }}</div>
|
|
</div>
|
|
{% endif %}
|
|
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- RIGHT COLUMN: TABBED CARDS -->
|
|
<div class="lg:col-span-5 space-y-4">
|
|
|
|
<!-- Main Tabbed Card -->
|
|
<div class="bg-white rounded-2xl shadow-sm border border-gray-100 overflow-hidden">
|
|
|
|
<!-- Tab Navigation -->
|
|
<div class="border-b border-gray-200 bg-gray-50 overflow-x-auto">
|
|
<div class="flex min-w-max">
|
|
<button class="tab-btn px-4 py-3 text-sm font-medium border-b-2 border-transparent hover:bg-gray-100 transition whitespace-nowrap"
|
|
onclick="showTab('applicants-pane')" data-tab="applicants-pane">
|
|
<i data-lucide="users" class="w-4 h-4 mr-2 inline"></i>
|
|
{% trans "Applications" %}
|
|
</button>
|
|
<button class="tab-btn px-4 py-3 text-sm font-medium border-b-2 border-transparent hover:bg-gray-100 transition whitespace-nowrap"
|
|
onclick="showTab('tracking-pane')" data-tab="tracking-pane">
|
|
<i data-lucide="route" class="w-4 h-4 mr-2 inline"></i>
|
|
{% trans "Tracking" %}
|
|
</button>
|
|
<button class="tab-btn px-4 py-3 text-sm font-medium border-b-2 border-transparent hover:bg-gray-100 transition whitespace-nowrap"
|
|
onclick="showTab('manage-pane')" data-tab="manage-pane">
|
|
<i data-lucide="clipboard-list" class="w-4 h-4 mr-2 inline"></i>
|
|
{% trans "Form Template" %}
|
|
</button>
|
|
<button class="tab-btn px-4 py-3 text-sm font-medium border-b-2 border-transparent hover:bg-gray-100 transition whitespace-nowrap"
|
|
onclick="showTab('staff-pane')" data-tab="staff-pane">
|
|
<i data-lucide="user-tie" class="w-4 h-4 mr-2 inline"></i>
|
|
{% trans "Assigned Staff" %}
|
|
</button>
|
|
<button class="tab-btn px-4 py-3 text-sm font-medium border-b-2 border-transparent hover:bg-gray-100 transition whitespace-nowrap"
|
|
onclick="showTab('linkedin-pane')" data-tab="linkedin-pane">
|
|
<i data-lucide="linkedin" class="w-4 h-4 mr-2 inline"></i>
|
|
{% trans "LinkedIn" %}
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Tab Content -->
|
|
<div class="p-4">
|
|
|
|
<!-- Applications Tab -->
|
|
<div id="applicants-pane" class="tab-content">
|
|
<div class="flex items-center justify-between mb-4">
|
|
<h5 class="text-base font-bold text-gray-900">{% trans "Total Applications" %} (<span id="total_candidates">{{ total_applications }}</span>)</h5>
|
|
</div>
|
|
|
|
<div class="space-y-2">
|
|
<a href="{% url 'application_create_for_job' job.slug %}" class="inline-flex items-center gap-2 w-full bg-temple-red hover:bg-[#7a1a29] text-white font-medium px-4 py-2.5 rounded-xl text-sm transition shadow-sm hover:shadow-md">
|
|
<i data-lucide="user-plus" class="w-4 h-4"></i>
|
|
{% trans "Create Application" %}
|
|
</a>
|
|
<a href="{% url 'applications_screening_view' job.slug %}" class="inline-flex items-center gap-2 w-full bg-temple-red hover:bg-[#7a1a29] text-white font-medium px-4 py-2.5 rounded-xl text-sm transition shadow-sm hover:shadow-md">
|
|
<i data-lucide="layers" class="w-4 h-4"></i>
|
|
{% trans "Manage Applications" %}
|
|
</a>
|
|
<a href="{% url 'interview_calendar' job.slug %}" class="inline-flex items-center gap-2 w-full bg-temple-red hover:bg-[#7a1a29] text-white font-medium px-4 py-2.5 rounded-xl text-sm transition shadow-sm hover:shadow-md">
|
|
<i data-lucide="calendar" class="w-4 h-4"></i>
|
|
{% trans "View Calendar" %}
|
|
</a>
|
|
|
|
{% if not job.form_template.is_active %}
|
|
{% if not jobzip_created %}
|
|
<a href="{% url 'request_cvs_download' job.slug %}" class="inline-flex items-center gap-2 w-full bg-temple-red hover:bg-[#7a1a29] text-white font-medium px-4 py-2.5 rounded-xl text-sm transition shadow-sm hover:shadow-md">
|
|
<i data-lucide="download" class="w-4 h-4"></i>
|
|
{% trans "Generate All CVs" %}
|
|
</a>
|
|
{% endif %}
|
|
{% if job.zip_created %}
|
|
<a href="{% url 'download_ready_cvs' job.slug %}" class="inline-flex items-center gap-2 w-full bg-gray-100 hover:bg-gray-200 text-gray-700 font-medium px-4 py-2.5 rounded-xl text-sm transition">
|
|
<i data-lucide="eye" class="w-4 h-4"></i>
|
|
{% trans "View All CVs" %}
|
|
</a>
|
|
{% endif %}
|
|
{% else %}
|
|
<p class="text-sm text-gray-500">{% trans "Bulk CV download is inactive. To activate please change the status of job to CLOSED." %}</p>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Tracking Tab -->
|
|
<div id="tracking-pane" class="tab-content hidden">
|
|
<h5 class="text-base font-bold text-gray-900 mb-3 flex items-center gap-2">
|
|
<i data-lucide="route" class="w-5 h-5 text-temple-red"></i>
|
|
{% trans "Applications Stages" %}
|
|
</h5>
|
|
{% include 'jobs/partials/applicant_tracking.html' %}
|
|
<p class="text-xs text-gray-500 mt-3">
|
|
{% trans "The applicant tracking flow is defined by the attached Form Template. View Form Template tab to manage stages and fields." %}
|
|
</p>
|
|
</div>
|
|
|
|
<!-- Form Template Tab -->
|
|
<div id="manage-pane" class="tab-content hidden">
|
|
<h5 class="text-base font-bold text-gray-900 mb-3 flex items-center gap-2">
|
|
<i data-lucide="clipboard-list" class="w-5 h-5 text-temple-red"></i>
|
|
{% trans "Form Management" %}
|
|
</h5>
|
|
<p class="text-sm text-gray-500 mb-4">{% trans "Manage custom application forms associated with this job posting." %}</p>
|
|
|
|
{% if user.is_superuser or user.is_staff and user == job.assigned_to %}
|
|
<a href="{% url 'form_builder' job.form_template.slug %}" class="inline-flex items-center gap-2 w-full bg-temple-red hover:bg-[#7a1a29] text-white font-medium px-4 py-2.5 rounded-xl text-sm transition shadow-sm hover:shadow-md">
|
|
<i data-lucide="list-alt" class="w-4 h-4"></i>
|
|
{% trans "Manage Job Form" %}
|
|
</a>
|
|
{% endif %}
|
|
</div>
|
|
|
|
<!-- Staff Tab -->
|
|
<div id="staff-pane" class="tab-content hidden">
|
|
<h5 class="text-base font-bold text-gray-900 mb-3 flex items-center gap-2">
|
|
<i data-lucide="user-tie" class="w-5 h-5 text-temple-red"></i>
|
|
{% trans "Staff Assignment" %}
|
|
</h5>
|
|
|
|
{% if job.assigned_to %}
|
|
<p class="text-sm text-gray-600 mb-4">
|
|
<strong>{% trans "Assigned to:" %}</strong> {{ job.assigned_to }}
|
|
</p>
|
|
{% endif %}
|
|
|
|
{% if not job.assigned_to or job.assigned_to == request.user %}
|
|
<button type="button" onclick="document.getElementById('staffAssignmentModal').classList.remove('hidden')"
|
|
class="inline-flex items-center gap-2 w-full bg-temple-red hover:bg-[#7a1a29] text-white font-medium px-4 py-2.5 rounded-xl text-sm transition shadow-sm hover:shadow-md">
|
|
<i data-lucide="user-plus" class="w-4 h-4"></i>
|
|
{% trans "Assign Staff Member" %}
|
|
</button>
|
|
{% endif %}
|
|
|
|
{% if not job.assigned_to %}
|
|
<div class="bg-amber-50 border border-amber-200 rounded-xl p-3 mt-4">
|
|
<div class="flex items-start gap-2 text-sm text-amber-700">
|
|
<i data-lucide="info" class="w-5 h-5 flex-shrink-0 mt-0.5"></i>
|
|
<p>{% trans "No staff members assigned to this job yet." %}</p>
|
|
</div>
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
|
|
<!-- LinkedIn Tab -->
|
|
<div id="linkedin-pane" class="tab-content hidden">
|
|
<h5 class="text-base font-bold text-gray-900 mb-3 flex items-center gap-2">
|
|
<i data-lucide="linkedin" class="w-5 h-5 text-temple-red"></i>
|
|
{% trans "LinkedIn Integration" %}
|
|
</h5>
|
|
|
|
<div class="space-y-3">
|
|
{% if job.posted_to_linkedin %}
|
|
<div class="bg-emerald-50 border border-emerald-200 rounded-xl p-3">
|
|
<div class="flex items-center gap-2 text-sm text-emerald-700">
|
|
<i data-lucide="check-circle" class="w-5 h-5"></i>
|
|
<span class="font-medium">{% trans "Posted successfully!" %}</span>
|
|
</div>
|
|
</div>
|
|
{% if job.linkedin_post_url %}
|
|
<a href="{{ job.linkedin_post_url }}" target="_blank" class="inline-flex items-center gap-2 w-full bg-gray-100 hover:bg-gray-200 text-gray-700 font-medium px-4 py-2.5 rounded-xl text-sm transition">
|
|
<i data-lucide="linkedin" class="w-4 h-4"></i>
|
|
{% trans "View on LinkedIn" %}
|
|
</a>
|
|
{% endif %}
|
|
<p class="text-xs text-gray-500 text-center">{% trans "Posted on:" %} {{ job.linkedin_posted_at|date:"M d, Y" }}</p>
|
|
{% else %}
|
|
<p class="text-sm text-gray-500">{% trans "This job has not been posted to LinkedIn yet." %}</p>
|
|
{% endif %}
|
|
|
|
<form method="post" action="{% url 'post_to_linkedin' job.slug %}" class="mt-2">
|
|
{% csrf_token %}
|
|
<button type="submit" class="inline-flex items-center gap-2 w-full bg-temple-red hover:bg-[#7a1a29] text-white font-medium px-4 py-2.5 rounded-xl text-sm transition shadow-sm hover:shadow-md
|
|
{% if not request.session.linkedin_authenticated %}opacity-50 cursor-not-allowed{% endif %}">
|
|
<i data-lucide="linkedin" class="w-4 h-4"></i>
|
|
{% if job.posted_to_linkedin %}{% trans "Re-post to LinkedIn" %}{% else %}{% trans "Post to LinkedIn" %}{% endif %}
|
|
</button>
|
|
</form>
|
|
|
|
<button type="button" onclick="document.getElementById('imageUploadModal').classList.remove('hidden')"
|
|
class="inline-flex items-center gap-2 w-full bg-gray-100 hover:bg-gray-200 text-gray-700 font-medium px-4 py-2.5 rounded-xl text-sm transition">
|
|
<i data-lucide="image" class="w-4 h-4"></i>
|
|
{% trans "Upload Image for Post" %}
|
|
</button>
|
|
|
|
{% if not request.session.linkedin_authenticated %}
|
|
<p class="text-xs text-gray-500 text-center">
|
|
{% trans "You need to" %} <a href="{% url 'linkedin_login' %}" class="text-temple-red hover:text-temple-red/80 font-medium">{% trans "authenticate with LinkedIn" %}</a> {% trans "first." %}
|
|
</p>
|
|
{% endif %}
|
|
|
|
{% if job.linkedin_post_status and 'ERROR' in job.linkedin_post_status %}
|
|
<div class="bg-red-50 border border-red-200 rounded-xl p-3 mt-3">
|
|
<div class="flex items-start gap-2 text-sm text-red-700">
|
|
<i data-lucide="alert-triangle" class="w-5 h-5 flex-shrink-0 mt-0.5"></i>
|
|
<p><strong>{% trans "Error:" %}</strong> {{ job.linkedin_post_status }}</p>
|
|
</div>
|
|
</div>
|
|
{% endif %}
|
|
|
|
{% if job.linkedin_post_formated_data %}
|
|
<button type="button" onclick="document.getElementById('linkedinModal').classList.remove('hidden')"
|
|
class="inline-flex items-center gap-2 w-full bg-temple-red hover:bg-[#7a1a29] text-white font-medium px-4 py-2.5 rounded-xl text-sm transition shadow-sm hover:shadow-md">
|
|
<i data-lucide="edit" class="w-4 h-4"></i>
|
|
{% trans "Update LinkedIn Content" %}
|
|
</button>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Application Category Chart -->
|
|
<div class="bg-white rounded-2xl shadow-sm border border-gray-100 overflow-hidden">
|
|
<div class="p-4 border-b border-gray-200 bg-gray-50">
|
|
<h6 class="text-sm font-bold text-gray-900 flex items-center gap-2">
|
|
<i data-lucide="pie-chart" class="w-5 h-5 text-temple-red"></i>
|
|
{% trans "Application Categories & Scores" %}
|
|
</h6>
|
|
</div>
|
|
<div class="p-4">
|
|
<div style="height: 300px;">
|
|
<canvas id="jobCategoryMatchChart"></canvas>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Position Stats -->
|
|
<div class="bg-white rounded-2xl shadow-sm border border-gray-100 overflow-hidden">
|
|
<div class="p-4 border-b border-gray-200 bg-gray-50">
|
|
<h6 class="text-sm font-bold text-gray-900 flex items-center gap-2">
|
|
<i data-lucide="briefcase" class="w-5 h-5 text-temple-red"></i>
|
|
{% trans "Position Statistics" %}
|
|
</h6>
|
|
</div>
|
|
<div class="p-4">
|
|
<div class="grid grid-cols-3 gap-3">
|
|
<div class="text-center p-3 bg-temple-red/5 rounded-xl border-l-4 border-temple-red">
|
|
<i data-lucide="door-open" class="w-6 h-6 text-temple-red mx-auto mb-2"></i>
|
|
<div class="text-2xl font-bold text-temple-red">{{ job.open_positions }}</div>
|
|
<p class="text-xs text-gray-600">{% trans "Open Positions" %}</p>
|
|
</div>
|
|
<div class="text-center p-3 bg-emerald-50 rounded-xl">
|
|
<i data-lucide="user-check" class="w-6 h-6 text-emerald-500 mx-auto mb-2"></i>
|
|
<div class="text-2xl font-bold text-emerald-600">{{ positions_filled }}</div>
|
|
<p class="text-xs text-gray-600">{% trans "Positions Filled" %}</p>
|
|
</div>
|
|
<div class="text-center p-3 bg-gray-100 rounded-xl">
|
|
<i data-lucide="hourglass" class="w-6 h-6 text-gray-500 mx-auto mb-2"></i>
|
|
<div class="text-2xl font-bold text-gray-600">{{ vacant_positions }}</div>
|
|
<p class="text-xs text-gray-600">{% trans "Vacant Positions" %}</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- KPIs -->
|
|
<div class="bg-white rounded-2xl shadow-sm border border-gray-100 overflow-hidden">
|
|
<div class="p-4 border-b border-gray-200 bg-gray-50">
|
|
<h6 class="text-sm font-bold text-gray-900 flex items-center gap-2">
|
|
<i data-lucide="info" class="w-5 h-5 text-temple-red"></i>
|
|
{% trans "Key Performance Indicators" %}
|
|
</h6>
|
|
</div>
|
|
<div class="p-4">
|
|
<div class="grid grid-cols-3 gap-3">
|
|
<div class="text-center p-3 bg-temple-red/5 rounded-xl border-l-4 border-temple-red">
|
|
<i data-lucide="star" class="w-6 h-6 text-temple-red mx-auto mb-2"></i>
|
|
<div class="text-2xl font-bold text-temple-red">{{ avg_match_score|floatformat:1 }}</div>
|
|
<p class="text-xs text-gray-600">{% trans "Avg. AI Score" %}</p>
|
|
</div>
|
|
<div class="text-center p-3 bg-emerald-50 rounded-xl">
|
|
<i data-lucide="trophy" class="w-6 h-6 text-emerald-500 mx-auto mb-2"></i>
|
|
<div class="text-2xl font-bold text-emerald-600">{{ high_potential_count }}</div>
|
|
<p class="text-xs text-gray-600">{% trans "High Potential" %}</p>
|
|
</div>
|
|
<div class="text-center p-3 bg-gray-100 rounded-xl">
|
|
<i data-lucide="percent" class="w-6 h-6 text-gray-500 mx-auto mb-2"></i>
|
|
<div class="text-2xl font-bold text-gray-600">{{ job.vacancy_fill_rate|floatformat:2 }}%</div>
|
|
<p class="text-xs text-gray-600">{% trans "Fill Rate" %}</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{% include "jobs/partials/image_upload.html" %}
|
|
{% include "jobs/partials/linkedin_content_form.html" %}
|
|
|
|
<!-- Edit Status Modal -->
|
|
<div id="editStatusModal" class="fixed inset-0 bg-black/50 backdrop-blur-sm z-50 hidden flex items-center justify-center p-4">
|
|
<div class="bg-white rounded-2xl shadow-xl max-w-lg w-full">
|
|
<form method="post" action="{% url 'job_detail' slug=job.slug %}">
|
|
{% csrf_token %}
|
|
<div class="flex items-center justify-between p-4 border-b border-gray-200">
|
|
<h5 class="text-lg font-bold text-gray-900">{% trans "Edit Job Status" %}</h5>
|
|
<button type="button" onclick="document.getElementById('editStatusModal').classList.add('hidden')" class="text-gray-400 hover:text-gray-600 transition">
|
|
<i data-lucide="x" class="w-5 h-5"></i>
|
|
</button>
|
|
</div>
|
|
<div class="p-4">
|
|
<div>
|
|
{% if status_form %}
|
|
<label for="{{ status_form.status.id_for_label }}" class="block text-sm font-semibold text-gray-700 mb-2">{% trans "Select New Status" %}</label>
|
|
<select name="{{ status_form.status.name }}" id="{{ status_form.status.id_for_label }}" class="w-full px-4 py-2.5 border border-gray-200 rounded-xl text-sm focus:ring-2 focus:ring-kaauh-blue/20 focus:border-kaauh-blue outline-none transition">
|
|
{% for value, label in status_form.status.field.choices %}
|
|
<option value="{{ value }}" {% if status_form.status.value == value %}selected{% endif %}>{{ label }}</option>
|
|
{% endfor %}
|
|
</select>
|
|
{% if status_form.status.errors %}
|
|
<div class="mt-1 text-xs text-red-600">{{ status_form.status.errors }}</div>
|
|
{% endif %}
|
|
{% else %}
|
|
<div class="text-red-600 text-sm">{% trans "Status form not available. Please check your view." %}</div>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
<div class="flex gap-3 p-4 border-t border-gray-200">
|
|
<button type="button" onclick="document.getElementById('editStatusModal').classList.add('hidden')" class="inline-flex items-center gap-2 flex-1 bg-gray-100 hover:bg-gray-200 text-gray-700 font-medium px-4 py-2.5 rounded-xl text-sm transition">
|
|
{% trans "Cancel" %}
|
|
</button>
|
|
<button type="submit" class="inline-flex items-center gap-2 flex-1 bg-temple-red hover:bg-[#7a1a29] text-white font-medium px-4 py-2.5 rounded-xl text-sm transition shadow-sm hover:shadow-md">
|
|
<i data-lucide="save" class="w-4 h-4"></i>
|
|
{% trans "Save Changes" %}
|
|
</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
|
|
{% endblock %}
|
|
|
|
{% block customJS %}
|
|
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
|
|
<script>
|
|
// Tab switching functionality
|
|
function showTab(tabId) {
|
|
// Hide all tab contents
|
|
document.querySelectorAll('.tab-content').forEach(content => {
|
|
content.classList.add('hidden');
|
|
});
|
|
|
|
// Remove active state from all tabs
|
|
document.querySelectorAll('.tab-btn').forEach(btn => {
|
|
btn.classList.remove('border-kaauh-blue', 'text-kaauh-blue', 'bg-white');
|
|
btn.classList.add('border-transparent', 'text-gray-600');
|
|
});
|
|
|
|
// Show selected tab content
|
|
document.getElementById(tabId).classList.remove('hidden');
|
|
|
|
// Add active state to selected tab
|
|
const activeBtn = document.querySelector(`[data-tab="${tabId}"]`);
|
|
activeBtn.classList.add('border-kaauh-blue', 'text-kaauh-blue', 'bg-white');
|
|
activeBtn.classList.remove('border-transparent', 'text-gray-600');
|
|
}
|
|
|
|
// Initialize first tab as active
|
|
showTab('applicants-pane');
|
|
|
|
// Copy job link functionality
|
|
document.getElementById('copyJobLinkButton').addEventListener('click', function() {
|
|
const urlToCopy = this.getAttribute('data-url');
|
|
navigator.clipboard.writeText(urlToCopy).then(() => {
|
|
const feedback = document.getElementById('copyFeedback');
|
|
feedback.classList.remove('hidden');
|
|
setTimeout(() => {
|
|
feedback.classList.add('hidden');
|
|
}, 2000);
|
|
}).catch(err => {
|
|
console.error('Could not copy text: ', err);
|
|
alert('{% trans "Copy failed. Please copy URL manually:" %} ' + urlToCopy);
|
|
});
|
|
});
|
|
|
|
// Chart.js Configuration
|
|
window.jobChartData = {
|
|
categories: {{ categories|safe|default:"[]" }},
|
|
applications_count: {{ applications_count|safe|default:"[]" }},
|
|
avg_scores: {{ avg_scores|safe|default:"[]" }}
|
|
};
|
|
|
|
document.addEventListener('DOMContentLoaded', function() {
|
|
const ctx = document.getElementById('jobCategoryMatchChart');
|
|
if (!ctx) {
|
|
console.warn('Job category chart canvas not found.');
|
|
return;
|
|
}
|
|
|
|
const chartCtx = ctx.getContext('2d');
|
|
const jobChartData = window.jobChartData || { categories: [], applications_count: [], avg_scores: [] };
|
|
const categories = jobChartData.categories || [];
|
|
const candidateCounts = jobChartData.applications_count || [];
|
|
const avgScores = jobChartData.avg_scores || [];
|
|
|
|
if (categories.length > 0) {
|
|
new Chart(chartCtx, {
|
|
type: 'doughnut',
|
|
data: {
|
|
labels: categories,
|
|
datasets: [{
|
|
label: '{% trans "Number of Applications" %}',
|
|
data: candidateCounts,
|
|
backgroundColor: [
|
|
'rgba(157, 34, 53, 0.7)',
|
|
'rgba(0, 99, 110, 0.7)',
|
|
'rgba(245, 158, 11, 0.7)',
|
|
'rgba(16, 185, 129, 0.7)',
|
|
'rgba(99, 102, 241, 0.7)',
|
|
'rgba(236, 72, 153, 0.7)',
|
|
],
|
|
borderColor: [
|
|
'rgba(157, 34, 53, 1)',
|
|
'rgba(0, 99, 110, 1)',
|
|
'rgba(245, 158, 11, 1)',
|
|
'rgba(16, 185, 129, 1)',
|
|
'rgba(99, 102, 241, 1)',
|
|
'rgba(236, 72, 153, 1)',
|
|
],
|
|
borderWidth: 1,
|
|
}]
|
|
},
|
|
options: {
|
|
responsive: true,
|
|
maintainAspectRatio: false,
|
|
plugins: {
|
|
legend: {
|
|
position: 'right',
|
|
},
|
|
title: {
|
|
display: false,
|
|
},
|
|
tooltip: {
|
|
callbacks: {
|
|
label: function(context) {
|
|
let label = context.label || '';
|
|
if (label) {
|
|
label += ': ';
|
|
}
|
|
label += context.parsed + '{% trans " application(s)" %}';
|
|
return label;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
});
|
|
} else {
|
|
chartCtx.canvas.parentNode.innerHTML = '<p class="text-center text-gray-500 text-sm mt-8">{% trans "No application category data available for this job." %}</p>';
|
|
}
|
|
});
|
|
|
|
// Initialize Lucide icons
|
|
lucide.createIcons();
|
|
</script>
|
|
{% endblock %} |