434 lines
21 KiB
HTML
434 lines
21 KiB
HTML
{% extends "base.html" %}
|
|
{% load static %}
|
|
|
|
{% block title %}Publish Schedule: {{ schedule.name }}{% endblock %}
|
|
|
|
{% block css %}
|
|
<style>
|
|
.publish-header {
|
|
background: linear-gradient(to right, #2d62ed, #3a7bd5);
|
|
color: white;
|
|
padding: 20px;
|
|
border-radius: 5px;
|
|
margin-bottom: 20px;
|
|
}
|
|
.checklist-item {
|
|
padding: 15px;
|
|
border-radius: 5px;
|
|
margin-bottom: 10px;
|
|
background-color: #f8f9fa;
|
|
border-left: 4px solid #6c757d;
|
|
}
|
|
.checklist-item.success {
|
|
border-left-color: #28a745;
|
|
background-color: rgba(40, 167, 69, 0.1);
|
|
}
|
|
.checklist-item.warning {
|
|
border-left-color: #ffc107;
|
|
background-color: rgba(255, 193, 7, 0.1);
|
|
}
|
|
.checklist-item.danger {
|
|
border-left-color: #dc3545;
|
|
background-color: rgba(220, 53, 69, 0.1);
|
|
}
|
|
.notification-preview {
|
|
background-color: #f8f9fa;
|
|
border-radius: 5px;
|
|
padding: 15px;
|
|
margin-top: 15px;
|
|
border: 1px solid #dee2e6;
|
|
}
|
|
.notification-preview .header {
|
|
border-bottom: 1px solid #dee2e6;
|
|
padding-bottom: 10px;
|
|
margin-bottom: 10px;
|
|
}
|
|
.notification-options {
|
|
background-color: #f8f9fa;
|
|
border-radius: 5px;
|
|
padding: 15px;
|
|
}
|
|
.confirmation-checkbox {
|
|
font-weight: bold;
|
|
}
|
|
</style>
|
|
{% endblock %}
|
|
|
|
{% block content %}
|
|
<!-- begin breadcrumb -->
|
|
<ol class="breadcrumb float-xl-end">
|
|
<li class="breadcrumb-item"><a href="{% url 'core:dashboard' %}">Home</a></li>
|
|
<li class="breadcrumb-item"><a href="{% url 'hr:dashboard' %}">HR</a></li>
|
|
<li class="breadcrumb-item"><a href="{% url 'hr:schedule_list' %}">Schedules</a></li>
|
|
<li class="breadcrumb-item"><a href="{% url 'hr:schedule_detail' schedule.id %}">{{ schedule.name }}</a></li>
|
|
<li class="breadcrumb-item active">Publish</li>
|
|
</ol>
|
|
<!-- end breadcrumb -->
|
|
|
|
<!-- begin page-header -->
|
|
<h1 class="page-header">Publish Schedule</h1>
|
|
<!-- end page-header -->
|
|
|
|
<!-- Publish Header -->
|
|
<div class="publish-header">
|
|
<div class="d-flex justify-content-between align-items-center">
|
|
<div>
|
|
<h2 class="mb-1">{{ schedule.name }}</h2>
|
|
<p class="mb-0">{{ schedule.start_date|date:"M d, Y" }} - {{ schedule.end_date|date:"M d, Y" }}</p>
|
|
</div>
|
|
<div>
|
|
<span class="badge bg-warning fs-6">Draft</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- begin row -->
|
|
<div class="row">
|
|
<!-- begin col-8 -->
|
|
<div class="col-xl-8">
|
|
<!-- begin panel -->
|
|
<div class="panel panel-inverse">
|
|
<!-- begin panel-heading -->
|
|
<div class="panel-heading">
|
|
<h4 class="panel-title">Publish Checklist</h4>
|
|
<div class="panel-heading-btn">
|
|
<a href="javascript:;" class="btn btn-xs btn-icon btn-default" data-toggle="panel-expand"><i class="fa fa-expand"></i></a>
|
|
<a href="javascript:;" class="btn btn-xs btn-icon btn-success" data-toggle="panel-reload"><i class="fa fa-redo"></i></a>
|
|
<a href="javascript:;" class="btn btn-xs btn-icon btn-warning" data-toggle="panel-collapse"><i class="fa fa-minus"></i></a>
|
|
</div>
|
|
</div>
|
|
<!-- end panel-heading -->
|
|
|
|
<!-- begin panel-body -->
|
|
<div class="panel-body">
|
|
<div class="alert alert-info">
|
|
<i class="fas fa-info-circle"></i> Publishing this schedule will make it visible to all employees and send notifications. Please review the checklist below before proceeding.
|
|
</div>
|
|
|
|
<!-- Checklist -->
|
|
<div class="checklist-container">
|
|
<!-- Schedule Period -->
|
|
<div class="checklist-item {% if schedule.start_date and schedule.end_date %}success{% else %}danger{% endif %}">
|
|
<div class="d-flex justify-content-between align-items-center">
|
|
<h5 class="mb-0">
|
|
<i class="fas fa-calendar-alt me-2"></i> Schedule Period
|
|
</h5>
|
|
{% if schedule.start_date and schedule.end_date %}
|
|
<span class="badge bg-success">Complete</span>
|
|
{% else %}
|
|
<span class="badge bg-danger">Incomplete</span>
|
|
{% endif %}
|
|
</div>
|
|
<p class="mb-0 mt-2">
|
|
{% if schedule.start_date and schedule.end_date %}
|
|
Schedule covers {{ schedule.start_date|date:"M d, Y" }} to {{ schedule.end_date|date:"M d, Y" }} ({{ schedule.duration }} days).
|
|
{% else %}
|
|
Schedule period is not properly defined. Please update the schedule with valid start and end dates.
|
|
{% endif %}
|
|
</p>
|
|
</div>
|
|
|
|
<!-- Assignments -->
|
|
<div class="checklist-item {% if assignment_count > 0 %}success{% else %}danger{% endif %}">
|
|
<div class="d-flex justify-content-between align-items-center">
|
|
<h5 class="mb-0">
|
|
<i class="fas fa-tasks me-2"></i> Shift Assignments
|
|
</h5>
|
|
{% if assignment_count > 0 %}
|
|
<span class="badge bg-success">{{ assignment_count }} Assignments</span>
|
|
{% else %}
|
|
<span class="badge bg-danger">No Assignments</span>
|
|
{% endif %}
|
|
</div>
|
|
<p class="mb-0 mt-2">
|
|
{% if assignment_count > 0 %}
|
|
Schedule has {{ assignment_count }} shift assignments for {{ employee_count }} employees.
|
|
{% else %}
|
|
No shift assignments have been created for this schedule. Employees will see an empty schedule.
|
|
{% endif %}
|
|
</p>
|
|
</div>
|
|
|
|
<!-- Coverage -->
|
|
<div class="checklist-item {% if coverage_percentage >= 90 %}success{% elif coverage_percentage >= 70 %}warning{% else %}danger{% endif %}">
|
|
<div class="d-flex justify-content-between align-items-center">
|
|
<h5 class="mb-0">
|
|
<i class="fas fa-user-check me-2"></i> Department Coverage
|
|
</h5>
|
|
<span class="badge {% if coverage_percentage >= 90 %}bg-success{% elif coverage_percentage >= 70 %}bg-warning{% else %}bg-danger{% endif %}">
|
|
{{ coverage_percentage }}% Coverage
|
|
</span>
|
|
</div>
|
|
<p class="mb-0 mt-2">
|
|
{% if coverage_percentage >= 90 %}
|
|
Excellent coverage! {{ employee_count }} out of {{ total_employees }} employees in the department have been scheduled.
|
|
{% elif coverage_percentage >= 70 %}
|
|
Moderate coverage. {{ employee_count }} out of {{ total_employees }} employees in the department have been scheduled.
|
|
{% else %}
|
|
Low coverage. Only {{ employee_count }} out of {{ total_employees }} employees in the department have been scheduled.
|
|
{% endif %}
|
|
</p>
|
|
</div>
|
|
|
|
<!-- Conflicts -->
|
|
<div class="checklist-item {% if conflict_count == 0 %}success{% else %}danger{% endif %}">
|
|
<div class="d-flex justify-content-between align-items-center">
|
|
<h5 class="mb-0">
|
|
<i class="fas fa-exclamation-triangle me-2"></i> Schedule Conflicts
|
|
</h5>
|
|
{% if conflict_count == 0 %}
|
|
<span class="badge bg-success">No Conflicts</span>
|
|
{% else %}
|
|
<span class="badge bg-danger">{{ conflict_count }} Conflicts</span>
|
|
{% endif %}
|
|
</div>
|
|
<p class="mb-0 mt-2">
|
|
{% if conflict_count == 0 %}
|
|
No scheduling conflicts detected. All shifts are properly assigned without overlaps.
|
|
{% else %}
|
|
{{ conflict_count }} scheduling conflicts detected. Please resolve these conflicts before publishing.
|
|
<a href="{% url 'hr:schedule_update' schedule.id %}" class="btn btn-sm btn-outline-danger mt-2">
|
|
<i class="fas fa-exclamation-circle"></i> View Conflicts
|
|
</a>
|
|
{% endif %}
|
|
</p>
|
|
</div>
|
|
|
|
<!-- Hours Distribution -->
|
|
<div class="checklist-item {% if hours_distribution == 'balanced' %}success{% elif hours_distribution == 'moderate' %}warning{% else %}danger{% endif %}">
|
|
<div class="d-flex justify-content-between align-items-center">
|
|
<h5 class="mb-0">
|
|
<i class="fas fa-balance-scale me-2"></i> Hours Distribution
|
|
</h5>
|
|
<span class="badge {% if hours_distribution == 'balanced' %}bg-success{% elif hours_distribution == 'moderate' %}bg-warning{% else %}bg-danger{% endif %}">
|
|
{{ hours_distribution|title }}
|
|
</span>
|
|
</div>
|
|
<p class="mb-0 mt-2">
|
|
{% if hours_distribution == 'balanced' %}
|
|
Hours are well distributed among employees. Average: {{ avg_hours_per_employee|floatformat:1 }} hours per employee.
|
|
{% elif hours_distribution == 'moderate' %}
|
|
Hours distribution could be improved. Some employees have significantly more hours than others.
|
|
{% else %}
|
|
Unbalanced hours distribution. There is a large disparity in assigned hours between employees.
|
|
{% endif %}
|
|
</p>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Notification Options -->
|
|
<form method="post" id="publishForm">
|
|
{% csrf_token %}
|
|
|
|
<div class="card mt-4">
|
|
<div class="card-header">
|
|
<h5 class="card-title mb-0">Notification Options</h5>
|
|
</div>
|
|
<div class="card-body">
|
|
<div class="notification-options">
|
|
<div class="form-check mb-3">
|
|
<input class="form-check-input" type="checkbox" id="sendEmail" name="send_email" checked>
|
|
<label class="form-check-label" for="sendEmail">
|
|
Send email notifications to employees
|
|
</label>
|
|
</div>
|
|
|
|
<div class="form-check mb-3">
|
|
<input class="form-check-input" type="checkbox" id="sendSms" name="send_sms">
|
|
<label class="form-check-label" for="sendSms">
|
|
Send SMS notifications to employees
|
|
</label>
|
|
</div>
|
|
|
|
<div class="form-check mb-3">
|
|
<input class="form-check-input" type="checkbox" id="sendSystem" name="send_system" checked>
|
|
<label class="form-check-label" for="sendSystem">
|
|
Send system notifications
|
|
</label>
|
|
</div>
|
|
|
|
<div class="form-group mt-4">
|
|
<label for="customMessage">Custom Message (Optional)</label>
|
|
<textarea class="form-control" id="customMessage" name="custom_message" rows="3" placeholder="Add a custom message to the notification..."></textarea>
|
|
</div>
|
|
|
|
<!-- Notification Preview -->
|
|
<div class="notification-preview mt-3">
|
|
<div class="header">
|
|
<strong>Notification Preview</strong>
|
|
</div>
|
|
<div class="body">
|
|
<p><strong>Subject:</strong> New Schedule Published: {{ schedule.name }}</p>
|
|
<p>Dear [Employee Name],</p>
|
|
<p>A new work schedule has been published for the period {{ schedule.start_date|date:"M d, Y" }} to {{ schedule.end_date|date:"M d, Y" }}.</p>
|
|
<p id="customMessagePreview" style="display: none;"></p>
|
|
<p>Please log in to the system to view your assigned shifts.</p>
|
|
<p>Thank you,<br>{{ request.user.get_full_name }}</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Confirmation -->
|
|
<div class="card mt-4">
|
|
<div class="card-header">
|
|
<h5 class="card-title mb-0">Confirmation</h5>
|
|
</div>
|
|
<div class="card-body">
|
|
<div class="form-check mb-4 confirmation-checkbox">
|
|
<input class="form-check-input" type="checkbox" id="confirmPublish" required>
|
|
<label class="form-check-label" for="confirmPublish">
|
|
I confirm that I have reviewed the schedule and it is ready to be published to all employees.
|
|
</label>
|
|
</div>
|
|
|
|
<div class="d-flex justify-content-between">
|
|
<a href="{% url 'hr:schedule_detail' schedule.id %}" class="btn btn-secondary">
|
|
<i class="fas fa-times"></i> Cancel
|
|
</a>
|
|
<button type="submit" class="btn btn-success" id="publishBtn" disabled>
|
|
<i class="fas fa-check"></i> Publish Schedule
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
<!-- end panel-body -->
|
|
</div>
|
|
<!-- end panel -->
|
|
</div>
|
|
<!-- end col-8 -->
|
|
|
|
<!-- begin col-4 -->
|
|
<div class="col-xl-4">
|
|
<!-- Schedule Summary -->
|
|
<div class="card mb-4">
|
|
<div class="card-header">
|
|
<h5 class="card-title mb-0">Schedule Summary</h5>
|
|
</div>
|
|
<div class="card-body">
|
|
<table class="table table-borderless">
|
|
<tbody>
|
|
<tr>
|
|
<th width="40%">Department:</th>
|
|
<td>
|
|
{% if schedule.department %}
|
|
{{ schedule.department.name }}
|
|
{% else %}
|
|
<span class="text-muted">All Departments</span>
|
|
{% endif %}
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<th>Period:</th>
|
|
<td>{{ schedule.start_date|date:"M d, Y" }} - {{ schedule.end_date|date:"M d, Y" }}</td>
|
|
</tr>
|
|
<tr>
|
|
<th>Duration:</th>
|
|
<td>{{ schedule.duration }} days</td>
|
|
</tr>
|
|
<tr>
|
|
<th>Total Assignments:</th>
|
|
<td>{{ assignment_count }}</td>
|
|
</tr>
|
|
<tr>
|
|
<th>Employees Scheduled:</th>
|
|
<td>{{ employee_count }} of {{ total_employees }}</td>
|
|
</tr>
|
|
<tr>
|
|
<th>Total Hours:</th>
|
|
<td>{{ total_hours }} hours</td>
|
|
</tr>
|
|
<tr>
|
|
<th>Avg Hours/Employee:</th>
|
|
<td>{{ avg_hours_per_employee|floatformat:1 }} hours</td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Publishing Guidelines -->
|
|
<div class="card mb-4">
|
|
<div class="card-header">
|
|
<h5 class="card-title mb-0">Publishing Guidelines</h5>
|
|
</div>
|
|
<div class="card-body">
|
|
<div class="alert alert-warning">
|
|
<i class="fas fa-exclamation-circle"></i> <strong>Important:</strong> Once published, the schedule will be visible to all employees.
|
|
</div>
|
|
|
|
<ul class="list-unstyled">
|
|
<li class="mb-2">
|
|
<i class="fas fa-check-circle text-success me-2"></i> Ensure all shifts are properly assigned
|
|
</li>
|
|
<li class="mb-2">
|
|
<i class="fas fa-check-circle text-success me-2"></i> Verify there are no scheduling conflicts
|
|
</li>
|
|
<li class="mb-2">
|
|
<i class="fas fa-check-circle text-success me-2"></i> Check that all required positions are covered
|
|
</li>
|
|
<li class="mb-2">
|
|
<i class="fas fa-check-circle text-success me-2"></i> Confirm compliance with labor regulations
|
|
</li>
|
|
<li class="mb-2">
|
|
<i class="fas fa-check-circle text-success me-2"></i> Review employee time-off requests
|
|
</li>
|
|
</ul>
|
|
|
|
<div class="alert alert-info mt-3">
|
|
<i class="fas fa-lightbulb"></i> <strong>Tip:</strong> You can still make changes to the schedule after publishing, but employees will be notified of any changes that affect them.
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<!-- end col-4 -->
|
|
</div>
|
|
<!-- end row -->
|
|
{% endblock %}
|
|
|
|
{% block js %}
|
|
<script>
|
|
$(document).ready(function() {
|
|
// Enable/disable publish button based on confirmation checkbox
|
|
$('#confirmPublish').change(function() {
|
|
if ($(this).is(':checked')) {
|
|
$('#publishBtn').prop('disabled', false);
|
|
} else {
|
|
$('#publishBtn').prop('disabled', true);
|
|
}
|
|
});
|
|
|
|
// Custom message preview
|
|
$('#customMessage').on('input', function() {
|
|
var message = $(this).val();
|
|
if (message.trim() !== '') {
|
|
$('#customMessagePreview').text(message).show();
|
|
} else {
|
|
$('#customMessagePreview').hide();
|
|
}
|
|
});
|
|
|
|
// Form validation
|
|
$('#publishForm').submit(function(e) {
|
|
{% if conflict_count > 0 %}
|
|
if (!confirm('WARNING: There are {{ conflict_count }} scheduling conflicts that have not been resolved. Are you sure you want to publish this schedule?')) {
|
|
e.preventDefault();
|
|
return false;
|
|
}
|
|
{% endif %}
|
|
|
|
{% if coverage_percentage < 70 %}
|
|
if (!confirm('WARNING: Department coverage is only {{ coverage_percentage }}%. This may result in understaffing. Are you sure you want to publish this schedule?')) {
|
|
e.preventDefault();
|
|
return false;
|
|
}
|
|
{% endif %}
|
|
});
|
|
});
|
|
</script>
|
|
{% endblock %}
|
|
|