2025-08-12 13:33:25 +03:00

803 lines
37 KiB
HTML

{% extends "base.html" %}
{% load static %}
{% block title %}Consent Forms - Patients{% endblock %}
{% block content %}
<!-- BEGIN breadcrumb -->
<ol class="breadcrumb float-xl-end">
<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 Forms</li>
</ol>
<!-- END breadcrumb -->
<!-- BEGIN page-header -->
<h1 class="page-header">
Consent Forms
<small>Manage consent form templates and requirements</small>
</h1>
<!-- END page-header -->
<!-- BEGIN row -->
<div class="row">
<div class="col-xl-12">
<!-- BEGIN panel -->
<div class="panel panel-inverse">
<div class="panel-heading">
<h4 class="panel-title">Consent Forms Management</h4>
<div class="panel-heading-btn">
<button type="button" class="btn btn-primary btn-sm me-2" data-bs-toggle="modal" data-bs-target="#createFormModal">
<i class="fa fa-plus me-2"></i>Create Form
</button>
<button type="button" class="btn btn-info btn-sm me-2" onclick="exportForms()">
<i class="fa fa-download me-2"></i>Export
</button>
<button type="button" class="btn btn-secondary btn-sm me-2" onclick="bulkActions()">
<i class="fa fa-tasks me-2"></i>Bulk Actions
</button>
<a href="javascript:;" class="btn btn-xs btn-icon btn-default" data-toggle="panel-expand"><i class="fa fa-expand"></i></a>
</div>
</div>
<div class="panel-body">
<!-- Statistics Cards -->
<div class="row mb-4">
<div class="col-md-3">
<div class="card border-primary">
<div class="card-body text-center">
<div class="fs-24px fw-bold text-primary">{{ total_forms }}</div>
<div class="small text-muted">Total Forms</div>
</div>
</div>
</div>
<div class="col-md-3">
<div class="card border-success">
<div class="card-body text-center">
<div class="fs-24px fw-bold text-success">{{ active_forms }}</div>
<div class="small text-muted">Active Forms</div>
</div>
</div>
</div>
<div class="col-md-3">
<div class="card border-warning">
<div class="card-body text-center">
<div class="fs-24px fw-bold text-warning">{{ pending_review_forms }}</div>
<div class="small text-muted">Pending Review</div>
</div>
</div>
</div>
<div class="col-md-3">
<div class="card border-info">
<div class="card-body text-center">
<div class="fs-24px fw-bold text-info">{{ required_forms }}</div>
<div class="small text-muted">Required Forms</div>
</div>
</div>
</div>
</div>
<!-- Filters and Search -->
<div class="row mb-3">
<div class="col-md-3">
<div class="form-floating">
<input type="text" class="form-control" id="search-input" placeholder="Search forms...">
<label for="search-input">Search forms...</label>
</div>
</div>
<div class="col-md-2">
<div class="form-floating">
<select class="form-select" id="category-filter">
<option value="">All Categories</option>
<option value="GENERAL">General</option>
<option value="SURGICAL">Surgical</option>
<option value="TREATMENT">Treatment</option>
<option value="RESEARCH">Research</option>
<option value="PRIVACY">Privacy</option>
<option value="FINANCIAL">Financial</option>
</select>
<label for="category-filter">Category</label>
</div>
</div>
<div class="col-md-2">
<div class="form-floating">
<select class="form-select" id="status-filter">
<option value="">All Status</option>
<option value="ACTIVE">Active</option>
<option value="DRAFT">Draft</option>
<option value="REVIEW">Under Review</option>
<option value="ARCHIVED">Archived</option>
<option value="EXPIRED">Expired</option>
</select>
<label for="status-filter">Status</label>
</div>
</div>
<div class="col-md-2">
<div class="form-floating">
<select class="form-select" id="required-filter">
<option value="">All Forms</option>
<option value="true">Required Only</option>
<option value="false">Optional Only</option>
</select>
<label for="required-filter">Required</label>
</div>
</div>
<div class="col-md-3">
<div class="btn-group w-100">
<button type="button" class="btn btn-outline-secondary" onclick="clearFilters()">
<i class="fa fa-times me-2"></i>Clear
</button>
<button type="button" class="btn btn-outline-primary" onclick="applyFilters()">
<i class="fa fa-search me-2"></i>Filter
</button>
<button type="button" class="btn btn-outline-info" onclick="refreshData()">
<i class="fa fa-refresh"></i>
</button>
</div>
</div>
</div>
<!-- Bulk Actions Bar -->
<div id="bulk-actions-bar" class="alert alert-info d-none">
<div class="d-flex justify-content-between align-items-center">
<div>
<span id="selected-count">0</span> form(s) selected
</div>
<div class="btn-group">
<button type="button" class="btn btn-sm btn-success" onclick="bulkActivate()">
<i class="fa fa-check me-2"></i>Activate
</button>
<button type="button" class="btn btn-sm btn-warning" onclick="bulkArchive()">
<i class="fa fa-archive me-2"></i>Archive
</button>
<button type="button" class="btn btn-sm btn-info" onclick="bulkExport()">
<i class="fa fa-download me-2"></i>Export
</button>
<button type="button" class="btn btn-sm btn-danger" onclick="bulkDelete()">
<i class="fa fa-trash me-2"></i>Delete
</button>
</div>
</div>
</div>
<!-- Forms Table -->
<div class="table-responsive">
<table class="table table-striped table-hover" id="forms-table">
<thead class="table-dark">
<tr>
<th width="40">
<div class="form-check">
<input class="form-check-input" type="checkbox" id="select-all">
</div>
</th>
<th>Form Details</th>
<th>Category</th>
<th>Status</th>
<th>Required</th>
<th>Version</th>
<th>Usage</th>
<th>Last Updated</th>
<th width="120">Actions</th>
</tr>
</thead>
<tbody>
{% for form in object_list %}
<tr data-form-id="{{ form.pk }}">
<td>
<div class="form-check">
<input class="form-check-input form-checkbox" type="checkbox" value="{{ form.pk }}">
</div>
</td>
<td>
<div class="d-flex align-items-center">
<div class="me-3">
<div class="w-40px h-40px bg-{% if form.is_required %}danger{% elif form.status == 'ACTIVE' %}success{% elif form.status == 'DRAFT' %}warning{% else %}secondary{% endif %} rounded d-flex align-items-center justify-content-center">
<i class="fa fa-{% if form.category == 'SURGICAL' %}cut{% elif form.category == 'TREATMENT' %}stethoscope{% elif form.category == 'RESEARCH' %}flask{% elif form.category == 'PRIVACY' %}lock{% elif form.category == 'FINANCIAL' %}dollar-sign{% else %}file-text{% endif %} text-white"></i>
</div>
</div>
<div>
<div class="fw-bold">
<a href="{% url 'patients:consent_form_detail' form.pk %}" class="text-decoration-none">
{{ form.title }}
</a>
</div>
<div class="small text-muted">{{ form.description|truncatechars:60 }}</div>
{% if form.is_required %}
<span class="badge bg-danger mt-1">Required</span>
{% endif %}
{% if form.has_expiration %}
<span class="badge bg-warning mt-1">
<i class="fa fa-clock me-1"></i>Expires
</span>
{% endif %}
</div>
</div>
</td>
<td>
<span class="badge bg-{% if form.category == 'SURGICAL' %}danger{% elif form.category == 'TREATMENT' %}primary{% elif form.category == 'RESEARCH' %}info{% elif form.category == 'PRIVACY' %}dark{% elif form.category == 'FINANCIAL' %}success{% else %}secondary{% endif %}">
{{ form.get_category_display }}
</span>
</td>
<td>
<span class="badge bg-{% if form.status == 'ACTIVE' %}success{% elif form.status == 'DRAFT' %}warning{% elif form.status == 'REVIEW' %}info{% elif form.status == 'ARCHIVED' %}secondary{% else %}danger{% endif %}">
{{ form.get_status_display }}
</span>
{% if form.status == 'REVIEW' %}
<br><small class="text-muted">Since {{ form.review_date|date:"M d" }}</small>
{% endif %}
</td>
<td>
{% if form.is_required %}
<span class="text-danger">
<i class="fa fa-exclamation-triangle me-1"></i>Required
</span>
{% else %}
<span class="text-muted">Optional</span>
{% endif %}
</td>
<td>
<span class="badge bg-light text-dark">v{{ form.version }}</span>
{% if form.has_newer_version %}
<br><small class="text-warning">
<i class="fa fa-exclamation-triangle me-1"></i>Update available
</small>
{% endif %}
</td>
<td>
<div class="small">
<div><strong>{{ form.usage_count }}</strong> signed</div>
<div class="text-muted">{{ form.pending_count }} pending</div>
</div>
</td>
<td>
<div class="small">
<div>{{ form.updated_at|date:"M d, Y" }}</div>
<div class="text-muted">{{ form.updated_at|time:"H:i" }}</div>
<div class="text-muted">by {{ form.updated_by.get_full_name|default:"System" }}</div>
</div>
</td>
<td>
<div class="btn-group">
<a href="{% url 'patients:consent_form_detail' form.pk %}" class="btn btn-outline-primary btn-sm" title="View Details">
<i class="fa fa-eye"></i>
</a>
<a href="{% url 'patients:consent_form_update' form.pk %}" class="btn btn-outline-secondary btn-sm" title="Edit Form">
<i class="fa fa-edit"></i>
</a>
<button type="button" class="btn btn-outline-info btn-sm" onclick="previewForm('{{ form.pk }}')" title="Preview">
<i class="fa fa-search"></i>
</button>
<div class="btn-group">
<button type="button" class="btn btn-outline-secondary btn-sm dropdown-toggle" data-bs-toggle="dropdown">
<i class="fa fa-cog"></i>
</button>
<ul class="dropdown-menu">
{% if form.status == 'DRAFT' %}
<li><a class="dropdown-item" href="#" onclick="activateForm('{{ form.pk }}')">
<i class="fa fa-check me-2"></i>Activate
</a></li>
{% endif %}
{% if form.status == 'ACTIVE' %}
<li><a class="dropdown-item" href="#" onclick="archiveForm('{{ form.pk }}')">
<i class="fa fa-archive me-2"></i>Archive
</a></li>
{% endif %}
<li><a class="dropdown-item" href="#" onclick="duplicateForm('{{ form.pk }}')">
<i class="fa fa-copy me-2"></i>Duplicate
</a></li>
<li><a class="dropdown-item" href="#" onclick="exportForm('{{ form.pk }}')">
<i class="fa fa-download me-2"></i>Export
</a></li>
<li><hr class="dropdown-divider"></li>
<li><a class="dropdown-item text-danger" href="{% url 'patients:consent_form_delete' form.pk %}">
<i class="fa fa-trash me-2"></i>Delete
</a></li>
</ul>
</div>
</div>
</td>
</tr>
{% empty %}
<tr>
<td colspan="9" class="text-center py-4">
<div class="text-muted">
<i class="fa fa-file-text fa-3x mb-3"></i>
<h5>No consent forms found</h5>
<p>Create your first consent form to get started.</p>
<button type="button" class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#createFormModal">
<i class="fa fa-plus me-2"></i>Create Consent Form
</button>
</div>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
<!-- Pagination -->
{% if is_paginated %}
<div class="d-flex justify-content-between align-items-center mt-3">
<div class="text-muted">
Showing {{ page_obj.start_index }} to {{ page_obj.end_index }} of {{ page_obj.paginator.count }} forms
</div>
<nav>
<ul class="pagination pagination-sm mb-0">
{% if page_obj.has_previous %}
<li class="page-item">
<a class="page-link" href="?page=1">&laquo; First</a>
</li>
<li class="page-item">
<a class="page-link" href="?page={{ page_obj.previous_page_number }}">Previous</a>
</li>
{% endif %}
{% for num in page_obj.paginator.page_range %}
{% if page_obj.number == num %}
<li class="page-item active">
<span class="page-link">{{ num }}</span>
</li>
{% elif num > page_obj.number|add:'-3' and num < page_obj.number|add:'3' %}
<li class="page-item">
<a class="page-link" href="?page={{ num }}">{{ num }}</a>
</li>
{% endif %}
{% endfor %}
{% if page_obj.has_next %}
<li class="page-item">
<a class="page-link" href="?page={{ page_obj.next_page_number }}">Next</a>
</li>
<li class="page-item">
<a class="page-link" href="?page={{ page_obj.paginator.num_pages }}">Last &raquo;</a>
</li>
{% endif %}
</ul>
</nav>
</div>
{% endif %}
</div>
</div>
<!-- END panel -->
</div>
</div>
<!-- END row -->
<!-- Create Form Modal -->
<div class="modal fade" id="createFormModal" tabindex="-1">
<div class="modal-dialog modal-lg">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Create New Consent Form</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body">
<form id="create-form">
<div class="row">
<div class="col-md-8">
<div class="form-floating mb-3">
<input type="text" class="form-control" id="form-title" required>
<label for="form-title">Form Title</label>
</div>
</div>
<div class="col-md-4">
<div class="form-floating mb-3">
<select class="form-select" id="form-category" required>
<option value="">Select category...</option>
<option value="GENERAL">General</option>
<option value="SURGICAL">Surgical</option>
<option value="TREATMENT">Treatment</option>
<option value="RESEARCH">Research</option>
<option value="PRIVACY">Privacy</option>
<option value="FINANCIAL">Financial</option>
</select>
<label for="form-category">Category</label>
</div>
</div>
</div>
<div class="mb-3">
<label for="form-description" class="form-label">Description</label>
<textarea class="form-control" id="form-description" rows="3" required></textarea>
</div>
<div class="row">
<div class="col-md-6">
<div class="form-check mb-3">
<input class="form-check-input" type="checkbox" id="form-required">
<label class="form-check-label" for="form-required">
Required for all patients
</label>
</div>
</div>
<div class="col-md-6">
<div class="form-check mb-3">
<input class="form-check-input" type="checkbox" id="form-expiration">
<label class="form-check-label" for="form-expiration">
Has expiration date
</label>
</div>
</div>
</div>
<div class="mb-3">
<label class="form-label">Template Source</label>
<div class="row">
<div class="col-md-4">
<div class="form-check">
<input class="form-check-input" type="radio" name="template-source" id="blank-template" value="blank" checked>
<label class="form-check-label" for="blank-template">
Start from blank
</label>
</div>
</div>
<div class="col-md-4">
<div class="form-check">
<input class="form-check-input" type="radio" name="template-source" id="existing-template" value="existing">
<label class="form-check-label" for="existing-template">
Copy existing form
</label>
</div>
</div>
<div class="col-md-4">
<div class="form-check">
<input class="form-check-input" type="radio" name="template-source" id="standard-template" value="standard">
<label class="form-check-label" for="standard-template">
Use standard template
</label>
</div>
</div>
</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="createForm()">
<i class="fa fa-plus me-2"></i>Create Form
</button>
</div>
</div>
</div>
</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">Form Preview</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body">
<div id="preview-content">
<!-- Preview content will be loaded here -->
</div>
</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="printPreview()">
<i class="fa fa-print me-2"></i>Print
</button>
</div>
</div>
</div>
</div>
{% endblock %}
{% block js %}
<script>
$(document).ready(function() {
// Initialize tooltips
$('[title]').tooltip();
// Auto-refresh every 30 seconds
setInterval(function() {
if (!$('.modal').hasClass('show')) {
refreshData();
}
}, 30000);
// Search functionality
$('#search-input').on('input', function() {
var searchTerm = $(this).val().toLowerCase();
filterTable();
});
// Filter functionality
$('#category-filter, #status-filter, #required-filter').on('change', function() {
filterTable();
});
// Select all checkbox
$('#select-all').on('change', function() {
$('.form-checkbox').prop('checked', $(this).is(':checked'));
updateBulkActions();
});
// Individual checkboxes
$(document).on('change', '.form-checkbox', function() {
updateBulkActions();
// Update select all checkbox
var totalCheckboxes = $('.form-checkbox').length;
var checkedCheckboxes = $('.form-checkbox:checked').length;
$('#select-all').prop('checked', totalCheckboxes === checkedCheckboxes);
});
});
function filterTable() {
var searchTerm = $('#search-input').val().toLowerCase();
var categoryFilter = $('#category-filter').val();
var statusFilter = $('#status-filter').val();
var requiredFilter = $('#required-filter').val();
$('#forms-table tbody tr').each(function() {
var row = $(this);
var text = row.text().toLowerCase();
var category = row.find('.badge').first().text().trim();
var status = row.find('.badge').eq(1).text().trim();
var isRequired = row.find('.text-danger').length > 0;
var showRow = true;
// Search filter
if (searchTerm && !text.includes(searchTerm)) {
showRow = false;
}
// Category filter
if (categoryFilter && !category.toLowerCase().includes(categoryFilter.toLowerCase())) {
showRow = false;
}
// Status filter
if (statusFilter && !status.toLowerCase().includes(statusFilter.toLowerCase())) {
showRow = false;
}
// Required filter
if (requiredFilter === 'true' && !isRequired) {
showRow = false;
} else if (requiredFilter === 'false' && isRequired) {
showRow = false;
}
row.toggle(showRow);
});
}
function clearFilters() {
$('#search-input').val('');
$('#category-filter').val('');
$('#status-filter').val('');
$('#required-filter').val('');
filterTable();
}
function applyFilters() {
filterTable();
toastr.info('Filters applied');
}
function refreshData() {
location.reload();
}
function updateBulkActions() {
var selectedCount = $('.form-checkbox:checked').length;
$('#selected-count').text(selectedCount);
if (selectedCount > 0) {
$('#bulk-actions-bar').removeClass('d-none');
} else {
$('#bulk-actions-bar').addClass('d-none');
}
}
function createForm() {
var formData = {
title: $('#form-title').val(),
category: $('#form-category').val(),
description: $('#form-description').val(),
is_required: $('#form-required').is(':checked'),
has_expiration: $('#form-expiration').is(':checked'),
template_source: $('input[name="template-source"]:checked').val()
};
if (!formData.title || !formData.category || !formData.description) {
toastr.error('Please fill in all required fields');
return;
}
$.ajax({
url: '{% url "patients:consent_form_create" %}',
method: 'POST',
data: formData,
success: function(response) {
$('#createFormModal').modal('hide');
toastr.success('Consent form created successfully');
setTimeout(function() {
window.location.href = response.redirect_url;
}, 1000);
},
error: function() {
toastr.error('Failed to create consent form');
}
});
}
function previewForm(formId) {
$.ajax({
url: '{% url "patients:consent_form_preview" 0 %}'.replace('0', formId),
method: 'GET',
success: function(response) {
$('#preview-content').html(response.html);
$('#previewModal').modal('show');
},
error: function() {
toastr.error('Failed to load form preview');
}
});
}
function activateForm(formId) {
if (confirm('Activate this consent form? It will become available for patient signatures.')) {
$.ajax({
url: '{% url "patients:consent_form_activate" 0 %}'.replace('0', formId),
method: 'POST',
success: function() {
toastr.success('Form activated successfully');
refreshData();
},
error: function() {
toastr.error('Failed to activate form');
}
});
}
}
function archiveForm(formId) {
if (confirm('Archive this consent form? It will no longer be available for new signatures.')) {
$.ajax({
url: '{% url "patients:consent_form_archive" 0 %}'.replace('0', formId),
method: 'POST',
success: function() {
toastr.success('Form archived successfully');
refreshData();
},
error: function() {
toastr.error('Failed to archive form');
}
});
}
}
function duplicateForm(formId) {
if (confirm('Create a copy of this consent form?')) {
$.ajax({
url: '{% url "patients:consent_form_duplicate" 0 %}'.replace('0', formId),
method: 'POST',
success: function(response) {
toastr.success('Form duplicated successfully');
setTimeout(function() {
window.location.href = response.redirect_url;
}, 1000);
},
error: function() {
toastr.error('Failed to duplicate form');
}
});
}
}
function exportForm(formId) {
window.open('{% url "patients:consent_form_export" 0 %}'.replace('0', formId), '_blank');
}
function exportForms() {
var selectedForms = $('.form-checkbox:checked').map(function() {
return $(this).val();
}).get();
if (selectedForms.length === 0) {
toastr.warning('Please select forms to export');
return;
}
var url = '{% url "patients:consent_forms_export" %}?forms=' + selectedForms.join(',');
window.open(url, '_blank');
}
function bulkActivate() {
var selectedForms = $('.form-checkbox:checked').map(function() {
return $(this).val();
}).get();
if (selectedForms.length === 0) {
toastr.warning('Please select forms to activate');
return;
}
if (confirm('Activate ' + selectedForms.length + ' selected form(s)?')) {
$.ajax({
url: '{% url "patients:consent_forms_bulk_activate" %}',
method: 'POST',
data: { forms: selectedForms },
success: function() {
toastr.success('Forms activated successfully');
refreshData();
},
error: function() {
toastr.error('Failed to activate forms');
}
});
}
}
function bulkArchive() {
var selectedForms = $('.form-checkbox:checked').map(function() {
return $(this).val();
}).get();
if (selectedForms.length === 0) {
toastr.warning('Please select forms to archive');
return;
}
if (confirm('Archive ' + selectedForms.length + ' selected form(s)?')) {
$.ajax({
url: '{% url "patients:consent_forms_bulk_archive" %}',
method: 'POST',
data: { forms: selectedForms },
success: function() {
toastr.success('Forms archived successfully');
refreshData();
},
error: function() {
toastr.error('Failed to archive forms');
}
});
}
}
function bulkExport() {
exportForms();
}
function bulkDelete() {
var selectedForms = $('.form-checkbox:checked').map(function() {
return $(this).val();
}).get();
if (selectedForms.length === 0) {
toastr.warning('Please select forms to delete');
return;
}
if (confirm('Delete ' + selectedForms.length + ' selected form(s)? This action cannot be undone.')) {
$.ajax({
url: '{% url "patients:consent_forms_bulk_delete" %}',
method: 'POST',
data: { forms: selectedForms },
success: function() {
toastr.success('Forms deleted successfully');
refreshData();
},
error: function() {
toastr.error('Failed to delete forms');
}
});
}
}
function printPreview() {
var printContent = $('#preview-content').html();
var printWindow = window.open('', '_blank');
printWindow.document.write('<html><head><title>Consent Form</title></head><body>' + printContent + '</body></html>');
printWindow.document.close();
printWindow.print();
}
</script>
{% endblock %}