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

572 lines
29 KiB
HTML

{% extends "base.html" %}
{% load static %}
{% block title %}{% if object %}Edit Template{% else %}Create Template{% endif %} - Communications{% endblock %}
{% block content %}
<div class="container-fluid">
<!-- Breadcrumb -->
<div class="row">
<div class="col-12">
<div class="page-title-box d-sm-flex align-items-center justify-content-between">
<h4 class="mb-sm-0">{% if object %}Edit Template{% else %}Create Template{% endif %}</h4>
<div class="page-title-right">
<ol class="breadcrumb m-0">
<li class="breadcrumb-item"><a href="{% url 'core:dashboard' %}">Dashboard</a></li>
<li class="breadcrumb-item"><a href="{% url 'communications:dashboard' %}">Communications</a></li>
<li class="breadcrumb-item"><a href="{% url 'communications:notification_template_list' %}">Templates</a></li>
<li class="breadcrumb-item active">{% if object %}Edit{% else %}Create{% endif %}</li>
</ol>
</div>
</div>
</div>
</div>
<form method="post" id="templateForm">
{% csrf_token %}
<div class="row">
<!-- Main Form -->
<div class="col-lg-8">
<div class="card">
<div class="card-header">
<h5 class="card-title mb-0">
<i class="fas fa-file-alt me-2"></i>
Template Details
</h5>
</div>
<div class="card-body">
<!-- Template Name -->
<div class="mb-3">
<label for="{{ form.template_name.id_for_label }}" class="form-label">Template Name *</label>
{{ form.template_name }}
{% if form.template_name.errors %}
<div class="invalid-feedback d-block">
{{ form.template_name.errors.0 }}
</div>
{% endif %}
<div class="form-text">Enter a unique, descriptive name for this template</div>
</div>
<!-- Category and Message Type -->
<div class="row">
<div class="col-md-6">
<div class="mb-3">
<label for="{{ form.category.id_for_label }}" class="form-label">Category *</label>
{{ form.category }}
{% if form.category.errors %}
<div class="invalid-feedback d-block">
{{ form.category.errors.0 }}
</div>
{% endif %}
</div>
</div>
<div class="col-md-6">
<div class="mb-3">
<label for="{{ form.message_type.id_for_label }}" class="form-label">Message Type *</label>
{{ form.message_type }}
{% if form.message_type.errors %}
<div class="invalid-feedback d-block">
{{ form.message_type.errors.0 }}
</div>
{% endif %}
</div>
</div>
</div>
<!-- Description -->
<div class="mb-3">
<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.0 }}
</div>
{% endif %}
<div class="form-text">Describe the purpose and usage of this template</div>
</div>
<!-- Language -->
<div class="mb-3">
<label for="{{ form.language.id_for_label }}" class="form-label">Language</label>
{{ form.language }}
{% if form.language.errors %}
<div class="invalid-feedback d-block">
{{ form.language.errors.0 }}
</div>
{% endif %}
<div class="form-text">Select the primary language for this template</div>
</div>
<!-- Subject Template -->
<div class="mb-3">
<label for="{{ form.subject_template.id_for_label }}" class="form-label">Subject Template *</label>
{{ form.subject_template }}
{% if form.subject_template.errors %}
<div class="invalid-feedback d-block">
{{ form.subject_template.errors.0 }}
</div>
{% endif %}
<div class="form-text">
<div class="d-flex justify-content-between">
<span>Use variables like {patient_name}, {appointment_date}</span>
<span id="subjectCharCount" class="text-muted">0 characters</span>
</div>
</div>
</div>
<!-- Body Template -->
<div class="mb-3">
<label for="{{ form.body_template.id_for_label }}" class="form-label">Body Template *</label>
{{ form.body_template }}
{% if form.body_template.errors %}
<div class="invalid-feedback d-block">
{{ form.body_template.errors.0 }}
</div>
{% endif %}
<div class="form-text">
<div class="d-flex justify-content-between">
<span>Enter the main content template with variables</span>
<span id="bodyCharCount" class="text-muted">0 characters</span>
</div>
</div>
</div>
<!-- Status -->
<div class="mb-3">
<div class="form-check">
{{ form.is_active }}
<label class="form-check-label" for="{{ form.is_active.id_for_label }}">
Active Template
</label>
</div>
{% if form.is_active.errors %}
<div class="invalid-feedback d-block">
{{ form.is_active.errors.0 }}
</div>
{% endif %}
<div class="form-text">Only active templates can be used for sending messages</div>
</div>
</div>
</div>
<!-- Template Variables -->
<div class="card">
<div class="card-header">
<div class="d-flex justify-content-between align-items-center">
<h5 class="card-title mb-0">
<i class="fas fa-code me-2"></i>
Template Variables
</h5>
<button type="button" class="btn btn-sm btn-outline-primary" onclick="addVariable()">
<i class="fas fa-plus me-1"></i>Add Variable
</button>
</div>
</div>
<div class="card-body">
<div id="variablesContainer">
<!-- Variables will be added here dynamically -->
<div class="variable-item mb-3" style="display: none;">
<div class="row">
<div class="col-md-4">
<input type="text" class="form-control" placeholder="Variable name" name="variable_names[]">
</div>
<div class="col-md-6">
<input type="text" class="form-control" placeholder="Description" name="variable_descriptions[]">
</div>
<div class="col-md-2">
<button type="button" class="btn btn-outline-danger btn-sm" onclick="removeVariable(this)">
<i class="fas fa-trash"></i>
</button>
</div>
</div>
</div>
</div>
<div class="text-muted">
<small>Define variables that can be used in your template. Variables should be referenced as {variable_name} in the template content.</small>
</div>
</div>
</div>
<!-- Template Preview -->
<div class="card">
<div class="card-header">
<h5 class="card-title mb-0">
<i class="fas fa-eye me-2"></i>
Live Preview
</h5>
</div>
<div class="card-body">
<div class="row">
<div class="col-md-6">
<h6 class="text-muted mb-2">Template Preview:</h6>
<div class="border rounded p-3" id="templatePreview">
<div class="mb-2">
<strong>Subject:</strong>
<div class="mt-1" id="previewSubject">[Subject will appear here]</div>
</div>
<div>
<strong>Body:</strong>
<div class="mt-1" id="previewBody">[Body will appear here]</div>
</div>
</div>
</div>
<div class="col-md-6">
<h6 class="text-muted mb-2">Sample Data:</h6>
<div class="bg-light p-3 rounded">
<div class="mb-2">
<label class="form-label form-label-sm">Patient Name:</label>
<input type="text" class="form-control form-control-sm" id="samplePatientName" value="John Doe" onchange="updatePreview()">
</div>
<div class="mb-2">
<label class="form-label form-label-sm">Appointment Date:</label>
<input type="text" class="form-control form-control-sm" id="sampleAppointmentDate" value="March 15, 2024" onchange="updatePreview()">
</div>
<div class="mb-2">
<label class="form-label form-label-sm">Provider Name:</label>
<input type="text" class="form-control form-control-sm" id="sampleProviderName" value="Dr. Smith" onchange="updatePreview()">
</div>
<div class="mb-2">
<label class="form-label form-label-sm">Hospital Name:</label>
<input type="text" class="form-control form-control-sm" id="sampleHospitalName" value="General Hospital" onchange="updatePreview()">
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Sidebar -->
<div class="col-lg-4">
<!-- Form Actions -->
<div class="card">
<div class="card-header">
<h5 class="card-title mb-0">
<i class="fas fa-cog me-2"></i>
Actions
</h5>
</div>
<div class="card-body">
<div class="d-grid gap-2">
<button type="submit" name="action" value="save" class="btn btn-primary">
<i class="fas fa-save me-1"></i>
Save Template
</button>
<button type="submit" name="action" value="save_and_test" class="btn btn-success">
<i class="fas fa-flask me-1"></i>
Save & Test
</button>
<hr>
<button type="button" class="btn btn-outline-info" onclick="previewTemplate()">
<i class="fas fa-eye me-1"></i>
Full Preview
</button>
<button type="button" class="btn btn-outline-secondary" onclick="validateTemplate()">
<i class="fas fa-check me-1"></i>
Validate Template
</button>
<hr>
<a href="{% url 'communications:notification_template_list' %}" class="btn btn-outline-danger">
<i class="fas fa-times me-1"></i>
Cancel
</a>
</div>
</div>
</div>
<!-- Template Help -->
<div class="card">
<div class="card-header">
<h5 class="card-title mb-0">
<i class="fas fa-question-circle me-2"></i>
Help & Variables
</h5>
</div>
<div class="card-body">
<div class="accordion" id="helpAccordion">
<div class="accordion-item">
<h2 class="accordion-header">
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#patientVariables">
Patient Variables
</button>
</h2>
<div id="patientVariables" class="accordion-collapse collapse" data-bs-parent="#helpAccordion">
<div class="accordion-body">
<small>
<code>{patient_name}</code> - Full patient name<br>
<code>{patient_first_name}</code> - First name only<br>
<code>{patient_id}</code> - Patient ID number<br>
<code>{patient_phone}</code> - Phone number<br>
<code>{patient_email}</code> - Email address<br>
<code>{patient_dob}</code> - Date of birth
</small>
</div>
</div>
</div>
<div class="accordion-item">
<h2 class="accordion-header">
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#appointmentVariables">
Appointment Variables
</button>
</h2>
<div id="appointmentVariables" class="accordion-collapse collapse" data-bs-parent="#helpAccordion">
<div class="accordion-body">
<small>
<code>{appointment_date}</code> - Appointment date<br>
<code>{appointment_time}</code> - Appointment time<br>
<code>{provider_name}</code> - Healthcare provider<br>
<code>{department}</code> - Department name<br>
<code>{location}</code> - Appointment location<br>
<code>{appointment_type}</code> - Type of appointment
</small>
</div>
</div>
</div>
<div class="accordion-item">
<h2 class="accordion-header">
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#systemVariables">
System Variables
</button>
</h2>
<div id="systemVariables" class="accordion-collapse collapse" data-bs-parent="#helpAccordion">
<div class="accordion-body">
<small>
<code>{hospital_name}</code> - Hospital name<br>
<code>{hospital_phone}</code> - Hospital phone<br>
<code>{hospital_address}</code> - Hospital address<br>
<code>{current_date}</code> - Current date<br>
<code>{current_time}</code> - Current time<br>
<code>{system_url}</code> - System URL
</small>
</div>
</div>
</div>
<div class="accordion-item">
<h2 class="accordion-header">
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#bestPractices">
Best Practices
</button>
</h2>
<div id="bestPractices" class="accordion-collapse collapse" data-bs-parent="#helpAccordion">
<div class="accordion-body">
<small>
• Use clear, descriptive template names<br>
• Keep subject lines concise and informative<br>
• Test templates before activating<br>
• Use appropriate variables for personalization<br>
• Consider different message types (SMS vs Email)<br>
• Include fallback text for missing variables
</small>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Variable Inserter -->
<div class="card">
<div class="card-header">
<h5 class="card-title mb-0">
<i class="fas fa-plus-circle me-2"></i>
Quick Insert
</h5>
</div>
<div class="card-body">
<div class="d-grid gap-1">
<button type="button" class="btn btn-outline-secondary btn-sm" onclick="insertVariable('patient_name')">
Insert {patient_name}
</button>
<button type="button" class="btn btn-outline-secondary btn-sm" onclick="insertVariable('appointment_date')">
Insert {appointment_date}
</button>
<button type="button" class="btn btn-outline-secondary btn-sm" onclick="insertVariable('provider_name')">
Insert {provider_name}
</button>
<button type="button" class="btn btn-outline-secondary btn-sm" onclick="insertVariable('hospital_name')">
Insert {hospital_name}
</button>
</div>
</div>
</div>
</div>
</div>
</form>
</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">Template Preview</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body">
<div id="fullPreviewContent"></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="saveTemplate()">Save Template</button>
</div>
</div>
</div>
</div>
{% endblock %}
{% block js %}
<script>
// Character counters
document.getElementById('{{ form.subject_template.id_for_label }}').addEventListener('input', function() {
document.getElementById('subjectCharCount').textContent = this.value.length + ' characters';
updatePreview();
});
document.getElementById('{{ form.body_template.id_for_label }}').addEventListener('input', function() {
document.getElementById('bodyCharCount').textContent = this.value.length + ' characters';
updatePreview();
});
// Variable management
function addVariable() {
const container = document.getElementById('variablesContainer');
const template = document.querySelector('.variable-item');
const newItem = template.cloneNode(true);
newItem.style.display = 'block';
container.appendChild(newItem);
}
function removeVariable(button) {
button.closest('.variable-item').remove();
}
// Preview functionality
function updatePreview() {
const subject = document.getElementById('{{ form.subject_template.id_for_label }}').value;
const body = document.getElementById('{{ form.body_template.id_for_label }}').value;
// Sample data
const sampleData = {
'patient_name': document.getElementById('samplePatientName').value,
'appointment_date': document.getElementById('sampleAppointmentDate').value,
'provider_name': document.getElementById('sampleProviderName').value,
'hospital_name': document.getElementById('sampleHospitalName').value
};
// Replace variables in subject and body
let previewSubject = subject;
let previewBody = body;
Object.keys(sampleData).forEach(key => {
const regex = new RegExp(`{${key}}`, 'g');
previewSubject = previewSubject.replace(regex, sampleData[key]);
previewBody = previewBody.replace(regex, sampleData[key]);
});
document.getElementById('previewSubject').textContent = previewSubject || '[Subject will appear here]';
document.getElementById('previewBody').innerHTML = previewBody.replace(/\n/g, '<br>') || '[Body will appear here]';
}
// Variable insertion
function insertVariable(variableName) {
const activeElement = document.activeElement;
const variable = `{${variableName}}`;
if (activeElement && (activeElement.id === '{{ form.subject_template.id_for_label }}' || activeElement.id === '{{ form.body_template.id_for_label }}')) {
const start = activeElement.selectionStart;
const end = activeElement.selectionEnd;
const text = activeElement.value;
activeElement.value = text.substring(0, start) + variable + text.substring(end);
activeElement.selectionStart = activeElement.selectionEnd = start + variable.length;
activeElement.focus();
updatePreview();
} else {
alert('Please click in the subject or body field first, then click the variable to insert.');
}
}
// Template actions
function previewTemplate() {
const subject = document.getElementById('{{ form.subject_template.id_for_label }}').value;
const body = document.getElementById('{{ form.body_template.id_for_label }}').value;
const previewContent = `
<div class="card">
<div class="card-header">
<h6 class="mb-0">${subject}</h6>
</div>
<div class="card-body">
${body.replace(/\n/g, '<br>')}
</div>
</div>
`;
document.getElementById('fullPreviewContent').innerHTML = previewContent;
new bootstrap.Modal(document.getElementById('previewModal')).show();
}
function validateTemplate() {
const subject = document.getElementById('{{ form.subject_template.id_for_label }}').value;
const body = document.getElementById('{{ form.body_template.id_for_label }}').value;
// Basic validation
const errors = [];
if (!subject.trim()) {
errors.push('Subject template is required');
}
if (!body.trim()) {
errors.push('Body template is required');
}
// Check for unmatched braces
const subjectBraces = (subject.match(/{/g) || []).length - (subject.match(/}/g) || []).length;
const bodyBraces = (body.match(/{/g) || []).length - (body.match(/}/g) || []).length;
if (subjectBraces !== 0) {
errors.push('Unmatched braces in subject template');
}
if (bodyBraces !== 0) {
errors.push('Unmatched braces in body template');
}
if (errors.length === 0) {
alert('Template validation passed! ✓');
} else {
alert('Template validation failed:\n\n' + errors.join('\n'));
}
}
function saveTemplate() {
document.querySelector('button[name="action"][value="save"]').click();
}
// Initialize
document.addEventListener('DOMContentLoaded', function() {
updatePreview();
// Add existing variables if editing
{% if object and object.variables %}
{% for variable in object.variables %}
addVariable();
const lastItem = document.querySelector('#variablesContainer .variable-item:last-child');
lastItem.querySelector('input[name="variable_names[]"]').value = '{{ variable.name }}';
lastItem.querySelector('input[name="variable_descriptions[]"]').value = '{{ variable.description }}';
{% endfor %}
{% endif %}
});
</script>
{% endblock %}