915 lines
41 KiB
HTML
915 lines
41 KiB
HTML
{% extends 'base.html' %}
|
|
{% load static %}
|
|
|
|
{% block title %}Employee Onboarding{% 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">Employee Onboarding</li>
|
|
</ul>
|
|
|
|
<div class="row align-items-center mb-3">
|
|
<div class="col">
|
|
<h1 class="page-header">Employee Onboarding</h1>
|
|
<p class="text-muted">Comprehensive new employee onboarding process</p>
|
|
</div>
|
|
<div class="col-auto">
|
|
<div class="btn-group">
|
|
<button class="btn btn-primary" onclick="startOnboarding()">
|
|
<i class="fa fa-plus me-2"></i>Start New Onboarding
|
|
</button>
|
|
<button class="btn btn-outline-secondary" onclick="exportOnboardingData()">
|
|
<i class="fa fa-download me-2"></i>Export Data
|
|
</button>
|
|
<button class="btn btn-outline-info" onclick="viewOnboardingStats()">
|
|
<i class="fa fa-chart-bar me-2"></i>Statistics
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Onboarding 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_onboarding }}</h4>
|
|
<p class="mb-0">Total Onboarding</p>
|
|
</div>
|
|
<div class="ms-3">
|
|
<i class="fa fa-users 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_onboarding }}</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">{{ in_progress_onboarding }}</h4>
|
|
<p class="mb-0">In Progress</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-danger text-white">
|
|
<div class="card-body">
|
|
<div class="d-flex align-items-center">
|
|
<div class="flex-grow-1">
|
|
<h4 class="mb-0">{{ overdue_onboarding }}</h4>
|
|
<p class="mb-0">Overdue</p>
|
|
</div>
|
|
<div class="ms-3">
|
|
<i class="fa fa-exclamation-triangle 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 email..." 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="not_started" {% if request.GET.status == 'not_started' %}selected{% endif %}>Not Started</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="on_hold" {% if request.GET.status == 'on_hold' %}selected{% endif %}>On Hold</option>
|
|
<option value="cancelled" {% if request.GET.status == 'cancelled' %}selected{% endif %}>Cancelled</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">Start Date From</label>
|
|
<input type="date" class="form-control" name="start_date_from" value="{{ request.GET.start_date_from }}">
|
|
</div>
|
|
<div class="col-md-2">
|
|
<label class="form-label">Start Date To</label>
|
|
<input type="date" class="form-control" name="start_date_to" value="{{ request.GET.start_date_to }}">
|
|
</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>
|
|
|
|
<!-- Onboarding List -->
|
|
<div class="row">
|
|
<div class="col-12">
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<h4 class="card-title">Onboarding Progress</h4>
|
|
<div class="card-tools">
|
|
<div class="btn-group">
|
|
<button class="btn btn-outline-primary btn-sm" onclick="bulkAction('complete')">
|
|
<i class="fa fa-check me-1"></i>Bulk Complete
|
|
</button>
|
|
<button class="btn btn-outline-warning btn-sm" onclick="bulkAction('hold')">
|
|
<i class="fa fa-pause me-1"></i>Bulk Hold
|
|
</button>
|
|
<button class="btn btn-outline-danger btn-sm" onclick="bulkAction('cancel')">
|
|
<i class="fa fa-times me-1"></i>Bulk Cancel
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="card-body">
|
|
{% if onboarding_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>Position</th>
|
|
<th>Start Date</th>
|
|
<th>Progress</th>
|
|
<th>Status</th>
|
|
<th>Assigned To</th>
|
|
<th>Due Date</th>
|
|
<th width="120">Actions</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{% for onboarding in onboarding_list %}
|
|
<tr>
|
|
<td>
|
|
<input type="checkbox" class="onboarding-checkbox" value="{{ onboarding.id }}">
|
|
</td>
|
|
<td>
|
|
<div class="d-flex align-items-center">
|
|
<div class="avatar avatar-sm me-2">
|
|
{% if onboarding.employee.photo %}
|
|
<img src="{{ onboarding.employee.photo.url }}" alt="{{ onboarding.employee.get_full_name }}" class="rounded-circle">
|
|
{% else %}
|
|
<div class="avatar-initial rounded-circle bg-primary">
|
|
{{ onboarding.employee.first_name|first }}{{ onboarding.employee.last_name|first }}
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
<div>
|
|
<h6 class="mb-0">{{ onboarding.employee.get_full_name }}</h6>
|
|
<small class="text-muted">{{ onboarding.employee.employee_id }}</small>
|
|
</div>
|
|
</div>
|
|
</td>
|
|
<td>{{ onboarding.employee.department.name|default:"Not assigned" }}</td>
|
|
<td>{{ onboarding.employee.position|default:"Not specified" }}</td>
|
|
<td>{{ onboarding.start_date|date:"M d, Y" }}</td>
|
|
<td>
|
|
<div class="progress" style="height: 20px;">
|
|
<div class="progress-bar bg-{{ onboarding.progress_color }}"
|
|
role="progressbar"
|
|
style="width: {{ onboarding.progress_percentage }}%"
|
|
aria-valuenow="{{ onboarding.progress_percentage }}"
|
|
aria-valuemin="0"
|
|
aria-valuemax="100">
|
|
{{ onboarding.progress_percentage }}%
|
|
</div>
|
|
</div>
|
|
<small class="text-muted">{{ onboarding.completed_tasks }}/{{ onboarding.total_tasks }} tasks</small>
|
|
</td>
|
|
<td>
|
|
<span class="badge bg-{{ onboarding.status_color }}">
|
|
{{ onboarding.get_status_display }}
|
|
</span>
|
|
</td>
|
|
<td>
|
|
{% if onboarding.assigned_to %}
|
|
<div class="d-flex align-items-center">
|
|
<div class="avatar avatar-xs me-1">
|
|
<div class="avatar-initial rounded-circle bg-secondary">
|
|
{{ onboarding.assigned_to.first_name|first }}{{ onboarding.assigned_to.last_name|first }}
|
|
</div>
|
|
</div>
|
|
<small>{{ onboarding.assigned_to.get_full_name }}</small>
|
|
</div>
|
|
{% else %}
|
|
<span class="text-muted">Not assigned</span>
|
|
{% endif %}
|
|
</td>
|
|
<td>
|
|
{% if onboarding.due_date %}
|
|
<span class="{% if onboarding.is_overdue %}text-danger{% elif onboarding.is_due_soon %}text-warning{% endif %}">
|
|
{{ onboarding.due_date|date:"M d, Y" }}
|
|
</span>
|
|
{% else %}
|
|
<span class="text-muted">No due date</span>
|
|
{% endif %}
|
|
</td>
|
|
<td>
|
|
<div class="btn-group btn-group-sm">
|
|
<button class="btn btn-outline-primary" onclick="viewOnboarding('{{ onboarding.id }}')" title="View Details">
|
|
<i class="fa fa-eye"></i>
|
|
</button>
|
|
<button class="btn btn-outline-secondary" onclick="editOnboarding('{{ onboarding.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">
|
|
<li><a class="dropdown-item" href="#" onclick="assignOnboarding('{{ onboarding.id }}')">
|
|
<i class="fa fa-user me-2"></i>Assign
|
|
</a></li>
|
|
<li><a class="dropdown-item" href="#" onclick="sendReminder('{{ onboarding.id }}')">
|
|
<i class="fa fa-bell me-2"></i>Send Reminder
|
|
</a></li>
|
|
<li><a class="dropdown-item" href="#" onclick="duplicateOnboarding('{{ onboarding.id }}')">
|
|
<i class="fa fa-copy me-2"></i>Duplicate
|
|
</a></li>
|
|
<li><hr class="dropdown-divider"></li>
|
|
<li><a class="dropdown-item text-danger" href="#" onclick="deleteOnboarding('{{ onboarding.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="Onboarding 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-users fa-3x text-muted mb-3"></i>
|
|
<h5 class="text-muted">No onboarding records found</h5>
|
|
<p class="text-muted">Start by creating a new employee onboarding process.</p>
|
|
<button class="btn btn-primary" onclick="startOnboarding()">
|
|
<i class="fa fa-plus me-2"></i>Start New Onboarding
|
|
</button>
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- New Onboarding Modal -->
|
|
<div class="modal fade" id="newOnboardingModal" tabindex="-1">
|
|
<div class="modal-dialog modal-lg">
|
|
<div class="modal-content">
|
|
<div class="modal-header">
|
|
<h5 class="modal-title">Start New Employee Onboarding</h5>
|
|
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
|
|
</div>
|
|
<form id="newOnboardingForm">
|
|
{% 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 available_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">Onboarding Template</label>
|
|
<select class="form-select" name="template">
|
|
<option value="">Select template...</option>
|
|
<option value="standard">Standard Onboarding</option>
|
|
<option value="clinical">Clinical Staff</option>
|
|
<option value="administrative">Administrative Staff</option>
|
|
<option value="management">Management</option>
|
|
<option value="it">IT Department</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">Start Date <span class="text-danger">*</span></label>
|
|
<input type="date" class="form-control" name="start_date" required>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-6">
|
|
<div class="mb-3">
|
|
<label class="form-label">Target Completion Date</label>
|
|
<input type="date" class="form-control" name="target_completion_date">
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="row">
|
|
<div class="col-md-6">
|
|
<div class="mb-3">
|
|
<label class="form-label">Assigned To</label>
|
|
<select class="form-select" name="assigned_to">
|
|
<option value="">Select HR representative...</option>
|
|
{% for hr_staff in hr_staff_list %}
|
|
<option value="{{ hr_staff.id }}">{{ hr_staff.get_full_name }}</option>
|
|
{% endfor %}
|
|
</select>
|
|
</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">Notes</label>
|
|
<textarea class="form-control" name="notes" rows="3" placeholder="Additional notes or special instructions..."></textarea>
|
|
</div>
|
|
<div class="mb-3">
|
|
<div class="form-check">
|
|
<input class="form-check-input" type="checkbox" name="send_welcome_email" id="sendWelcomeEmail" checked>
|
|
<label class="form-check-label" for="sendWelcomeEmail">
|
|
Send welcome email to employee
|
|
</label>
|
|
</div>
|
|
</div>
|
|
<div class="mb-3">
|
|
<div class="form-check">
|
|
<input class="form-check-input" type="checkbox" name="notify_manager" id="notifyManager" checked>
|
|
<label class="form-check-label" for="notifyManager">
|
|
Notify department manager
|
|
</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-play me-2"></i>Start Onboarding
|
|
</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Assign Onboarding Modal -->
|
|
<div class="modal fade" id="assignOnboardingModal" tabindex="-1">
|
|
<div class="modal-dialog">
|
|
<div class="modal-content">
|
|
<div class="modal-header">
|
|
<h5 class="modal-title">Assign Onboarding</h5>
|
|
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
|
|
</div>
|
|
<form id="assignOnboardingForm">
|
|
{% csrf_token %}
|
|
<input type="hidden" name="onboarding_id" id="assignOnboardingId">
|
|
<div class="modal-body">
|
|
<div class="mb-3">
|
|
<label class="form-label">Assign To <span class="text-danger">*</span></label>
|
|
<select class="form-select" name="assigned_to" required>
|
|
<option value="">Select HR representative...</option>
|
|
{% for hr_staff in hr_staff_list %}
|
|
<option value="{{ hr_staff.id }}">{{ hr_staff.get_full_name }}</option>
|
|
{% endfor %}
|
|
</select>
|
|
</div>
|
|
<div class="mb-3">
|
|
<label class="form-label">Notes</label>
|
|
<textarea class="form-control" name="assignment_notes" rows="3" placeholder="Assignment notes or instructions..."></textarea>
|
|
</div>
|
|
<div class="mb-3">
|
|
<div class="form-check">
|
|
<input class="form-check-input" type="checkbox" name="notify_assignee" id="notifyAssignee" checked>
|
|
<label class="form-check-label" for="notifyAssignee">
|
|
Notify assignee via email
|
|
</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-user me-2"></i>Assign
|
|
</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="onboarding_ids" id="bulkOnboardingIds">
|
|
<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:employee_onboarding" %}?' + formData;
|
|
});
|
|
|
|
// New onboarding form
|
|
$('#newOnboardingForm').on('submit', function(e) {
|
|
e.preventDefault();
|
|
createOnboarding();
|
|
});
|
|
|
|
// Assign onboarding form
|
|
$('#assignOnboardingForm').on('submit', function(e) {
|
|
e.preventDefault();
|
|
assignOnboardingSubmit();
|
|
});
|
|
|
|
// 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) {
|
|
loadEmployeeDetails(employeeId);
|
|
}
|
|
});
|
|
|
|
// Template selection change
|
|
$('select[name="template"]').on('change', function() {
|
|
var template = $(this).val();
|
|
if (template) {
|
|
loadTemplateDetails(template);
|
|
}
|
|
});
|
|
}
|
|
|
|
function startOnboarding() {
|
|
$('#newOnboardingModal').modal('show');
|
|
}
|
|
|
|
function createOnboarding() {
|
|
var formData = $('#newOnboardingForm').serialize();
|
|
|
|
$.post('{% url "hr:create_onboarding" %}', formData, function(data) {
|
|
if (data.success) {
|
|
toastr.success('Onboarding process started successfully');
|
|
$('#newOnboardingModal').modal('hide');
|
|
location.reload();
|
|
} else {
|
|
toastr.error('Failed to start onboarding: ' + data.error);
|
|
}
|
|
});
|
|
}
|
|
|
|
function loadEmployeeDetails(employeeId) {
|
|
$.get('{% url "hr:get_employee_details" %}', {employee_id: employeeId}, function(data) {
|
|
if (data.success) {
|
|
// Auto-fill some fields based on employee data
|
|
if (data.employee.department) {
|
|
// Set appropriate template based on department
|
|
var template = 'standard';
|
|
if (data.employee.department.name.toLowerCase().includes('clinical')) {
|
|
template = 'clinical';
|
|
} else if (data.employee.department.name.toLowerCase().includes('admin')) {
|
|
template = 'administrative';
|
|
} else if (data.employee.department.name.toLowerCase().includes('it')) {
|
|
template = 'it';
|
|
}
|
|
$('select[name="template"]').val(template);
|
|
}
|
|
|
|
// Set start date to employee's start date if available
|
|
if (data.employee.start_date) {
|
|
$('input[name="start_date"]').val(data.employee.start_date);
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
function loadTemplateDetails(template) {
|
|
$.get('{% url "hr:get_onboarding_template" %}', {template: template}, function(data) {
|
|
if (data.success) {
|
|
// Show template details or estimated duration
|
|
toastr.info('Template loaded: ' + data.template.name + ' (Estimated duration: ' + data.template.duration + ' days)');
|
|
}
|
|
});
|
|
}
|
|
|
|
function viewOnboarding(onboardingId) {
|
|
window.location.href = '{% url "hr:onboarding_detail" 0 %}'.replace('0', onboardingId);
|
|
}
|
|
|
|
function editOnboarding(onboardingId) {
|
|
window.location.href = '{% url "hr:onboarding_edit" 0 %}'.replace('0', onboardingId);
|
|
}
|
|
|
|
function assignOnboarding(onboardingId) {
|
|
$('#assignOnboardingId').val(onboardingId);
|
|
$('#assignOnboardingModal').modal('show');
|
|
}
|
|
|
|
function assignOnboardingSubmit() {
|
|
var formData = $('#assignOnboardingForm').serialize();
|
|
|
|
$.post('{% url "hr:assign_onboarding" %}', formData, function(data) {
|
|
if (data.success) {
|
|
toastr.success('Onboarding assigned successfully');
|
|
$('#assignOnboardingModal').modal('hide');
|
|
location.reload();
|
|
} else {
|
|
toastr.error('Failed to assign onboarding: ' + data.error);
|
|
}
|
|
});
|
|
}
|
|
|
|
function sendReminder(onboardingId) {
|
|
$.post('{% url "hr:send_onboarding_reminder" %}', {
|
|
onboarding_id: onboardingId,
|
|
csrfmiddlewaretoken: '{{ csrf_token }}'
|
|
}, function(data) {
|
|
if (data.success) {
|
|
toastr.success('Reminder sent successfully');
|
|
} else {
|
|
toastr.error('Failed to send reminder: ' + data.error);
|
|
}
|
|
});
|
|
}
|
|
|
|
function duplicateOnboarding(onboardingId) {
|
|
$.post('{% url "hr:duplicate_onboarding" %}', {
|
|
onboarding_id: onboardingId,
|
|
csrfmiddlewaretoken: '{{ csrf_token }}'
|
|
}, function(data) {
|
|
if (data.success) {
|
|
toastr.success('Onboarding duplicated successfully');
|
|
location.reload();
|
|
} else {
|
|
toastr.error('Failed to duplicate onboarding: ' + data.error);
|
|
}
|
|
});
|
|
}
|
|
|
|
function deleteOnboarding(onboardingId) {
|
|
if (confirm('Are you sure you want to delete this onboarding process? This action cannot be undone.')) {
|
|
$.post('{% url "hr:delete_onboarding" %}', {
|
|
onboarding_id: onboardingId,
|
|
csrfmiddlewaretoken: '{{ csrf_token }}'
|
|
}, function(data) {
|
|
if (data.success) {
|
|
toastr.success('Onboarding deleted successfully');
|
|
location.reload();
|
|
} else {
|
|
toastr.error('Failed to delete onboarding: ' + data.error);
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
function toggleSelectAll() {
|
|
var selectAll = $('#selectAll').prop('checked');
|
|
$('.onboarding-checkbox').prop('checked', selectAll);
|
|
}
|
|
|
|
function getSelectedOnboarding() {
|
|
var selected = [];
|
|
$('.onboarding-checkbox:checked').each(function() {
|
|
selected.push($(this).val());
|
|
});
|
|
return selected;
|
|
}
|
|
|
|
function bulkAction(action) {
|
|
var selected = getSelectedOnboarding();
|
|
|
|
if (selected.length === 0) {
|
|
toastr.warning('Please select at least one onboarding process');
|
|
return;
|
|
}
|
|
|
|
$('#bulkActionType').val(action);
|
|
$('#bulkOnboardingIds').val(selected.join(','));
|
|
|
|
var title = '';
|
|
var content = '';
|
|
var submitText = '';
|
|
|
|
switch (action) {
|
|
case 'complete':
|
|
title = 'Complete Selected Onboarding';
|
|
content = '<p>Are you sure you want to mark the selected onboarding processes as completed?</p>' +
|
|
'<div class="mb-3">' +
|
|
'<label class="form-label">Completion Notes</label>' +
|
|
'<textarea class="form-control" name="completion_notes" rows="3" placeholder="Optional completion notes..."></textarea>' +
|
|
'</div>';
|
|
submitText = 'Complete';
|
|
break;
|
|
case 'hold':
|
|
title = 'Put Selected Onboarding On Hold';
|
|
content = '<p>Are you sure you want to put the selected onboarding processes on hold?</p>' +
|
|
'<div class="mb-3">' +
|
|
'<label class="form-label">Reason for Hold <span class="text-danger">*</span></label>' +
|
|
'<textarea class="form-control" name="hold_reason" rows="3" placeholder="Reason for putting on hold..." required></textarea>' +
|
|
'</div>';
|
|
submitText = 'Put On Hold';
|
|
break;
|
|
case 'cancel':
|
|
title = 'Cancel Selected Onboarding';
|
|
content = '<p class="text-danger">Are you sure you want to cancel the selected onboarding processes? This action cannot be undone.</p>' +
|
|
'<div class="mb-3">' +
|
|
'<label class="form-label">Cancellation Reason <span class="text-danger">*</span></label>' +
|
|
'<textarea class="form-control" name="cancellation_reason" rows="3" placeholder="Reason for cancellation..." required></textarea>' +
|
|
'</div>';
|
|
submitText = 'Cancel';
|
|
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_onboarding_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 resetFilters() {
|
|
window.location.href = '{% url "hr:employee_onboarding" %}';
|
|
}
|
|
|
|
function exportOnboardingData() {
|
|
var params = new URLSearchParams(window.location.search);
|
|
params.set('export', 'true');
|
|
window.location.href = '{% url "hr:employee_onboarding" %}?' + params.toString();
|
|
}
|
|
|
|
function viewOnboardingStats() {
|
|
window.location.href = '{% url "hr:onboarding_statistics" %}';
|
|
}
|
|
</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;
|
|
}
|
|
|
|
@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 %}
|
|
|