1010 lines
40 KiB
HTML
1010 lines
40 KiB
HTML
{% extends 'base.html' %}
|
|
{% load static %}
|
|
|
|
{% block title %}{% if note.pk %}Edit{% else %}Create{% endif %} Surgical Note{% endblock %}
|
|
|
|
{% block css %}
|
|
<link href="{% static 'assets/plugins/select2/dist/css/select2.min.css' %}" rel="stylesheet" />
|
|
<link href="{% static 'assets/plugins/bootstrap-datepicker/dist/css/bootstrap-datepicker.min.css' %}" rel="stylesheet" />
|
|
<link href="{% static 'assets/plugins/bootstrap-timepicker/css/bootstrap-timepicker.min.css' %}" rel="stylesheet" />
|
|
<style>
|
|
.form-header {
|
|
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
color: white;
|
|
border-radius: 0.5rem;
|
|
padding: 2rem;
|
|
margin-bottom: 2rem;
|
|
}
|
|
|
|
.form-section {
|
|
background: white;
|
|
border: 1px solid #dee2e6;
|
|
border-radius: 0.375rem;
|
|
margin-bottom: 1.5rem;
|
|
}
|
|
|
|
.section-header {
|
|
background: #f8f9fa;
|
|
border-bottom: 1px solid #dee2e6;
|
|
padding: 1rem 1.5rem;
|
|
font-weight: 600;
|
|
color: #495057;
|
|
display: flex;
|
|
align-items: center;
|
|
}
|
|
|
|
.section-content {
|
|
padding: 1.5rem;
|
|
}
|
|
|
|
.required-field::after {
|
|
content: " *";
|
|
color: #dc3545;
|
|
}
|
|
|
|
.form-floating > .form-control:focus ~ label,
|
|
.form-floating > .form-control:not(:placeholder-shown) ~ label {
|
|
opacity: .65;
|
|
transform: scale(.85) translateY(-0.5rem) translateX(0.15rem);
|
|
}
|
|
|
|
.signature-pad {
|
|
border: 2px dashed #dee2e6;
|
|
border-radius: 0.375rem;
|
|
background: #f8f9fa;
|
|
padding: 1rem;
|
|
text-align: center;
|
|
min-height: 200px;
|
|
position: relative;
|
|
}
|
|
|
|
.signature-canvas {
|
|
border: 1px solid #dee2e6;
|
|
border-radius: 0.25rem;
|
|
background: white;
|
|
cursor: crosshair;
|
|
}
|
|
|
|
.template-selector {
|
|
background: #e3f2fd;
|
|
border: 1px solid #bbdefb;
|
|
border-radius: 0.375rem;
|
|
padding: 1rem;
|
|
margin-bottom: 1.5rem;
|
|
}
|
|
|
|
.auto-save-indicator {
|
|
position: fixed;
|
|
top: 20px;
|
|
right: 20px;
|
|
background: #28a745;
|
|
color: white;
|
|
padding: 0.5rem 1rem;
|
|
border-radius: 0.25rem;
|
|
font-size: 0.875rem;
|
|
z-index: 1050;
|
|
display: none;
|
|
}
|
|
|
|
.field-help {
|
|
font-size: 0.875rem;
|
|
color: #6c757d;
|
|
margin-top: 0.25rem;
|
|
}
|
|
|
|
@media (max-width: 768px) {
|
|
.form-header {
|
|
padding: 1.5rem;
|
|
}
|
|
|
|
.section-content {
|
|
padding: 1rem;
|
|
}
|
|
|
|
.signature-pad {
|
|
min-height: 150px;
|
|
}
|
|
}
|
|
|
|
@media print {
|
|
.btn, .form-actions, .template-selector {
|
|
display: none !important;
|
|
}
|
|
|
|
.form-section {
|
|
border: none;
|
|
box-shadow: none;
|
|
}
|
|
|
|
.section-header {
|
|
background: none;
|
|
border-bottom: 2px solid #000;
|
|
color: #000;
|
|
}
|
|
}
|
|
</style>
|
|
{% endblock %}
|
|
|
|
{% block content %}
|
|
<div id="content" class="app-content">
|
|
<!-- Auto-save indicator -->
|
|
<div class="auto-save-indicator" id="auto-save-indicator">
|
|
<i class="fas fa-check me-1"></i>Auto-saved
|
|
</div>
|
|
|
|
<!-- Page Header -->
|
|
<div class="d-flex align-items-center mb-3">
|
|
<div>
|
|
<ol class="breadcrumb">
|
|
<li class="breadcrumb-item"><a href="{% url 'core:dashboard' %}">Dashboard</a></li>
|
|
<li class="breadcrumb-item"><a href="{% url 'operating_theatre:dashboard' %}">Operating Theatre</a></li>
|
|
<li class="breadcrumb-item"><a href="{% url 'operating_theatre:surgical_note_list' %}">Surgical Notes</a></li>
|
|
<li class="breadcrumb-item active">{% if note.pk %}Edit{% else %}Create{% endif %} Note</li>
|
|
</ol>
|
|
<h1 class="page-header mb-0">
|
|
<i class="fas fa-file-medical me-2"></i>
|
|
{% if note.pk %}Edit Surgical Note{% else %}Create Surgical Note{% endif %}
|
|
</h1>
|
|
</div>
|
|
<div class="ms-auto">
|
|
<button type="button" class="btn btn-outline-secondary me-2" onclick="saveDraft()">
|
|
<i class="fas fa-save me-1"></i>Save Draft
|
|
</button>
|
|
<a href="{% url 'operating_theatre:surgical_note_list' %}" class="btn btn-outline-primary">
|
|
<i class="fas fa-arrow-left me-1"></i>Back to List
|
|
</a>
|
|
</div>
|
|
</div>
|
|
|
|
<form method="post" id="surgical-note-form" novalidate>
|
|
{% csrf_token %}
|
|
|
|
<!-- Template Selector -->
|
|
{% if not note.pk %}
|
|
<div class="template-selector">
|
|
<div class="row align-items-center">
|
|
<div class="col-md-8">
|
|
<h6 class="mb-1">
|
|
<i class="fas fa-clipboard-list me-2"></i>Use Template
|
|
</h6>
|
|
<p class="mb-0 text-muted">Start with a pre-defined template to speed up note creation</p>
|
|
</div>
|
|
<div class="col-md-4">
|
|
<select class="form-select" id="template-selector">
|
|
<option value="">Select Template...</option>
|
|
{% for template in templates %}
|
|
<option value="{{ template.id }}" data-content="{{ template.content }}">
|
|
{{ template.name }}
|
|
</option>
|
|
{% endfor %}
|
|
</select>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{% endif %}
|
|
|
|
<!-- Basic Information -->
|
|
<div class="form-section">
|
|
<div class="section-header">
|
|
<i class="fas fa-info-circle me-2"></i>Basic Information
|
|
</div>
|
|
<div class="section-content">
|
|
<div class="row">
|
|
<div class="col-md-6">
|
|
<div class="form-floating mb-3">
|
|
{{ form.patient }}
|
|
<label for="{{ form.patient.id_for_label }}" class="required-field">Patient</label>
|
|
{% if form.patient.errors %}
|
|
<div class="invalid-feedback d-block">{{ form.patient.errors.0 }}</div>
|
|
{% endif %}
|
|
<div class="field-help">Search and select the patient for this surgical note</div>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-6">
|
|
<div class="form-floating mb-3">
|
|
{{ form.surgeon }}
|
|
<label for="{{ form.surgeon.id_for_label }}" class="required-field">Primary Surgeon</label>
|
|
{% if form.surgeon.errors %}
|
|
<div class="invalid-feedback d-block">{{ form.surgeon.errors.0 }}</div>
|
|
{% endif %}
|
|
<div class="field-help">Select the primary surgeon performing the procedure</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="row">
|
|
<div class="col-md-6">
|
|
<div class="form-floating mb-3">
|
|
{{ form.procedure_name }}
|
|
<label for="{{ form.procedure_name.id_for_label }}" class="required-field">Procedure Name</label>
|
|
{% if form.procedure_name.errors %}
|
|
<div class="invalid-feedback d-block">{{ form.procedure_name.errors.0 }}</div>
|
|
{% endif %}
|
|
<div class="field-help">Enter the name of the surgical procedure</div>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-6">
|
|
<div class="form-floating mb-3">
|
|
{{ form.procedure_code }}
|
|
<label for="{{ form.procedure_code.id_for_label }}">Procedure Code</label>
|
|
{% if form.procedure_code.errors %}
|
|
<div class="invalid-feedback d-block">{{ form.procedure_code.errors.0 }}</div>
|
|
{% endif %}
|
|
<div class="field-help">CPT or ICD-10 procedure code (optional)</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="row">
|
|
<div class="col-md-4">
|
|
<div class="mb-3">
|
|
<label for="{{ form.surgery_date.id_for_label }}" class="form-label required-field">Surgery Date</label>
|
|
{{ form.surgery_date }}
|
|
{% if form.surgery_date.errors %}
|
|
<div class="invalid-feedback d-block">{{ form.surgery_date.errors.0 }}</div>
|
|
{% endif %}
|
|
<div class="field-help">Date when the surgery was performed</div>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-4">
|
|
<div class="mb-3">
|
|
<label for="{{ form.start_time.id_for_label }}" class="form-label">Start Time</label>
|
|
{{ form.start_time }}
|
|
{% if form.start_time.errors %}
|
|
<div class="invalid-feedback d-block">{{ form.start_time.errors.0 }}</div>
|
|
{% endif %}
|
|
<div class="field-help">Time when the surgery started</div>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-4">
|
|
<div class="mb-3">
|
|
<label for="{{ form.end_time.id_for_label }}" class="form-label">End Time</label>
|
|
{{ form.end_time }}
|
|
{% if form.end_time.errors %}
|
|
<div class="invalid-feedback d-block">{{ form.end_time.errors.0 }}</div>
|
|
{% endif %}
|
|
<div class="field-help">Time when the surgery ended</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="row">
|
|
<div class="col-md-6">
|
|
<div class="mb-3">
|
|
<label for="{{ form.status.id_for_label }}" class="form-label required-field">Status</label>
|
|
{{ form.status }}
|
|
{% if form.status.errors %}
|
|
<div class="invalid-feedback d-block">{{ form.status.errors.0 }}</div>
|
|
{% endif %}
|
|
<div class="field-help">Current status of the surgical note</div>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-6">
|
|
<div class="mb-3">
|
|
<label for="{{ form.priority.id_for_label }}" class="form-label">Priority</label>
|
|
{{ form.priority }}
|
|
{% if form.priority.errors %}
|
|
<div class="invalid-feedback d-block">{{ form.priority.errors.0 }}</div>
|
|
{% endif %}
|
|
<div class="field-help">Priority level for this surgical note</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Surgical Team -->
|
|
<div class="form-section">
|
|
<div class="section-header">
|
|
<i class="fas fa-users me-2"></i>Surgical Team
|
|
</div>
|
|
<div class="section-content">
|
|
<div class="row">
|
|
<div class="col-md-6">
|
|
<div class="form-floating mb-3">
|
|
{{ form.assistant_surgeon }}
|
|
<label for="{{ form.assistant_surgeon.id_for_label }}">Assistant Surgeon</label>
|
|
{% if form.assistant_surgeon.errors %}
|
|
<div class="invalid-feedback d-block">{{ form.assistant_surgeon.errors.0 }}</div>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
<div class="col-md-6">
|
|
<div class="form-floating mb-3">
|
|
{{ form.anesthesiologist }}
|
|
<label for="{{ form.anesthesiologist.id_for_label }}">Anesthesiologist</label>
|
|
{% if form.anesthesiologist.errors %}
|
|
<div class="invalid-feedback d-block">{{ form.anesthesiologist.errors.0 }}</div>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="row">
|
|
<div class="col-md-6">
|
|
<div class="form-floating mb-3">
|
|
{{ form.scrub_nurse }}
|
|
<label for="{{ form.scrub_nurse.id_for_label }}">Scrub Nurse</label>
|
|
{% if form.scrub_nurse.errors %}
|
|
<div class="invalid-feedback d-block">{{ form.scrub_nurse.errors.0 }}</div>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
<div class="col-md-6">
|
|
<div class="form-floating mb-3">
|
|
{{ form.circulating_nurse }}
|
|
<label for="{{ form.circulating_nurse.id_for_label }}">Circulating Nurse</label>
|
|
{% if form.circulating_nurse.errors %}
|
|
<div class="invalid-feedback d-block">{{ form.circulating_nurse.errors.0 }}</div>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Pre-operative Information -->
|
|
<div class="form-section">
|
|
<div class="section-header">
|
|
<i class="fas fa-clipboard-check me-2"></i>Pre-operative Information
|
|
</div>
|
|
<div class="section-content">
|
|
<div class="mb-3">
|
|
<label for="{{ form.preoperative_diagnosis.id_for_label }}" class="form-label required-field">Pre-operative Diagnosis</label>
|
|
{{ form.preoperative_diagnosis }}
|
|
{% if form.preoperative_diagnosis.errors %}
|
|
<div class="invalid-feedback d-block">{{ form.preoperative_diagnosis.errors.0 }}</div>
|
|
{% endif %}
|
|
<div class="field-help">Enter the diagnosis before surgery</div>
|
|
</div>
|
|
|
|
<div class="mb-3">
|
|
<label for="{{ form.indication.id_for_label }}" class="form-label">Indication for Surgery</label>
|
|
{{ form.indication }}
|
|
{% if form.indication.errors %}
|
|
<div class="invalid-feedback d-block">{{ form.indication.errors.0 }}</div>
|
|
{% endif %}
|
|
<div class="field-help">Reason or indication for performing the surgery</div>
|
|
</div>
|
|
|
|
<div class="row">
|
|
<div class="col-md-6">
|
|
<div class="form-floating mb-3">
|
|
{{ form.anesthesia_type }}
|
|
<label for="{{ form.anesthesia_type.id_for_label }}">Anesthesia Type</label>
|
|
{% if form.anesthesia_type.errors %}
|
|
<div class="invalid-feedback d-block">{{ form.anesthesia_type.errors.0 }}</div>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
<div class="col-md-6">
|
|
<div class="form-floating mb-3">
|
|
{{ form.patient_position }}
|
|
<label for="{{ form.patient_position.id_for_label }}">Patient Position</label>
|
|
{% if form.patient_position.errors %}
|
|
<div class="invalid-feedback d-block">{{ form.patient_position.errors.0 }}</div>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Operative Procedure -->
|
|
<div class="form-section">
|
|
<div class="section-header">
|
|
<i class="fas fa-procedures me-2"></i>Operative Procedure
|
|
</div>
|
|
<div class="section-content">
|
|
<div class="mb-3">
|
|
<label for="{{ form.procedure_description.id_for_label }}" class="form-label required-field">Procedure Description</label>
|
|
{{ form.procedure_description }}
|
|
{% if form.procedure_description.errors %}
|
|
<div class="invalid-feedback d-block">{{ form.procedure_description.errors.0 }}</div>
|
|
{% endif %}
|
|
<div class="field-help">Detailed description of the surgical procedure performed</div>
|
|
</div>
|
|
|
|
<div class="mb-3">
|
|
<label for="{{ form.technique.id_for_label }}" class="form-label">Surgical Technique</label>
|
|
{{ form.technique }}
|
|
{% if form.technique.errors %}
|
|
<div class="invalid-feedback d-block">{{ form.technique.errors.0 }}</div>
|
|
{% endif %}
|
|
<div class="field-help">Specific surgical techniques and approaches used</div>
|
|
</div>
|
|
|
|
<div class="mb-3">
|
|
<label for="{{ form.findings.id_for_label }}" class="form-label">Operative Findings</label>
|
|
{{ form.findings }}
|
|
{% if form.findings.errors %}
|
|
<div class="invalid-feedback d-block">{{ form.findings.errors.0 }}</div>
|
|
{% endif %}
|
|
<div class="field-help">Findings discovered during the surgical procedure</div>
|
|
</div>
|
|
|
|
<div class="mb-3">
|
|
<label for="{{ form.specimens.id_for_label }}" class="form-label">Specimens Removed</label>
|
|
{{ form.specimens }}
|
|
{% if form.specimens.errors %}
|
|
<div class="invalid-feedback d-block">{{ form.specimens.errors.0 }}</div>
|
|
{% endif %}
|
|
<div class="field-help">Description of any specimens removed during surgery</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Post-operative Information -->
|
|
<div class="form-section">
|
|
<div class="section-header">
|
|
<i class="fas fa-heartbeat me-2"></i>Post-operative Information
|
|
</div>
|
|
<div class="section-content">
|
|
<div class="mb-3">
|
|
<label for="{{ form.postoperative_diagnosis.id_for_label }}" class="form-label">Post-operative Diagnosis</label>
|
|
{{ form.postoperative_diagnosis }}
|
|
{% if form.postoperative_diagnosis.errors %}
|
|
<div class="invalid-feedback d-block">{{ form.postoperative_diagnosis.errors.0 }}</div>
|
|
{% endif %}
|
|
<div class="field-help">Final diagnosis after surgery completion</div>
|
|
</div>
|
|
|
|
<div class="mb-3">
|
|
<label for="{{ form.complications.id_for_label }}" class="form-label">Complications</label>
|
|
{{ form.complications }}
|
|
{% if form.complications.errors %}
|
|
<div class="invalid-feedback d-block">{{ form.complications.errors.0 }}</div>
|
|
{% endif %}
|
|
<div class="field-help">Any complications that occurred during or after surgery</div>
|
|
</div>
|
|
|
|
<div class="mb-3">
|
|
<label for="{{ form.postoperative_instructions.id_for_label }}" class="form-label">Post-operative Instructions</label>
|
|
{{ form.postoperative_instructions }}
|
|
{% if form.postoperative_instructions.errors %}
|
|
<div class="invalid-feedback d-block">{{ form.postoperative_instructions.errors.0 }}</div>
|
|
{% endif %}
|
|
<div class="field-help">Instructions for post-operative care and recovery</div>
|
|
</div>
|
|
|
|
<div class="row">
|
|
<div class="col-md-6">
|
|
<div class="form-floating mb-3">
|
|
{{ form.estimated_blood_loss }}
|
|
<label for="{{ form.estimated_blood_loss.id_for_label }}">Estimated Blood Loss (mL)</label>
|
|
{% if form.estimated_blood_loss.errors %}
|
|
<div class="invalid-feedback d-block">{{ form.estimated_blood_loss.errors.0 }}</div>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
<div class="col-md-6">
|
|
<div class="form-floating mb-3">
|
|
{{ form.fluids_given }}
|
|
<label for="{{ form.fluids_given.id_for_label }}">Fluids Given (mL)</label>
|
|
{% if form.fluids_given.errors %}
|
|
<div class="invalid-feedback d-block">{{ form.fluids_given.errors.0 }}</div>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Equipment and Implants -->
|
|
<div class="form-section">
|
|
<div class="section-header">
|
|
<i class="fas fa-tools me-2"></i>Equipment and Implants
|
|
</div>
|
|
<div class="section-content">
|
|
<div class="mb-3">
|
|
<label for="{{ form.equipment_used.id_for_label }}" class="form-label">Equipment Used</label>
|
|
{{ form.equipment_used }}
|
|
{% if form.equipment_used.errors %}
|
|
<div class="invalid-feedback d-block">{{ form.equipment_used.errors.0 }}</div>
|
|
{% endif %}
|
|
<div class="field-help">List of surgical equipment and instruments used</div>
|
|
</div>
|
|
|
|
<div class="mb-3">
|
|
<label for="{{ form.implants.id_for_label }}" class="form-label">Implants and Prosthetics</label>
|
|
{{ form.implants }}
|
|
{% if form.implants.errors %}
|
|
<div class="invalid-feedback d-block">{{ form.implants.errors.0 }}</div>
|
|
{% endif %}
|
|
<div class="field-help">Details of any implants or prosthetic devices used</div>
|
|
</div>
|
|
|
|
<div class="mb-3">
|
|
<label for="{{ form.suture_materials.id_for_label }}" class="form-label">Suture Materials</label>
|
|
{{ form.suture_materials }}
|
|
{% if form.suture_materials.errors %}
|
|
<div class="invalid-feedback d-block">{{ form.suture_materials.errors.0 }}</div>
|
|
{% endif %}
|
|
<div class="field-help">Types and sizes of suture materials used</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Additional Notes -->
|
|
<div class="form-section">
|
|
<div class="section-header">
|
|
<i class="fas fa-sticky-note me-2"></i>Additional Information
|
|
</div>
|
|
<div class="section-content">
|
|
<div class="mb-3">
|
|
<label for="{{ form.additional_notes.id_for_label }}" class="form-label">Additional Notes</label>
|
|
{{ form.additional_notes }}
|
|
{% if form.additional_notes.errors %}
|
|
<div class="invalid-feedback d-block">{{ form.additional_notes.errors.0 }}</div>
|
|
{% endif %}
|
|
<div class="field-help">Any additional observations or notes</div>
|
|
</div>
|
|
|
|
<div class="row">
|
|
<div class="col-md-6">
|
|
<div class="form-check mb-3">
|
|
{{ form.is_emergency }}
|
|
<label class="form-check-label" for="{{ form.is_emergency.id_for_label }}">
|
|
Emergency Surgery
|
|
</label>
|
|
<div class="field-help">Check if this was an emergency surgical procedure</div>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-6">
|
|
<div class="form-check mb-3">
|
|
{{ form.requires_followup }}
|
|
<label class="form-check-label" for="{{ form.requires_followup.id_for_label }}">
|
|
Requires Follow-up
|
|
</label>
|
|
<div class="field-help">Check if patient requires follow-up care</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Electronic Signature -->
|
|
{% if note.pk and note.status == 'completed' %}
|
|
<div class="form-section">
|
|
<div class="section-header">
|
|
<i class="fas fa-signature me-2"></i>Electronic Signature
|
|
</div>
|
|
<div class="section-content">
|
|
<div class="signature-pad">
|
|
<h6 class="mb-3">Surgeon's Signature</h6>
|
|
<canvas id="signature-canvas" class="signature-canvas" width="600" height="150"></canvas>
|
|
<div class="mt-3">
|
|
<button type="button" class="btn btn-outline-secondary btn-sm me-2" onclick="clearSignature()">
|
|
<i class="fas fa-eraser me-1"></i>Clear
|
|
</button>
|
|
<button type="button" class="btn btn-primary btn-sm" onclick="saveSignature()">
|
|
<i class="fas fa-save me-1"></i>Save Signature
|
|
</button>
|
|
</div>
|
|
<input type="hidden" id="signature-data" name="signature_data">
|
|
</div>
|
|
|
|
<div class="row mt-3">
|
|
<div class="col-md-6">
|
|
<div class="form-floating">
|
|
<input type="text" class="form-control" id="signature-name"
|
|
value="{{ user.get_full_name }}" readonly>
|
|
<label for="signature-name">Signatory Name</label>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-6">
|
|
<div class="form-floating">
|
|
<input type="datetime-local" class="form-control" id="signature-date"
|
|
value="{% now 'Y-m-d\TH:i' %}" readonly>
|
|
<label for="signature-date">Signature Date & Time</label>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{% endif %}
|
|
|
|
<!-- Form Actions -->
|
|
<div class="form-actions d-flex justify-content-between align-items-center">
|
|
<div>
|
|
<button type="button" class="btn btn-outline-secondary" onclick="previewNote()">
|
|
<i class="fas fa-eye me-1"></i>Preview
|
|
</button>
|
|
<button type="button" class="btn btn-outline-info" onclick="validateForm()">
|
|
<i class="fas fa-check-circle me-1"></i>Validate
|
|
</button>
|
|
</div>
|
|
|
|
<div>
|
|
<a href="{% url 'operating_theatre:surgical_note_list' %}" class="btn btn-outline-secondary me-2">
|
|
<i class="fas fa-times me-1"></i>Cancel
|
|
</a>
|
|
<button type="submit" name="action" value="save_draft" class="btn btn-outline-primary me-2">
|
|
<i class="fas fa-save me-1"></i>Save Draft
|
|
</button>
|
|
<button type="submit" name="action" value="save_complete" class="btn btn-success">
|
|
<i class="fas fa-check me-1"></i>
|
|
{% if note.pk %}Update Note{% else %}Create Note{% endif %}
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
|
|
<!-- Preview Modal -->
|
|
<div class="modal fade" id="previewModal" tabindex="-1">
|
|
<div class="modal-dialog modal-xl">
|
|
<div class="modal-content">
|
|
<div class="modal-header">
|
|
<h5 class="modal-title">
|
|
<i class="fas fa-eye me-2"></i>Surgical Note Preview
|
|
</h5>
|
|
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
|
|
</div>
|
|
<div class="modal-body" id="preview-content">
|
|
<!-- Preview content will be loaded here -->
|
|
</div>
|
|
<div class="modal-footer">
|
|
<button type="button" class="btn btn-outline-secondary" data-bs-dismiss="modal">
|
|
<i class="fas fa-times me-1"></i>Close
|
|
</button>
|
|
<button type="button" class="btn btn-primary" onclick="printPreview()">
|
|
<i class="fas fa-print me-1"></i>Print Preview
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Validation Modal -->
|
|
<div class="modal fade" id="validationModal" tabindex="-1">
|
|
<div class="modal-dialog">
|
|
<div class="modal-content">
|
|
<div class="modal-header">
|
|
<h5 class="modal-title">
|
|
<i class="fas fa-check-circle me-2"></i>Form Validation
|
|
</h5>
|
|
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
|
|
</div>
|
|
<div class="modal-body" id="validation-content">
|
|
<!-- Validation results will be loaded here -->
|
|
</div>
|
|
<div class="modal-footer">
|
|
<button type="button" class="btn btn-primary" data-bs-dismiss="modal">
|
|
<i class="fas fa-check me-1"></i>OK
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{% endblock %}
|
|
|
|
{% block js %}
|
|
<script src="{% static 'assets/plugins/select2/dist/js/select2.min.js' %}"></script>
|
|
<script src="{% static 'assets/plugins/bootstrap-datepicker/dist/js/bootstrap-datepicker.min.js' %}"></script>
|
|
<script src="{% static 'assets/plugins/bootstrap-timepicker/js/bootstrap-timepicker.min.js' %}"></script>
|
|
|
|
<script>
|
|
$(document).ready(function() {
|
|
// Initialize Select2 for dropdowns
|
|
$('.form-select').select2({
|
|
theme: 'bootstrap-5',
|
|
width: '100%'
|
|
});
|
|
|
|
// Initialize date picker
|
|
$('input[type="date"]').datepicker({
|
|
format: 'yyyy-mm-dd',
|
|
autoclose: true,
|
|
todayHighlight: true
|
|
});
|
|
|
|
// Initialize time picker
|
|
$('input[type="time"]').timepicker({
|
|
showMeridian: false,
|
|
defaultTime: false
|
|
});
|
|
|
|
// Template selector
|
|
$('#template-selector').on('change', function() {
|
|
const templateContent = $(this).find(':selected').data('content');
|
|
if (templateContent) {
|
|
loadTemplate(templateContent);
|
|
}
|
|
});
|
|
|
|
// Auto-save functionality
|
|
let autoSaveTimer;
|
|
$('form input, form textarea, form select').on('input change', function() {
|
|
clearTimeout(autoSaveTimer);
|
|
autoSaveTimer = setTimeout(autoSave, 30000); // Auto-save after 30 seconds of inactivity
|
|
});
|
|
|
|
// Form validation
|
|
$('#surgical-note-form').on('submit', function(e) {
|
|
if (!validateRequiredFields()) {
|
|
e.preventDefault();
|
|
showValidationErrors();
|
|
}
|
|
});
|
|
|
|
// Initialize signature pad if present
|
|
if (document.getElementById('signature-canvas')) {
|
|
initializeSignaturePad();
|
|
}
|
|
});
|
|
|
|
function loadTemplate(templateContent) {
|
|
if (confirm('Loading a template will replace current content. Continue?')) {
|
|
try {
|
|
const template = JSON.parse(templateContent);
|
|
|
|
// Populate form fields from template
|
|
Object.keys(template).forEach(key => {
|
|
const field = document.querySelector(`[name="${key}"]`);
|
|
if (field) {
|
|
field.value = template[key];
|
|
$(field).trigger('change');
|
|
}
|
|
});
|
|
|
|
showAlert('Template loaded successfully', 'success');
|
|
} catch (error) {
|
|
showAlert('Error loading template', 'danger');
|
|
}
|
|
}
|
|
}
|
|
|
|
function autoSave() {
|
|
const formData = new FormData(document.getElementById('surgical-note-form'));
|
|
formData.append('action', 'auto_save');
|
|
|
|
fetch(window.location.href, {
|
|
method: 'POST',
|
|
body: formData,
|
|
headers: {
|
|
'X-CSRFToken': document.querySelector('[name=csrfmiddlewaretoken]').value
|
|
}
|
|
})
|
|
.then(response => response.json())
|
|
.then(data => {
|
|
if (data.success) {
|
|
showAutoSaveIndicator();
|
|
}
|
|
})
|
|
.catch(error => {
|
|
console.error('Auto-save failed:', error);
|
|
});
|
|
}
|
|
|
|
function showAutoSaveIndicator() {
|
|
const indicator = document.getElementById('auto-save-indicator');
|
|
indicator.style.display = 'block';
|
|
setTimeout(() => {
|
|
indicator.style.display = 'none';
|
|
}, 2000);
|
|
}
|
|
|
|
function saveDraft() {
|
|
const form = document.getElementById('surgical-note-form');
|
|
const actionInput = document.createElement('input');
|
|
actionInput.type = 'hidden';
|
|
actionInput.name = 'action';
|
|
actionInput.value = 'save_draft';
|
|
form.appendChild(actionInput);
|
|
form.submit();
|
|
}
|
|
|
|
function previewNote() {
|
|
const formData = new FormData(document.getElementById('surgical-note-form'));
|
|
|
|
fetch('{% url "operating_theatre:surgical_note_preview" %}', {
|
|
method: 'POST',
|
|
body: formData,
|
|
headers: {
|
|
'X-CSRFToken': document.querySelector('[name=csrfmiddlewaretoken]').value
|
|
}
|
|
})
|
|
.then(response => response.text())
|
|
.then(html => {
|
|
document.getElementById('preview-content').innerHTML = html;
|
|
new bootstrap.Modal(document.getElementById('previewModal')).show();
|
|
})
|
|
.catch(error => {
|
|
showAlert('Error generating preview', 'danger');
|
|
});
|
|
}
|
|
|
|
function validateForm() {
|
|
const errors = [];
|
|
const requiredFields = document.querySelectorAll('[required]');
|
|
|
|
requiredFields.forEach(field => {
|
|
if (!field.value.trim()) {
|
|
errors.push(`${field.labels[0].textContent} is required`);
|
|
}
|
|
});
|
|
|
|
// Custom validation rules
|
|
const startTime = document.querySelector('[name="start_time"]').value;
|
|
const endTime = document.querySelector('[name="end_time"]').value;
|
|
|
|
if (startTime && endTime && startTime >= endTime) {
|
|
errors.push('End time must be after start time');
|
|
}
|
|
|
|
const bloodLoss = document.querySelector('[name="estimated_blood_loss"]').value;
|
|
if (bloodLoss && (isNaN(bloodLoss) || bloodLoss < 0)) {
|
|
errors.push('Blood loss must be a positive number');
|
|
}
|
|
|
|
// Display validation results
|
|
let content = '';
|
|
if (errors.length === 0) {
|
|
content = `
|
|
<div class="alert alert-success">
|
|
<i class="fas fa-check-circle me-2"></i>
|
|
All form fields are valid and ready for submission.
|
|
</div>
|
|
`;
|
|
} else {
|
|
content = `
|
|
<div class="alert alert-danger">
|
|
<h6><i class="fas fa-exclamation-triangle me-2"></i>Validation Errors:</h6>
|
|
<ul class="mb-0">
|
|
${errors.map(error => `<li>${error}</li>`).join('')}
|
|
</ul>
|
|
</div>
|
|
`;
|
|
}
|
|
|
|
document.getElementById('validation-content').innerHTML = content;
|
|
new bootstrap.Modal(document.getElementById('validationModal')).show();
|
|
}
|
|
|
|
function validateRequiredFields() {
|
|
const requiredFields = document.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');
|
|
}
|
|
});
|
|
|
|
return isValid;
|
|
}
|
|
|
|
function showValidationErrors() {
|
|
showAlert('Please fill in all required fields', 'danger');
|
|
}
|
|
|
|
function printPreview() {
|
|
const printContent = document.getElementById('preview-content').innerHTML;
|
|
const printWindow = window.open('', '_blank');
|
|
printWindow.document.write(`
|
|
<html>
|
|
<head>
|
|
<title>Surgical Note Preview</title>
|
|
<link href="{% static 'assets/css/vendor.min.css' %}" rel="stylesheet">
|
|
<link href="{% static 'assets/css/app.min.css' %}" rel="stylesheet">
|
|
<style>
|
|
body { font-family: Arial, sans-serif; }
|
|
@media print {
|
|
.no-print { display: none !important; }
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
${printContent}
|
|
</body>
|
|
</html>
|
|
`);
|
|
printWindow.document.close();
|
|
printWindow.print();
|
|
}
|
|
|
|
// Signature pad functionality
|
|
let signaturePad;
|
|
|
|
function initializeSignaturePad() {
|
|
const canvas = document.getElementById('signature-canvas');
|
|
const ctx = canvas.getContext('2d');
|
|
|
|
let isDrawing = false;
|
|
let lastX = 0;
|
|
let lastY = 0;
|
|
|
|
canvas.addEventListener('mousedown', startDrawing);
|
|
canvas.addEventListener('mousemove', draw);
|
|
canvas.addEventListener('mouseup', stopDrawing);
|
|
canvas.addEventListener('mouseout', stopDrawing);
|
|
|
|
// Touch events for mobile
|
|
canvas.addEventListener('touchstart', handleTouch);
|
|
canvas.addEventListener('touchmove', handleTouch);
|
|
canvas.addEventListener('touchend', stopDrawing);
|
|
|
|
function startDrawing(e) {
|
|
isDrawing = true;
|
|
[lastX, lastY] = getMousePos(e);
|
|
}
|
|
|
|
function draw(e) {
|
|
if (!isDrawing) return;
|
|
|
|
const [currentX, currentY] = getMousePos(e);
|
|
|
|
ctx.beginPath();
|
|
ctx.moveTo(lastX, lastY);
|
|
ctx.lineTo(currentX, currentY);
|
|
ctx.strokeStyle = '#000';
|
|
ctx.lineWidth = 2;
|
|
ctx.lineCap = 'round';
|
|
ctx.stroke();
|
|
|
|
[lastX, lastY] = [currentX, currentY];
|
|
}
|
|
|
|
function stopDrawing() {
|
|
isDrawing = false;
|
|
}
|
|
|
|
function getMousePos(e) {
|
|
const rect = canvas.getBoundingClientRect();
|
|
return [
|
|
e.clientX - rect.left,
|
|
e.clientY - rect.top
|
|
];
|
|
}
|
|
|
|
function handleTouch(e) {
|
|
e.preventDefault();
|
|
const touch = e.touches[0];
|
|
const mouseEvent = new MouseEvent(e.type === 'touchstart' ? 'mousedown' :
|
|
e.type === 'touchmove' ? 'mousemove' : 'mouseup', {
|
|
clientX: touch.clientX,
|
|
clientY: touch.clientY
|
|
});
|
|
canvas.dispatchEvent(mouseEvent);
|
|
}
|
|
}
|
|
|
|
function clearSignature() {
|
|
const canvas = document.getElementById('signature-canvas');
|
|
const ctx = canvas.getContext('2d');
|
|
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
|
document.getElementById('signature-data').value = '';
|
|
}
|
|
|
|
function saveSignature() {
|
|
const canvas = document.getElementById('signature-canvas');
|
|
const signatureData = canvas.toDataURL();
|
|
document.getElementById('signature-data').value = signatureData;
|
|
showAlert('Signature saved', 'success');
|
|
}
|
|
|
|
function showAlert(message, type) {
|
|
const alertDiv = document.createElement('div');
|
|
alertDiv.className = `alert alert-${type} alert-dismissible fade show position-fixed`;
|
|
alertDiv.style.cssText = 'top: 20px; right: 20px; z-index: 1060; min-width: 300px;';
|
|
alertDiv.innerHTML = `
|
|
${message}
|
|
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
|
|
`;
|
|
|
|
document.body.appendChild(alertDiv);
|
|
|
|
setTimeout(() => {
|
|
if (alertDiv.parentNode) {
|
|
alertDiv.remove();
|
|
}
|
|
}, 5000);
|
|
}
|
|
</script>
|
|
{% endblock %}
|
|
|