476 lines
23 KiB
HTML
476 lines
23 KiB
HTML
{% extends "base.html" %}
|
|
{% load static %}
|
|
|
|
{% block title %}{{ appointment.patient.get_full_name }} - Appointment Details{% endblock %}
|
|
|
|
{% block content %}
|
|
<div class="container-fluid">
|
|
<!-- Breadcrumb -->
|
|
<div class="row">
|
|
<div class="col-12">
|
|
<div class="page-title-box d-sm-flex align-items-center justify-content-between">
|
|
<h4 class="mb-sm-0">Appointment Details</h4>
|
|
<div class="page-title-right">
|
|
<ol class="breadcrumb m-0">
|
|
<li class="breadcrumb-item"><a href="{% url 'core:dashboard' %}">Dashboard</a></li>
|
|
<li class="breadcrumb-item"><a href="{% url 'appointments:dashboard' %}">Appointments</a></li>
|
|
<li class="breadcrumb-item"><a href="{% url 'appointments:appointment_list' %}">Requests</a></li>
|
|
<li class="breadcrumb-item active">Details</li>
|
|
</ol>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="row">
|
|
<!-- Main Content -->
|
|
<div class="col-lg-8">
|
|
<!-- Appointment Overview -->
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<div class="d-flex justify-content-between align-items-center">
|
|
<h5 class="card-title mb-0">
|
|
<i class="fas fa-calendar-alt me-2"></i>
|
|
Appointment Overview
|
|
</h5>
|
|
<div class="d-flex gap-2">
|
|
<span class="badge bg-{% if appointment.status == 'scheduled' %}primary{% elif appointment.status == 'completed' %}success{% elif appointment.status == 'cancelled' %}danger{% else %}warning{% endif %} fs-6">
|
|
{{ appointment.get_status_display }}
|
|
</span>
|
|
<span class="badge bg-{% if appointment.priority == 'high' %}danger{% elif appointment.priority == 'medium' %}warning{% else %}success{% endif %} fs-6">
|
|
{{ appointment.get_priority_display }} Priority
|
|
</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="card-body">
|
|
<div class="row">
|
|
<div class="col-md-6">
|
|
<div class="table-responsive">
|
|
<table class="table table-borderless">
|
|
<tr>
|
|
<td class="fw-bold text-muted">Patient:</td>
|
|
<td>
|
|
<div class="d-flex align-items-center">
|
|
<div class="avatar-sm me-2">
|
|
<div class="avatar-title bg-primary text-white rounded-circle">
|
|
{{ appointment.patient.first_name.0 }}{{ appointment.patient.last_name.0 }}
|
|
</div>
|
|
</div>
|
|
<div>
|
|
<h6 class="mb-0">{{ appointment.patient.get_full_name }}</h6>
|
|
<small class="text-muted">{{ appointment.patient.patient_id }}</small>
|
|
</div>
|
|
</div>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="fw-bold text-muted">Provider:</td>
|
|
<td>
|
|
<h6 class="mb-0">{{ appointment.provider.get_full_name }}</h6>
|
|
<small class="text-muted">{{ appointment.department|default:"General Practice" }}</small>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="fw-bold text-muted">Date & Time:</td>
|
|
<td>
|
|
<h6 class="mb-0">{{ appointment.scheduled_datetime|date:"l, F d, Y" }}</h6>
|
|
<small class="text-muted">{{ appointment.scheduled_datetime|time:"g:i A" }} ({{ appointment.duration_minutes }} min)</small>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="fw-bold text-muted">Type:</td>
|
|
<td>
|
|
<span class="badge bg-info">{{ appointment.get_appointment_type_display }}</span>
|
|
{% if appointment.is_telemedicine %}
|
|
<span class="badge bg-success ms-1">
|
|
<i class="fas fa-video me-1"></i>Virtual
|
|
</span>
|
|
{% endif %}
|
|
</td>
|
|
</tr>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-6">
|
|
<div class="table-responsive">
|
|
<table class="table table-borderless">
|
|
<tr>
|
|
<td class="fw-bold text-muted">Location:</td>
|
|
<td>{{ appointment.location|default:"TBD" }}</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="fw-bold text-muted">Reason:</td>
|
|
<td>{{ appointment.reason|default:"Not specified" }}</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="fw-bold text-muted">Created:</td>
|
|
<td>
|
|
{{ appointment.created_at|date:"M d, Y g:i A" }}
|
|
{% if appointment.created_by %}
|
|
<br><small class="text-muted">by {{ appointment.created_by.get_full_name }}</small>
|
|
{% endif %}
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="fw-bold text-muted">Reminders:</td>
|
|
<td>
|
|
{% if appointment.send_reminders %}
|
|
<span class="text-success"><i class="fas fa-check me-1"></i>Enabled</span>
|
|
{% else %}
|
|
<span class="text-muted"><i class="fas fa-times me-1"></i>Disabled</span>
|
|
{% endif %}
|
|
</td>
|
|
</tr>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{% if appointment.notes %}
|
|
<div class="mt-3">
|
|
<h6 class="text-muted">Notes:</h6>
|
|
<div class="bg-light p-3 rounded">
|
|
{{ appointment.notes|linebreaks }}
|
|
</div>
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Queue Information -->
|
|
{% if queue_entry %}
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<h5 class="card-title mb-0">
|
|
<i class="fas fa-users me-2"></i>
|
|
Queue Information
|
|
</h5>
|
|
</div>
|
|
<div class="card-body">
|
|
<div class="row">
|
|
<div class="col-md-6">
|
|
<div class="table-responsive">
|
|
<table class="table table-borderless">
|
|
<tr>
|
|
<td class="fw-bold text-muted">Queue:</td>
|
|
<td>{{ queue_entry.queue.name }}</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="fw-bold text-muted">Position:</td>
|
|
<td>
|
|
<span class="badge bg-primary">#{{ queue_entry.position }}</span>
|
|
</td>
|
|
</tr>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-6">
|
|
<div class="table-responsive">
|
|
<table class="table table-borderless">
|
|
<tr>
|
|
<td class="fw-bold text-muted">Status:</td>
|
|
<td>
|
|
<span class="badge bg-{% if queue_entry.status == 'waiting' %}warning{% elif queue_entry.status == 'called' %}info{% elif queue_entry.status == 'in_progress' %}primary{% else %}success{% endif %}">
|
|
{{ queue_entry.get_status_display }}
|
|
</span>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="fw-bold text-muted">Estimated Wait:</td>
|
|
<td>{{ queue_entry.estimated_wait_time|default:"Calculating..." }}</td>
|
|
</tr>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{% endif %}
|
|
|
|
<!-- Telemedicine Session -->
|
|
{% if telemedicine_session %}
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<h5 class="card-title mb-0">
|
|
<i class="fas fa-video me-2"></i>
|
|
Telemedicine Session
|
|
</h5>
|
|
</div>
|
|
<div class="card-body">
|
|
<div class="row">
|
|
<div class="col-md-6">
|
|
<div class="table-responsive">
|
|
<table class="table table-borderless">
|
|
<tr>
|
|
<td class="fw-bold text-muted">Session ID:</td>
|
|
<td><code>{{ telemedicine_session.session_id }}</code></td>
|
|
</tr>
|
|
<tr>
|
|
<td class="fw-bold text-muted">Platform:</td>
|
|
<td>{{ telemedicine_session.get_platform_display }}</td>
|
|
</tr>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-6">
|
|
<div class="table-responsive">
|
|
<table class="table table-borderless">
|
|
<tr>
|
|
<td class="fw-bold text-muted">Status:</td>
|
|
<td>
|
|
<span class="badge bg-{% if telemedicine_session.status == 'scheduled' %}primary{% elif telemedicine_session.status == 'active' %}success{% elif telemedicine_session.status == 'completed' %}info{% else %}secondary{% endif %}">
|
|
{{ telemedicine_session.get_status_display }}
|
|
</span>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="fw-bold text-muted">Duration:</td>
|
|
<td>
|
|
{% if telemedicine_session.started_at and telemedicine_session.ended_at %}
|
|
{{ telemedicine_session.duration }} minutes
|
|
{% else %}
|
|
Not started
|
|
{% endif %}
|
|
</td>
|
|
</tr>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{% if telemedicine_session.meeting_url %}
|
|
<div class="mt-3">
|
|
<a href="{{ telemedicine_session.meeting_url }}" class="btn btn-success" target="_blank">
|
|
<i class="fas fa-video me-1"></i>
|
|
Join Meeting
|
|
</a>
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
|
|
<!-- Sidebar -->
|
|
<div class="col-lg-4">
|
|
<!-- Quick Actions -->
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<h5 class="card-title mb-0">
|
|
<i class="fas fa-bolt me-2"></i>
|
|
Quick Actions
|
|
</h5>
|
|
</div>
|
|
<div class="card-body">
|
|
<div class="d-grid gap-2">
|
|
{% if appointment.status == 'scheduled' %}
|
|
<button class="btn btn-success" onclick="checkInPatient()">
|
|
<i class="fas fa-check me-1"></i>
|
|
Check In Patient
|
|
</button>
|
|
<button class="btn btn-warning" onclick="rescheduleAppointment()">
|
|
<i class="fas fa-clock me-1"></i>
|
|
Reschedule
|
|
</button>
|
|
<button class="btn btn-danger" onclick="cancelAppointment()">
|
|
<i class="fas fa-times me-1"></i>
|
|
Cancel
|
|
</button>
|
|
{% elif appointment.status == 'in_progress' %}
|
|
<button class="btn btn-primary" onclick="completeAppointment()">
|
|
<i class="fas fa-check-circle me-1"></i>
|
|
Complete Appointment
|
|
</button>
|
|
{% endif %}
|
|
|
|
<hr>
|
|
|
|
<a href="" class="btn btn-outline-primary">
|
|
<i class="fas fa-edit me-1"></i>
|
|
Edit Details
|
|
</a>
|
|
<button class="btn btn-outline-secondary" onclick="printAppointment()">
|
|
<i class="fas fa-print me-1"></i>
|
|
Print Details
|
|
</button>
|
|
<button class="btn btn-outline-info" onclick="sendReminder()">
|
|
<i class="fas fa-bell me-1"></i>
|
|
Send Reminder
|
|
</button>
|
|
|
|
<hr>
|
|
|
|
<a href="" class="btn btn-outline-danger">
|
|
<i class="fas fa-trash me-1"></i>
|
|
Delete Appointment
|
|
</a>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Patient Information -->
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<h5 class="card-title mb-0">
|
|
<i class="fas fa-user me-2"></i>
|
|
Patient Information
|
|
</h5>
|
|
</div>
|
|
<div class="card-body">
|
|
<div class="table-responsive">
|
|
<table class="table table-borderless table-sm">
|
|
<tr>
|
|
<td class="fw-bold text-muted">Name:</td>
|
|
<td>{{ appointment.patient.get_full_name }}</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="fw-bold text-muted">ID:</td>
|
|
<td>{{ appointment.patient.patient_id }}</td>
|
|
</tr>
|
|
{% if appointment.patient.date_of_birth %}
|
|
<tr>
|
|
<td class="fw-bold text-muted">DOB:</td>
|
|
<td>{{ appointment.patient.date_of_birth|date:"M d, Y" }}</td>
|
|
</tr>
|
|
{% endif %}
|
|
{% if appointment.patient.phone %}
|
|
<tr>
|
|
<td class="fw-bold text-muted">Phone:</td>
|
|
<td>{{ appointment.patient.phone }}</td>
|
|
</tr>
|
|
{% endif %}
|
|
{% if appointment.patient.email %}
|
|
<tr>
|
|
<td class="fw-bold text-muted">Email:</td>
|
|
<td>{{ appointment.patient.email }}</td>
|
|
</tr>
|
|
{% endif %}
|
|
</table>
|
|
</div>
|
|
<div class="mt-3">
|
|
<a href="{% url 'patients:patient_detail' appointment.patient.pk %}" class="btn btn-sm btn-outline-primary">
|
|
<i class="fas fa-external-link-alt me-1"></i>
|
|
View Full Profile
|
|
</a>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Provider Information -->
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<h5 class="card-title mb-0">
|
|
<i class="fas fa-user-md me-2"></i>
|
|
Provider Information
|
|
</h5>
|
|
</div>
|
|
<div class="card-body">
|
|
<div class="table-responsive">
|
|
<table class="table table-borderless table-sm">
|
|
<tr>
|
|
<td class="fw-bold text-muted">Name:</td>
|
|
<td>{{ appointment.provider.get_full_name }}</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="fw-bold text-muted">Department:</td>
|
|
<td>{{ appointment.department|default:"General Practice" }}</td>
|
|
</tr>
|
|
{% if appointment.provider.email %}
|
|
<tr>
|
|
<td class="fw-bold text-muted">Email:</td>
|
|
<td>{{ appointment.provider.email }}</td>
|
|
</tr>
|
|
{% endif %}
|
|
</table>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{% endblock %}
|
|
|
|
{% block js %}
|
|
<script>
|
|
function checkInPatient() {
|
|
if (confirm('Check in this patient?')) {
|
|
// HTMX or AJAX call to check in patient
|
|
fetch(`{% url 'appointments:check_in_patient' appointment.id %}`, {
|
|
method: 'POST',
|
|
headers: {
|
|
'X-CSRFToken': document.querySelector('[name=csrfmiddlewaretoken]').value,
|
|
'Content-Type': 'application/json',
|
|
}
|
|
})
|
|
.then(response => response.json())
|
|
.then(data => {
|
|
if (data.success) {
|
|
location.reload();
|
|
} else {
|
|
alert('Error checking in patient: ' + data.error);
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
function rescheduleAppointment() {
|
|
// Redirect to reschedule page or open modal
|
|
window.location.href = `{% url 'appointments:reschedule_appointment' appointment.id %}`;
|
|
}
|
|
|
|
function cancelAppointment() {
|
|
if (confirm('Are you sure you want to cancel this appointment?')) {
|
|
// HTMX or AJAX call to cancel appointment
|
|
fetch(`{% url 'appointments:cancel_appointment' appointment.id %}`, {
|
|
method: 'POST',
|
|
headers: {
|
|
'X-CSRFToken': document.querySelector('[name=csrfmiddlewaretoken]').value,
|
|
'Content-Type': 'application/json',
|
|
}
|
|
})
|
|
.then(response => response.json())
|
|
.then(data => {
|
|
if (data.success) {
|
|
location.reload();
|
|
} else {
|
|
alert('Error cancelling appointment: ' + data.error);
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
function completeAppointment() {
|
|
if (confirm('Mark this appointment as completed?')) {
|
|
fetch(`{% url 'appointments:complete_appointment' appointment.id %}`, {
|
|
method: 'POST',
|
|
headers: {
|
|
'X-CSRFToken': document.querySelector('[name=csrfmiddlewaretoken]').value,
|
|
'Content-Type': 'application/json',
|
|
}
|
|
})
|
|
.then(response => response.json())
|
|
.then(data => {
|
|
if (data.success) {
|
|
location.reload();
|
|
} else {
|
|
alert('Error completing appointment: ' + data.error);
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
function printAppointment() {
|
|
window.print();
|
|
}
|
|
|
|
function sendReminder() {
|
|
if (confirm('Send appointment reminder to patient?')) {
|
|
// HTMX or AJAX call to send reminder
|
|
alert('Reminder sent successfully!');
|
|
}
|
|
}
|
|
</script>
|
|
{% endblock %}
|
|
|