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

516 lines
25 KiB
HTML

{% extends "base.html" %}
{% load static %}
{% block title %}Note Templates{% endblock %}
{% block css %}
<link href="{% static 'plugins/bootstrap-icons/font/bootstrap-icons.css' %}" rel="stylesheet" />
<style>
.template-badge {
font-size: 0.85rem;
padding: 0.35em 0.65em;
}
.template-type-progress {
background-color: var(--bs-primary);
color: white;
}
.template-type-admission {
background-color: var(--bs-danger);
color: white;
}
.template-type-discharge {
background-color: var(--bs-success);
color: white;
}
.template-type-procedure {
background-color: var(--bs-warning);
color: white;
}
.template-type-consultation {
background-color: var(--bs-info);
color: white;
}
.template-card {
transition: all 0.2s;
height: 100%;
}
.template-card:hover {
transform: translateY(-3px);
box-shadow: 0 4px 8px rgba(0,0,0,0.1);
}
.template-card .card-footer {
background-color: transparent;
border-top: 1px solid var(--bs-gray-200);
}
.template-card .card-body {
padding-bottom: 0.5rem;
}
.template-actions {
position: absolute;
top: 0.5rem;
right: 0.5rem;
display: none;
}
.template-card:hover .template-actions {
display: block;
}
.view-toggle-btn.active {
background-color: var(--bs-light);
border-color: var(--bs-gray-400);
}
.filter-section {
background-color: rgba(var(--bs-light-rgb), 0.5);
padding: 1rem;
border-radius: 0.5rem;
margin-bottom: 1.5rem;
border: 1px solid var(--bs-gray-300);
}
</style>
{% endblock %}
{% block content %}
<!-- begin breadcrumb -->
<ol class="breadcrumb float-xl-end">
<li class="breadcrumb-item"><a href="{% url 'dashboard' %}">Home</a></li>
<li class="breadcrumb-item"><a href="{% url 'emr:dashboard' %}">EMR</a></li>
<li class="breadcrumb-item active">Note Templates</li>
</ol>
<!-- end breadcrumb -->
<!-- begin page-header -->
<h1 class="page-header">Note Templates <small>Standardized clinical documentation templates</small></h1>
<!-- end page-header -->
<!-- begin row -->
<div class="row">
<!-- begin col-12 -->
<div class="col-xl-12">
<!-- begin panel -->
<div class="panel panel-inverse">
<div class="panel-heading">
<h4 class="panel-title">Template Management</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>
<div class="panel-body">
<!-- begin filter section -->
<div class="filter-section">
<div class="row">
<div class="col-md-8">
<div class="input-group">
<span class="input-group-text"><i class="fa fa-search"></i></span>
<input type="text" class="form-control" id="templateSearch" placeholder="Search templates by title, description, or content...">
<button class="btn btn-primary" type="button" id="searchBtn">Search</button>
</div>
</div>
<div class="col-md-4 text-end">
<div class="btn-group" role="group">
<button type="button" class="btn btn-outline-secondary view-toggle-btn active" data-view="grid">
<i class="fa fa-th-large"></i>
</button>
<button type="button" class="btn btn-outline-secondary view-toggle-btn" data-view="list">
<i class="fa fa-list"></i>
</button>
</div>
<a href="{% url 'emr:note_template_create' %}" class="btn btn-success ms-2">
<i class="fa fa-plus me-1"></i> New Template
</a>
</div>
</div>
<div class="row mt-3">
<div class="col-md-3">
<select class="form-select" id="filterType">
<option value="">All Note Types</option>
<option value="PROGRESS">Progress Note</option>
<option value="ADMISSION">Admission Note</option>
<option value="DISCHARGE">Discharge Summary</option>
<option value="PROCEDURE">Procedure Note</option>
<option value="CONSULTATION">Consultation Note</option>
<option value="OTHER">Other</option>
</select>
</div>
<div class="col-md-3">
<select class="form-select" id="filterDepartment">
<option value="">All Departments</option>
{% for department in departments %}
<option value="{{ department.id }}">{{ department.name }}</option>
{% endfor %}
</select>
</div>
<div class="col-md-3">
<select class="form-select" id="filterSpecialty">
<option value="">All Specialties</option>
{% for specialty in specialties %}
<option value="{{ specialty.id }}">{{ specialty.name }}</option>
{% endfor %}
</select>
</div>
<div class="col-md-3">
<select class="form-select" id="sortBy">
<option value="title">Sort by Title</option>
<option value="created_at">Sort by Date Created</option>
<option value="updated_at">Sort by Last Updated</option>
<option value="usage_count">Sort by Usage Count</option>
</select>
</div>
</div>
</div>
<!-- end filter section -->
<!-- begin stats section -->
<div class="row mb-4">
<div class="col-md-3">
<div class="widget widget-stats bg-blue">
<div class="stats-icon"><i class="fa fa-file-alt"></i></div>
<div class="stats-info">
<h4>Total Templates</h4>
<p>{{ templates.count }}</p>
</div>
<div class="stats-link">
<a href="javascript:;">View Detail <i class="fa fa-arrow-alt-circle-right"></i></a>
</div>
</div>
</div>
<div class="col-md-3">
<div class="widget widget-stats bg-info">
<div class="stats-icon"><i class="fa fa-star"></i></div>
<div class="stats-info">
<h4>Most Used Template</h4>
<p>{{ most_used_template.title|default:"None" }}</p>
</div>
<div class="stats-link">
<a href="javascript:;">View Detail <i class="fa fa-arrow-alt-circle-right"></i></a>
</div>
</div>
</div>
<div class="col-md-3">
<div class="widget widget-stats bg-orange">
<div class="stats-icon"><i class="fa fa-clock"></i></div>
<div class="stats-info">
<h4>Recently Updated</h4>
<p>{{ recently_updated_count }}</p>
</div>
<div class="stats-link">
<a href="javascript:;">View Detail <i class="fa fa-arrow-alt-circle-right"></i></a>
</div>
</div>
</div>
<div class="col-md-3">
<div class="widget widget-stats bg-red">
<div class="stats-icon"><i class="fa fa-exclamation-triangle"></i></div>
<div class="stats-info">
<h4>Outdated Templates</h4>
<p>{{ outdated_templates_count }}</p>
</div>
<div class="stats-link">
<a href="javascript:;">View Detail <i class="fa fa-arrow-alt-circle-right"></i></a>
</div>
</div>
</div>
</div>
<!-- end stats section -->
<!-- begin grid view -->
<div id="gridView">
<div class="row" id="templateGrid">
{% for template in templates %}
<div class="col-md-4 mb-4 template-item"
data-type="{{ template.note_type }}"
data-department="{{ template.department.id|default:'' }}"
data-specialty="{{ template.specialty.id|default:'' }}">
<div class="card template-card">
<div class="card-body">
<div class="template-actions">
<div class="dropdown">
<button class="btn btn-sm btn-default dropdown-toggle" type="button" id="dropdownMenuButton{{ template.id }}" data-bs-toggle="dropdown" aria-expanded="false">
<i class="fa fa-ellipsis-v"></i>
</button>
<ul class="dropdown-menu dropdown-menu-end" aria-labelledby="dropdownMenuButton{{ template.id }}">
<li><a class="dropdown-item" href="{% url 'emr:note_template_detail' template.id %}"><i class="fa fa-eye me-2"></i> View</a></li>
<li><a class="dropdown-item" href="{% url 'emr:note_template_update' template.id %}"><i class="fa fa-edit me-2"></i> Edit</a></li>
<li><a class="dropdown-item" href="{% url 'emr:note_template_duplicate' template.id %}"><i class="fa fa-copy me-2"></i> Duplicate</a></li>
<li><hr class="dropdown-divider"></li>
<li><a class="dropdown-item text-danger" href="{% url 'emr:note_template_delete' template.id %}"><i class="fa fa-trash me-2"></i> Delete</a></li>
</ul>
</div>
</div>
<h5 class="card-title mb-2">{{ template.title }}</h5>
<span class="badge template-type-{{ template.note_type|lower }} mb-2">{{ template.get_note_type_display }}</span>
<p class="card-text text-muted mb-2">{{ template.description|truncatechars:100 }}</p>
<div class="d-flex justify-content-between align-items-center mb-2">
<small class="text-muted">v{{ template.version }}</small>
<small class="text-muted">Used {{ template.usage_count }} times</small>
</div>
</div>
<div class="card-footer">
<div class="d-flex justify-content-between align-items-center">
<small class="text-muted">Updated {{ template.updated_at|date:"M d, Y" }}</small>
<a href="{% url 'emr:note_template_detail' template.id %}" class="btn btn-sm btn-primary">
<i class="fa fa-eye me-1"></i> View
</a>
</div>
</div>
</div>
</div>
{% empty %}
<div class="col-12">
<div class="alert alert-info">
<i class="fa fa-info-circle me-2"></i> No note templates found. <a href="{% url 'emr:note_template_create' %}">Create your first template</a>.
</div>
</div>
{% endfor %}
</div>
</div>
<!-- end grid view -->
<!-- begin list view -->
<div id="listView" style="display: none;">
<div class="table-responsive">
<table class="table table-striped table-bordered" id="templateTable">
<thead>
<tr>
<th>Title</th>
<th>Type</th>
<th>Department</th>
<th>Specialty</th>
<th>Version</th>
<th>Last Updated</th>
<th>Usage Count</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
{% for template in templates %}
<tr class="template-item"
data-type="{{ template.note_type }}"
data-department="{{ template.department.id|default:'' }}"
data-specialty="{{ template.specialty.id|default:'' }}">
<td>{{ template.title }}</td>
<td>
<span class="badge template-type-{{ template.note_type|lower }}">{{ template.get_note_type_display }}</span>
</td>
<td>{{ template.department.name|default:"All Departments" }}</td>
<td>{{ template.specialty.name|default:"All Specialties" }}</td>
<td>v{{ template.version }}</td>
<td>{{ template.updated_at|date:"M d, Y" }}</td>
<td>{{ template.usage_count }}</td>
<td>
<a href="{% url 'emr:note_template_detail' template.id %}" class="btn btn-sm btn-primary" data-bs-toggle="tooltip" title="View">
<i class="fa fa-eye"></i>
</a>
<a href="{% url 'emr:note_template_update' template.id %}" class="btn btn-sm btn-info" data-bs-toggle="tooltip" title="Edit">
<i class="fa fa-edit"></i>
</a>
<a href="{% url 'emr:note_template_duplicate' template.id %}" class="btn btn-sm btn-success" data-bs-toggle="tooltip" title="Duplicate">
<i class="fa fa-copy"></i>
</a>
<a href="{% url 'emr:note_template_delete' template.id %}" class="btn btn-sm btn-danger" data-bs-toggle="tooltip" title="Delete">
<i class="fa fa-trash"></i>
</a>
</td>
</tr>
{% empty %}
<tr>
<td colspan="8" class="text-center">No note templates found.</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
<!-- end list view -->
<!-- begin pagination -->
<div class="d-flex justify-content-between align-items-center mt-4">
<div>
Showing <span id="visibleCount">{{ templates.count }}</span> of {{ templates.count }} templates
</div>
<nav aria-label="Page navigation">
<ul class="pagination">
<li class="page-item disabled">
<a class="page-link" href="#" aria-label="Previous">
<span aria-hidden="true">&laquo;</span>
</a>
</li>
<li class="page-item active"><a class="page-link" href="#">1</a></li>
<li class="page-item"><a class="page-link" href="#">2</a></li>
<li class="page-item"><a class="page-link" href="#">3</a></li>
<li class="page-item">
<a class="page-link" href="#" aria-label="Next">
<span aria-hidden="true">&raquo;</span>
</a>
</li>
</ul>
</nav>
</div>
<!-- end pagination -->
</div>
</div>
<!-- end panel -->
</div>
<!-- end col-12 -->
</div>
<!-- end row -->
<!-- begin template preview modal -->
<div class="modal fade" id="templatePreviewModal" tabindex="-1" aria-labelledby="templatePreviewModalLabel" aria-hidden="true">
<div class="modal-dialog modal-lg">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="templatePreviewModalLabel">Template Preview</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<div id="templatePreviewContent"></div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
<a href="#" id="useTemplateBtn" class="btn btn-primary">Use Template</a>
</div>
</div>
</div>
</div>
<!-- end template preview modal -->
{% endblock %}
{% block js %}
<script>
$(document).ready(function() {
// Initialize tooltips
$('[data-bs-toggle="tooltip"]').tooltip();
// View toggle
$('.view-toggle-btn').click(function() {
$('.view-toggle-btn').removeClass('active');
$(this).addClass('active');
const view = $(this).data('view');
if (view === 'grid') {
$('#gridView').show();
$('#listView').hide();
} else {
$('#gridView').hide();
$('#listView').show();
}
});
// Filter function
function filterTemplates() {
const searchTerm = $('#templateSearch').val().toLowerCase();
const filterType = $('#filterType').val();
const filterDepartment = $('#filterDepartment').val();
const filterSpecialty = $('#filterSpecialty').val();
let visibleCount = 0;
$('.template-item').each(function() {
const title = $(this).find('.card-title, td:first-child').text().toLowerCase();
const description = $(this).find('.card-text').text().toLowerCase();
const type = $(this).data('type');
const department = $(this).data('department').toString();
const specialty = $(this).data('specialty').toString();
const matchesSearch = title.includes(searchTerm) || description.includes(searchTerm);
const matchesType = filterType === '' || type === filterType;
const matchesDepartment = filterDepartment === '' || department === filterDepartment;
const matchesSpecialty = filterSpecialty === '' || specialty === filterSpecialty;
if (matchesSearch && matchesType && matchesDepartment && matchesSpecialty) {
$(this).show();
visibleCount++;
} else {
$(this).hide();
}
});
$('#visibleCount').text(visibleCount);
}
// Sort function
function sortTemplates() {
const sortBy = $('#sortBy').val();
const gridItems = $('#templateGrid .template-item').get();
const listItems = $('#templateTable tbody tr').get();
// Sort grid items
gridItems.sort(function(a, b) {
let valueA, valueB;
if (sortBy === 'title') {
valueA = $(a).find('.card-title').text().toLowerCase();
valueB = $(b).find('.card-title').text().toLowerCase();
return valueA.localeCompare(valueB);
} else if (sortBy === 'created_at' || sortBy === 'updated_at') {
valueA = new Date($(a).find('small.text-muted:contains("Updated")').text().replace('Updated ', ''));
valueB = new Date($(b).find('small.text-muted:contains("Updated")').text().replace('Updated ', ''));
return valueB - valueA; // Descending order for dates
} else if (sortBy === 'usage_count') {
valueA = parseInt($(a).find('small.text-muted:contains("Used")').text().replace('Used ', '').replace(' times', ''));
valueB = parseInt($(b).find('small.text-muted:contains("Used")').text().replace('Used ', '').replace(' times', ''));
return valueB - valueA; // Descending order for usage count
}
return 0;
});
// Sort list items
listItems.sort(function(a, b) {
let valueA, valueB;
if (sortBy === 'title') {
valueA = $(a).find('td:first-child').text().toLowerCase();
valueB = $(b).find('td:first-child').text().toLowerCase();
return valueA.localeCompare(valueB);
} else if (sortBy === 'created_at' || sortBy === 'updated_at') {
valueA = new Date($(a).find('td:nth-child(6)').text());
valueB = new Date($(b).find('td:nth-child(6)').text());
return valueB - valueA; // Descending order for dates
} else if (sortBy === 'usage_count') {
valueA = parseInt($(a).find('td:nth-child(7)').text());
valueB = parseInt($(b).find('td:nth-child(7)').text());
return valueB - valueA; // Descending order for usage count
}
return 0;
});
// Reappend sorted items
$('#templateGrid').append(gridItems);
$('#templateTable tbody').append(listItems);
}
// Event listeners
$('#templateSearch, #filterType, #filterDepartment, #filterSpecialty').on('change keyup', filterTemplates);
$('#searchBtn').click(filterTemplates);
$('#sortBy').change(sortTemplates);
// Template preview
$('.template-preview-btn').click(function() {
const templateId = $(this).data('template-id');
// AJAX call to get template content
$.ajax({
url: `/emr/note-templates/${templateId}/preview/`,
type: 'GET',
success: function(data) {
$('#templatePreviewContent').html(data.content);
$('#templatePreviewModalLabel').text(data.title);
$('#useTemplateBtn').attr('href', `/emr/clinical-notes/create/?template=${templateId}`);
$('#templatePreviewModal').modal('show');
},
error: function() {
alert('Error loading template preview');
}
});
});
});
</script>
{% endblock %}