635 lines
31 KiB
HTML
635 lines
31 KiB
HTML
{% extends "base.html" %}
|
|
{% load i18n static %}
|
|
|
|
{% block title %}{{ job.title }} - University ATS{% endblock %}
|
|
|
|
{% block customCSS %}
|
|
<style>
|
|
/* ================================================= */
|
|
/* THEME VARIABLES AND GLOBAL STYLES */
|
|
/* ================================================= */
|
|
:root {
|
|
--kaauh-teal: #00636e; /* Primary */
|
|
--kaauh-teal-dark: #004a53;
|
|
--kaauh-teal-light: #4bb3be; /* For active glow */
|
|
--kaauh-border: #eaeff3;
|
|
--kaauh-primary-text: #343a40;
|
|
|
|
/* Consistent Status/Color Map (aligning with theme/bootstrap defaults) */
|
|
--color-draft: #6c757d; /* Secondary Gray */
|
|
--color-active: var(--kaauh-teal); /* Primary Teal */
|
|
--color-closed: #ffc107; /* Warning Yellow */
|
|
--color-cancelled: #dc3545; /* Danger Red */
|
|
--color-archived: #343a40; /* Dark text/Muted */
|
|
}
|
|
|
|
/* Primary Color Overrides for Bootstrap Classes */
|
|
.text-primary { color: var(--kaauh-teal) !important; }
|
|
.bg-primary { background-color: var(--kaauh-teal) !important; }
|
|
|
|
/* Status Badge Theme Mapping */
|
|
.status-badge.bg-success { background-color: var(--color-active) !important; }
|
|
.status-badge.bg-secondary { background-color: var(--color-draft) !important; }
|
|
.status-badge.bg-warning { background-color: var(--color-closed) !important; }
|
|
.status-badge.bg-danger { background-color: var(--color-cancelled) !important; }
|
|
/* Ensure text colors are consistent for standard BS classes */
|
|
.text-success { color: #28a745 !important; }
|
|
.text-info { color: #17a2b8 !important; }
|
|
.text-secondary { color: var(--kaauh-primary-text) !important; }
|
|
|
|
/* Header styling */
|
|
.job-header-card {
|
|
background: linear-gradient(135deg, var(--kaauh-teal), var(--kaauh-teal-dark));
|
|
color: white;
|
|
border-radius: 0.75rem 0.75rem 0 0;
|
|
padding: 1.5rem;
|
|
box-shadow: 0 4px 10px rgba(0,0,0,0.15);
|
|
}
|
|
.job-header-card h2 {
|
|
font-weight: 700;
|
|
margin: 0;
|
|
font-size: 1.8rem;
|
|
}
|
|
/* Status badge - Consolidated style for all badges */
|
|
.status-badge {
|
|
font-size: 0.9rem;
|
|
padding: 0.4em 0.8em;
|
|
border-radius: 0.4rem;
|
|
font-weight: 700;
|
|
text-transform: uppercase;
|
|
letter-spacing: 0.7px;
|
|
display: inline-flex;
|
|
align-items: center;
|
|
color: white; /* Ensure badge text is white */
|
|
}
|
|
|
|
/* Card enhancements */
|
|
.card {
|
|
border: 1px solid var(--kaauh-border);
|
|
border-radius: 0.75rem;
|
|
overflow: hidden;
|
|
box-shadow: 0 4px 12px rgba(0,0,0,0.06);
|
|
transition: transform 0.2s;
|
|
background-color: white;
|
|
}
|
|
.card:not(.no-hover):hover {
|
|
transform: translateY(-2px);
|
|
box-shadow: 0 6px 16px rgba(0,0,0,0.1);
|
|
}
|
|
|
|
/* Standard Card Header */
|
|
.card-header {
|
|
font-weight: 600;
|
|
padding: 1rem 1.25rem;
|
|
background-color: #f8f9fa;
|
|
border-bottom: 1px solid var(--kaauh-border);
|
|
}
|
|
|
|
/* Tabs Theming - Applies to the right column */
|
|
.nav-tabs {
|
|
border-bottom: 1px solid var(--kaauh-border);
|
|
background-color: #f8f9fa;
|
|
padding: 0;
|
|
}
|
|
|
|
.nav-tabs .nav-link {
|
|
border: none;
|
|
border-bottom: 3px solid transparent;
|
|
color: var(--kaauh-primary-text);
|
|
font-weight: 500;
|
|
padding: 0.75rem 1rem;
|
|
margin-right: 0.5rem;
|
|
transition: all 0.2s;
|
|
}
|
|
/* Active Link */
|
|
.nav-tabs .nav-link.active {
|
|
color: var(--kaauh-teal-dark) !important;
|
|
background-color: white !important;
|
|
border-bottom: 3px solid var(--kaauh-teal) !important;
|
|
font-weight: 600;
|
|
z-index: 2;
|
|
border-right-color: transparent !important;
|
|
margin-bottom: -1px;
|
|
}
|
|
|
|
/* Main Action Button Style */
|
|
.btn-main-action, .btn-main-action:hover, .btn-main-action:active {
|
|
background-color: var(--kaauh-teal) !important;
|
|
border-color: var(--kaauh-teal) !important;
|
|
color: white !important;
|
|
font-weight: 600;
|
|
padding: 0.6rem 1.2rem;
|
|
transition: all 0.2s ease;
|
|
display: inline-flex;
|
|
align-items: center;
|
|
gap: 0.5rem;
|
|
justify-content: center;
|
|
text-align: center;
|
|
}
|
|
.btn-outline-secondary { /* Apply primary colors for 'outline-secondary' used as theme buttons */
|
|
color: var(--kaauh-teal-dark) !important;
|
|
border-color: var(--kaauh-teal) !important;
|
|
}
|
|
.btn-outline-light { /* Fixing text color for the edit status button */
|
|
color: white !important;
|
|
border-color: white !important;
|
|
}
|
|
.btn-outline-light .text-primary { /* Override the edit icon's custom primary text class */
|
|
color: white !important;
|
|
}
|
|
.kpi-card {
|
|
border-left: 4px solid var(--kaauh-teal);
|
|
background-color: #f0faff;
|
|
}
|
|
|
|
</style>
|
|
{% endblock %}
|
|
|
|
{% block content %}
|
|
<div class="container-fluid py-4">
|
|
|
|
<nav aria-label="breadcrumb">
|
|
<ol class="breadcrumb">
|
|
<li class="breadcrumb-item"><a href="{% url 'dashboard' %}" class="text-secondary">Home</a></li>
|
|
<li class="breadcrumb-item"><a href="{% url 'job_list' %}" class="text-secondary">Jobs</a></li>
|
|
<li class="breadcrumb-item active" aria-current="page" style="
|
|
color: #F43B5E; /* Rosy Accent Color */
|
|
font-weight: 600;
|
|
">Job Detail</li>
|
|
</ol>
|
|
</nav>
|
|
<div class="row g-4">
|
|
|
|
{# LEFT COLUMN: JOB DETAILS (NO TABS) #}
|
|
<div class="col-lg-7">
|
|
<div class="card shadow-sm no-hover">
|
|
|
|
{# HEADER SECTION #}
|
|
<div class="job-header-card d-flex justify-content-between align-items-center flex-wrap">
|
|
<div>
|
|
<h2 class="mb-1">{{ job.title }}</h2>
|
|
<small class="text-light">{% trans "JOB ID: "%}{{ job.internal_job_id }}</small>
|
|
|
|
{# Deadline #}
|
|
{% if job.application_deadline %}
|
|
<div class="text-light mt-1">
|
|
<i class="fas fa-calendar-times me-2"></i>
|
|
<strong>{% trans "Deadline:" %}</strong> <span class="text-warning fw-bold">{{ job.application_deadline }}</span>
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
|
|
<div class="d-flex align-items-center gap-2 mt-2 mt-md-0">
|
|
|
|
{# Status badge #}
|
|
<div class="d-flex align-items-center">
|
|
<span class="status-badge
|
|
{% if job.status == "ACTIVE" %}bg-success
|
|
{% elif job.status == "DRAFT" %}bg-secondary
|
|
{% elif job.status == "CLOSED" %}bg-warning
|
|
{% elif job.status == "CANCELLED" %}bg-danger
|
|
{% elif job.status == "ARCHIVED" %}bg-secondary
|
|
{% else %}bg-secondary{% endif %}">
|
|
{{ job.get_status_display }}
|
|
</span>
|
|
|
|
<button type="button" class="btn btn-outline-light btn-sm ms-2" data-bs-toggle="modal" data-bs-target="#editStatusModal">
|
|
<i class="fas fa-edit"></i>
|
|
</button>
|
|
</div>
|
|
|
|
{# Share Public Link Button #}
|
|
<button
|
|
type="button"
|
|
class="btn btn-main-action btn-sm"
|
|
id="copyJobLinkButton"
|
|
data-url="{{ job.application_url }}">
|
|
<i class="fas fa-link"></i>
|
|
{% trans "Share Public Link" %}
|
|
</button>
|
|
|
|
<span id="copyFeedback" class="text-success ms-2 small" style="display:none;">
|
|
{% trans "Copied!" %}
|
|
</span>
|
|
</div>
|
|
</div>
|
|
|
|
{# CONTENT: CORE DETAILS (No Tabs) #}
|
|
<div class="card-body">
|
|
|
|
<h5 class="text-muted mb-3">{% trans "Administrative & Location" %}
|
|
<a href="{% url 'job_update' job.slug %}" class="btn btn-main-action btn-sm"><li class="fa fa-edit"></li>{% trans "Edit JOb" %}</a>
|
|
</h5>
|
|
<div class="row g-3 mb-4 border-bottom pb-3 small text-secondary">
|
|
<div class="col-md-6">
|
|
<i class="fas fa-building me-2 text-primary"></i> <strong>{% trans "Department:" %}</strong> {{ job.department|default:"N/A" }}
|
|
</div>
|
|
<div class="col-md-6">
|
|
<i class="fas fa-hashtag me-2 text-primary"></i> <strong>{% trans "Position No:" %}</strong> {{ job.position_number|default:"N/A" }}
|
|
</div>
|
|
<div class="col-md-6">
|
|
<i class="fas fa-briefcase me-2 text-primary"></i> <strong>{% trans "Job Type:" %}</strong> {{ job.get_job_type_display }}
|
|
</div>
|
|
<div class="col-md-6">
|
|
<i class="fas fa-map-pin me-2 text-primary"></i> <strong>{% trans "Workplace:" %}</strong> {{ job.get_workplace_type_display }}
|
|
</div>
|
|
<div class="col-md-6">
|
|
<i class="fas fa-globe me-2 text-primary"></i> <strong>{% trans "Location:" %}</strong> {{ job.get_location_display }}
|
|
</div>
|
|
<div class="col-md-6">
|
|
<i class="fa-solid fa-money-bill me-2 text-primary"></i> <strong>{% trans "Salary:" %}</strong> {{ job.salary_range |default:"N/A" }}
|
|
</div>
|
|
<div class="col-md-6">
|
|
<i class="fas fa-user-tie me-2 text-primary"></i> <strong>{% trans "Created By:" %}</strong> {{ job.created_by|default:"N/A" }}
|
|
</div>
|
|
<div class="col-md-6">
|
|
<i class="fas fa-plus me-2 text-primary"></i> <strong>{% trans "Created At:" %}</strong> {{ job.created_at|default:"N/A" }}
|
|
</div>
|
|
<div class="col-md-6">
|
|
<i class="fas fa-edit me-2 text-primary"></i> <strong>{% trans "Updated At:" %}</strong> {{ job.updated_at|default:"N/A" }}
|
|
</div>
|
|
</div>
|
|
|
|
{# Description Blocks (Main Content) #}
|
|
{% if job.description %}
|
|
<div class="mb-4">
|
|
<h5>{% trans "Job Description" %}</h5>
|
|
<div class="text-secondary">{{ job.description|safe }}</div>
|
|
</div>
|
|
{% endif %}
|
|
{% if job.qualifications %}
|
|
<div class="mb-4">
|
|
<h5>{% trans "Required Qualifications" %}</h5>
|
|
<div class="text-secondary">{{ job.qualifications|safe }}</div>
|
|
</div>
|
|
{% endif %}
|
|
{% if job.benefits %}
|
|
<div class="mb-4">
|
|
<h5>{% trans "Benefits" %}</h5>
|
|
<div class="text-secondary">{{ job.benefits|safe}}</div>
|
|
</div>
|
|
{% endif %}
|
|
{% if job.application_instructions %}
|
|
<div class="mb-4">
|
|
<h5>{% trans "Application Instructions" %}</h5>
|
|
<div class="text-secondary">{{ job.application_instructions|safe }}</div>
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
|
|
</div>
|
|
</div>
|
|
|
|
{# RIGHT COLUMN: TABBED CARDS #}
|
|
<div class="col-lg-5">
|
|
|
|
<div class="card shadow-sm no-hover mb-4">
|
|
{# RIGHT TABS NAVIGATION #}
|
|
<ul class="nav nav-tabs" id="rightJobTabs" role="tablist">
|
|
<li class="nav-item flex-fill" role="presentation">
|
|
<button class="nav-link active" id="applicants-tab" data-bs-toggle="tab" data-bs-target="#applicants-pane" type="button" role="tab" aria-controls="applicants-pane" aria-selected="true">
|
|
<i class="fas fa-users me-1"></i> {% trans "Applicants" %}
|
|
</button>
|
|
</li>
|
|
<li class="nav-item flex-fill" role="presentation">
|
|
<button class="nav-link" id="tracking-tab" data-bs-toggle="tab" data-bs-target="#tracking-pane" type="button" role="tab" aria-controls="tracking-pane" aria-selected="false">
|
|
<i class="fas fa-project-diagram me-1"></i> {% trans "Tracking" %}
|
|
</button>
|
|
</li>
|
|
<li class="nav-item flex-fill" role="presentation">
|
|
<button class="nav-link" id="manage-tab" data-bs-toggle="tab" data-bs-target="#manage-pane" type="button" role="tab" aria-controls="manage-pane" aria-selected="false">
|
|
<i class="fas fa-cogs me-1"></i> {% trans "Form Template" %}
|
|
</button>
|
|
</li>
|
|
<li class="nav-item flex-fill" role="presentation">
|
|
<button class="nav-link" id="linkedin-tab" data-bs-toggle="tab" data-bs-target="#linkedin-pane" type="button" role="tab" aria-controls="linkedin-pane" aria-selected="false">
|
|
<i class="fab fa-linkedin me-1 text-info"></i> {% trans "LinkedIn" %}
|
|
</button>
|
|
</li>
|
|
</ul>
|
|
|
|
<div class="tab-content p-3" id="rightJobTabsContent">
|
|
|
|
{# TAB 1: APPLICANTS CONTENT #}
|
|
<div class="tab-pane fade show active" id="applicants-pane" role="tabpanel" aria-labelledby="applicants-tab">
|
|
<h5 class="mb-3">{% trans "Total Applicants" %} (<span id="total_candidates">{{ total_applicants }}</span>)</h5>
|
|
|
|
<div class="d-grid gap-3">
|
|
<a href="{% url 'candidate_create_for_job' job.slug %}" class="btn btn-main-action">
|
|
<i class="fas fa-user-plus me-1"></i> {% trans "Create Applicant" %}
|
|
</a>
|
|
<a href="{% url 'candidate_screening_view' job.slug %}" class="btn btn-main-action">
|
|
<i class="fas fa-layer-group me-1"></i> {% trans "Manage Applicants" %}
|
|
</a>
|
|
</div>
|
|
</div>
|
|
|
|
{# TAB 2: TRACKING CONTENT #}
|
|
<div class="tab-pane fade" id="tracking-pane" role="tabpanel" aria-labelledby="tracking-tab">
|
|
<h5 class="mb-3"><i class="fas fa-project-diagram me-2 text-primary"></i>{% trans "Applicant Stages" %}</h5>
|
|
{% include 'jobs/partials/applicant_tracking.html' %}
|
|
<p class="text-muted small">
|
|
{% trans "The applicant tracking flow is defined by the attached Form Template. View the Form Template tab to manage stages and fields." %}
|
|
</p>
|
|
{# Placeholder for stage tracker component #}
|
|
</div>
|
|
|
|
{# TAB 3: MANAGEMENT (Form Template) CONTENT #}
|
|
<div class="tab-pane fade" id="manage-pane" role="tabpanel" aria-labelledby="manage-tab">
|
|
<h5 class="mb-3"><i class="fas fa-clipboard-list me-2 text-primary"></i>{% trans "Form Management" %}</h5>
|
|
<div class="d-grid gap-3">
|
|
<p class="text-muted small mb-3">
|
|
{% trans "Manage the custom application forms associated with this job posting." %}
|
|
</p>
|
|
|
|
{% if not job.form_template %}
|
|
<a href="{% url 'create_form_template' %}" class="btn btn-main-action">
|
|
<i class="fas fa-plus-circle me-1"></i> {% trans "Create New Form Template" %}
|
|
</a>
|
|
{% else %}
|
|
<a href="{% url 'application_submit_form' job.form_template.pk %}" class="btn btn-outline-secondary w-100">
|
|
<i class="fas fa-list-alt me-1"></i> {% trans "View Form Template" %}
|
|
</a>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
|
|
{# TAB 4: LINKEDIN INTEGRATION CONTENT #}
|
|
<div class="tab-pane fade" id="linkedin-pane" role="tabpanel" aria-labelledby="linkedin-tab">
|
|
<h5 class="mb-3"><i class="fab fa-linkedin me-2 text-info"></i>{% trans "LinkedIn Integration" %}</h5>
|
|
<div class="d-grid gap-3">
|
|
{% if job.posted_to_linkedin %}
|
|
<div class="alert alert-success p-2 small mb-0">
|
|
<i class="fas fa-check-circle me-1"></i> {% trans "Posted successfully!" %}
|
|
</div>
|
|
{% if job.linkedin_post_url %}
|
|
<a href="{{ job.linkedin_post_url }}" target="_blank" class="btn btn-outline-secondary">
|
|
<i class="fab fa-linkedin me-1"></i> {% trans "View on LinkedIn" %}
|
|
</a>
|
|
{% endif %}
|
|
<small class="text-muted d-block text-center">
|
|
{% trans "Posted on:" %} {{ job.linkedin_posted_at|date:"M d, Y" }}
|
|
</small>
|
|
{% else %}
|
|
<p class="text-muted small mb-0">{% 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="btn btn-main-action w-100"
|
|
{% if not request.session.linkedin_authenticated %}disabled{% endif %}>
|
|
<i class="fab fa-linkedin me-1"></i>
|
|
{% if job.posted_to_linkedin %}{% trans "Re-post to LinkedIn" %}{% else %}{% trans "Post to LinkedIn" %}{% endif %}
|
|
</button>
|
|
</form>
|
|
|
|
<button type="button" class="btn btn-outline-secondary w-100" data-bs-toggle="modal" data-bs-target="#myModalForm">
|
|
<i class="fas fa-image me-1"></i> {% trans "Upload Image for Post" %}
|
|
</button>
|
|
|
|
{% if not request.session.linkedin_authenticated %}
|
|
<small class="text-muted d-block text-center">
|
|
{% trans "You need to" %} <a href="{% url 'linkedin_login' %}">{% trans "authenticate with LinkedIn" %}</a> {% trans "first." %}
|
|
</small>
|
|
{% endif %}
|
|
|
|
{% if job.linkedin_post_status and 'ERROR' in job.linkedin_post_status %}
|
|
<div class="alert alert-danger mt-3 p-2 small">
|
|
<i class="fas fa-exclamation-triangle me-1"></i>
|
|
<small>{% trans "Error:" %} {{ job.linkedin_post_status }}</small>
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{# Card 2: Candidate Category Chart #}
|
|
<div class="card shadow-sm no-hover mb-4">
|
|
<div class="card-header">
|
|
<h6 class="mb-0">
|
|
<i class="fas fa-chart-pie me-2 text-primary"></i>
|
|
{% trans "Candidate Categories & Scores" %}
|
|
</h6>
|
|
</div>
|
|
<div class="card-body p-4">
|
|
<div style="height: 300px;">
|
|
<canvas id="jobCategoryMatchChart"></canvas>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{# Card 3: KPIs #}
|
|
<div class="card shadow-sm no-hover mb-4">
|
|
<div class="card-header">
|
|
<h6 class="mb-0">
|
|
<i class="fas fa-info-circle me-1 text-primary"></i>
|
|
{% trans "Key Performance Indicators" %}
|
|
</h6>
|
|
</div>
|
|
<div class="card-body p-4">
|
|
|
|
<div class="row g-3 stats-grid">
|
|
|
|
{# 1. Job Avg. Score #}
|
|
<div class="col-6">
|
|
<div class="card text-center h-100 kpi-card">
|
|
<div class="card-body p-2">
|
|
<i class="fas fa-star text-primary mb-1 d-block" style="font-size: 1.2rem;"></i>
|
|
<div class="h4 mb-0 text-primary fw-bold">{{ avg_match_score|floatformat:1 }}</div>
|
|
<small class="text-muted d-block">{% trans "Avg. AI Score" %}</small>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{# 2. High Potential Count #}
|
|
<div class="col-6">
|
|
<div class="card text-center h-100">
|
|
<div class="card-body p-2">
|
|
<i class="fas fa-trophy text-success mb-1 d-block" style="font-size: 1.2rem;"></i>
|
|
<div class="h4 mb-0 text-success fw-bold">{{ high_potential_count }}</div>
|
|
<small class="text-muted d-block">{% trans "High Potential" %}</small>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{# 3. Avg. Time to Interview #}
|
|
<div class="col-6">
|
|
<div class="card text-center h-100">
|
|
<div class="card-body p-2">
|
|
<i class="fas fa-calendar-alt text-info mb-1 d-block" style="font-size: 1.2rem;"></i>
|
|
<div class="h4 mb-0 text-info fw-bold">{{ avg_t2i_days|floatformat:1 }}d</div>
|
|
<small class="text-muted d-block">{% trans "Time to Interview" %}</small>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{# 4. Avg. Exam Review Time #}
|
|
<div class="col-6">
|
|
<div class="card text-center h-100">
|
|
<div class="card-body p-2">
|
|
<i class="fas fa-hourglass-half text-secondary mb-1 d-block" style="font-size: 1.2rem;"></i>
|
|
<div class="h4 mb-0 text-secondary fw-bold">{{ avg_t_in_exam_days|floatformat:1 }}d</div>
|
|
<small class="text-muted d-block">{% trans "Avg. Exam Review" %}</small>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{% include "jobs/partials/image_upload.html" %}
|
|
|
|
<div class="modal fade" id="editStatusModal" tabindex="-1" aria-labelledby="editStatusModalLabel" aria-hidden="true">
|
|
<div class="modal-dialog">
|
|
<div class="modal-content">
|
|
<form method="post" action="{% url 'job_detail' slug=job.slug %}">
|
|
{% csrf_token %}
|
|
<div class="modal-header">
|
|
<h5 class="modal-title" id="editStatusModalLabel">{% trans "Edit Job Status" %}</h5>
|
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="{% trans 'Close' %}"></button>
|
|
</div>
|
|
<div class="modal-body">
|
|
<div class="mb-3">
|
|
{% if status_form %}
|
|
<label for="{{ status_form.status.id_for_label }}" class="form-label">{% trans "Select New Status" %}</label>
|
|
{{ status_form.status }}
|
|
{% if status_form.status.errors %}
|
|
<div class="text-danger small mt-1">{{ status_form.status.errors }}</div>
|
|
{% endif %}
|
|
{% else %}
|
|
<div class="text-danger">{% trans "Status form not available. Please check your view." %}</div>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
<div class="modal-footer">
|
|
<button type="button" class="btn btn-outline-secondary btn-lg" data-bs-dismiss="modal">{% trans "Cancel" %}</button>
|
|
<button type="submit" class="btn btn-main-action">{% trans "Save Changes" %}</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{% endblock %}
|
|
|
|
|
|
{% block customJS%}
|
|
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
|
|
<script>
|
|
// Pass data from Django to JavaScript safely
|
|
window.jobChartData = {
|
|
categories: {{ categories|safe|default:"[]" }},
|
|
candidate_counts: {{ candidate_counts|safe|default:"[]" }},
|
|
avg_scores: {{ avg_scores|safe|default:"[]" }}
|
|
};
|
|
|
|
document.getElementById('copyJobLinkButton').addEventListener('click', function() {
|
|
// 1. Get the URL from the data attribute
|
|
const urlToCopy = this.getAttribute('data-url');
|
|
|
|
// 2. Use the modern Clipboard API
|
|
navigator.clipboard.writeText(urlToCopy).then(() => {
|
|
|
|
// 3. Show feedback message
|
|
const feedback = document.getElementById('copyFeedback');
|
|
feedback.style.display = 'inline';
|
|
|
|
// 4. Hide feedback after 2 seconds
|
|
setTimeout(() => {
|
|
feedback.style.display = 'none';
|
|
}, 2000);
|
|
|
|
}).catch(err => {
|
|
// Fallback for older browsers or security issues
|
|
console.error('Could not copy text: ', err);
|
|
alert("Copy failed. Please copy the URL manually: " + urlToCopy);
|
|
});
|
|
});
|
|
|
|
// Chart for Candidate Categories and Match Scores
|
|
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');
|
|
|
|
// Safely get job_category_data from Django context
|
|
// Using window.jobChartData to avoid template parsing issues
|
|
const jobChartData = window.jobChartData || { categories: [], candidate_counts: [], avg_scores: []};
|
|
|
|
const categories = jobChartData.categories || [];
|
|
const candidateCounts = jobChartData.candidate_counts || [];
|
|
const avgScores = jobChartData.avg_scores || [];
|
|
|
|
if (categories.length > 0) { // Only render if there's data
|
|
const chart = new Chart(chartCtx, {
|
|
type: 'doughnut',
|
|
data: {
|
|
labels: categories,
|
|
datasets: [
|
|
{
|
|
label: 'Number of Candidates',
|
|
data: candidateCounts,
|
|
backgroundColor: [
|
|
'rgba(0, 99, 110, 0.7)', // --kaauh-teal
|
|
'rgba(23, 162, 184, 0.7)', // Teal shade
|
|
'rgba(0, 150, 136, 0.7)', // Teal green
|
|
'rgba(0, 188, 212, 0.7)', // Cyan
|
|
'rgba(38, 166, 154, 0.7)', // Turquoise
|
|
'rgba(77, 182, 172, 0.7)', // Medium teal
|
|
// Add more colors if you expect more categories
|
|
],
|
|
borderColor: [
|
|
'rgba(0, 99, 110, 1)',
|
|
'rgba(23, 162, 184, 1)',
|
|
'rgba(0, 150, 136, 1)',
|
|
'rgba(0, 188, 212, 1)',
|
|
'rgba(38, 166, 154, 1)',
|
|
'rgba(77, 182, 172, 1)',
|
|
// Add more colors if you expect more categories
|
|
],
|
|
borderWidth: 1,
|
|
}
|
|
]
|
|
},
|
|
options: {
|
|
responsive: true,
|
|
maintainAspectRatio: false, // Important for fixed height container
|
|
plugins: {
|
|
legend: {
|
|
position: 'right', // Position legend for doughnut chart
|
|
},
|
|
title: {
|
|
display: false, // Chart title is handled by the card header
|
|
},
|
|
tooltip: {
|
|
callbacks: {
|
|
label: function(context) {
|
|
let label = context.label || '';
|
|
if (label) {
|
|
label += ': ';
|
|
}
|
|
label += context.parsed + ' candidate(s)';
|
|
return label;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
});
|
|
} else {
|
|
// Display a message if no data is available
|
|
chartCtx.canvas.parentNode.innerHTML = '<p class="text-center text-muted mt-4">No candidate category data available for this job.</p>';
|
|
}
|
|
});
|
|
</script>
|
|
|
|
{% endblock %} |