hospital-management/templates/patients/consents/consent_template_list.html
Marwan Alwali 610e165e17 update
2025-09-04 19:19:52 +03:00

709 lines
28 KiB
HTML

{% extends 'base.html' %}
{% load static %}
{% block title %}Consent Templates Management{% endblock %}
{% block extra_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>
.template-card {
border: none;
border-radius: 15px;
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.08);
transition: all 0.3s ease;
margin-bottom: 1.5rem;
}
.template-card:hover {
transform: translateY(-5px);
box-shadow: 0 10px 25px rgba(0, 0, 0, 0.15);
}
.template-header {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
border-radius: 15px 15px 0 0;
padding: 1.5rem;
}
.template-body {
padding: 1.5rem;
}
.template-status {
position: absolute;
top: 15px;
right: 15px;
z-index: 10;
}
.template-actions {
border-top: 1px solid #e9ecef;
padding: 1rem 1.5rem;
background: #f8f9fa;
border-radius: 0 0 15px 15px;
}
.stats-card {
background: linear-gradient(135deg, #28a745 0%, #20c997 100%);
border: none;
color: white;
border-radius: 15px;
transition: transform 0.3s ease;
}
.stats-card:hover {
transform: translateY(-3px);
}
.stats-icon {
width: 60px;
height: 60px;
border-radius: 50%;
background: rgba(255, 255, 255, 0.2);
display: flex;
align-items: center;
justify-content: center;
font-size: 24px;
}
.filter-section {
background: #f8f9fa;
border-radius: 15px;
padding: 1.5rem;
margin-bottom: 2rem;
}
.template-preview {
max-height: 100px;
overflow: hidden;
position: relative;
}
.template-preview::after {
content: '';
position: absolute;
bottom: 0;
left: 0;
right: 0;
height: 30px;
background: linear-gradient(transparent, white);
}
.category-badge {
font-size: 0.75rem;
padding: 0.375rem 0.75rem;
border-radius: 50px;
}
.usage-stats {
font-size: 0.875rem;
color: #6c757d;
}
.template-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(350px, 1fr));
gap: 1.5rem;
}
.quick-actions {
position: fixed;
bottom: 30px;
right: 30px;
z-index: 1000;
}
.fab-button {
width: 60px;
height: 60px;
border-radius: 50%;
background: linear-gradient(135deg, #007bff 0%, #0056b3 100%);
border: none;
color: white;
font-size: 24px;
box-shadow: 0 4px 15px rgba(0, 123, 255, 0.4);
transition: all 0.3s ease;
}
.fab-button:hover {
transform: scale(1.1);
box-shadow: 0 6px 20px rgba(0, 123, 255, 0.6);
}
</style>
{% endblock %}
{% block content %}
<div id="content" class="app-content">
<!-- Page Header -->
<div class="d-flex align-items-center mb-4">
<div>
<ol class="breadcrumb">
<li class="breadcrumb-item"><a href="{% url 'core:dashboard' %}">Dashboard</a></li>
<li class="breadcrumb-item"><a href="{% url 'patients:patient_list' %}">Patients</a></li>
<li class="breadcrumb-item active">Consent Templates</li>
</ol>
<h1 class="page-header mb-0">
<i class="fas fa-file-contract me-3"></i>Consent Templates Management
</h1>
<p class="text-muted mb-0">Manage and organize consent form templates for patient care</p>
</div>
<div class="ms-auto">
<div class="btn-group">
<button type="button" class="btn btn-info" data-bs-toggle="modal" data-bs-target="#importModal">
<i class="fas fa-upload me-2"></i>Import Templates
</button>
<a href="{% url 'patients:consent_template_create' %}" class="btn btn-primary">
<i class="fas fa-plus me-2"></i>New Template
</a>
</div>
</div>
</div>
<!-- Statistics Cards -->
<div class="row mb-4">
<div class="col-xl-3 col-md-6">
<div class="card stats-card">
<div class="card-body d-flex align-items-center">
<div class="stats-icon me-3">
<i class="fas fa-file-contract"></i>
</div>
<div>
<div class="fs-6 text-white-75">Total Templates</div>
<div class="fs-3 fw-bold">{{ stats.total_templates|default:0 }}</div>
</div>
</div>
</div>
</div>
<div class="col-xl-3 col-md-6">
<div class="card stats-card">
<div class="card-body d-flex align-items-center">
<div class="stats-icon me-3">
<i class="fas fa-check-circle"></i>
</div>
<div>
<div class="fs-6 text-white-75">Active Templates</div>
<div class="fs-3 fw-bold">{{ stats.active_templates|default:0 }}</div>
</div>
</div>
</div>
</div>
<div class="col-xl-3 col-md-6">
<div class="card stats-card">
<div class="card-body d-flex align-items-center">
<div class="stats-icon me-3">
<i class="fas fa-users"></i>
</div>
<div>
<div class="fs-6 text-white-75">Total Usage</div>
<div class="fs-3 fw-bold">{{ stats.total_usage|default:0 }}</div>
</div>
</div>
</div>
</div>
<div class="col-xl-3 col-md-6">
<div class="card stats-card">
<div class="card-body d-flex align-items-center">
<div class="stats-icon me-3">
<i class="fas fa-calendar-week"></i>
</div>
<div>
<div class="fs-6 text-white-75">This Week</div>
<div class="fs-3 fw-bold">{{ stats.weekly_usage|default:0 }}</div>
</div>
</div>
</div>
</div>
</div>
<!-- Filters and Search -->
<div class="filter-section">
<div class="row g-3">
<div class="col-md-4">
<label class="form-label">Search Templates</label>
<div class="input-group">
<input type="text" class="form-control" id="searchInput" placeholder="Search by name, category, or content...">
<button class="btn btn-outline-secondary" type="button">
<i class="fas fa-search"></i>
</button>
</div>
</div>
<div class="col-md-2">
<label class="form-label">Category</label>
<select class="form-select" id="categoryFilter">
<option value="">All Categories</option>
<option value="general">General Consent</option>
<option value="surgical">Surgical Procedures</option>
<option value="anesthesia">Anesthesia</option>
<option value="research">Research Participation</option>
<option value="treatment">Treatment Consent</option>
<option value="discharge">Discharge Instructions</option>
<option value="privacy">Privacy & Data</option>
</select>
</div>
<div class="col-md-2">
<label class="form-label">Status</label>
<select class="form-select" id="statusFilter">
<option value="">All Status</option>
<option value="active">Active</option>
<option value="draft">Draft</option>
<option value="archived">Archived</option>
<option value="under_review">Under Review</option>
</select>
</div>
<div class="col-md-2">
<label class="form-label">Language</label>
<select class="form-select" id="languageFilter">
<option value="">All Languages</option>
<option value="en">English</option>
<option value="ar">Arabic</option>
<option value="ur">Urdu</option>
<option value="hi">Hindi</option>
</select>
</div>
<div class="col-md-2 d-flex align-items-end">
<div class="btn-group w-100">
<button type="button" class="btn btn-outline-primary" onclick="applyFilters()">
<i class="fas fa-filter"></i>
</button>
<button type="button" class="btn btn-outline-secondary" onclick="clearFilters()">
<i class="fas fa-times"></i>
</button>
</div>
</div>
</div>
</div>
<!-- View Toggle -->
<div class="d-flex justify-content-between align-items-center mb-3">
<div>
<span class="text-muted">Showing {{ templates.count|default:0 }} templates</span>
</div>
<div class="btn-group" role="group">
<input type="radio" class="btn-check" name="viewMode" id="gridView" checked>
<label class="btn btn-outline-secondary" for="gridView">
<i class="fas fa-th-large"></i> Grid
</label>
<input type="radio" class="btn-check" name="viewMode" id="listView">
<label class="btn btn-outline-secondary" for="listView">
<i class="fas fa-list"></i> List
</label>
</div>
</div>
<!-- Templates Grid View -->
<div id="gridViewContainer" class="template-grid">
{% for template in templates %}
<div class="template-card" data-category="{{ template.category }}" data-status="{{ template.status }}" data-language="{{ template.language }}">
<div class="template-status">
{% if template.status == 'active' %}
<span class="badge bg-success">Active</span>
{% elif template.status == 'draft' %}
<span class="badge bg-warning">Draft</span>
{% elif template.status == 'archived' %}
<span class="badge bg-secondary">Archived</span>
{% else %}
<span class="badge bg-info">{{ template.get_status_display }}</span>
{% endif %}
</div>
<div class="template-header">
<h5 class="mb-2">
<i class="fas fa-file-contract me-2"></i>
{{ template.name }}
</h5>
<div class="d-flex justify-content-between align-items-center">
<span class="category-badge bg-light text-dark">{{ template.get_category_display }}</span>
<small class="opacity-75">
<i class="fas fa-language me-1"></i>{{ template.get_language_display }}
</small>
</div>
</div>
<div class="template-body">
<div class="template-preview">
<p class="text-muted small">{{ template.description|truncatewords:20 }}</p>
</div>
<div class="usage-stats mt-3">
<div class="row text-center">
<div class="col-4">
<div class="fw-bold">{{ template.usage_count|default:0 }}</div>
<small>Used</small>
</div>
<div class="col-4">
<div class="fw-bold">{{ template.version|default:"1.0" }}</div>
<small>Version</small>
</div>
<div class="col-4">
<div class="fw-bold">{{ template.updated_at|date:"M d" }}</div>
<small>Updated</small>
</div>
</div>
</div>
</div>
<div class="template-actions">
<div class="d-flex justify-content-between align-items-center">
<div class="btn-group btn-group-sm">
<a href="{% url 'patients:consent_template_detail' template.id %}" class="btn btn-outline-primary" title="View Details">
<i class="fas fa-eye"></i>
</a>
<a href="{% url 'patients:consent_template_edit' template.id %}" class="btn btn-outline-secondary" title="Edit">
<i class="fas fa-edit"></i>
</a>
<button type="button" class="btn btn-outline-info" onclick="previewTemplate({{ template.id }})" title="Preview">
<i class="fas fa-search"></i>
</button>
</div>
<div class="btn-group btn-group-sm">
<button type="button" class="btn btn-outline-success" onclick="duplicateTemplate({{ template.id }})" title="Duplicate">
<i class="fas fa-copy"></i>
</button>
<button type="button" class="btn btn-outline-warning" onclick="exportTemplate({{ template.id }})" title="Export">
<i class="fas fa-download"></i>
</button>
{% if template.status != 'archived' %}
<button type="button" class="btn btn-outline-danger" onclick="archiveTemplate({{ template.id }})" title="Archive">
<i class="fas fa-archive"></i>
</button>
{% endif %}
</div>
</div>
</div>
</div>
{% empty %}
<div class="col-12">
<div class="text-center py-5">
<i class="fas fa-file-contract fa-4x text-muted mb-4"></i>
<h4 class="text-muted">No Consent Templates Found</h4>
<p class="text-muted mb-4">Get started by creating your first consent template</p>
<a href="{% url 'patients:consent_template_create' %}" class="btn btn-primary btn-lg">
<i class="fas fa-plus me-2"></i>Create First Template
</a>
</div>
</div>
{% endfor %}
</div>
<!-- Templates List View (Hidden by default) -->
<div id="listViewContainer" class="d-none">
<div class="card">
<div class="card-body">
<div class="table-responsive">
<table class="table table-hover" id="templatesTable">
<thead class="table-dark">
<tr>
<th>Template Name</th>
<th>Category</th>
<th>Language</th>
<th>Status</th>
<th>Usage Count</th>
<th>Last Updated</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
{% for template in templates %}
<tr>
<td>
<div class="d-flex align-items-center">
<i class="fas fa-file-contract text-primary me-2"></i>
<div>
<div class="fw-bold">{{ template.name }}</div>
<small class="text-muted">{{ template.description|truncatewords:8 }}</small>
</div>
</div>
</td>
<td>
<span class="badge bg-info">{{ template.get_category_display }}</span>
</td>
<td>{{ template.get_language_display }}</td>
<td>
{% if template.status == 'active' %}
<span class="badge bg-success">Active</span>
{% elif template.status == 'draft' %}
<span class="badge bg-warning">Draft</span>
{% elif template.status == 'archived' %}
<span class="badge bg-secondary">Archived</span>
{% else %}
<span class="badge bg-info">{{ template.get_status_display }}</span>
{% endif %}
</td>
<td>{{ template.usage_count|default:0 }}</td>
<td>{{ template.updated_at|date:"M d, Y" }}</td>
<td>
<div class="btn-group btn-group-sm">
<a href="{% url 'patients:consent_template_detail' template.id %}" class="btn btn-outline-primary" title="View">
<i class="fas fa-eye"></i>
</a>
<a href="{% url 'patients:consent_template_edit' template.id %}" class="btn btn-outline-secondary" title="Edit">
<i class="fas fa-edit"></i>
</a>
<button type="button" class="btn btn-outline-info" onclick="previewTemplate({{ template.id }})" title="Preview">
<i class="fas fa-search"></i>
</button>
</div>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
</div>
</div>
<!-- Floating Action Button -->
<div class="quick-actions">
<button type="button" class="fab-button" data-bs-toggle="dropdown">
<i class="fas fa-plus"></i>
</button>
<ul class="dropdown-menu dropdown-menu-end">
<li><a class="dropdown-item" href="{% url 'patients:consent_template_create' %}">
<i class="fas fa-file-plus me-2"></i>New Template
</a></li>
<li><a class="dropdown-item" href="#" data-bs-toggle="modal" data-bs-target="#importModal">
<i class="fas fa-upload me-2"></i>Import Template
</a></li>
<li><hr class="dropdown-divider"></li>
<li><a class="dropdown-item" href="#" onclick="exportAllTemplates()">
<i class="fas fa-download me-2"></i>Export All
</a></li>
</ul>
</div>
</div>
<!-- Import Modal -->
<div class="modal fade" id="importModal" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">
<i class="fas fa-upload me-2"></i>Import Consent Templates
</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body">
<form id="importForm" enctype="multipart/form-data">
{% csrf_token %}
<div class="mb-3">
<label class="form-label">Select Template File</label>
<input type="file" class="form-control" name="template_file" accept=".json,.xml,.docx" required>
<div class="form-text">Supported formats: JSON, XML, DOCX</div>
</div>
<div class="mb-3">
<div class="form-check">
<input class="form-check-input" type="checkbox" id="overwriteExisting" name="overwrite_existing">
<label class="form-check-label" for="overwriteExisting">
Overwrite existing templates with same name
</label>
</div>
</div>
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
<button type="button" class="btn btn-primary" onclick="importTemplates()">
<i class="fas fa-upload me-2"></i>Import
</button>
</div>
</div>
</div>
</div>
<!-- Preview Modal -->
<div class="modal fade" id="previewModal" tabindex="-1">
<div class="modal-dialog modal-lg">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">
<i class="fas fa-search me-2"></i>Template Preview
</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body" id="previewContent">
<!-- Preview content will be loaded here -->
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
<button type="button" class="btn btn-primary" onclick="useTemplate()">
<i class="fas fa-check me-2"></i>Use This Template
</button>
</div>
</div>
</div>
</div>
{% endblock %}
{% block extra_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>
$(document).ready(function() {
// Initialize DataTable for list view
$('#templatesTable').DataTable({
responsive: true,
pageLength: 25,
order: [[5, 'desc']], // Sort by last updated
columnDefs: [
{ orderable: false, targets: [6] }
]
});
// View mode toggle
$('input[name="viewMode"]').change(function() {
if ($(this).attr('id') === 'gridView') {
$('#gridViewContainer').removeClass('d-none');
$('#listViewContainer').addClass('d-none');
} else {
$('#gridViewContainer').addClass('d-none');
$('#listViewContainer').removeClass('d-none');
}
});
// Search functionality
$('#searchInput').on('input', function() {
const searchTerm = $(this).val().toLowerCase();
filterTemplates();
});
// Filter change handlers
$('#categoryFilter, #statusFilter, #languageFilter').change(function() {
filterTemplates();
});
});
function filterTemplates() {
const searchTerm = $('#searchInput').val().toLowerCase();
const category = $('#categoryFilter').val();
const status = $('#statusFilter').val();
const language = $('#languageFilter').val();
$('.template-card').each(function() {
const card = $(this);
const cardText = card.text().toLowerCase();
const cardCategory = card.data('category');
const cardStatus = card.data('status');
const cardLanguage = card.data('language');
let show = true;
// Search filter
if (searchTerm && !cardText.includes(searchTerm)) {
show = false;
}
// Category filter
if (category && cardCategory !== category) {
show = false;
}
// Status filter
if (status && cardStatus !== status) {
show = false;
}
// Language filter
if (language && cardLanguage !== language) {
show = false;
}
card.toggle(show);
});
}
function applyFilters() {
filterTemplates();
}
function clearFilters() {
$('#searchInput').val('');
$('#categoryFilter, #statusFilter, #languageFilter').val('');
$('.template-card').show();
}
function previewTemplate(templateId) {
// Load template preview
$.ajax({
url: `/patients/consent-templates/${templateId}/preview/`,
method: 'GET',
success: function(response) {
$('#previewContent').html(response);
$('#previewModal').modal('show');
},
error: function() {
alert('Error loading template preview.');
}
});
}
function duplicateTemplate(templateId) {
if (confirm('Are you sure you want to duplicate this template?')) {
$.ajax({
url: `/patients/consent-templates/${templateId}/duplicate/`,
method: 'POST',
headers: {
'X-CSRFToken': $('[name=csrfmiddlewaretoken]').val()
},
success: function(response) {
if (response.success) {
location.reload();
} else {
alert('Error duplicating template: ' + response.error);
}
},
error: function() {
alert('Error duplicating template.');
}
});
}
}
function exportTemplate(templateId) {
window.location.href = `/patients/consent-templates/${templateId}/export/`;
}
function archiveTemplate(templateId) {
if (confirm('Are you sure you want to archive this template?')) {
$.ajax({
url: `/patients/consent-templates/${templateId}/archive/`,
method: 'POST',
headers: {
'X-CSRFToken': $('[name=csrfmiddlewaretoken]').val()
},
success: function(response) {
if (response.success) {
location.reload();
} else {
alert('Error archiving template: ' + response.error);
}
},
error: function() {
alert('Error archiving template.');
}
});
}
}
function importTemplates() {
const formData = new FormData($('#importForm')[0]);
$.ajax({
url: '{% url "patients:consent_template_import" %}',
method: 'POST',
data: formData,
processData: false,
contentType: false,
success: function(response) {
if (response.success) {
$('#importModal').modal('hide');
location.reload();
} else {
alert('Error importing templates: ' + response.error);
}
},
error: function() {
alert('Error importing templates.');
}
});
}
function exportAllTemplates() {
window.location.href = '{% url "patients:consent_template_export_all" %}';
}
function useTemplate() {
// Implementation for using the previewed template
$('#previewModal').modal('hide');
alert('Template usage functionality will be implemented.');
}
</script>
{% endblock %}