473 lines
23 KiB
HTML
473 lines
23 KiB
HTML
{% extends "base.html" %}
|
|
{% load static %}
|
|
|
|
{% block title %}{% if object %}Edit Appointment{% else %}New Appointment{% endif %}{% endblock %}
|
|
|
|
{% block content %}
|
|
<div class="d-flex align-items-center mb-3">
|
|
<div>
|
|
<ol class="breadcrumb">
|
|
<li class="breadcrumb-item"><a href="{% url 'appointments:dashboard' %}">Appointments</a></li>
|
|
<li class="breadcrumb-item"><a href="{% url 'appointments:appointment_list' %}">Appointments</a></li>
|
|
<li class="breadcrumb-item active">{% if object %}Edit{% else %}New{% endif %}</li>
|
|
</ol>
|
|
<h1 class="page-header mb-0">
|
|
{% if object %}Edit Appointment{% else %}Schedule New Appointment{% endif %}
|
|
</h1>
|
|
</div>
|
|
<div class="ms-auto">
|
|
<a href="{% url 'appointments:appointment_list' %}" class="btn btn-secondary">
|
|
<i class="fas fa-arrow-left me-2"></i>Back to List
|
|
</a>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="row">
|
|
<div class="col-xl-8">
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<h4 class="card-title">
|
|
<i class="fas fa-calendar-plus me-2"></i>
|
|
Appointment Details
|
|
</h4>
|
|
</div>
|
|
<div class="card-body">
|
|
{% if messages %}
|
|
{% for message in messages %}
|
|
<div class="alert alert-{{ message.tags }} alert-dismissible fade show" role="alert">
|
|
{{ message }}
|
|
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
|
|
</div>
|
|
{% endfor %}
|
|
{% endif %}
|
|
|
|
<form method="post" novalidate>
|
|
{% csrf_token %}
|
|
|
|
<div class="row">
|
|
<div class="col-md-6">
|
|
<div class="mb-3">
|
|
<label for="{{ form.patient.id_for_label }}" class="form-label">Patient *</label>
|
|
<select class="form-select {% if form.patient.errors %}is-invalid{% endif %}"
|
|
id="{{ form.patient.id_for_label }}"
|
|
name="{{ form.patient.name }}"
|
|
required>
|
|
<option value="">Select Patient</option>
|
|
{% for choice in form.patient.field.choices %}
|
|
{% if choice.0 %}
|
|
<option value="{{ choice.0 }}" {% if choice.0 == form.patient.value %}selected{% endif %}>
|
|
{{ choice.1 }}
|
|
</option>
|
|
{% endif %}
|
|
{% endfor %}
|
|
</select>
|
|
{% if form.patient.errors %}
|
|
<div class="invalid-feedback">
|
|
{{ form.patient.errors.0 }}
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
<div class="col-md-6">
|
|
<div class="mb-3">
|
|
<label for="{{ form.doctor.id_for_label }}" class="form-label">Doctor *</label>
|
|
<select class="form-select {% if form.doctor.errors %}is-invalid{% endif %}"
|
|
id="{{ form.doctor.id_for_label }}"
|
|
name="{{ form.doctor.name }}"
|
|
required>
|
|
<option value="">Select Doctor</option>
|
|
{% for choice in form.doctor.field.choices %}
|
|
{% if choice.0 %}
|
|
<option value="{{ choice.0 }}" {% if choice.0 == form.doctor.value %}selected{% endif %}>
|
|
{{ choice.1 }}
|
|
</option>
|
|
{% endif %}
|
|
{% endfor %}
|
|
</select>
|
|
{% if form.doctor.errors %}
|
|
<div class="invalid-feedback">
|
|
{{ form.doctor.errors.0 }}
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="row">
|
|
<div class="col-md-6">
|
|
<div class="mb-3">
|
|
<label for="{{ form.appointment_date.id_for_label }}" class="form-label">Date *</label>
|
|
<input type="date"
|
|
class="form-control {% if form.appointment_date.errors %}is-invalid{% endif %}"
|
|
id="{{ form.appointment_date.id_for_label }}"
|
|
name="{{ form.appointment_date.name }}"
|
|
value="{{ form.appointment_date.value|default:'' }}"
|
|
required>
|
|
{% if form.appointment_date.errors %}
|
|
<div class="invalid-feedback">
|
|
{{ form.appointment_date.errors.0 }}
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
<div class="col-md-6">
|
|
<div class="mb-3">
|
|
<label for="{{ form.appointment_time.id_for_label }}" class="form-label">Time *</label>
|
|
<input type="time"
|
|
class="form-control {% if form.appointment_time.errors %}is-invalid{% endif %}"
|
|
id="{{ form.appointment_time.id_for_label }}"
|
|
name="{{ form.appointment_time.name }}"
|
|
value="{{ form.appointment_time.value|default:'' }}"
|
|
required>
|
|
{% if form.appointment_time.errors %}
|
|
<div class="invalid-feedback">
|
|
{{ form.appointment_time.errors.0 }}
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="row">
|
|
<div class="col-md-6">
|
|
<div class="mb-3">
|
|
<label for="{{ form.appointment_type.id_for_label }}" class="form-label">Type *</label>
|
|
<select class="form-select {% if form.appointment_type.errors %}is-invalid{% endif %}"
|
|
id="{{ form.appointment_type.id_for_label }}"
|
|
name="{{ form.appointment_type.name }}"
|
|
required>
|
|
<option value="">Select Type</option>
|
|
{% for choice in form.appointment_type.field.choices %}
|
|
<option value="{{ choice.0 }}" {% if choice.0 == form.appointment_type.value %}selected{% endif %}>
|
|
{{ choice.1 }}
|
|
</option>
|
|
{% endfor %}
|
|
</select>
|
|
{% if form.appointment_type.errors %}
|
|
<div class="invalid-feedback">
|
|
{{ form.appointment_type.errors.0 }}
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
<div class="col-md-6">
|
|
<div class="mb-3">
|
|
<label for="{{ form.status.id_for_label }}" class="form-label">Status</label>
|
|
<select class="form-select {% if form.status.errors %}is-invalid{% endif %}"
|
|
id="{{ form.status.id_for_label }}"
|
|
name="{{ form.status.name }}">
|
|
{% for choice in form.status.field.choices %}
|
|
<option value="{{ choice.0 }}" {% if choice.0 == form.status.value %}selected{% endif %}>
|
|
{{ choice.1 }}
|
|
</option>
|
|
{% endfor %}
|
|
</select>
|
|
{% if form.status.errors %}
|
|
<div class="invalid-feedback">
|
|
{{ form.status.errors.0 }}
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="mb-3">
|
|
<label for="{{ form.reason.id_for_label }}" class="form-label">Reason for Visit *</label>
|
|
<textarea class="form-control {% if form.reason.errors %}is-invalid{% endif %}"
|
|
id="{{ form.reason.id_for_label }}"
|
|
name="{{ form.reason.name }}"
|
|
rows="3"
|
|
placeholder="Describe the reason for this appointment..."
|
|
required>{{ form.reason.value|default:'' }}</textarea>
|
|
{% if form.reason.errors %}
|
|
<div class="invalid-feedback">
|
|
{{ form.reason.errors.0 }}
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
|
|
<div class="mb-3">
|
|
<label for="{{ form.notes.id_for_label }}" class="form-label">Additional Notes</label>
|
|
<textarea class="form-control {% if form.notes.errors %}is-invalid{% endif %}"
|
|
id="{{ form.notes.id_for_label }}"
|
|
name="{{ form.notes.name }}"
|
|
rows="3"
|
|
placeholder="Any additional notes or special instructions...">{{ form.notes.value|default:'' }}</textarea>
|
|
{% if form.notes.errors %}
|
|
<div class="invalid-feedback">
|
|
{{ form.notes.errors.0 }}
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
|
|
<div class="row">
|
|
<div class="col-md-6">
|
|
<div class="mb-3">
|
|
<label for="{{ form.duration.id_for_label }}" class="form-label">Duration (minutes)</label>
|
|
<input type="number"
|
|
class="form-control {% if form.duration.errors %}is-invalid{% endif %}"
|
|
id="{{ form.duration.id_for_label }}"
|
|
name="{{ form.duration.name }}"
|
|
value="{{ form.duration.value|default:'30' }}"
|
|
min="15"
|
|
max="240"
|
|
step="15">
|
|
{% if form.duration.errors %}
|
|
<div class="invalid-feedback">
|
|
{{ form.duration.errors.0 }}
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
<div class="col-md-6">
|
|
<div class="mb-3">
|
|
<label for="{{ form.priority.id_for_label }}" class="form-label">Priority</label>
|
|
<select class="form-select {% if form.priority.errors %}is-invalid{% endif %}"
|
|
id="{{ form.priority.id_for_label }}"
|
|
name="{{ form.priority.name }}">
|
|
{% for choice in form.priority.field.choices %}
|
|
<option value="{{ choice.0 }}" {% if choice.0 == form.priority.value %}selected{% endif %}>
|
|
{{ choice.1 }}
|
|
</option>
|
|
{% endfor %}
|
|
</select>
|
|
{% if form.priority.errors %}
|
|
<div class="invalid-feedback">
|
|
{{ form.priority.errors.0 }}
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="row">
|
|
<div class="col-md-6">
|
|
<div class="form-check mb-3">
|
|
<input class="form-check-input"
|
|
type="checkbox"
|
|
id="{{ form.is_follow_up.id_for_label }}"
|
|
name="{{ form.is_follow_up.name }}"
|
|
{% if form.is_follow_up.value %}checked{% endif %}>
|
|
<label class="form-check-label" for="{{ form.is_follow_up.id_for_label }}">
|
|
Follow-up Appointment
|
|
</label>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-6">
|
|
<div class="form-check mb-3">
|
|
<input class="form-check-input"
|
|
type="checkbox"
|
|
id="{{ form.send_reminder.id_for_label }}"
|
|
name="{{ form.send_reminder.name }}"
|
|
{% if form.send_reminder.value %}checked{% endif %}>
|
|
<label class="form-check-label" for="{{ form.send_reminder.id_for_label }}">
|
|
Send Reminder
|
|
</label>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="d-flex justify-content-between">
|
|
<div>
|
|
{% if object %}
|
|
<a href="{% url 'appointments:appointment_detail' object.pk %}" class="btn btn-secondary">
|
|
<i class="fas fa-times me-2"></i>Cancel
|
|
</a>
|
|
{% else %}
|
|
<a href="{% url 'appointments:appointment_list' %}" class="btn btn-secondary">
|
|
<i class="fas fa-times me-2"></i>Cancel
|
|
</a>
|
|
{% endif %}
|
|
</div>
|
|
<div>
|
|
<button type="submit" class="btn btn-primary">
|
|
<i class="fas fa-save me-2"></i>
|
|
{% if object %}Update Appointment{% else %}Schedule Appointment{% endif %}
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="col-xl-4">
|
|
<!-- Available Slots -->
|
|
<div class="card mb-4">
|
|
<div class="card-header">
|
|
<h5 class="card-title">
|
|
<i class="fas fa-clock me-2"></i>
|
|
Available Slots
|
|
</h5>
|
|
</div>
|
|
<div class="card-body">
|
|
<div id="available-slots">
|
|
<div class="text-center text-muted">
|
|
<i class="fas fa-calendar-alt fa-2x mb-2"></i>
|
|
<div>Select a doctor and date to view available slots</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Doctor Schedule -->
|
|
<div class="card mb-4">
|
|
<div class="card-header">
|
|
<h5 class="card-title">
|
|
<i class="fas fa-user-md me-2"></i>
|
|
Doctor Schedule
|
|
</h5>
|
|
</div>
|
|
<div class="card-body">
|
|
<div id="doctor-schedule">
|
|
<div class="text-center text-muted">
|
|
<i class="fas fa-stethoscope fa-2x mb-2"></i>
|
|
<div>Select a doctor to view their schedule</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Appointment Guidelines -->
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<h5 class="card-title">
|
|
<i class="fas fa-info-circle me-2"></i>
|
|
Guidelines
|
|
</h5>
|
|
</div>
|
|
<div class="card-body">
|
|
<ul class="list-unstyled mb-0">
|
|
<li class="mb-2">
|
|
<i class="fas fa-check text-success me-2"></i>
|
|
Appointments should be scheduled at least 24 hours in advance
|
|
</li>
|
|
<li class="mb-2">
|
|
<i class="fas fa-check text-success me-2"></i>
|
|
Emergency cases should be marked as high priority
|
|
</li>
|
|
<li class="mb-2">
|
|
<i class="fas fa-check text-success me-2"></i>
|
|
Follow-up appointments are typically 15-30 minutes
|
|
</li>
|
|
<li>
|
|
<i class="fas fa-check text-success me-2"></i>
|
|
Reminders are sent 24 hours before the appointment
|
|
</li>
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
document.addEventListener('DOMContentLoaded', function() {
|
|
const doctorSelect = document.getElementById('{{ form.doctor.id_for_label }}');
|
|
const dateInput = document.getElementById('{{ form.appointment_date.id_for_label }}');
|
|
const timeInput = document.getElementById('{{ form.appointment_time.id_for_label }}');
|
|
|
|
// Set minimum date to today
|
|
const today = new Date().toISOString().split('T')[0];
|
|
dateInput.min = today;
|
|
|
|
// Load available slots when doctor or date changes
|
|
function loadAvailableSlots() {
|
|
const doctorId = doctorSelect.value;
|
|
const selectedDate = dateInput.value;
|
|
|
|
if (doctorId && selectedDate) {
|
|
// Simulate loading available slots
|
|
const slotsContainer = document.getElementById('available-slots');
|
|
slotsContainer.innerHTML = `
|
|
<div class="text-center">
|
|
<div class="spinner-border spinner-border-sm text-primary mb-2" role="status"></div>
|
|
<div class="small">Loading available slots...</div>
|
|
</div>
|
|
`;
|
|
|
|
// Simulate API call
|
|
setTimeout(() => {
|
|
const slots = ['09:00', '09:30', '10:00', '10:30', '11:00', '14:00', '14:30', '15:00', '15:30'];
|
|
let slotsHtml = '<div class="row g-2">';
|
|
|
|
slots.forEach(slot => {
|
|
slotsHtml += `
|
|
<div class="col-6">
|
|
<button type="button" class="btn btn-outline-primary btn-sm w-100 slot-btn"
|
|
onclick="selectTimeSlot('${slot}')">
|
|
${slot}
|
|
</button>
|
|
</div>
|
|
`;
|
|
});
|
|
|
|
slotsHtml += '</div>';
|
|
slotsContainer.innerHTML = slotsHtml;
|
|
}, 1000);
|
|
}
|
|
}
|
|
|
|
// Load doctor schedule
|
|
function loadDoctorSchedule() {
|
|
const doctorId = doctorSelect.value;
|
|
|
|
if (doctorId) {
|
|
const scheduleContainer = document.getElementById('doctor-schedule');
|
|
scheduleContainer.innerHTML = `
|
|
<div class="small mb-2"><strong>Working Hours:</strong></div>
|
|
<div class="small text-muted mb-1">Monday - Friday: 9:00 AM - 5:00 PM</div>
|
|
<div class="small text-muted mb-1">Saturday: 9:00 AM - 1:00 PM</div>
|
|
<div class="small text-muted">Sunday: Closed</div>
|
|
<hr class="my-2">
|
|
<div class="small mb-2"><strong>Specializations:</strong></div>
|
|
<div class="small text-muted">General Medicine, Cardiology</div>
|
|
`;
|
|
}
|
|
}
|
|
|
|
doctorSelect.addEventListener('change', function() {
|
|
loadAvailableSlots();
|
|
loadDoctorSchedule();
|
|
});
|
|
|
|
dateInput.addEventListener('change', loadAvailableSlots);
|
|
|
|
// Select time slot function
|
|
window.selectTimeSlot = function(time) {
|
|
timeInput.value = time;
|
|
|
|
// Update button states
|
|
document.querySelectorAll('.slot-btn').forEach(btn => {
|
|
btn.classList.remove('btn-primary');
|
|
btn.classList.add('btn-outline-primary');
|
|
});
|
|
|
|
event.target.classList.remove('btn-outline-primary');
|
|
event.target.classList.add('btn-primary');
|
|
};
|
|
|
|
// Form validation
|
|
const form = document.querySelector('form');
|
|
form.addEventListener('submit', function(e) {
|
|
const requiredFields = form.querySelectorAll('[required]');
|
|
let isValid = true;
|
|
|
|
requiredFields.forEach(field => {
|
|
if (!field.value.trim()) {
|
|
field.classList.add('is-invalid');
|
|
isValid = false;
|
|
} else {
|
|
field.classList.remove('is-invalid');
|
|
}
|
|
});
|
|
|
|
if (!isValid) {
|
|
e.preventDefault();
|
|
alert('Please fill in all required fields.');
|
|
}
|
|
});
|
|
});
|
|
</script>
|
|
{% endblock %}
|
|
|