Marwan Alwali b9b8c69129 update
2025-08-31 10:47:23 +03:00

517 lines
19 KiB
HTML

{% extends 'base.html' %}
{% load static %}
{% block title %}Surgical Notes{% endblock %}
{% block css %}
<link href="{% static 'assets/plugins/datatables.net-bs5/css/dataTables.bootstrap5.min.css' %}" rel="stylesheet" />
<link href="{% static 'assets/plugins/datatables.net-responsive-bs5/css/responsive.bootstrap5.min.css' %}" rel="stylesheet" />
<style>
.stats-card {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
border-radius: 0.5rem;
padding: 1.5rem;
margin-bottom: 1.5rem;
}
.stats-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 1rem;
margin-bottom: 2rem;
}
.stat-item {
background: white;
border: 1px solid #dee2e6;
border-radius: 0.375rem;
padding: 1.5rem;
text-align: center;
}
.stat-icon {
width: 50px;
height: 50px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
margin: 0 auto 1rem;
color: white;
}
.stat-icon.primary { background: #007bff; }
.stat-icon.success { background: #28a745; }
.stat-icon.warning { background: #ffc107; }
.stat-icon.info { background: #17a2b8; }
.stat-number {
font-size: 2rem;
font-weight: bold;
color: #495057;
margin-bottom: 0.5rem;
}
.stat-label {
color: #6c757d;
font-size: 0.875rem;
font-weight: 600;
}
.filter-section {
background: white;
border: 1px solid #dee2e6;
border-radius: 0.375rem;
padding: 1.5rem;
margin-bottom: 1.5rem;
}
.note-status {
padding: 0.25rem 0.75rem;
border-radius: 0.25rem;
font-size: 0.75rem;
font-weight: 600;
text-transform: uppercase;
}
.status-draft { background: #f8f9fa; color: #6c757d; }
.status-in-progress { background: #fff3cd; color: #856404; }
.status-completed { background: #d4edda; color: #155724; }
.status-signed { background: #d1ecf1; color: #0c5460; }
.status-amended { background: #f8d7da; color: #721c24; }
.priority-badge {
padding: 0.25rem 0.5rem;
border-radius: 0.25rem;
font-size: 0.75rem;
font-weight: 600;
}
.priority-low { background: #d4edda; color: #155724; }
.priority-medium { background: #fff3cd; color: #856404; }
.priority-high { background: #f8d7da; color: #721c24; }
.priority-critical { background: #f5c6cb; color: #721c24; }
@media (max-width: 768px) {
.stats-grid {
grid-template-columns: repeat(2, 1fr);
}
.stat-item {
padding: 1rem;
}
.stat-number {
font-size: 1.5rem;
}
}
</style>
{% endblock %}
{% block content %}
<div id="content" class="app-content">
<!-- 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 active">Surgical Notes</li>
</ol>
<h1 class="page-header mb-0">
<i class="fas fa-file-medical me-2"></i>Surgical Notes Management
</h1>
</div>
<div class="ms-auto">
<a href="{% url 'operating_theatre:surgical_note_create' %}" class="btn btn-primary">
<i class="fas fa-plus me-1"></i>Create Note
</a>
</div>
</div>
<!-- Statistics Cards -->
<div class="stats-grid">
<div class="stat-item">
<div class="stat-icon primary">
<i class="fas fa-file-medical"></i>
</div>
<div class="stat-number" id="total-notes">{{ stats.total_notes|default:0 }}</div>
<div class="stat-label">Total Notes</div>
</div>
<div class="stat-item">
<div class="stat-icon warning">
<i class="fas fa-edit"></i>
</div>
<div class="stat-number" id="draft-notes">{{ stats.draft_notes|default:0 }}</div>
<div class="stat-label">Draft Notes</div>
</div>
<div class="stat-item">
<div class="stat-icon success">
<i class="fas fa-check-circle"></i>
</div>
<div class="stat-number" id="completed-notes">{{ stats.completed_notes|default:0 }}</div>
<div class="stat-label">Completed</div>
</div>
<div class="stat-item">
<div class="stat-icon info">
<i class="fas fa-signature"></i>
</div>
<div class="stat-number" id="signed-notes">{{ stats.signed_notes|default:0 }}</div>
<div class="stat-label">Signed</div>
</div>
</div>
<!-- Filters -->
<div class="filter-section">
<div class="row">
<div class="col-md-3">
<label class="form-label">Status</label>
<select class="form-select" id="status-filter">
<option value="">All Statuses</option>
<option value="draft">Draft</option>
<option value="in_progress">In Progress</option>
<option value="completed">Completed</option>
<option value="signed">Signed</option>
<option value="amended">Amended</option>
</select>
</div>
<div class="col-md-3">
<label class="form-label">Surgeon</label>
<select class="form-select" id="surgeon-filter">
<option value="">All Surgeons</option>
{% for surgeon in surgeons %}
<option value="{{ surgeon.id }}">{{ surgeon.get_full_name }}</option>
{% endfor %}
</select>
</div>
<div class="col-md-3">
<label class="form-label">Date Range</label>
<select class="form-select" id="date-filter">
<option value="">All Dates</option>
<option value="today">Today</option>
<option value="week">This Week</option>
<option value="month">This Month</option>
<option value="custom">Custom Range</option>
</select>
</div>
<div class="col-md-3">
<label class="form-label">Priority</label>
<select class="form-select" id="priority-filter">
<option value="">All Priorities</option>
<option value="low">Low</option>
<option value="medium">Medium</option>
<option value="high">High</option>
<option value="critical">Critical</option>
</select>
</div>
</div>
<div class="row mt-3" id="custom-date-range" style="display: none;">
<div class="col-md-6">
<label class="form-label">From Date</label>
<input type="date" class="form-control" id="from-date">
</div>
<div class="col-md-6">
<label class="form-label">To Date</label>
<input type="date" class="form-control" id="to-date">
</div>
</div>
<div class="row mt-3">
<div class="col-md-8">
<label class="form-label">Search</label>
<div class="input-group">
<input type="text" class="form-control" id="search-input"
placeholder="Search by patient name, procedure, or note content...">
<button class="btn btn-outline-secondary" type="button" id="search-btn">
<i class="fas fa-search"></i>
</button>
</div>
</div>
<div class="col-md-4 d-flex align-items-end">
<button class="btn btn-outline-primary me-2" onclick="applyFilters()">
<i class="fas fa-filter me-1"></i>Apply
</button>
<button class="btn btn-outline-secondary" onclick="clearFilters()">
<i class="fas fa-times me-1"></i>Clear
</button>
</div>
</div>
</div>
<!-- Notes Table -->
<div class="card">
<div class="card-header d-flex justify-content-between align-items-center">
<h5 class="mb-0">
<i class="fas fa-list me-2"></i>Surgical Notes
</h5>
<div class="btn-group">
<button class="btn btn-outline-success btn-sm" onclick="exportNotes('excel')">
<i class="fas fa-file-excel me-1"></i>Excel
</button>
<button class="btn btn-outline-danger btn-sm" onclick="exportNotes('pdf')">
<i class="fas fa-file-pdf me-1"></i>PDF
</button>
<button class="btn btn-outline-secondary btn-sm" onclick="printNotes()">
<i class="fas fa-print me-1"></i>Print
</button>
</div>
</div>
<div class="card-body">
<div class="table-responsive">
<table id="notesTable" class="table table-striped table-bordered align-middle">
<thead>
<tr>
<th width="5%">
<input type="checkbox" id="select-all">
</th>
<th>Patient</th>
<th>Procedure</th>
<th>Surgeon</th>
<th>Date</th>
<th>Status</th>
<th>Priority</th>
<th>Last Modified</th>
<th width="15%">Actions</th>
</tr>
</thead>
<tbody>
{% for note in notes %}
<tr>
<td>
<input type="checkbox" class="note-checkbox" value="{{ note.id }}">
</td>
<td>
<div class="d-flex align-items-center">
<div class="avatar avatar-sm me-2">
<i class="fas fa-user-circle fa-2x text-muted"></i>
</div>
<div>
<div class="fw-bold">{{ note.patient.get_full_name }}</div>
<small class="text-muted">ID: {{ note.patient.patient_id }}</small>
</div>
</div>
</td>
<td>
<div class="fw-bold">{{ note.procedure_name }}</div>
<small class="text-muted">{{ note.procedure_code|default:"" }}</small>
</td>
<td>
<div>{{ note.surgeon.get_full_name }}</div>
<small class="text-muted">{{ note.surgeon.specialization|default:"" }}</small>
</td>
<td>
<div>{{ note.surgery_date|date:"M d, Y" }}</div>
<small class="text-muted">{{ note.surgery_date|time:"g:i A" }}</small>
</td>
<td>
<span class="note-status status-{{ note.status }}">
{{ note.get_status_display }}
</span>
</td>
<td>
<span class="priority-badge priority-{{ note.priority }}">
{{ note.get_priority_display }}
</span>
</td>
<td>
<div>{{ note.updated_at|date:"M d, Y" }}</div>
<small class="text-muted">{{ note.updated_at|time:"g:i A" }}</small>
</td>
<td>
<div class="btn-group btn-group-sm">
<a href="{% url 'operating_theatre:surgical_note_detail' note.pk %}"
class="btn btn-outline-primary" title="View">
<i class="fas fa-eye"></i>
</a>
{% if note.status != 'signed' %}
<a href="{% url 'operating_theatre:surgical_note_edit' note.pk %}"
class="btn btn-outline-warning" title="Edit">
<i class="fas fa-edit"></i>
</a>
{% endif %}
<button class="btn btn-outline-success"
onclick="printNote('{{ note.pk }}')" title="Print">
<i class="fas fa-print"></i>
</button>
{% if note.status != 'signed' %}
<a href="{% url 'operating_theatre:surgical_note_delete' note.pk %}"
class="btn btn-outline-danger" title="Delete">
<i class="fas fa-trash"></i>
</a>
{% endif %}
</div>
</td>
</tr>
{% empty %}
<tr>
<td colspan="9" class="text-center py-4">
<i class="fas fa-file-medical fa-3x text-muted mb-3"></i>
<p class="text-muted">No surgical notes found</p>
<a href="{% url 'operating_theatre:surgical_note_create' %}" class="btn btn-primary">
<i class="fas fa-plus me-1"></i>Create First Note
</a>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
</div>
</div>
{% endblock %}
{% block js %}
<script src="{% static 'assets/plugins/datatables.net/js/jquery.dataTables.min.js' %}"></script>
<script src="{% static 'assets/plugins/datatables.net-bs5/js/dataTables.bootstrap5.min.js' %}"></script>
<script src="{% static 'assets/plugins/datatables.net-responsive/js/dataTables.responsive.min.js' %}"></script>
<script src="{% static 'assets/plugins/datatables.net-responsive-bs5/js/responsive.bootstrap5.min.js' %}"></script>
<script>
$(document).ready(function() {
// Initialize DataTable
const table = $('#notesTable').DataTable({
responsive: true,
pageLength: 25,
order: [[4, 'desc']], // Sort by date descending
columnDefs: [
{ orderable: false, targets: [0, 8] } // Disable sorting for checkbox and actions
],
language: {
search: "",
searchPlaceholder: "Search notes...",
lengthMenu: "Show _MENU_ notes per page",
info: "Showing _START_ to _END_ of _TOTAL_ notes",
infoEmpty: "No notes available",
infoFiltered: "(filtered from _MAX_ total notes)"
}
});
// Custom search
$('#search-input').on('keyup', function() {
table.search(this.value).draw();
});
// Select all checkbox
$('#select-all').on('change', function() {
$('.note-checkbox').prop('checked', this.checked);
});
// Date filter change
$('#date-filter').on('change', function() {
if ($(this).val() === 'custom') {
$('#custom-date-range').show();
} else {
$('#custom-date-range').hide();
}
});
// Load statistics
loadStats();
});
function loadStats() {
$.ajax({
url: '{% url "operating_theatre:surgical_note_stats" %}',
success: function(data) {
$('#total-notes').text(data.total_notes);
$('#draft-notes').text(data.draft_notes);
$('#completed-notes').text(data.completed_notes);
$('#signed-notes').text(data.signed_notes);
}
});
}
function applyFilters() {
const filters = {
status: $('#status-filter').val(),
surgeon: $('#surgeon-filter').val(),
date_range: $('#date-filter').val(),
priority: $('#priority-filter').val(),
from_date: $('#from-date').val(),
to_date: $('#to-date').val(),
search: $('#search-input').val()
};
// Apply filters via AJAX
$.ajax({
url: '{% url "operating_theatre:surgical_note_list" %}',
data: filters,
success: function(response) {
// Reload table data
location.reload();
}
});
}
function clearFilters() {
$('#status-filter').val('');
$('#surgeon-filter').val('');
$('#date-filter').val('');
$('#priority-filter').val('');
$('#from-date').val('');
$('#to-date').val('');
$('#search-input').val('');
$('#custom-date-range').hide();
// Clear DataTable search
$('#notesTable').DataTable().search('').draw();
}
function exportNotes(format) {
const selectedNotes = $('.note-checkbox:checked').map(function() {
return this.value;
}).get();
const params = new URLSearchParams({
format: format,
notes: selectedNotes.join(',')
});
window.open(`{% url "operating_theatre:surgical_note_export" %}?${params}`, '_blank');
}
function printNotes() {
const selectedNotes = $('.note-checkbox:checked').map(function() {
return this.value;
}).get();
if (selectedNotes.length === 0) {
alert('Please select notes to print');
return;
}
const params = new URLSearchParams({
notes: selectedNotes.join(',')
});
window.open(`{% url "operating_theatre:surgical_note_print" %}?${params}`, '_blank');
}
function printNote(noteId) {
window.open(`{% url "operating_theatre:surgical_note_print" %}?notes=${noteId}`, '_blank');
}
// Keyboard shortcuts
$(document).keydown(function(e) {
// Ctrl+N for new note
if (e.ctrlKey && e.keyCode === 78) {
e.preventDefault();
window.location.href = '{% url "operating_theatre:surgical_note_create" %}';
}
// Ctrl+F for search
if (e.ctrlKey && e.keyCode === 70) {
e.preventDefault();
$('#search-input').focus();
}
});
</script>
{% endblock %}