hospital-management/templates/hr/schedule_form.html
2025-08-12 13:33:25 +03:00

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 %}