467 lines
21 KiB
HTML
467 lines
21 KiB
HTML
{% extends "base.html" %}
|
|
{% load static %}
|
|
|
|
{% block title %}{% if object %}Edit{% else %}Create{% endif %} Consent Form - Patients{% endblock %}
|
|
|
|
{% block css %}
|
|
<link href="{% static 'assets/plugins/summernote/dist/summernote-lite.min.css' %}" rel="stylesheet" />
|
|
{% 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"><a href="{% url 'patients:consent_form_list' %}">Consent Forms</a></li>
|
|
{% if object %}
|
|
<li class="breadcrumb-item"><a href="{% url 'patients:consent_form_detail' object.pk %}">{{ object.title|truncatechars:20 }}</a></li>
|
|
<li class="breadcrumb-item active">Edit</li>
|
|
{% else %}
|
|
<li class="breadcrumb-item active">Create</li>
|
|
{% endif %}
|
|
</ol>
|
|
<!-- END breadcrumb -->
|
|
|
|
<!-- BEGIN page-header -->
|
|
<h1 class="page-header">
|
|
{% if object %}Edit Consent Form{% else %}Create Consent Form{% endif %}
|
|
<small>{% if object %}{{ object.title }}{% else %}New consent form template{% endif %}</small>
|
|
</h1>
|
|
<!-- END page-header -->
|
|
|
|
<div class="row">
|
|
<div class="col-xl-8">
|
|
<!-- BEGIN panel -->
|
|
<div class="panel panel-inverse">
|
|
<div class="panel-heading">
|
|
<h4 class="panel-title">Form Details</h4>
|
|
<div class="panel-heading-btn">
|
|
<button type="button" class="btn btn-xs btn-info me-2" onclick="saveDraft()">
|
|
<i class="fa fa-save"></i> Save Draft
|
|
</button>
|
|
<button type="button" class="btn btn-xs btn-secondary me-2" onclick="previewForm()">
|
|
<i class="fa fa-eye"></i> Preview
|
|
</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">
|
|
<form method="post" id="consent-form" novalidate>
|
|
{% csrf_token %}
|
|
|
|
<div class="row mb-3">
|
|
<div class="col-md-8">
|
|
<div class="form-floating">
|
|
{{ form.title }}
|
|
<label for="{{ form.title.id_for_label }}">Form Title <span class="text-danger">*</span></label>
|
|
{% if form.title.errors %}
|
|
<div class="invalid-feedback d-block">{{ form.title.errors.0 }}</div>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
<div class="col-md-4">
|
|
<div class="form-floating">
|
|
{{ form.category }}
|
|
<label for="{{ form.category.id_for_label }}">Category <span class="text-danger">*</span></label>
|
|
{% if form.category.errors %}
|
|
<div class="invalid-feedback d-block">{{ form.category.errors.0 }}</div>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="mb-3">
|
|
<label for="{{ form.description.id_for_label }}" class="form-label">Description <span class="text-danger">*</span></label>
|
|
{{ form.description }}
|
|
{% if form.description.errors %}
|
|
<div class="invalid-feedback d-block">{{ form.description.errors.0 }}</div>
|
|
{% endif %}
|
|
<div class="form-text">Brief description of what this consent form covers.</div>
|
|
</div>
|
|
|
|
<div class="row mb-3">
|
|
<div class="col-md-4">
|
|
<div class="form-floating">
|
|
{{ form.version }}
|
|
<label for="{{ form.version.id_for_label }}">Version</label>
|
|
{% if form.version.errors %}
|
|
<div class="invalid-feedback d-block">{{ form.version.errors.0 }}</div>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
<div class="col-md-4">
|
|
<div class="form-floating">
|
|
{{ form.status }}
|
|
<label for="{{ form.status.id_for_label }}">Status <span class="text-danger">*</span></label>
|
|
{% if form.status.errors %}
|
|
<div class="invalid-feedback d-block">{{ form.status.errors.0 }}</div>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
<div class="col-md-4">
|
|
<div class="form-floating">
|
|
{{ form.effective_date }}
|
|
<label for="{{ form.effective_date.id_for_label }}">Effective Date</label>
|
|
{% if form.effective_date.errors %}
|
|
<div class="invalid-feedback d-block">{{ form.effective_date.errors.0 }}</div>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="row mb-3">
|
|
<div class="col-md-6">
|
|
<div class="form-check">
|
|
{{ form.is_required }}
|
|
<label class="form-check-label" for="{{ form.is_required.id_for_label }}">
|
|
<i class="fa fa-exclamation-triangle text-danger me-1"></i>Required for all patients
|
|
</label>
|
|
{% if form.is_required.errors %}
|
|
<div class="invalid-feedback d-block">{{ form.is_required.errors.0 }}</div>
|
|
{% endif %}
|
|
<div class="form-text">If checked, this form must be signed by all patients.</div>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-6">
|
|
<div class="form-check">
|
|
{{ form.has_expiration }}
|
|
<label class="form-check-label" for="{{ form.has_expiration.id_for_label }}">
|
|
<i class="fa fa-clock text-warning me-1"></i>Has expiration date
|
|
</label>
|
|
{% if form.has_expiration.errors %}
|
|
<div class="invalid-feedback d-block">{{ form.has_expiration.errors.0 }}</div>
|
|
{% endif %}
|
|
<div class="form-text">If checked, signed forms will expire after a set period.</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="row mb-3" id="expiration-fields" style="display: none;">
|
|
<div class="col-md-6">
|
|
<div class="form-floating">
|
|
{{ form.expiration_days }}
|
|
<label for="{{ form.expiration_days.id_for_label }}">Expiration Days</label>
|
|
{% if form.expiration_days.errors %}
|
|
<div class="invalid-feedback d-block">{{ form.expiration_days.errors.0 }}</div>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
<div class="col-md-6">
|
|
<div class="form-floating">
|
|
{{ form.renewal_reminder_days }}
|
|
<label for="{{ form.renewal_reminder_days.id_for_label }}">Reminder Days</label>
|
|
{% if form.renewal_reminder_days.errors %}
|
|
<div class="invalid-feedback d-block">{{ form.renewal_reminder_days.errors.0 }}</div>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="mb-3">
|
|
<label for="{{ form.content.id_for_label }}" class="form-label">Form Content <span class="text-danger">*</span></label>
|
|
{{ form.content }}
|
|
{% if form.content.errors %}
|
|
<div class="invalid-feedback d-block">{{ form.content.errors.0 }}</div>
|
|
{% endif %}
|
|
<div class="form-text">Use the rich text editor to create the consent form content. Include all necessary legal language and patient information fields.</div>
|
|
</div>
|
|
|
|
<div class="mb-3">
|
|
<label for="{{ form.signature_requirements.id_for_label }}" class="form-label">Signature Requirements</label>
|
|
{{ form.signature_requirements }}
|
|
{% if form.signature_requirements.errors %}
|
|
<div class="invalid-feedback d-block">{{ form.signature_requirements.errors.0 }}</div>
|
|
{% endif %}
|
|
<div class="form-text">Specify who needs to sign this form (patient, guardian, witness, etc.).</div>
|
|
</div>
|
|
|
|
<div class="mb-3">
|
|
<label for="{{ form.legal_notes.id_for_label }}" class="form-label">Legal Notes</label>
|
|
{{ form.legal_notes }}
|
|
{% if form.legal_notes.errors %}
|
|
<div class="invalid-feedback d-block">{{ form.legal_notes.errors.0 }}</div>
|
|
{% endif %}
|
|
<div class="form-text">Internal notes about legal requirements, compliance, or special considerations.</div>
|
|
</div>
|
|
|
|
<!-- Form Actions -->
|
|
<div class="d-flex justify-content-between">
|
|
<div>
|
|
<a href="{% if object %}{% url 'patients:consent_form_detail' object.pk %}{% else %}{% url 'patients:consent_form_list' %}{% endif %}" class="btn btn-secondary">
|
|
<i class="fa fa-arrow-left me-2"></i>Cancel
|
|
</a>
|
|
</div>
|
|
<div>
|
|
<button type="button" class="btn btn-info me-2" onclick="saveDraft()">
|
|
<i class="fa fa-save me-2"></i>Save Draft
|
|
</button>
|
|
<button type="submit" class="btn btn-primary">
|
|
<i class="fa fa-check me-2"></i>{% if object %}Update{% else %}Create{% endif %} Form
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
<!-- END panel -->
|
|
</div>
|
|
|
|
<div class="col-xl-4">
|
|
<!-- BEGIN panel -->
|
|
<div class="panel panel-inverse">
|
|
<div class="panel-heading">
|
|
<h4 class="panel-title">Form Templates</h4>
|
|
</div>
|
|
<div class="panel-body">
|
|
<div class="alert alert-info">
|
|
<h6 class="alert-heading">Standard Templates</h6>
|
|
<p class="mb-2">Choose from pre-built templates:</p>
|
|
<div class="d-grid gap-2">
|
|
<button type="button" class="btn btn-outline-primary btn-sm" onclick="loadTemplate('general')">
|
|
General Consent
|
|
</button>
|
|
<button type="button" class="btn btn-outline-primary btn-sm" onclick="loadTemplate('surgical')">
|
|
Surgical Consent
|
|
</button>
|
|
<button type="button" class="btn btn-outline-primary btn-sm" onclick="loadTemplate('treatment')">
|
|
Treatment Consent
|
|
</button>
|
|
<button type="button" class="btn btn-outline-primary btn-sm" onclick="loadTemplate('research')">
|
|
Research Consent
|
|
</button>
|
|
<button type="button" class="btn btn-outline-primary btn-sm" onclick="loadTemplate('privacy')">
|
|
Privacy Agreement
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<!-- END panel -->
|
|
|
|
<!-- BEGIN panel -->
|
|
<div class="panel panel-inverse">
|
|
<div class="panel-heading">
|
|
<h4 class="panel-title">Form Guidelines</h4>
|
|
</div>
|
|
<div class="panel-body">
|
|
<div class="alert alert-warning">
|
|
<h6 class="alert-heading">Legal Requirements</h6>
|
|
<ul class="mb-0 small">
|
|
<li>Include clear description of procedures/treatments</li>
|
|
<li>List all risks and benefits</li>
|
|
<li>Provide alternative treatment options</li>
|
|
<li>Include patient rights and withdrawal options</li>
|
|
<li>Ensure language is clear and understandable</li>
|
|
<li>Include contact information for questions</li>
|
|
</ul>
|
|
</div>
|
|
|
|
<div class="card border-success mb-3">
|
|
<div class="card-header bg-success text-white">
|
|
<h6 class="card-title mb-0">Best Practices</h6>
|
|
</div>
|
|
<div class="card-body">
|
|
<ul class="list-unstyled mb-0 small">
|
|
<li><i class="fa fa-check text-success me-2"></i>Use simple, clear language</li>
|
|
<li><i class="fa fa-check text-success me-2"></i>Avoid medical jargon</li>
|
|
<li><i class="fa fa-check text-success me-2"></i>Include visual aids if helpful</li>
|
|
<li><i class="fa fa-check text-success me-2"></i>Provide adequate white space</li>
|
|
<li><i class="fa fa-check text-success me-2"></i>Test readability level</li>
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<!-- END panel -->
|
|
|
|
<!-- BEGIN panel -->
|
|
<div class="panel panel-inverse">
|
|
<div class="panel-heading">
|
|
<h4 class="panel-title">Form Status</h4>
|
|
</div>
|
|
<div class="panel-body">
|
|
<div id="form-status">
|
|
<div class="alert alert-secondary">
|
|
<i class="fa fa-info-circle me-2"></i>
|
|
<span id="status-text">Form not saved</span>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="small text-muted">
|
|
<div>Last saved: <span id="last-saved">Never</span></div>
|
|
<div>Auto-save: <span class="text-success">Enabled</span></div>
|
|
<div>Word count: <span id="word-count">0</span></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<!-- END panel -->
|
|
</div>
|
|
</div>
|
|
{% endblock %}
|
|
|
|
{% block js %}
|
|
<script src="{% static 'assets/plugins/summernote/dist/summernote-lite.min.js' %}"></script>
|
|
|
|
<script>
|
|
var autoSaveTimer;
|
|
|
|
$(document).ready(function() {
|
|
// Initialize Summernote
|
|
$('#{{ form.content.id_for_label }}').summernote({
|
|
height: 400,
|
|
toolbar: [
|
|
['style', ['style']],
|
|
['font', ['bold', 'underline', 'clear']],
|
|
['color', ['color']],
|
|
['para', ['ul', 'ol', 'paragraph']],
|
|
['table', ['table']],
|
|
['insert', ['link']],
|
|
['view', ['fullscreen', 'codeview', 'help']]
|
|
],
|
|
callbacks: {
|
|
onChange: function(contents, $editable) {
|
|
updateWordCount(contents);
|
|
clearTimeout(autoSaveTimer);
|
|
autoSaveTimer = setTimeout(function() {
|
|
saveDraft();
|
|
}, 5000);
|
|
}
|
|
}
|
|
});
|
|
|
|
// Expiration fields toggle
|
|
$('#{{ form.has_expiration.id_for_label }}').on('change', function() {
|
|
if ($(this).is(':checked')) {
|
|
$('#expiration-fields').show();
|
|
} else {
|
|
$('#expiration-fields').hide();
|
|
}
|
|
});
|
|
|
|
// Initial state
|
|
if ($('#{{ form.has_expiration.id_for_label }}').is(':checked')) {
|
|
$('#expiration-fields').show();
|
|
}
|
|
|
|
// Form validation
|
|
$('#consent-form').on('submit', function(e) {
|
|
if (!validateForm()) {
|
|
e.preventDefault();
|
|
}
|
|
});
|
|
|
|
// Auto-save functionality
|
|
$('#consent-form input, #consent-form select, #consent-form textarea').on('change input', function() {
|
|
clearTimeout(autoSaveTimer);
|
|
autoSaveTimer = setTimeout(function() {
|
|
saveDraft();
|
|
}, 5000);
|
|
});
|
|
|
|
// Initial word count
|
|
updateWordCount($('#{{ form.content.id_for_label }}').summernote('code'));
|
|
});
|
|
|
|
function validateForm() {
|
|
var isValid = true;
|
|
var errors = [];
|
|
|
|
// Required field validation
|
|
var requiredFields = ['{{ form.title.id_for_label }}', '{{ form.category.id_for_label }}', '{{ form.description.id_for_label }}', '{{ form.status.id_for_label }}'];
|
|
|
|
requiredFields.forEach(function(fieldId) {
|
|
var field = $('#' + fieldId);
|
|
if (!field.val()) {
|
|
field.addClass('is-invalid');
|
|
errors.push(field.closest('.form-floating').find('label').text().replace(' *', '') + ' is required');
|
|
isValid = false;
|
|
} else {
|
|
field.removeClass('is-invalid');
|
|
}
|
|
});
|
|
|
|
// Content validation
|
|
var content = $('#{{ form.content.id_for_label }}').summernote('code');
|
|
if (!content || content.trim() === '' || content === '<p><br></p>') {
|
|
$('#{{ form.content.id_for_label }}').addClass('is-invalid');
|
|
errors.push('Form content is required');
|
|
isValid = false;
|
|
} else {
|
|
$('#{{ form.content.id_for_label }}').removeClass('is-invalid');
|
|
}
|
|
|
|
if (!isValid) {
|
|
toastr.error('Please fix the following errors:\n' + errors.join('\n'));
|
|
}
|
|
|
|
return isValid;
|
|
}
|
|
|
|
function updateWordCount(content) {
|
|
var text = $(content).text();
|
|
var wordCount = text.trim() === '' ? 0 : text.trim().split(/\s+/).length;
|
|
$('#word-count').text(wordCount);
|
|
}
|
|
|
|
function saveDraft() {
|
|
var formData = $('#consent-form').serialize();
|
|
var content = $('#{{ form.content.id_for_label }}').summernote('code');
|
|
formData += '&content=' + encodeURIComponent(content);
|
|
|
|
$.ajax({
|
|
url: '{% url "patients:save_consent_form_draft" %}',
|
|
method: 'POST',
|
|
data: formData,
|
|
success: function(response) {
|
|
updateFormStatus('Draft saved', 'success');
|
|
$('#last-saved').text(new Date().toLocaleTimeString());
|
|
},
|
|
error: function() {
|
|
updateFormStatus('Failed to save draft', 'danger');
|
|
}
|
|
});
|
|
}
|
|
|
|
function updateFormStatus(message, type) {
|
|
var alertClass = 'alert-' + type;
|
|
$('#form-status').html('<div class="alert ' + alertClass + '"><i class="fa fa-info-circle me-2"></i>' + message + '</div>');
|
|
}
|
|
|
|
function previewForm() {
|
|
var content = $('#{{ form.content.id_for_label }}').summernote('code');
|
|
var title = $('#{{ form.title.id_for_label }}').val();
|
|
|
|
var previewWindow = window.open('', '_blank');
|
|
previewWindow.document.write('<html><head><title>' + title + '</title></head><body>' + content + '</body></html>');
|
|
previewWindow.document.close();
|
|
}
|
|
|
|
function loadTemplate(templateType) {
|
|
if (confirm('Load template? This will replace the current content.')) {
|
|
$.ajax({
|
|
url: '{% url "patients:get_consent_form_template" %}',
|
|
method: 'GET',
|
|
data: { template_type: templateType },
|
|
success: function(response) {
|
|
$('#{{ form.title.id_for_label }}').val(response.title);
|
|
$('#{{ form.category.id_for_label }}').val(response.category);
|
|
$('#{{ form.description.id_for_label }}').val(response.description);
|
|
$('#{{ form.content.id_for_label }}').summernote('code', response.content);
|
|
$('#{{ form.signature_requirements.id_for_label }}').val(response.signature_requirements);
|
|
|
|
if (response.is_required) {
|
|
$('#{{ form.is_required.id_for_label }}').prop('checked', true);
|
|
}
|
|
|
|
toastr.success('Template loaded successfully');
|
|
},
|
|
error: function() {
|
|
toastr.error('Failed to load template');
|
|
}
|
|
});
|
|
}
|
|
}
|
|
</script>
|
|
{% endblock %}
|
|
|