chart fix

This commit is contained in:
Faheed 2025-11-02 14:30:39 +03:00
parent f4bddfc391
commit dace6bc0c3
10 changed files with 100 additions and 143 deletions

4
.gitignore vendored
View File

@ -27,7 +27,7 @@ var/
*.log
*.pot
*.sqlite3
local_settings.py
settings.py
db.sqlite3
# Virtual environment
@ -95,7 +95,7 @@ coverage.xml
# Django stuff:
# Local settings
local_settings.py
settings.py
# Database sqlite files:
# The base directory for relative paths in .gitignore

View File

@ -135,9 +135,9 @@ WSGI_APPLICATION = 'NorahUniversity.wsgi.application'
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.postgresql_psycopg2',
'NAME': 'norahuniversity',
'USER': 'norahuniversity',
'PASSWORD': 'norahuniversity',
'NAME': 'haikal_db',
'USER': 'faheed',
'PASSWORD': 'Faheed@215',
'HOST': '127.0.0.1',
'PORT': '5432',
}
@ -185,26 +185,14 @@ ACCOUNT_SIGNUP_FIELDS = ['email*', 'password1*', 'password2*']
ACCOUNT_UNIQUE_EMAIL = True
ACCOUNT_EMAIL_VERIFICATION = 'none'
ACCOUNT_USER_MODEL_USERNAME_FIELD = None
ACCOUNT_EMAIL_VERIFICATION = "mandatory"
ACCOUNT_EMAIL_VERIFICATION = "mandatory"
ACCOUNT_LOGIN_ON_EMAIL_CONFIRMATION = True
ACCOUNT_FORMS = {'signup': 'recruitment.forms.StaffSignupForm'}
EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
EMAIL_HOST = '10.10.1.110' #'smtp.gmail.com'
EMAIL_PORT = 2225 #587
EMAIL_USE_TLS = False
EMAIL_USE_SSL = False
EMAIL_TIMEOUT = 10
DEFAULT_FROM_EMAIL = 'norahuniversity@example.com'
# Gmail SMTP credentials
# Remove the comment below if you want to use Gmail SMTP server
# EMAIL_HOST_USER = 'your_email@gmail.com'
# EMAIL_HOST_PASSWORD = 'your_password'
EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'
# Crispy Forms Configuration
CRISPY_ALLOWED_TEMPLATE_PACKS = "bootstrap5"

View File

@ -30,54 +30,113 @@
box-shadow: 0 6px 16px rgba(0,0,0,0.1);
}
/* Card Header and Icon Styling */
/* ------------------------------------------------------------- */
/* CONSOLIDATED CARD HEADER STYLES (for both main and stat cards)*/
/* ------------------------------------------------------------- */
.card-header {
font-weight: 600;
padding: 1.25rem;
/* Consistent, reduced padding for compact look */
padding: 0.5rem 0.75rem;
border-bottom: 1px solid var(--kaauh-border);
background-color: #f8f9fa;
display: flex;
justify-content: space-between;
align-items: center;
height: auto;
}
.card-header h3, .card-header h2 {
/* Target h2 (for main headers) and h3 (for stat card headers) */
.card-header h1, .card-header h2, .card-header h3, .card-header h6 {
display: flex;
align-items: center;
color: var(--kaauh-primary-text);
font-size: 1.25rem;
font-weight: 600;
margin: 0;
/* Font size for MAIN card titles (e.g., "Data Scope: All Jobs") */
font-size: 1.25rem;
font-weight: 600;
}
/* Override for H3 inside stat cards to make them very small */
.stats .card-header h3 {
font-size: 0.85rem; /* Smallest size for the 9-card layout */
font-weight: 600;
white-space: nowrap; /* Prevent title from wrapping */
overflow: hidden;
text-overflow: ellipsis;
max-width: 100%;
}
.stat-icon {
color: var(--kaauh-teal);
font-size: 1.75rem;
margin-right: 0.75rem;
font-size: 0.8rem; /* Small icon size */
margin-right: 0.25rem;
/* Note: For 9-card density, you might still want to hide this on mobile */
}
/* Stats Grid Layout */
/* ------------------------------------------------------------- */
/* STATS GRID LAYOUT (9 Columns) */
/* ------------------------------------------------------------- */
.stats {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
gap: 1.5rem;
margin-bottom: 3rem;
/* Force 9 columns */
grid-template-columns: repeat(9, 1fr);
gap: 0.75rem;
margin-bottom: 2rem;
}
/* Stat Card Specific Styling */
.stat-value {
font-size: 2.8rem;
/* This is the most important number */
font-size: 1.5rem; /* Increased slightly for focus, was 1rem/1.25rem */
text-align: center;
color: var(--kaauh-teal-dark);
font-weight: 700;
padding: 1rem 1rem 0.5rem;
font-weight: 700;
padding: 0.5rem 0.25rem 0.1rem; /* Very little bottom padding */
line-height: 1.2;
}
.stat-caption {
font-size: 0.9rem;
/* Smallest text for the label below the value */
font-size: 0.7rem; /* Minimized size */
text-align: center;
color: #6c757d;
padding-bottom: 1rem;
padding: 0 0.25rem 0.5rem;
line-height: 1.1;
}
/* ------------------------------------------------------------- */
/* RESPONSIVE DESIGN */
/* ------------------------------------------------------------- */
/* On tablets and smaller laptops (1200px and down) */
@media (max-width: 1200px) {
.stats {
/* Switch to 4 columns on medium screens */
grid-template-columns: repeat(4, 1fr);
gap: 1rem;
}
.stat-value {
font-size: 1.75rem; /* Increase value size slightly when more space is available */
}
.stat-caption {
font-size: 0.8rem;
}
}
/* On phones (576px and down) */
@media (max-width: 576px) {
.stats {
/* Stack to 2 columns on mobile for readability */
grid-template-columns: repeat(2, 1fr);
gap: 0.75rem;
}
.stat-value {
font-size: 1.5rem;
}
.stat-caption {
font-size: 0.75rem;
}
}
/* Dropdown/Filter Styling */
@ -130,7 +189,7 @@
<h2>
<i class="fas fa-search stat-icon"></i>
{% if current_job %}
{% trans "Data Scope: " %} **{{ current_job.title }}**
{% trans "Data Scope: " %}{{ current_job.title }}
{% else %}
{% trans "Data Scope: All Jobs" %}
{% endif %}
@ -153,9 +212,7 @@
{# STATS CARDS SECTION (12 KPIs) #}
{# -------------------------------------------------------------------------- #}
{% include 'recruitment/partials/stats_cards.html' %}
{# Note: The content of 'recruitment/partials/stats_cards.html' uses h3 which is styled correctly here #}
{# -------------------------------------------------------------------------- #}
{# CHARTS SECTION #}
@ -197,7 +254,7 @@
<h2>
<i class="fas fa-funnel-dollar stat-icon"></i>
{% if current_job %}
{% trans "Pipeline Funnel: " %} **{{ current_job.title }}**
{% trans "Pipeline Funnel: " %}{{ current_job.title }}
{% else %}
{% trans "Total Pipeline Funnel (All Jobs)" %}
{% endif %}
@ -223,28 +280,13 @@
</div>
</div>
<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 "Candidates From Each Sources" %}
</h6>
</div>
<div class="card-body p-4">
<div style="height: 300px;">
<canvas id="candidatesourceschart"></canvas>
</div>
</div>
</div>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.0/dist/chart.umd.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/luxon@3.4.4"></script>
<script src="https://cdn.jsdelivr.net/npm/chartjs-adapter-luxon@1.3.1"></script>
<script>
// Pass context data safely to JavaScript
const totalCandidatesScoped = parseInt('{{ total_candidates|default:0 }}');
@ -462,80 +504,7 @@
// Chart for Candidate Categories and Match Scores
document.addEventListener('DOMContentLoaded', function() {
const ctx = document.getElementById('candidatesourceschart');
if (!ctx) {
console.warn('Candidates sources chart element not found.');
return;
}
const chartCtx = ctx.getContext('2d');
// Safely get job_category_data from Django context
// Using window.jobChartData to avoid template parsing issues
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: candidates_count_in_each_source,
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 %}

View File

@ -10,7 +10,7 @@
<h3><i class="fas fa-list stat-icon"></i> {% trans "Total Jobs" %}</h3>
</div>
<div class="stat-value">{{ total_jobs_global }}</div>
<div class="stat-caption">{% trans "All Active & Drafted Positions (Global)" %}</div>
<div class="stat-caption">{% trans "All Active & Drafted Positions" %}</div>
</div>
{# SCOPED - 2. Total Active Jobs #}
@ -19,7 +19,7 @@
<h3><i class="fas fa-briefcase stat-icon"></i> {% trans "Active Jobs" %}</h3>
</div>
<div class="stat-value">{{ total_active_jobs }}</div>
<div class="stat-caption">{% trans "Currently Open Requisitions (Scoped)" %}</div>
<div class="stat-caption">{% trans "Currently Open Requisitions" %}</div>
</div>
{# SCOPED - 3. Total Candidates #}
@ -28,7 +28,7 @@
<h3><i class="fas fa-users stat-icon"></i> {% trans "Total Candidates" %}</h3>
</div>
<div class="stat-value">{{ total_candidates }}</div>
<div class="stat-caption">{% trans "Total Profiles in Current Scope" %}</div>
<div class="stat-caption">{% trans "Total applications" %}</div>
</div>
{# SCOPED - 4. Open Positions #}
@ -37,7 +37,7 @@
<h3><i class="fas fa-th-list stat-icon"></i> {% trans "Open Positions" %}</h3>
</div>
<div class="stat-value">{{ total_open_positions }}</div>
<div class="stat-caption">{% trans "Total Slots to be Filled (Scoped)" %}</div>
<div class="stat-caption">{% trans "Total Slots to be Filled " %}</div>
</div>
{# GLOBAL - 5. Total Participants #}
@ -46,11 +46,11 @@
<h3><i class="fas fa-address-book stat-icon"></i> {% trans "Total Participants" %}</h3>
</div>
<div class="stat-value">{{ total_participants }}</div>
<div class="stat-caption">{% trans "Total Recruiters/Interviewers (Global)" %}</div>
<div class="stat-caption">{% trans "Total Recruiters/Interviewers" %}</div>
</div>
{# GLOBAL - 6. Total LinkedIn Posts #}
<div class="card">
{% comment %} <div class="card">
<div class="card-header">
<h3><i class="fab fa-linkedin stat-icon"></i> {% trans "LinkedIn Posts" %}</h3>
</div>
@ -65,13 +65,13 @@
<div class="stat-value">{{ new_candidates_7days }}</div>
<div class="stat-caption">{% trans "Incoming applications last week" %}</div>
</div>
{% endcomment %}
<div class="card">
<div class="card-header">
<h3><i class="fas fa-cogs stat-icon"></i> {% trans "Avg. Apps per Job" %}</h3>
</div>
<div class="stat-value">{{ average_applications|floatformat:1 }}</div>
<div class="stat-caption">{% trans "Average Applications per Job (Scoped)" %}</div>
<div class="stat-caption">{% trans "Average Applications per Job" %}</div>
</div>
{# --- Efficiency & Quality Metrics --- #}
@ -81,7 +81,7 @@
<h3><i class="fas fa-clock stat-icon"></i> {% trans "Time-to-Hire" %}</h3>
</div>
<div class="stat-value">{{ avg_time_to_hire_days }}</div>
<div class="stat-caption">{% trans "Avg. Days (Application to Hired)" %}</div>
<div class="stat-caption">{% trans "Average Days" %}</div>
</div>
<div class="card">
@ -89,7 +89,7 @@
<h3><i class="fas fa-star stat-icon"></i> {% trans "Avg. Match Score" %}</h3>
</div>
<div class="stat-value">{{ avg_match_score|floatformat:1 }}</div>
<div class="stat-caption">{% trans "Average AI Score (Current Scope)" %}</div>
<div class="stat-caption">{% trans "Average AI Score " %}</div>
</div>
<div class="card">
@ -100,13 +100,13 @@
<div class="stat-caption">{% trans "Score ≥ 75% Profiles" %} ({{ high_potential_ratio|floatformat:1 }}%)</div>
</div>
<div class="card">
{% comment %} <div class="card">
<div class="card-header">
<h3><i class="fas fa-calendar-alt stat-icon"></i> {% trans "Meetings This Week" %}</h3>
</div>
<div class="stat-value">{{ meetings_scheduled_this_week }}</div>
<div class="stat-caption">{% trans "Scheduled Interviews (Current Week)" %}</div>
</div>
</div> {% endcomment %}
</div>