929 lines
42 KiB
HTML
929 lines
42 KiB
HTML
{% extends 'base.html' %}
|
|
{% load static %}
|
|
|
|
{% block title %}Performance Evaluation{% endblock %}
|
|
|
|
{% block content %}
|
|
<div id="content" class="app-content">
|
|
<div class="container">
|
|
<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">Performance Evaluation</li>
|
|
</ul>
|
|
|
|
<div class="row align-items-center mb-3">
|
|
<div class="col">
|
|
<h1 class="page-header">Performance Evaluation</h1>
|
|
<p class="text-muted">Employee performance review and evaluation system</p>
|
|
</div>
|
|
<div class="col-auto">
|
|
<div class="btn-group">
|
|
<button class="btn btn-primary" onclick="createEvaluation()">
|
|
<i class="fa fa-plus me-2"></i>New Evaluation
|
|
</button>
|
|
<button class="btn btn-outline-secondary" onclick="exportEvaluations()">
|
|
<i class="fa fa-download me-2"></i>Export
|
|
</button>
|
|
<button class="btn btn-outline-info" onclick="viewAnalytics()">
|
|
<i class="fa fa-chart-line me-2"></i>Analytics
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Evaluation Overview -->
|
|
<div class="row mb-4">
|
|
<div class="col-md-3">
|
|
<div class="card bg-primary text-white">
|
|
<div class="card-body">
|
|
<div class="d-flex align-items-center">
|
|
<div class="flex-grow-1">
|
|
<h4 class="mb-0">{{ total_evaluations }}</h4>
|
|
<p class="mb-0">Total Evaluations</p>
|
|
</div>
|
|
<div class="ms-3">
|
|
<i class="fa fa-clipboard-list fa-2x"></i>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-3">
|
|
<div class="card bg-success text-white">
|
|
<div class="card-body">
|
|
<div class="d-flex align-items-center">
|
|
<div class="flex-grow-1">
|
|
<h4 class="mb-0">{{ completed_evaluations }}</h4>
|
|
<p class="mb-0">Completed</p>
|
|
</div>
|
|
<div class="ms-3">
|
|
<i class="fa fa-check-circle fa-2x"></i>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-3">
|
|
<div class="card bg-warning text-white">
|
|
<div class="card-body">
|
|
<div class="d-flex align-items-center">
|
|
<div class="flex-grow-1">
|
|
<h4 class="mb-0">{{ pending_evaluations }}</h4>
|
|
<p class="mb-0">Pending</p>
|
|
</div>
|
|
<div class="ms-3">
|
|
<i class="fa fa-clock fa-2x"></i>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-3">
|
|
<div class="card bg-info text-white">
|
|
<div class="card-body">
|
|
<div class="d-flex align-items-center">
|
|
<div class="flex-grow-1">
|
|
<h4 class="mb-0">{{ average_score|floatformat:1 }}</h4>
|
|
<p class="mb-0">Average Score</p>
|
|
</div>
|
|
<div class="ms-3">
|
|
<i class="fa fa-star fa-2x"></i>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Search and Filters -->
|
|
<div class="row mb-4">
|
|
<div class="col-12">
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<h4 class="card-title">Search & Filter</h4>
|
|
<div class="card-tools">
|
|
<button class="btn btn-outline-secondary btn-sm" onclick="resetFilters()">
|
|
<i class="fa fa-refresh me-1"></i>Reset
|
|
</button>
|
|
</div>
|
|
</div>
|
|
<div class="card-body">
|
|
<form id="searchForm" class="row g-3">
|
|
<div class="col-md-3">
|
|
<label class="form-label">Search Employee</label>
|
|
<input type="text" class="form-control" name="search" placeholder="Name, ID, or department..." value="{{ request.GET.search }}">
|
|
</div>
|
|
<div class="col-md-2">
|
|
<label class="form-label">Status</label>
|
|
<select class="form-select" name="status">
|
|
<option value="">All Statuses</option>
|
|
<option value="draft" {% if request.GET.status == 'draft' %}selected{% endif %}>Draft</option>
|
|
<option value="in_progress" {% if request.GET.status == 'in_progress' %}selected{% endif %}>In Progress</option>
|
|
<option value="completed" {% if request.GET.status == 'completed' %}selected{% endif %}>Completed</option>
|
|
<option value="approved" {% if request.GET.status == 'approved' %}selected{% endif %}>Approved</option>
|
|
<option value="rejected" {% if request.GET.status == 'rejected' %}selected{% endif %}>Rejected</option>
|
|
</select>
|
|
</div>
|
|
<div class="col-md-2">
|
|
<label class="form-label">Evaluation Period</label>
|
|
<select class="form-select" name="period">
|
|
<option value="">All Periods</option>
|
|
<option value="annual" {% if request.GET.period == 'annual' %}selected{% endif %}>Annual</option>
|
|
<option value="semi_annual" {% if request.GET.period == 'semi_annual' %}selected{% endif %}>Semi-Annual</option>
|
|
<option value="quarterly" {% if request.GET.period == 'quarterly' %}selected{% endif %}>Quarterly</option>
|
|
<option value="probationary" {% if request.GET.period == 'probationary' %}selected{% endif %}>Probationary</option>
|
|
</select>
|
|
</div>
|
|
<div class="col-md-2">
|
|
<label class="form-label">Department</label>
|
|
<select class="form-select" name="department">
|
|
<option value="">All Departments</option>
|
|
{% for dept in departments %}
|
|
<option value="{{ dept.id }}" {% if request.GET.department == dept.id|stringformat:"s" %}selected{% endif %}>{{ dept.name }}</option>
|
|
{% endfor %}
|
|
</select>
|
|
</div>
|
|
<div class="col-md-2">
|
|
<label class="form-label">Evaluator</label>
|
|
<select class="form-select" name="evaluator">
|
|
<option value="">All Evaluators</option>
|
|
{% for evaluator in evaluators %}
|
|
<option value="{{ evaluator.id }}" {% if request.GET.evaluator == evaluator.id|stringformat:"s" %}selected{% endif %}>{{ evaluator.get_full_name }}</option>
|
|
{% endfor %}
|
|
</select>
|
|
</div>
|
|
<div class="col-md-1">
|
|
<label class="form-label"> </label>
|
|
<button type="submit" class="btn btn-primary form-control">
|
|
<i class="fa fa-search"></i>
|
|
</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Evaluation List -->
|
|
<div class="row">
|
|
<div class="col-12">
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<h4 class="card-title">Performance Evaluations</h4>
|
|
<div class="card-tools">
|
|
<div class="btn-group">
|
|
<button class="btn btn-outline-primary btn-sm" onclick="bulkAction('approve')">
|
|
<i class="fa fa-check me-1"></i>Bulk Approve
|
|
</button>
|
|
<button class="btn btn-outline-warning btn-sm" onclick="bulkAction('remind')">
|
|
<i class="fa fa-bell me-1"></i>Send Reminders
|
|
</button>
|
|
<button class="btn btn-outline-info btn-sm" onclick="generateReport()">
|
|
<i class="fa fa-file-pdf me-1"></i>Generate Report
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="card-body">
|
|
{% if evaluation_list %}
|
|
<div class="table-responsive">
|
|
<table class="table table-striped table-hover">
|
|
<thead>
|
|
<tr>
|
|
<th width="30">
|
|
<input type="checkbox" id="selectAll" onchange="toggleSelectAll()">
|
|
</th>
|
|
<th>Employee</th>
|
|
<th>Department</th>
|
|
<th>Evaluator</th>
|
|
<th>Period</th>
|
|
<th>Score</th>
|
|
<th>Status</th>
|
|
<th>Due Date</th>
|
|
<th>Last Updated</th>
|
|
<th width="120">Actions</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{% for evaluation in evaluation_list %}
|
|
<tr>
|
|
<td>
|
|
<input type="checkbox" class="evaluation-checkbox" value="{{ evaluation.id }}">
|
|
</td>
|
|
<td>
|
|
<div class="d-flex align-items-center">
|
|
<div class="avatar avatar-sm me-2">
|
|
{% if evaluation.employee.photo %}
|
|
<img src="{{ evaluation.employee.photo.url }}" alt="{{ evaluation.employee.get_full_name }}" class="rounded-circle">
|
|
{% else %}
|
|
<div class="avatar-initial rounded-circle bg-primary">
|
|
{{ evaluation.employee.first_name|first }}{{ evaluation.employee.last_name|first }}
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
<div>
|
|
<h6 class="mb-0">{{ evaluation.employee.get_full_name }}</h6>
|
|
<small class="text-muted">{{ evaluation.employee.employee_id }}</small>
|
|
</div>
|
|
</div>
|
|
</td>
|
|
<td>{{ evaluation.employee.department.name|default:"Not assigned" }}</td>
|
|
<td>
|
|
{% if evaluation.evaluator %}
|
|
<div class="d-flex align-items-center">
|
|
<div class="avatar avatar-xs me-1">
|
|
<div class="avatar-initial rounded-circle bg-secondary">
|
|
{{ evaluation.evaluator.first_name|first }}{{ evaluation.evaluator.last_name|first }}
|
|
</div>
|
|
</div>
|
|
<small>{{ evaluation.evaluator.get_full_name }}</small>
|
|
</div>
|
|
{% else %}
|
|
<span class="text-muted">Not assigned</span>
|
|
{% endif %}
|
|
</td>
|
|
<td>
|
|
<span class="badge bg-secondary">{{ evaluation.get_period_display }}</span>
|
|
</td>
|
|
<td>
|
|
{% if evaluation.overall_score %}
|
|
<div class="d-flex align-items-center">
|
|
<div class="progress me-2" style="width: 60px; height: 20px;">
|
|
<div class="progress-bar bg-{{ evaluation.score_color }}"
|
|
role="progressbar"
|
|
style="width: {{ evaluation.score_percentage }}%"
|
|
aria-valuenow="{{ evaluation.overall_score }}"
|
|
aria-valuemin="0"
|
|
aria-valuemax="5">
|
|
</div>
|
|
</div>
|
|
<span class="fw-bold">{{ evaluation.overall_score|floatformat:1 }}/5</span>
|
|
</div>
|
|
{% else %}
|
|
<span class="text-muted">Not scored</span>
|
|
{% endif %}
|
|
</td>
|
|
<td>
|
|
<span class="badge bg-{{ evaluation.status_color }}">
|
|
{{ evaluation.get_status_display }}
|
|
</span>
|
|
</td>
|
|
<td>
|
|
{% if evaluation.due_date %}
|
|
<span class="{% if evaluation.is_overdue %}text-danger{% elif evaluation.is_due_soon %}text-warning{% endif %}">
|
|
{{ evaluation.due_date|date:"M d, Y" }}
|
|
</span>
|
|
{% else %}
|
|
<span class="text-muted">No due date</span>
|
|
{% endif %}
|
|
</td>
|
|
<td>
|
|
<small class="text-muted">{{ evaluation.updated_at|date:"M d, Y" }}</small>
|
|
</td>
|
|
<td>
|
|
<div class="btn-group btn-group-sm">
|
|
<button class="btn btn-outline-primary" onclick="viewEvaluation('{{ evaluation.id }}')" title="View Details">
|
|
<i class="fa fa-eye"></i>
|
|
</button>
|
|
<button class="btn btn-outline-secondary" onclick="editEvaluation('{{ evaluation.id }}')" title="Edit">
|
|
<i class="fa fa-edit"></i>
|
|
</button>
|
|
<div class="btn-group btn-group-sm">
|
|
<button class="btn btn-outline-info dropdown-toggle" data-bs-toggle="dropdown" title="More Actions">
|
|
<i class="fa fa-ellipsis-v"></i>
|
|
</button>
|
|
<ul class="dropdown-menu">
|
|
{% if evaluation.status == 'completed' %}
|
|
<li><a class="dropdown-item" href="#" onclick="approveEvaluation('{{ evaluation.id }}')">
|
|
<i class="fa fa-check me-2"></i>Approve
|
|
</a></li>
|
|
<li><a class="dropdown-item" href="#" onclick="rejectEvaluation('{{ evaluation.id }}')">
|
|
<i class="fa fa-times me-2"></i>Reject
|
|
</a></li>
|
|
<li><hr class="dropdown-divider"></li>
|
|
{% endif %}
|
|
<li><a class="dropdown-item" href="#" onclick="duplicateEvaluation('{{ evaluation.id }}')">
|
|
<i class="fa fa-copy me-2"></i>Duplicate
|
|
</a></li>
|
|
<li><a class="dropdown-item" href="#" onclick="printEvaluation('{{ evaluation.id }}')">
|
|
<i class="fa fa-print me-2"></i>Print
|
|
</a></li>
|
|
<li><a class="dropdown-item" href="#" onclick="sendReminder('{{ evaluation.id }}')">
|
|
<i class="fa fa-bell me-2"></i>Send Reminder
|
|
</a></li>
|
|
<li><hr class="dropdown-divider"></li>
|
|
<li><a class="dropdown-item text-danger" href="#" onclick="deleteEvaluation('{{ evaluation.id }}')">
|
|
<i class="fa fa-trash me-2"></i>Delete
|
|
</a></li>
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
</td>
|
|
</tr>
|
|
{% endfor %}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
|
|
<!-- Pagination -->
|
|
{% if is_paginated %}
|
|
<nav aria-label="Evaluation pagination">
|
|
<ul class="pagination justify-content-center">
|
|
{% if page_obj.has_previous %}
|
|
<li class="page-item">
|
|
<a class="page-link" href="?page=1{% if request.GET.search %}&search={{ request.GET.search }}{% endif %}{% if request.GET.status %}&status={{ request.GET.status }}{% endif %}{% if request.GET.department %}&department={{ request.GET.department }}{% endif %}">First</a>
|
|
</li>
|
|
<li class="page-item">
|
|
<a class="page-link" href="?page={{ page_obj.previous_page_number }}{% if request.GET.search %}&search={{ request.GET.search }}{% endif %}{% if request.GET.status %}&status={{ request.GET.status }}{% endif %}{% if request.GET.department %}&department={{ request.GET.department }}{% endif %}">Previous</a>
|
|
</li>
|
|
{% endif %}
|
|
|
|
<li class="page-item active">
|
|
<span class="page-link">{{ page_obj.number }} of {{ page_obj.paginator.num_pages }}</span>
|
|
</li>
|
|
|
|
{% if page_obj.has_next %}
|
|
<li class="page-item">
|
|
<a class="page-link" href="?page={{ page_obj.next_page_number }}{% if request.GET.search %}&search={{ request.GET.search }}{% endif %}{% if request.GET.status %}&status={{ request.GET.status }}{% endif %}{% if request.GET.department %}&department={{ request.GET.department }}{% endif %}">Next</a>
|
|
</li>
|
|
<li class="page-item">
|
|
<a class="page-link" href="?page={{ page_obj.paginator.num_pages }}{% if request.GET.search %}&search={{ request.GET.search }}{% endif %}{% if request.GET.status %}&status={{ request.GET.status }}{% endif %}{% if request.GET.department %}&department={{ request.GET.department }}{% endif %}">Last</a>
|
|
</li>
|
|
{% endif %}
|
|
</ul>
|
|
</nav>
|
|
{% endif %}
|
|
{% else %}
|
|
<div class="text-center py-4">
|
|
<i class="fa fa-clipboard-list fa-3x text-muted mb-3"></i>
|
|
<h5 class="text-muted">No evaluations found</h5>
|
|
<p class="text-muted">Start by creating a new performance evaluation.</p>
|
|
<button class="btn btn-primary" onclick="createEvaluation()">
|
|
<i class="fa fa-plus me-2"></i>New Evaluation
|
|
</button>
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- New Evaluation Modal -->
|
|
<div class="modal fade" id="newEvaluationModal" tabindex="-1">
|
|
<div class="modal-dialog modal-lg">
|
|
<div class="modal-content">
|
|
<div class="modal-header">
|
|
<h5 class="modal-title">Create New Performance Evaluation</h5>
|
|
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
|
|
</div>
|
|
<form id="newEvaluationForm">
|
|
{% csrf_token %}
|
|
<div class="modal-body">
|
|
<div class="row">
|
|
<div class="col-md-6">
|
|
<div class="mb-3">
|
|
<label class="form-label">Employee <span class="text-danger">*</span></label>
|
|
<select class="form-select" name="employee" required>
|
|
<option value="">Select employee...</option>
|
|
{% for employee in employees %}
|
|
<option value="{{ employee.id }}">{{ employee.get_full_name }} ({{ employee.employee_id }})</option>
|
|
{% endfor %}
|
|
</select>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-6">
|
|
<div class="mb-3">
|
|
<label class="form-label">Evaluator <span class="text-danger">*</span></label>
|
|
<select class="form-select" name="evaluator" required>
|
|
<option value="">Select evaluator...</option>
|
|
{% for evaluator in evaluators %}
|
|
<option value="{{ evaluator.id }}">{{ evaluator.get_full_name }}</option>
|
|
{% endfor %}
|
|
</select>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="row">
|
|
<div class="col-md-6">
|
|
<div class="mb-3">
|
|
<label class="form-label">Evaluation Period <span class="text-danger">*</span></label>
|
|
<select class="form-select" name="period" required>
|
|
<option value="">Select period...</option>
|
|
<option value="annual">Annual Review</option>
|
|
<option value="semi_annual">Semi-Annual Review</option>
|
|
<option value="quarterly">Quarterly Review</option>
|
|
<option value="probationary">Probationary Review</option>
|
|
<option value="special">Special Review</option>
|
|
</select>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-6">
|
|
<div class="mb-3">
|
|
<label class="form-label">Evaluation Template</label>
|
|
<select class="form-select" name="template">
|
|
<option value="">Select template...</option>
|
|
<option value="standard">Standard Employee</option>
|
|
<option value="clinical">Clinical Staff</option>
|
|
<option value="management">Management</option>
|
|
<option value="support">Support Staff</option>
|
|
<option value="custom">Custom</option>
|
|
</select>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="row">
|
|
<div class="col-md-6">
|
|
<div class="mb-3">
|
|
<label class="form-label">Review Period Start</label>
|
|
<input type="date" class="form-control" name="review_period_start">
|
|
</div>
|
|
</div>
|
|
<div class="col-md-6">
|
|
<div class="mb-3">
|
|
<label class="form-label">Review Period End</label>
|
|
<input type="date" class="form-control" name="review_period_end">
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="row">
|
|
<div class="col-md-6">
|
|
<div class="mb-3">
|
|
<label class="form-label">Due Date</label>
|
|
<input type="date" class="form-control" name="due_date">
|
|
</div>
|
|
</div>
|
|
<div class="col-md-6">
|
|
<div class="mb-3">
|
|
<label class="form-label">Priority</label>
|
|
<select class="form-select" name="priority">
|
|
<option value="normal">Normal</option>
|
|
<option value="high">High</option>
|
|
<option value="urgent">Urgent</option>
|
|
</select>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="mb-3">
|
|
<label class="form-label">Evaluation Goals</label>
|
|
<textarea class="form-control" name="goals" rows="3" placeholder="Specific goals and objectives for this evaluation..."></textarea>
|
|
</div>
|
|
<div class="mb-3">
|
|
<div class="form-check">
|
|
<input class="form-check-input" type="checkbox" name="notify_employee" id="notifyEmployee" checked>
|
|
<label class="form-check-label" for="notifyEmployee">
|
|
Notify employee about evaluation
|
|
</label>
|
|
</div>
|
|
</div>
|
|
<div class="mb-3">
|
|
<div class="form-check">
|
|
<input class="form-check-input" type="checkbox" name="self_evaluation" id="selfEvaluation">
|
|
<label class="form-check-label" for="selfEvaluation">
|
|
Include self-evaluation component
|
|
</label>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="modal-footer">
|
|
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
|
|
<button type="submit" class="btn btn-primary">
|
|
<i class="fa fa-plus me-2"></i>Create Evaluation
|
|
</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Bulk Action Modal -->
|
|
<div class="modal fade" id="bulkActionModal" tabindex="-1">
|
|
<div class="modal-dialog">
|
|
<div class="modal-content">
|
|
<div class="modal-header">
|
|
<h5 class="modal-title" id="bulkActionTitle">Bulk Action</h5>
|
|
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
|
|
</div>
|
|
<form id="bulkActionForm">
|
|
{% csrf_token %}
|
|
<input type="hidden" name="action" id="bulkActionType">
|
|
<input type="hidden" name="evaluation_ids" id="bulkEvaluationIds">
|
|
<div class="modal-body">
|
|
<div id="bulkActionContent">
|
|
<!-- Content will be populated based on action type -->
|
|
</div>
|
|
</div>
|
|
<div class="modal-footer">
|
|
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
|
|
<button type="submit" class="btn btn-primary" id="bulkActionSubmit">
|
|
<i class="fa fa-check me-2"></i>Confirm
|
|
</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{% endblock %}
|
|
|
|
{% block js %}
|
|
<script>
|
|
$(document).ready(function() {
|
|
setupEventHandlers();
|
|
setupFormValidation();
|
|
});
|
|
|
|
function setupEventHandlers() {
|
|
// Search form
|
|
$('#searchForm').on('submit', function(e) {
|
|
e.preventDefault();
|
|
var formData = $(this).serialize();
|
|
window.location.href = '{% url "hr:performance_evaluation" %}?' + formData;
|
|
});
|
|
|
|
// New evaluation form
|
|
$('#newEvaluationForm').on('submit', function(e) {
|
|
e.preventDefault();
|
|
createEvaluationSubmit();
|
|
});
|
|
|
|
// Bulk action form
|
|
$('#bulkActionForm').on('submit', function(e) {
|
|
e.preventDefault();
|
|
executeBulkAction();
|
|
});
|
|
}
|
|
|
|
function setupFormValidation() {
|
|
// Employee selection change
|
|
$('select[name="employee"]').on('change', function() {
|
|
var employeeId = $(this).val();
|
|
if (employeeId) {
|
|
loadEmployeeInfo(employeeId);
|
|
}
|
|
});
|
|
|
|
// Template selection change
|
|
$('select[name="template"]').on('change', function() {
|
|
var template = $(this).val();
|
|
if (template) {
|
|
loadTemplateInfo(template);
|
|
}
|
|
});
|
|
}
|
|
|
|
function createEvaluation() {
|
|
$('#newEvaluationModal').modal('show');
|
|
}
|
|
|
|
function createEvaluationSubmit() {
|
|
var formData = $('#newEvaluationForm').serialize();
|
|
|
|
$.post('{% url "hr:create_evaluation" %}', formData, function(data) {
|
|
if (data.success) {
|
|
toastr.success('Performance evaluation created successfully');
|
|
$('#newEvaluationModal').modal('hide');
|
|
location.reload();
|
|
} else {
|
|
toastr.error('Failed to create evaluation: ' + data.error);
|
|
}
|
|
});
|
|
}
|
|
|
|
function loadEmployeeInfo(employeeId) {
|
|
$.get('{% url "hr:get_employee_details" %}', {employee_id: employeeId}, function(data) {
|
|
if (data.success) {
|
|
// Auto-select appropriate evaluator (manager)
|
|
if (data.employee.manager_id) {
|
|
$('select[name="evaluator"]').val(data.employee.manager_id);
|
|
}
|
|
|
|
// Set appropriate template based on role
|
|
var template = 'standard';
|
|
if (data.employee.is_clinical) {
|
|
template = 'clinical';
|
|
} else if (data.employee.is_manager) {
|
|
template = 'management';
|
|
} else if (data.employee.is_support) {
|
|
template = 'support';
|
|
}
|
|
$('select[name="template"]').val(template);
|
|
}
|
|
});
|
|
}
|
|
|
|
function loadTemplateInfo(template) {
|
|
$.get('{% url "hr:get_evaluation_template" %}', {template: template}, function(data) {
|
|
if (data.success) {
|
|
toastr.info('Template loaded: ' + data.template.name);
|
|
}
|
|
});
|
|
}
|
|
|
|
function viewEvaluation(evaluationId) {
|
|
window.location.href = '{% url "hr:evaluation_detail" 0 %}'.replace('0', evaluationId);
|
|
}
|
|
|
|
function editEvaluation(evaluationId) {
|
|
window.location.href = '{% url "hr:evaluation_edit" 0 %}'.replace('0', evaluationId);
|
|
}
|
|
|
|
function approveEvaluation(evaluationId) {
|
|
$.post('{% url "hr:approve_evaluation" %}', {
|
|
evaluation_id: evaluationId,
|
|
csrfmiddlewaretoken: '{{ csrf_token }}'
|
|
}, function(data) {
|
|
if (data.success) {
|
|
toastr.success('Evaluation approved successfully');
|
|
location.reload();
|
|
} else {
|
|
toastr.error('Failed to approve evaluation: ' + data.error);
|
|
}
|
|
});
|
|
}
|
|
|
|
function rejectEvaluation(evaluationId) {
|
|
var reason = prompt('Please provide a reason for rejection:');
|
|
if (reason) {
|
|
$.post('{% url "hr:reject_evaluation" %}', {
|
|
evaluation_id: evaluationId,
|
|
reason: reason,
|
|
csrfmiddlewaretoken: '{{ csrf_token }}'
|
|
}, function(data) {
|
|
if (data.success) {
|
|
toastr.success('Evaluation rejected');
|
|
location.reload();
|
|
} else {
|
|
toastr.error('Failed to reject evaluation: ' + data.error);
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
function duplicateEvaluation(evaluationId) {
|
|
$.post('{% url "hr:duplicate_evaluation" %}', {
|
|
evaluation_id: evaluationId,
|
|
csrfmiddlewaretoken: '{{ csrf_token }}'
|
|
}, function(data) {
|
|
if (data.success) {
|
|
toastr.success('Evaluation duplicated successfully');
|
|
location.reload();
|
|
} else {
|
|
toastr.error('Failed to duplicate evaluation: ' + data.error);
|
|
}
|
|
});
|
|
}
|
|
|
|
function printEvaluation(evaluationId) {
|
|
window.open('{% url "hr:print_evaluation" 0 %}'.replace('0', evaluationId), '_blank');
|
|
}
|
|
|
|
function sendReminder(evaluationId) {
|
|
$.post('{% url "hr:send_evaluation_reminder" %}', {
|
|
evaluation_id: evaluationId,
|
|
csrfmiddlewaretoken: '{{ csrf_token }}'
|
|
}, function(data) {
|
|
if (data.success) {
|
|
toastr.success('Reminder sent successfully');
|
|
} else {
|
|
toastr.error('Failed to send reminder: ' + data.error);
|
|
}
|
|
});
|
|
}
|
|
|
|
function deleteEvaluation(evaluationId) {
|
|
if (confirm('Are you sure you want to delete this evaluation? This action cannot be undone.')) {
|
|
$.post('{% url "hr:delete_evaluation" %}', {
|
|
evaluation_id: evaluationId,
|
|
csrfmiddlewaretoken: '{{ csrf_token }}'
|
|
}, function(data) {
|
|
if (data.success) {
|
|
toastr.success('Evaluation deleted successfully');
|
|
location.reload();
|
|
} else {
|
|
toastr.error('Failed to delete evaluation: ' + data.error);
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
function toggleSelectAll() {
|
|
var selectAll = $('#selectAll').prop('checked');
|
|
$('.evaluation-checkbox').prop('checked', selectAll);
|
|
}
|
|
|
|
function getSelectedEvaluations() {
|
|
var selected = [];
|
|
$('.evaluation-checkbox:checked').each(function() {
|
|
selected.push($(this).val());
|
|
});
|
|
return selected;
|
|
}
|
|
|
|
function bulkAction(action) {
|
|
var selected = getSelectedEvaluations();
|
|
|
|
if (selected.length === 0) {
|
|
toastr.warning('Please select at least one evaluation');
|
|
return;
|
|
}
|
|
|
|
$('#bulkActionType').val(action);
|
|
$('#bulkEvaluationIds').val(selected.join(','));
|
|
|
|
var title = '';
|
|
var content = '';
|
|
var submitText = '';
|
|
|
|
switch (action) {
|
|
case 'approve':
|
|
title = 'Approve Selected Evaluations';
|
|
content = '<p>Are you sure you want to approve the selected evaluations?</p>' +
|
|
'<div class="mb-3">' +
|
|
'<label class="form-label">Approval Notes</label>' +
|
|
'<textarea class="form-control" name="approval_notes" rows="3" placeholder="Optional approval notes..."></textarea>' +
|
|
'</div>';
|
|
submitText = 'Approve';
|
|
break;
|
|
case 'remind':
|
|
title = 'Send Reminders';
|
|
content = '<p>Send reminder notifications for the selected evaluations?</p>' +
|
|
'<div class="mb-3">' +
|
|
'<label class="form-label">Custom Message</label>' +
|
|
'<textarea class="form-control" name="reminder_message" rows="3" placeholder="Optional custom message..."></textarea>' +
|
|
'</div>';
|
|
submitText = 'Send Reminders';
|
|
break;
|
|
}
|
|
|
|
$('#bulkActionTitle').text(title);
|
|
$('#bulkActionContent').html(content);
|
|
$('#bulkActionSubmit').html('<i class="fa fa-check me-2"></i>' + submitText);
|
|
$('#bulkActionModal').modal('show');
|
|
}
|
|
|
|
function executeBulkAction() {
|
|
var formData = $('#bulkActionForm').serialize();
|
|
|
|
$.post('{% url "hr:bulk_evaluation_action" %}', formData, function(data) {
|
|
if (data.success) {
|
|
toastr.success('Bulk action completed successfully');
|
|
$('#bulkActionModal').modal('hide');
|
|
location.reload();
|
|
} else {
|
|
toastr.error('Failed to execute bulk action: ' + data.error);
|
|
}
|
|
});
|
|
}
|
|
|
|
function generateReport() {
|
|
var params = new URLSearchParams(window.location.search);
|
|
params.set('report', 'true');
|
|
window.open('{% url "hr:evaluation_report" %}?' + params.toString(), '_blank');
|
|
}
|
|
|
|
function resetFilters() {
|
|
window.location.href = '{% url "hr:performance_evaluation" %}';
|
|
}
|
|
|
|
function exportEvaluations() {
|
|
var params = new URLSearchParams(window.location.search);
|
|
params.set('export', 'true');
|
|
window.location.href = '{% url "hr:performance_evaluation" %}?' + params.toString();
|
|
}
|
|
|
|
function viewAnalytics() {
|
|
window.location.href = '{% url "hr:evaluation_analytics" %}';
|
|
}
|
|
</script>
|
|
|
|
<style>
|
|
.avatar {
|
|
width: 32px;
|
|
height: 32px;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
}
|
|
|
|
.avatar-sm {
|
|
width: 24px;
|
|
height: 24px;
|
|
font-size: 0.75rem;
|
|
}
|
|
|
|
.avatar-xs {
|
|
width: 20px;
|
|
height: 20px;
|
|
font-size: 0.625rem;
|
|
}
|
|
|
|
.avatar img {
|
|
width: 100%;
|
|
height: 100%;
|
|
object-fit: cover;
|
|
}
|
|
|
|
.avatar-initial {
|
|
background-color: #6c757d;
|
|
color: white;
|
|
font-weight: 600;
|
|
font-size: 0.875rem;
|
|
}
|
|
|
|
.progress {
|
|
background-color: #e9ecef;
|
|
}
|
|
|
|
.progress-bar {
|
|
transition: width 0.3s ease;
|
|
}
|
|
|
|
.card-tools {
|
|
margin-left: auto;
|
|
}
|
|
|
|
.btn-group-sm .btn {
|
|
padding: 0.25rem 0.5rem;
|
|
font-size: 0.875rem;
|
|
}
|
|
|
|
.table th {
|
|
border-top: none;
|
|
font-weight: 600;
|
|
color: #495057;
|
|
background-color: #f8f9fa;
|
|
}
|
|
|
|
.table-hover tbody tr:hover {
|
|
background-color: rgba(0, 0, 0, 0.025);
|
|
}
|
|
|
|
.badge {
|
|
font-size: 0.75em;
|
|
font-weight: 500;
|
|
}
|
|
|
|
.text-danger {
|
|
color: #dc3545 !important;
|
|
}
|
|
|
|
.text-warning {
|
|
color: #fd7e14 !important;
|
|
}
|
|
|
|
.text-success {
|
|
color: #198754 !important;
|
|
}
|
|
|
|
.dropdown-menu {
|
|
border: 1px solid #dee2e6;
|
|
box-shadow: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.075);
|
|
}
|
|
|
|
.dropdown-item:hover {
|
|
background-color: #f8f9fa;
|
|
}
|
|
|
|
.modal-header {
|
|
border-bottom: 1px solid #dee2e6;
|
|
}
|
|
|
|
.modal-footer {
|
|
border-top: 1px solid #dee2e6;
|
|
}
|
|
|
|
.form-check-input:checked {
|
|
background-color: #0d6efd;
|
|
border-color: #0d6efd;
|
|
}
|
|
|
|
.score-excellent { background-color: #198754 !important; }
|
|
.score-good { background-color: #20c997 !important; }
|
|
.score-satisfactory { background-color: #ffc107 !important; }
|
|
.score-needs-improvement { background-color: #fd7e14 !important; }
|
|
.score-unsatisfactory { background-color: #dc3545 !important; }
|
|
|
|
@media (max-width: 768px) {
|
|
.btn-group {
|
|
flex-direction: column;
|
|
}
|
|
|
|
.btn-group .btn {
|
|
margin-bottom: 0.25rem;
|
|
}
|
|
|
|
.table-responsive {
|
|
font-size: 0.875rem;
|
|
}
|
|
|
|
.avatar {
|
|
width: 24px;
|
|
height: 24px;
|
|
}
|
|
}
|
|
</style>
|
|
{% endblock %}
|
|
|