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

650 lines
29 KiB
HTML

{% extends "base.html" %}
{% load static %}
{% block title %}{% if form.instance.id %}Edit{% else %}Create{% endif %} Note Template{% endblock %}
{% block css %}
<link href="{% static 'plugins/bootstrap-icons/font/bootstrap-icons.css' %}" rel="stylesheet" />
<link href="{% static 'plugins/summernote/dist/summernote-bs5.css' %}" rel="stylesheet" />
<link href="{% static 'plugins/select2/dist/css/select2.min.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;
}
.form-section {
background-color: rgba(var(--bs-light-rgb), 0.5);
padding: 1.5rem;
border-radius: 0.5rem;
margin-bottom: 1.5rem;
border: 1px solid var(--bs-gray-300);
}
.form-section-title {
font-weight: 600;
margin-bottom: 1rem;
color: var(--bs-primary);
border-bottom: 1px solid var(--bs-gray-300);
padding-bottom: 0.5rem;
}
.help-sidebar {
background-color: rgba(var(--bs-light-rgb), 0.5);
padding: 1.5rem;
border-radius: 0.5rem;
border: 1px solid var(--bs-gray-300);
position: sticky;
top: 1rem;
}
.help-section {
margin-bottom: 1.5rem;
}
.help-section:last-child {
margin-bottom: 0;
}
.help-section-title {
font-weight: 600;
margin-bottom: 0.5rem;
color: var(--bs-primary);
}
.help-section-content {
font-size: 0.9rem;
}
.template-preview {
background-color: white;
padding: 1.5rem;
border-radius: 0.5rem;
border: 1px solid var(--bs-gray-300);
margin-top: 1rem;
}
.template-preview-title {
font-weight: 600;
margin-bottom: 0.5rem;
color: var(--bs-primary);
border-bottom: 1px solid var(--bs-gray-300);
padding-bottom: 0.5rem;
}
.template-section {
margin-bottom: 1rem;
padding: 0.5rem;
border: 1px dashed var(--bs-gray-400);
border-radius: 0.25rem;
}
.template-section-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 0.5rem;
}
.template-section-title {
font-weight: 600;
color: var(--bs-primary);
}
.template-section-actions {
display: flex;
gap: 0.25rem;
}
.template-section-content {
padding: 0.5rem;
background-color: rgba(var(--bs-light-rgb), 0.5);
border-radius: 0.25rem;
}
.section-drag-handle {
cursor: move;
color: var(--bs-gray-600);
}
.section-drag-handle:hover {
color: var(--bs-primary);
}
.select2-container--default .select2-selection--multiple {
border-color: var(--bs-gray-300);
}
.select2-container--default.select2-container--focus .select2-selection--multiple {
border-color: var(--bs-primary);
}
.note-editor .note-toolbar {
background-color: #f8f9fa;
}
.note-editor .note-statusbar {
background-color: #f8f9fa;
}
.note-editor.note-frame {
border-color: 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"><a href="{% url 'emr:note_template_list' %}">Note Templates</a></li>
<li class="breadcrumb-item active">{% if form.instance.id %}Edit{% else %}Create{% endif %} Template</li>
</ol>
<!-- end breadcrumb -->
<!-- begin page-header -->
<h1 class="page-header">{% if form.instance.id %}Edit{% else %}Create{% endif %} Note Template <small>Standardized clinical documentation template</small></h1>
<!-- end page-header -->
<!-- begin row -->
<div class="row">
<!-- begin col-9 -->
<div class="col-xl-9">
<!-- begin panel -->
<div class="panel panel-inverse">
<div class="panel-heading">
<h4 class="panel-title">Template Form</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">
<form method="post" id="templateForm">
{% csrf_token %}
<!-- Basic Information Section -->
<div class="form-section">
<div class="form-section-title">Basic Information</div>
<div class="row mb-3">
<div class="col-md-8">
<label for="{{ form.title.id_for_label }}" class="form-label">Template Title <span class="text-danger">*</span></label>
{{ form.title }}
{% if form.title.errors %}
<div class="invalid-feedback d-block">
{{ form.title.errors }}
</div>
{% endif %}
<div class="form-text">Enter a descriptive title for this template.</div>
</div>
<div class="col-md-4">
<label for="{{ form.note_type.id_for_label }}" class="form-label">Note Type <span class="text-danger">*</span></label>
{{ form.note_type }}
{% if form.note_type.errors %}
<div class="invalid-feedback d-block">
{{ form.note_type.errors }}
</div>
{% endif %}
<div class="form-text">Select the type of clinical note.</div>
</div>
</div>
<div class="row mb-3">
<div class="col-md-12">
<label for="{{ form.description.id_for_label }}" class="form-label">Description</label>
{{ form.description }}
{% if form.description.errors %}
<div class="invalid-feedback d-block">
{{ form.description.errors }}
</div>
{% endif %}
<div class="form-text">Provide a brief description of this template's purpose and usage.</div>
</div>
</div>
<div class="row mb-3">
<div class="col-md-6">
<label for="{{ form.department.id_for_label }}" class="form-label">Department</label>
{{ form.department }}
{% if form.department.errors %}
<div class="invalid-feedback d-block">
{{ form.department.errors }}
</div>
{% endif %}
<div class="form-text">Select a specific department or leave blank for all departments.</div>
</div>
<div class="col-md-6">
<label for="{{ form.specialty.id_for_label }}" class="form-label">Specialty</label>
{{ form.specialty }}
{% if form.specialty.errors %}
<div class="invalid-feedback d-block">
{{ form.specialty.errors }}
</div>
{% endif %}
<div class="form-text">Select a specific specialty or leave blank for all specialties.</div>
</div>
</div>
<div class="row mb-3">
<div class="col-md-6">
<label for="{{ form.version.id_for_label }}" class="form-label">Version <span class="text-danger">*</span></label>
{{ form.version }}
{% if form.version.errors %}
<div class="invalid-feedback d-block">
{{ form.version.errors }}
</div>
{% endif %}
<div class="form-text">Enter version number (e.g., 1.0, 2.1).</div>
</div>
<div class="col-md-6">
<label for="{{ form.status.id_for_label }}" class="form-label">Status <span class="text-danger">*</span></label>
{{ form.status }}
{% if form.status.errors %}
<div class="invalid-feedback d-block">
{{ form.status.errors }}
</div>
{% endif %}
<div class="form-text">Set the current status of this template.</div>
</div>
</div>
<div class="row">
<div class="col-md-12">
<label for="{{ form.keywords.id_for_label }}" class="form-label">Keywords</label>
{{ form.keywords }}
{% if form.keywords.errors %}
<div class="invalid-feedback d-block">
{{ form.keywords.errors }}
</div>
{% endif %}
<div class="form-text">Enter keywords to help users find this template (comma-separated).</div>
</div>
</div>
</div>
<!-- End Basic Information Section -->
<!-- Template Content Section -->
<div class="form-section">
<div class="form-section-title">Template Content</div>
<div class="row mb-3">
<div class="col-md-12">
<div class="form-check form-switch mb-3">
<input class="form-check-input" type="checkbox" id="structuredContentToggle" {% if form.instance.structured_content %}checked{% endif %}>
<label class="form-check-label" for="structuredContentToggle">Use Structured Content (Sections)</label>
</div>
</div>
</div>
<!-- Free Text Content -->
<div id="freeTextContent" {% if form.instance.structured_content %}style="display: none;"{% endif %}>
<div class="row">
<div class="col-md-12">
<label for="{{ form.content.id_for_label }}" class="form-label">Template Content <span class="text-danger">*</span></label>
{{ form.content }}
{% if form.content.errors %}
<div class="invalid-feedback d-block">
{{ form.content.errors }}
</div>
{% endif %}
<div class="form-text">Create the template content using the rich text editor.</div>
</div>
</div>
</div>
<!-- End Free Text Content -->
<!-- Structured Content -->
<div id="structuredContent" {% if not form.instance.structured_content %}style="display: none;"{% endif %}>
<div class="row mb-3">
<div class="col-md-12">
<button type="button" class="btn btn-sm btn-success" id="addSectionBtn">
<i class="fa fa-plus me-1"></i> Add Section
</button>
</div>
</div>
<div id="sectionContainer">
{% if form.instance.structured_content %}
{% for section in form.instance.structured_content %}
<div class="template-section" data-section-id="{{ forloop.counter0 }}">
<div class="template-section-header">
<div class="section-drag-handle">
<i class="fa fa-grip-vertical"></i>
</div>
<input type="text" class="form-control form-control-sm section-title"
name="section_title_{{ forloop.counter0 }}"
value="{{ section.title }}"
placeholder="Section Title">
<div class="template-section-actions">
<button type="button" class="btn btn-sm btn-outline-danger remove-section-btn">
<i class="fa fa-times"></i>
</button>
</div>
</div>
<div class="template-section-content">
<textarea class="form-control section-content summernote"
name="section_content_{{ forloop.counter0 }}"
rows="5">{{ section.content }}</textarea>
</div>
</div>
{% endfor %}
{% endif %}
</div>
<input type="hidden" name="structured_content_json" id="structuredContentJson" value="">
</div>
<!-- End Structured Content -->
</div>
<!-- End Template Content Section -->
<!-- Version History Section (for updates only) -->
{% if form.instance.id %}
<div class="form-section">
<div class="form-section-title">Version Update Information</div>
<div class="row mb-3">
<div class="col-md-12">
<label for="changeDescription" class="form-label">Change Description <span class="text-danger">*</span></label>
<textarea class="form-control" id="changeDescription" name="change_description" rows="3" required></textarea>
<div class="form-text">Describe the changes made in this version update.</div>
</div>
</div>
<div class="row">
<div class="col-md-6">
<div class="form-check">
<input class="form-check-input" type="checkbox" id="majorVersionUpdate" name="major_version_update">
<label class="form-check-label" for="majorVersionUpdate">
This is a major version update
</label>
</div>
<div class="form-text">Check if this update includes significant changes that warrant a major version increment.</div>
</div>
</div>
</div>
{% endif %}
<!-- End Version History Section -->
<!-- Submit Buttons -->
<div class="d-flex justify-content-between mt-4">
<a href="{% url 'emr:note_template_list' %}" class="btn btn-secondary">
<i class="fa fa-arrow-left me-1"></i> Cancel
</a>
<div>
<button type="button" class="btn btn-info me-2" id="previewBtn">
<i class="fa fa-eye me-1"></i> Preview
</button>
<button type="submit" class="btn btn-primary">
<i class="fa fa-save me-1"></i> {% if form.instance.id %}Update{% else %}Create{% endif %} Template
</button>
</div>
</div>
</form>
</div>
</div>
<!-- end panel -->
</div>
<!-- end col-9 -->
<!-- begin col-3 -->
<div class="col-xl-3">
<!-- Help Sidebar -->
<div class="help-sidebar">
<h5 class="mb-3">Template Guidelines</h5>
<div class="help-section">
<div class="help-section-title">
<i class="fa fa-info-circle me-1"></i> Template Types
</div>
<div class="help-section-content">
<p>Choose the appropriate note type:</p>
<ul class="mb-0">
<li><strong>Progress Note:</strong> For routine patient visits and updates</li>
<li><strong>Admission Note:</strong> For new patient admissions</li>
<li><strong>Discharge Summary:</strong> For patient discharge documentation</li>
<li><strong>Procedure Note:</strong> For documenting medical procedures</li>
<li><strong>Consultation Note:</strong> For specialist consultations</li>
</ul>
</div>
</div>
<div class="help-section">
<div class="help-section-title">
<i class="fa fa-lightbulb me-1"></i> Best Practices
</div>
<div class="help-section-content">
<ul class="mb-0">
<li>Use clear, concise language</li>
<li>Include all required clinical elements</li>
<li>Organize content in logical sections</li>
<li>Use consistent formatting</li>
<li>Include placeholders for variable data</li>
<li>Consider regulatory requirements</li>
</ul>
</div>
</div>
<div class="help-section">
<div class="help-section-title">
<i class="fa fa-puzzle-piece me-1"></i> Template Variables
</div>
<div class="help-section-content">
<p>Use these variables to auto-populate data:</p>
<ul class="mb-0">
<li><code>{{patient.name}}</code> - Patient's full name</li>
<li><code>{{patient.dob}}</code> - Date of birth</li>
<li><code>{{patient.mrn}}</code> - Medical record number</li>
<li><code>{{provider.name}}</code> - Provider's name</li>
<li><code>{{date.today}}</code> - Current date</li>
<li><code>{{time.now}}</code> - Current time</li>
</ul>
</div>
</div>
<div class="help-section">
<div class="help-section-title">
<i class="fa fa-question-circle me-1"></i> Need Help?
</div>
<div class="help-section-content">
<p>For additional assistance:</p>
<ul class="mb-0">
<li><a href="#" target="_blank">View Template Documentation</a></li>
<li><a href="#" target="_blank">Contact Support</a></li>
</ul>
</div>
</div>
</div>
<!-- End Help Sidebar -->
</div>
<!-- end col-3 -->
</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 class="template-preview">
<div class="template-preview-title" id="previewTitle">Template Title</div>
<div id="previewContent"></div>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
</div>
</div>
</div>
</div>
<!-- end template preview modal -->
{% endblock %}
{% block js %}
<script src="{% static 'plugins/summernote/dist/summernote-bs5.min.js' %}"></script>
<script src="{% static 'plugins/select2/dist/js/select2.min.js' %}"></script>
<script src="{% static 'plugins/sortablejs/Sortable.min.js' %}"></script>
<script>
$(document).ready(function() {
// Initialize Summernote
$('.summernote').summernote({
height: 300,
toolbar: [
['style', ['style']],
['font', ['bold', 'underline', 'clear']],
['color', ['color']],
['para', ['ul', 'ol', 'paragraph']],
['table', ['table']],
['insert', ['link']],
['view', ['fullscreen', 'codeview', 'help']]
],
placeholder: 'Enter template content here...',
callbacks: {
onImageUpload: function(files) {
// Image upload handling if needed
}
}
});
// Initialize Select2
$('.select2').select2({
tags: true,
tokenSeparators: [',', ' '],
placeholder: "Enter keywords...",
width: '100%'
});
// Toggle between free text and structured content
$('#structuredContentToggle').change(function() {
if ($(this).is(':checked')) {
$('#freeTextContent').hide();
$('#structuredContent').show();
} else {
$('#structuredContent').hide();
$('#freeTextContent').show();
}
});
// Add new section
$('#addSectionBtn').click(function() {
const sectionId = $('.template-section').length;
const newSection = `
<div class="template-section" data-section-id="${sectionId}">
<div class="template-section-header">
<div class="section-drag-handle">
<i class="fa fa-grip-vertical"></i>
</div>
<input type="text" class="form-control form-control-sm section-title"
name="section_title_${sectionId}"
placeholder="Section Title">
<div class="template-section-actions">
<button type="button" class="btn btn-sm btn-outline-danger remove-section-btn">
<i class="fa fa-times"></i>
</button>
</div>
</div>
<div class="template-section-content">
<textarea class="form-control section-content summernote"
name="section_content_${sectionId}"
rows="5"></textarea>
</div>
</div>
`;
$('#sectionContainer').append(newSection);
// Initialize Summernote for the new section
$(`textarea[name="section_content_${sectionId}"]`).summernote({
height: 200,
toolbar: [
['style', ['style']],
['font', ['bold', 'underline', 'clear']],
['color', ['color']],
['para', ['ul', 'ol', 'paragraph']],
['table', ['table']],
['insert', ['link']],
['view', ['fullscreen', 'codeview', 'help']]
],
placeholder: 'Enter section content here...'
});
});
// Remove section
$(document).on('click', '.remove-section-btn', function() {
if (confirm('Are you sure you want to remove this section?')) {
$(this).closest('.template-section').remove();
}
});
// Make sections sortable
const sectionContainer = document.getElementById('sectionContainer');
if (sectionContainer) {
new Sortable(sectionContainer, {
handle: '.section-drag-handle',
animation: 150
});
}
// Preview button
$('#previewBtn').click(function() {
let title = $('#id_title').val() || 'Template Preview';
let content = '';
if ($('#structuredContentToggle').is(':checked')) {
// Structured content preview
$('.template-section').each(function() {
const sectionTitle = $(this).find('.section-title').val();
const sectionContent = $(this).find('.section-content').summernote('code');
content += `
<div class="template-section-preview">
<h5>${sectionTitle || 'Untitled Section'}</h5>
<div>${sectionContent}</div>
</div>
`;
});
} else {
// Free text content preview
content = $('#id_content').summernote('code');
}
$('#previewTitle').text(title);
$('#previewContent').html(content);
$('#templatePreviewModal').modal('show');
});
// Form submission
$('#templateForm').submit(function() {
if ($('#structuredContentToggle').is(':checked')) {
// Prepare structured content JSON
const structuredContent = [];
$('.template-section').each(function() {
const sectionId = $(this).data('section-id');
const sectionTitle = $(this).find('.section-title').val();
const sectionContent = $(this).find('.section-content').summernote('code');
structuredContent.push({
title: sectionTitle,
content: sectionContent
});
});
$('#structuredContentJson').val(JSON.stringify(structuredContent));
}
return true;
});
});
</script>
{% endblock %}