hospital-management/templates/hr/training/training_management.html
Marwan Alwali 23158e9fbf update
2025-09-08 03:00:23 +03:00

369 lines
16 KiB
HTML

{% extends 'base.html' %}
{% load static %}
{% block title %}Training Management{% endblock %}
{% block content %}
<div id="content" class="app-content">
<div class="container-fluid">
<ul class="breadcrumb">
<li class="breadcrumb-item"><a href="{% url 'core:dashboard' %}">Dashboard</a></li>
<li class="breadcrumb-item"><a href="{% url 'hr:dashboard' %}">HR</a></li>
<li class="breadcrumb-item active">Training Management</li>
</ul>
<div class="row align-items-center mb-3">
<div class="col">
<h1 class="page-header">Training Management</h1>
<p class="text-muted">Employee training records and certification tracking</p>
</div>
<div class="col-auto">
<a class="btn btn-primary" href="{% url 'hr:training_record_create' %}">
<i class="fa fa-plus me-2"></i>New Training Record
</a>
</div>
</div>
<!-- Overview -->
<div class="row mb-4">
<div class="col-md-3">
<div class="card bg-primary text-white">
<div class="card-body d-flex align-items-center">
<div class="flex-grow-1">
<h4 class="mb-0">{{ total_records }}</h4>
<p class="mb-0">Total Records</p>
</div>
<i class="fa fa-database fa-2x ms-3"></i>
</div>
</div>
</div>
<div class="col-md-3">
<div class="card bg-success text-white">
<div class="card-body d-flex align-items-center">
<div class="flex-grow-1">
<h4 class="mb-0">{{ completed_trainings }}</h4>
<p class="mb-0">Completed</p>
</div>
<i class="fa fa-check-circle fa-2x ms-3"></i>
</div>
</div>
</div>
<div class="col-md-3">
<div class="card bg-warning text-white">
<div class="card-body d-flex align-items-center">
<div class="flex-grow-1">
<h4 class="mb-0">{{ pending_trainings }}</h4>
<p class="mb-0">Scheduled / In Progress</p>
</div>
<i class="fa fa-clock fa-2x ms-3"></i>
</div>
</div>
</div>
<div class="col-md-3">
<div class="card bg-danger text-white">
<div class="card-body d-flex align-items-center">
<div class="flex-grow-1">
<h4 class="mb-0">{{ overdue_trainings }}</h4>
<p class="mb-0">Expired / Overdue</p>
</div>
<i class="fa fa-exclamation-triangle fa-2x ms-3"></i>
</div>
</div>
</div>
</div>
<!-- Tabs -->
<div class="card">
<div class="card-header">
<ul class="nav nav-tabs card-header-tabs" id="trainingTabs" role="tablist">
<li class="nav-item" role="presentation">
<button class="nav-link active" id="records-tab" data-bs-toggle="tab" data-bs-target="#records" type="button" role="tab">
<i class="fa fa-list me-2"></i>Training Records
</button>
</li>
<li class="nav-item" role="presentation">
<button class="nav-link" id="compliance-tab" data-bs-toggle="tab" data-bs-target="#compliance" type="button" role="tab">
<i class="fa fa-shield-alt me-2"></i>Compliance
</button>
</li>
</ul>
</div>
<div class="card-body">
<div class="tab-content" id="trainingTabContent">
<!-- Training Records -->
<div class="tab-pane fade show active" id="records" role="tabpanel">
<div class="row mb-3">
<div class="col-md-4">
<div class="input-group">
<input type="text" class="form-control" placeholder="Search records..." id="recordSearch">
<button class="btn btn-outline-secondary" type="button">
<i class="fa fa-search"></i>
</button>
</div>
</div>
<div class="col-md-3">
<select class="form-select" id="recordType">
<option value="">All Types</option>
<option value="ORIENTATION">Orientation</option>
<option value="MANDATORY">Mandatory</option>
<option value="CONTINUING_ED">Continuing Education</option>
<option value="CERTIFICATION">Certification</option>
<option value="SKILLS">Skills Training</option>
<option value="SAFETY">Safety Training</option>
<option value="COMPLIANCE">Compliance Training</option>
<option value="LEADERSHIP">Leadership Development</option>
<option value="TECHNICAL">Technical Training</option>
<option value="OTHER">Other</option>
</select>
</div>
<div class="col-md-3">
<select class="form-select" id="recordStatus">
<option value="">All Status</option>
<option value="SCHEDULED">Scheduled</option>
<option value="IN_PROGRESS">In Progress</option>
<option value="COMPLETED">Completed</option>
<option value="CANCELLED">Cancelled</option>
<option value="NO_SHOW">No Show</option>
<option value="FAILED">Failed</option>
</select>
</div>
<div class="col-md-2 text-end">
<a class="btn btn-primary w-100" href="{% url 'hr:training_record_create' %}">
<i class="fa fa-plus me-1"></i>Create
</a>
</div>
</div>
<div class="table-responsive">
<table class="table table-hover" id="recordsTable">
<thead>
<tr>
<th>Employee</th>
<th>Training</th>
<th>Type</th>
<th>Training Date</th>
<th>Completion</th>
<th>Status</th>
<th>Expiry</th>
<th>Score</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
{% for tr in training_records %}
<tr>
<td>
<div>
<strong>{{ tr.employee.get_full_name }}</strong><br>
<small class="text-muted">{{ tr.employee.job_title }}</small>
</div>
</td>
<td>
<div>
<strong>{{ tr.training_name }}</strong>
{% if tr.training_provider %}<br><small class="text-muted">{{ tr.training_provider }}</small>{% endif %}
</div>
</td>
<td><span class="badge bg-secondary">{{ tr.get_training_type_display }}</span></td>
<td>{{ tr.training_date|date:"M d, Y" }}</td>
<td>
{% if tr.completion_date %}
{{ tr.completion_date|date:"M d, Y" }}
{% else %}
<span class="text-muted"></span>
{% endif %}
</td>
<td>
{% if tr.status == 'COMPLETED' %}
<span class="badge bg-success">Completed</span>
{% elif tr.status == 'IN_PROGRESS' %}
<span class="badge bg-warning">In Progress</span>
{% elif tr.status == 'SCHEDULED' %}
<span class="badge bg-info">Scheduled</span>
{% elif tr.status == 'CANCELLED' %}
<span class="badge bg-secondary">Cancelled</span>
{% elif tr.status == 'NO_SHOW' %}
<span class="badge bg-dark">No Show</span>
{% elif tr.status == 'FAILED' %}
<span class="badge bg-danger">Failed</span>
{% else %}
<span class="badge bg-light text-dark">{{ tr.get_status_display }}</span>
{% endif %}
</td>
<td>
{% if tr.expiry_date %}
<span class="{% if tr.is_expired %}text-danger{% elif tr.is_due_for_renewal %}text-warning{% endif %}">
{{ tr.expiry_date|date:"M d, Y" }}
</span>
{% else %}
<span class="text-muted">No expiry</span>
{% endif %}
</td>
<td>
{% if tr.score is not None %}
{{ tr.score }}
{% else %}
<span class="text-muted"></span>
{% endif %}
</td>
<td>
<div class="btn-group btn-group-sm">
<a class="btn btn-outline-primary" href="{% url 'hr:training_record_detail' tr.id %}" title="View">
<i class="fa fa-eye"></i>
</a>
<a class="btn btn-outline-secondary" href="{% url 'hr:training_record_update' tr.id %}" title="Edit">
<i class="fa fa-edit"></i>
</a>
<a class="btn btn-outline-danger" href="{% url 'hr:training_record_delete' tr.id %}" title="Delete">
<i class="fa fa-trash"></i>
</a>
</div>
</td>
</tr>
{% empty %}
<tr>
<td colspan="9" class="text-center py-4">
<i class="fa fa-graduation-cap fa-3x text-muted mb-3"></i>
<h5 class="text-muted">No training records found</h5>
<a class="btn btn-primary mt-2" href="{% url 'hr:training_record_create' %}">
<i class="fa fa-plus me-2"></i>Create Training Record
</a>
</td>
</tr>
{% endfor %}
</tbody>
</table>
{% if is_paginated %}
{% include 'partial/pagination.html'%}
{% endif %}
</div>
</div>
<!-- Compliance -->
<div class="tab-pane fade" id="compliance" role="tabpanel">
<div class="row mb-4">
<div class="col-md-6">
<div class="card">
<div class="card-header"><h5 class="card-title mb-0">Compliance Overview</h5></div>
<div class="card-body">
<div class="row">
<div class="col-6 text-center">
<div class="h2 text-success">{{ compliance_rate }}%</div>
<div class="text-muted">Overall Compliance</div>
</div>
<div class="col-6 text-center">
<div class="h2 text-warning">{{ expiring_soon_count }}</div>
<div class="text-muted">Expiring Soon (≤ 30d)</div>
</div>
</div>
</div>
</div>
</div>
<div class="col-md-6">
<div class="card">
<div class="card-header"><h5 class="card-title mb-0">Department Compliance</h5></div>
<div class="card-body">
{% for dept in department_compliance %}
<div class="d-flex justify-content-between align-items-center mb-2">
<span>{{ dept.name }}</span>
<div class="d-flex align-items-center">
<div class="progress me-2" style="width: 120px; height: 8px;">
<div class="progress-bar bg-{{ dept.compliance_color }}" style="width: {{ dept.compliance_rate }}%"></div>
</div>
<span class="text-muted">{{ dept.compliance_rate }}%</span>
</div>
</div>
{% empty %}
<p class="text-muted mb-0">No departments.</p>
{% endfor %}
</div>
</div>
</div>
</div>
<div class="card">
<div class="card-header"><h5 class="card-title mb-0">Compliance Alerts</h5></div>
<div class="card-body">
{% if compliance_alerts %}
<div class="table-responsive">
<table class="table table-sm">
<thead>
<tr>
<th>Employee</th>
<th>Requirement</th>
<th>Due Date</th>
<th>Priority</th>
</tr>
</thead>
<tbody>
{% for alert in compliance_alerts %}
<tr>
<td>{{ alert.employee.get_full_name }}</td>
<td>{{ alert.requirement }}</td>
<td><span class="text-{{ alert.urgency_color }}">{{ alert.due_date|date:"M d, Y" }}</span></td>
<td><span class="badge bg-{{ alert.priority_color }}">{{ alert.get_priority_display }}</span></td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% else %}
<div class="text-center py-4">
<i class="fa fa-shield-alt fa-3x text-success mb-3"></i>
<h5 class="text-success">All Clear!</h5>
<p class="text-muted">No compliance alerts at this time.</p>
</div>
{% endif %}
</div>
</div>
</div><!-- /Compliance -->
</div>
</div>
</div>
</div>
</div>
{% endblock %}
{% block js %}
<script>
// Simple client-side filtering for the records table
function filterRecords() {
const search = (document.getElementById('recordSearch').value || '').toLowerCase();
const type = document.getElementById('recordType').value;
const status = document.getElementById('recordStatus').value;
document.querySelectorAll('#recordsTable tbody tr').forEach(row => {
const text = row.innerText.toLowerCase();
const rowType = row.querySelector('td:nth-child(3) .badge')?.innerText.trim().toUpperCase() || '';
const rowStatus = row.querySelector('td:nth-child(6) .badge')?.innerText.trim().toUpperCase().replace(' ', '_') || '';
const matchesSearch = !search || text.includes(search);
const matchesType = !type || rowType === document.querySelector(`#recordType option[value="${type}"]`).innerText.toUpperCase();
const matchesStatus = !status || rowStatus === status;
row.style.display = (matchesSearch && matchesType && matchesStatus) ? '' : 'none';
});
}
document.addEventListener('DOMContentLoaded', () => {
['recordSearch', 'recordType', 'recordStatus'].forEach(id => {
const el = document.getElementById(id);
if (el) el.addEventListener('input', filterRecords);
if (el && el.tagName === 'SELECT') el.addEventListener('change', filterRecords);
});
});
</script>
<style>
.progress { background-color: #e9ecef; }
.progress-bar { transition: width .3s ease; }
.nav-tabs .nav-link { border: none; color: #6c757d; }
.nav-tabs .nav-link.active { background: transparent; border-bottom: 2px solid #0d6efd; color: #0d6efd; }
.badge { font-size: .75em; font-weight: 500; }
.table th { border-top: none; font-weight: 600; color: #495057; background-color: #f8f9fa; }
.table-hover tbody tr:hover { background-color: rgba(0,0,0,.025); }
@media (max-width: 768px){ .table-responsive{ font-size:.875rem; } }
</style>
{% endblock %}