441 lines
19 KiB
HTML
441 lines
19 KiB
HTML
{% extends "base.html" %}
|
|
{% load static %}
|
|
|
|
{% block title %}{% if form.instance.id %}Edit{% else %}Create{% endif %} Schedule | HR Management{% endblock %}
|
|
|
|
{% block css %}
|
|
<!-- DatePicker CSS -->
|
|
<link href="{% static 'plugins/bootstrap-datepicker/dist/css/bootstrap-datepicker.min.css' %}" rel="stylesheet" />
|
|
<!-- Select2 CSS -->
|
|
<link href="{% static 'plugins/select2/dist/css/select2.min.css' %}" rel="stylesheet" />
|
|
<style>
|
|
.form-floating > .form-control:focus ~ label,
|
|
.form-floating > .form-control:not(:placeholder-shown) ~ label,
|
|
.form-floating > .form-select ~ label {
|
|
opacity: .65;
|
|
transform: scale(.85) translateY(-0.5rem) translateX(0.15rem);
|
|
}
|
|
.form-floating > label {
|
|
position: absolute;
|
|
top: 0;
|
|
left: 0;
|
|
height: 100%;
|
|
padding: 1rem 0.75rem;
|
|
pointer-events: none;
|
|
border: 1px solid transparent;
|
|
transform-origin: 0 0;
|
|
transition: opacity .1s ease-in-out, transform .1s ease-in-out;
|
|
}
|
|
.help-sidebar {
|
|
background-color: #f8f9fa;
|
|
border-radius: 5px;
|
|
padding: 15px;
|
|
}
|
|
.help-sidebar h5 {
|
|
border-bottom: 1px solid #dee2e6;
|
|
padding-bottom: 10px;
|
|
margin-bottom: 15px;
|
|
}
|
|
.help-item {
|
|
margin-bottom: 15px;
|
|
}
|
|
.help-item h6 {
|
|
color: #2d62ed;
|
|
}
|
|
.date-range-preview {
|
|
background-color: #e9ecef;
|
|
border-radius: 5px;
|
|
padding: 10px;
|
|
margin-top: 10px;
|
|
}
|
|
.preview-card {
|
|
background-color: #f8f9fa;
|
|
border-radius: 5px;
|
|
padding: 15px;
|
|
margin-top: 20px;
|
|
}
|
|
.preview-header {
|
|
border-bottom: 1px solid #dee2e6;
|
|
padding-bottom: 10px;
|
|
margin-bottom: 15px;
|
|
}
|
|
.preview-badge {
|
|
font-size: 0.8rem;
|
|
padding: 0.25rem 0.5rem;
|
|
}
|
|
</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 active">{% if form.instance.id %}Edit{% else %}Create{% endif %} Schedule</li>
|
|
</ol>
|
|
<!-- end breadcrumb -->
|
|
|
|
<!-- begin page-header -->
|
|
<h1 class="page-header">{% if form.instance.id %}Edit{% else %}Create{% endif %} Schedule</h1>
|
|
<!-- end page-header -->
|
|
|
|
<!-- 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">Schedule Form</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">
|
|
<form method="post" id="scheduleForm">
|
|
{% csrf_token %}
|
|
|
|
{% if form.errors %}
|
|
<div class="alert alert-danger">
|
|
<strong>Error!</strong> Please correct the errors below.
|
|
<ul>
|
|
{% for field in form %}
|
|
{% for error in field.errors %}
|
|
<li><strong>{{ field.label }}:</strong> {{ error }}</li>
|
|
{% endfor %}
|
|
{% endfor %}
|
|
{% for error in form.non_field_errors %}
|
|
<li>{{ error }}</li>
|
|
{% endfor %}
|
|
</ul>
|
|
</div>
|
|
{% endif %}
|
|
|
|
<!-- Basic Information -->
|
|
<div class="card mb-4">
|
|
<div class="card-header">
|
|
<h5 class="card-title mb-0">Basic Information</h5>
|
|
</div>
|
|
<div class="card-body">
|
|
<div class="row g-3">
|
|
<!-- Schedule Name -->
|
|
<div class="col-md-12">
|
|
<div class="form-floating">
|
|
{{ form.name }}
|
|
<label for="{{ form.name.id_for_label }}">Schedule Name *</label>
|
|
</div>
|
|
{% if form.name.help_text %}
|
|
<small class="form-text text-muted">{{ form.name.help_text }}</small>
|
|
{% endif %}
|
|
</div>
|
|
|
|
<!-- Department -->
|
|
<div class="col-md-12">
|
|
<div class="form-group">
|
|
<label for="{{ form.department.id_for_label }}">Department</label>
|
|
{{ form.department }}
|
|
{% if form.department.help_text %}
|
|
<small class="form-text text-muted">{{ form.department.help_text }}</small>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Date Range -->
|
|
<div class="col-md-6">
|
|
<div class="form-floating">
|
|
{{ form.start_date }}
|
|
<label for="{{ form.start_date.id_for_label }}">Start Date *</label>
|
|
</div>
|
|
{% if form.start_date.help_text %}
|
|
<small class="form-text text-muted">{{ form.start_date.help_text }}</small>
|
|
{% endif %}
|
|
</div>
|
|
|
|
<div class="col-md-6">
|
|
<div class="form-floating">
|
|
{{ form.end_date }}
|
|
<label for="{{ form.end_date.id_for_label }}">End Date *</label>
|
|
</div>
|
|
{% if form.end_date.help_text %}
|
|
<small class="form-text text-muted">{{ form.end_date.help_text }}</small>
|
|
{% endif %}
|
|
</div>
|
|
|
|
<!-- Date Range Preview -->
|
|
<div class="col-md-12">
|
|
<div class="date-range-preview">
|
|
<div class="d-flex justify-content-between align-items-center">
|
|
<div>
|
|
<strong>Duration:</strong> <span id="duration-days">0</span> days
|
|
</div>
|
|
<div>
|
|
<strong>Period:</strong> <span id="date-range-text">Select dates</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Status -->
|
|
<div class="col-md-12">
|
|
<div class="form-group">
|
|
<label for="{{ form.status.id_for_label }}">Status</label>
|
|
{{ form.status }}
|
|
{% if form.status.help_text %}
|
|
<small class="form-text text-muted">{{ form.status.help_text }}</small>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Description and Notes -->
|
|
<div class="card mb-4">
|
|
<div class="card-header">
|
|
<h5 class="card-title mb-0">Additional Information</h5>
|
|
</div>
|
|
<div class="card-body">
|
|
<div class="row g-3">
|
|
<!-- Description -->
|
|
<div class="col-md-12">
|
|
<div class="form-group">
|
|
<label for="{{ form.description.id_for_label }}">Description</label>
|
|
{{ form.description }}
|
|
{% if form.description.help_text %}
|
|
<small class="form-text text-muted">{{ form.description.help_text }}</small>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Notes -->
|
|
<div class="col-md-12">
|
|
<div class="form-group">
|
|
<label for="{{ form.notes.id_for_label }}">Notes</label>
|
|
{{ form.notes }}
|
|
{% if form.notes.help_text %}
|
|
<small class="form-text text-muted">{{ form.notes.help_text }}</small>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Form Actions -->
|
|
<div class="d-flex justify-content-between">
|
|
<a href="{% url 'hr:schedule_list' %}" class="btn btn-secondary">
|
|
<i class="fas fa-times"></i> Cancel
|
|
</a>
|
|
<div>
|
|
<button type="submit" name="save_draft" class="btn btn-primary me-2">
|
|
<i class="fas fa-save"></i> Save as Draft
|
|
</button>
|
|
<button type="submit" name="save_publish" class="btn btn-success">
|
|
<i class="fas fa-check"></i> Save and Publish
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
<!-- end panel-body -->
|
|
</div>
|
|
<!-- end panel -->
|
|
</div>
|
|
<!-- end col-8 -->
|
|
|
|
<!-- begin col-4 -->
|
|
<div class="col-xl-4">
|
|
<!-- Schedule Preview -->
|
|
<div class="card mb-4">
|
|
<div class="card-header">
|
|
<h5 class="card-title mb-0">Schedule Preview</h5>
|
|
</div>
|
|
<div class="card-body">
|
|
<div class="preview-card">
|
|
<div class="preview-header d-flex justify-content-between align-items-center">
|
|
<h5 id="preview-name">Schedule Name</h5>
|
|
<span id="preview-status" class="badge bg-warning preview-badge">Draft</span>
|
|
</div>
|
|
<div class="mb-3">
|
|
<strong>Department:</strong>
|
|
<span id="preview-department">Not selected</span>
|
|
</div>
|
|
<div class="mb-3">
|
|
<strong>Period:</strong>
|
|
<span id="preview-period">Not set</span>
|
|
</div>
|
|
<div class="mb-3">
|
|
<strong>Description:</strong>
|
|
<p id="preview-description" class="text-muted">No description provided</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Help Sidebar -->
|
|
<div class="help-sidebar">
|
|
<h5><i class="fas fa-info-circle"></i> Help & Guidelines</h5>
|
|
|
|
<div class="help-item">
|
|
<h6>Schedule Naming</h6>
|
|
<p>Use clear, descriptive names that include the department and date range when applicable. For example: "Nursing Staff - July 2023" or "ER Weekend Rotation Q3".</p>
|
|
</div>
|
|
|
|
<div class="help-item">
|
|
<h6>Date Selection</h6>
|
|
<p>Schedule periods typically align with pay periods or calendar weeks. Common durations are 1 week, 2 weeks, or 4 weeks.</p>
|
|
</div>
|
|
|
|
<div class="help-item">
|
|
<h6>Status Options</h6>
|
|
<ul>
|
|
<li><strong>Draft:</strong> Still in planning phase, not visible to staff</li>
|
|
<li><strong>Published:</strong> Finalized and visible to all staff</li>
|
|
<li><strong>Archived:</strong> Historical schedule, no longer active</li>
|
|
</ul>
|
|
</div>
|
|
|
|
<div class="help-item">
|
|
<h6>After Creation</h6>
|
|
<p>Once the schedule is created, you'll be able to add individual shift assignments for employees.</p>
|
|
</div>
|
|
|
|
<div class="alert alert-info">
|
|
<i class="fas fa-lightbulb"></i> <strong>Tip:</strong> Save as draft while building the schedule, then publish when it's ready for staff to view.
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<!-- end col-4 -->
|
|
</div>
|
|
<!-- end row -->
|
|
{% endblock %}
|
|
|
|
{% block js %}
|
|
<!-- DatePicker JS -->
|
|
<script src="{% static 'plugins/bootstrap-datepicker/dist/js/bootstrap-datepicker.min.js' %}"></script>
|
|
<!-- Select2 JS -->
|
|
<script src="{% static 'plugins/select2/dist/js/select2.min.js' %}"></script>
|
|
|
|
<script>
|
|
$(document).ready(function() {
|
|
// Initialize Select2
|
|
$('#{{ form.department.id_for_label }}').select2({
|
|
placeholder: "Select a department (optional)",
|
|
allowClear: true
|
|
});
|
|
|
|
// Initialize DatePicker
|
|
$('#{{ form.start_date.id_for_label }}, #{{ form.end_date.id_for_label }}').datepicker({
|
|
format: 'yyyy-mm-dd',
|
|
autoclose: true,
|
|
todayHighlight: true
|
|
});
|
|
|
|
// Calculate duration and update preview
|
|
function updateDatePreview() {
|
|
var startDate = $('#{{ form.start_date.id_for_label }}').val();
|
|
var endDate = $('#{{ form.end_date.id_for_label }}').val();
|
|
|
|
if (startDate && endDate) {
|
|
var start = new Date(startDate);
|
|
var end = new Date(endDate);
|
|
|
|
// Calculate duration in days
|
|
var duration = Math.round((end - start) / (1000 * 60 * 60 * 24)) + 1;
|
|
|
|
// Format dates for display
|
|
var startFormatted = start.toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' });
|
|
var endFormatted = end.toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' });
|
|
|
|
// Update preview
|
|
$('#duration-days').text(duration);
|
|
$('#date-range-text').text(startFormatted + ' - ' + endFormatted);
|
|
$('#preview-period').text(startFormatted + ' - ' + endFormatted);
|
|
} else {
|
|
$('#duration-days').text('0');
|
|
$('#date-range-text').text('Select dates');
|
|
$('#preview-period').text('Not set');
|
|
}
|
|
}
|
|
|
|
// Update preview when form fields change
|
|
$('#{{ form.name.id_for_label }}').on('input', function() {
|
|
$('#preview-name').text($(this).val() || 'Schedule Name');
|
|
});
|
|
|
|
$('#{{ form.department.id_for_label }}').on('change', function() {
|
|
var deptText = $(this).find('option:selected').text();
|
|
$('#preview-department').text(deptText !== '' ? deptText : 'Not selected');
|
|
});
|
|
|
|
$('#{{ form.description.id_for_label }}').on('input', function() {
|
|
$('#preview-description').text($(this).val() || 'No description provided');
|
|
});
|
|
|
|
$('#{{ form.status.id_for_label }}').on('change', function() {
|
|
var status = $(this).val();
|
|
var statusText = $(this).find('option:selected').text();
|
|
var badgeClass = 'bg-warning';
|
|
|
|
if (status === 'PUBLISHED') {
|
|
badgeClass = 'bg-success';
|
|
} else if (status === 'ARCHIVED') {
|
|
badgeClass = 'bg-secondary';
|
|
}
|
|
|
|
$('#preview-status')
|
|
.removeClass('bg-warning bg-success bg-secondary')
|
|
.addClass(badgeClass)
|
|
.text(statusText);
|
|
});
|
|
|
|
$('#{{ form.start_date.id_for_label }}, #{{ form.end_date.id_for_label }}').on('change', updateDatePreview);
|
|
|
|
// Initialize preview with existing values
|
|
$('#preview-name').text($('#{{ form.name.id_for_label }}').val() || 'Schedule Name');
|
|
$('#preview-description').text($('#{{ form.description.id_for_label }}').val() || 'No description provided');
|
|
|
|
var deptText = $('#{{ form.department.id_for_label }}').find('option:selected').text();
|
|
$('#preview-department').text(deptText !== '' ? deptText : 'Not selected');
|
|
|
|
var status = $('#{{ form.status.id_for_label }}').val();
|
|
var statusText = $('#{{ form.status.id_for_label }}').find('option:selected').text();
|
|
|
|
if (status === 'PUBLISHED') {
|
|
$('#preview-status').removeClass('bg-warning').addClass('bg-success').text(statusText);
|
|
} else if (status === 'ARCHIVED') {
|
|
$('#preview-status').removeClass('bg-warning').addClass('bg-secondary').text(statusText);
|
|
} else {
|
|
$('#preview-status').text(statusText);
|
|
}
|
|
|
|
updateDatePreview();
|
|
|
|
// Form validation
|
|
$('#scheduleForm').submit(function(e) {
|
|
var startDate = $('#{{ form.start_date.id_for_label }}').val();
|
|
var endDate = $('#{{ form.end_date.id_for_label }}').val();
|
|
|
|
if (startDate && endDate) {
|
|
var start = new Date(startDate);
|
|
var end = new Date(endDate);
|
|
|
|
if (end < start) {
|
|
alert('End date cannot be before start date.');
|
|
e.preventDefault();
|
|
return false;
|
|
}
|
|
}
|
|
});
|
|
});
|
|
</script>
|
|
{% endblock %}
|
|
|