587 lines
24 KiB
HTML
587 lines
24 KiB
HTML
{% extends 'base.html' %}
|
|
{% load static %}
|
|
|
|
{% block title %}{{ template.name }} - Consent Template{% endblock %}
|
|
|
|
{% block extra_css %}
|
|
<style>
|
|
.template-header {
|
|
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
color: white;
|
|
border-radius: 15px;
|
|
padding: 2rem;
|
|
margin-bottom: 2rem;
|
|
}
|
|
.template-content {
|
|
background: #fff;
|
|
border-radius: 15px;
|
|
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.08);
|
|
padding: 2rem;
|
|
margin-bottom: 2rem;
|
|
}
|
|
.template-preview {
|
|
border: 2px dashed #dee2e6;
|
|
border-radius: 10px;
|
|
padding: 2rem;
|
|
background: #f8f9fa;
|
|
min-height: 400px;
|
|
}
|
|
.template-meta {
|
|
background: #f8f9fa;
|
|
border-radius: 10px;
|
|
padding: 1.5rem;
|
|
}
|
|
.version-badge {
|
|
background: linear-gradient(135deg, #28a745 0%, #20c997 100%);
|
|
color: white;
|
|
padding: 0.5rem 1rem;
|
|
border-radius: 50px;
|
|
font-weight: bold;
|
|
}
|
|
.usage-chart {
|
|
height: 200px;
|
|
}
|
|
.action-buttons {
|
|
position: sticky;
|
|
top: 20px;
|
|
z-index: 100;
|
|
}
|
|
.template-variables {
|
|
background: #e3f2fd;
|
|
border-left: 4px solid #2196f3;
|
|
padding: 1rem;
|
|
border-radius: 0 10px 10px 0;
|
|
margin: 1rem 0;
|
|
}
|
|
.variable-tag {
|
|
background: #2196f3;
|
|
color: white;
|
|
padding: 0.25rem 0.5rem;
|
|
border-radius: 15px;
|
|
font-size: 0.75rem;
|
|
margin: 0.25rem;
|
|
display: inline-block;
|
|
}
|
|
.template-history {
|
|
max-height: 300px;
|
|
overflow-y: auto;
|
|
}
|
|
.history-item {
|
|
border-left: 3px solid #dee2e6;
|
|
padding-left: 1rem;
|
|
margin-bottom: 1rem;
|
|
position: relative;
|
|
}
|
|
.history-item::before {
|
|
content: '';
|
|
position: absolute;
|
|
left: -6px;
|
|
top: 0;
|
|
width: 10px;
|
|
height: 10px;
|
|
border-radius: 50%;
|
|
background: #007bff;
|
|
}
|
|
.template-stats {
|
|
display: grid;
|
|
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
|
gap: 1rem;
|
|
margin-bottom: 2rem;
|
|
}
|
|
.stat-card {
|
|
background: white;
|
|
border-radius: 10px;
|
|
padding: 1.5rem;
|
|
text-align: center;
|
|
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.05);
|
|
border: 1px solid #e9ecef;
|
|
}
|
|
.stat-icon {
|
|
width: 50px;
|
|
height: 50px;
|
|
border-radius: 50%;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
margin: 0 auto 1rem;
|
|
font-size: 20px;
|
|
}
|
|
</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"><a href="{% url 'patients:consent_template_list' %}">Consent Templates</a></li>
|
|
<li class="breadcrumb-item active">{{ template.name }}</li>
|
|
</ol>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="row">
|
|
<!-- Main Content -->
|
|
<div class="col-lg-8">
|
|
<!-- Template Header -->
|
|
<div class="template-header">
|
|
<div class="d-flex justify-content-between align-items-start">
|
|
<div>
|
|
<h1 class="mb-2">
|
|
<i class="fas fa-file-contract me-3"></i>{{ template.name }}
|
|
</h1>
|
|
<p class="mb-3 opacity-75">{{ template.description }}</p>
|
|
<div class="d-flex align-items-center gap-3">
|
|
<span class="version-badge">
|
|
<i class="fas fa-code-branch me-1"></i>Version {{ template.version|default:"1.0" }}
|
|
</span>
|
|
<span class="badge bg-light text-dark">
|
|
<i class="fas fa-tag me-1"></i>{{ template.get_category_display }}
|
|
</span>
|
|
<span class="badge bg-light text-dark">
|
|
<i class="fas fa-language me-1"></i>{{ template.get_language_display }}
|
|
</span>
|
|
</div>
|
|
</div>
|
|
<div class="text-end">
|
|
{% if template.status == 'active' %}
|
|
<span class="badge bg-success fs-6 px-3 py-2">
|
|
<i class="fas fa-check-circle me-1"></i>Active
|
|
</span>
|
|
{% elif template.status == 'draft' %}
|
|
<span class="badge bg-warning fs-6 px-3 py-2">
|
|
<i class="fas fa-edit me-1"></i>Draft
|
|
</span>
|
|
{% elif template.status == 'archived' %}
|
|
<span class="badge bg-secondary fs-6 px-3 py-2">
|
|
<i class="fas fa-archive me-1"></i>Archived
|
|
</span>
|
|
{% else %}
|
|
<span class="badge bg-info fs-6 px-3 py-2">
|
|
<i class="fas fa-clock me-1"></i>{{ template.get_status_display }}
|
|
</span>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Template Statistics -->
|
|
<div class="template-stats">
|
|
<div class="stat-card">
|
|
<div class="stat-icon bg-primary text-white">
|
|
<i class="fas fa-users"></i>
|
|
</div>
|
|
<h4 class="mb-1">{{ template.usage_count|default:0 }}</h4>
|
|
<small class="text-muted">Times Used</small>
|
|
</div>
|
|
<div class="stat-card">
|
|
<div class="stat-icon bg-success text-white">
|
|
<i class="fas fa-calendar-check"></i>
|
|
</div>
|
|
<h4 class="mb-1">{{ template.last_used|date:"M d"|default:"Never" }}</h4>
|
|
<small class="text-muted">Last Used</small>
|
|
</div>
|
|
<div class="stat-card">
|
|
<div class="stat-icon bg-info text-white">
|
|
<i class="fas fa-star"></i>
|
|
</div>
|
|
<h4 class="mb-1">{{ template.rating|floatformat:1|default:"N/A" }}</h4>
|
|
<small class="text-muted">Average Rating</small>
|
|
</div>
|
|
<div class="stat-card">
|
|
<div class="stat-icon bg-warning text-white">
|
|
<i class="fas fa-clock"></i>
|
|
</div>
|
|
<h4 class="mb-1">{{ template.avg_completion_time|default:"N/A" }}</h4>
|
|
<small class="text-muted">Avg. Completion</small>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Template Content Preview -->
|
|
<div class="template-content">
|
|
<div class="d-flex justify-content-between align-items-center mb-3">
|
|
<h5 class="mb-0">
|
|
<i class="fas fa-eye me-2"></i>Template Preview
|
|
</h5>
|
|
<div class="btn-group btn-group-sm">
|
|
<button type="button" class="btn btn-outline-primary" onclick="togglePreviewMode('formatted')">
|
|
<i class="fas fa-file-alt"></i> Formatted
|
|
</button>
|
|
<button type="button" class="btn btn-outline-secondary" onclick="togglePreviewMode('raw')">
|
|
<i class="fas fa-code"></i> Raw
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<div id="formattedPreview" class="template-preview">
|
|
<div class="mb-4">
|
|
<h3 class="text-center mb-4">{{ template.name }}</h3>
|
|
<div class="content">
|
|
{{ template.content|safe|default:"<p class='text-muted text-center'>No content available for preview.</p>" }}
|
|
</div>
|
|
</div>
|
|
|
|
{% if template.variables %}
|
|
<div class="template-variables">
|
|
<h6 class="mb-2">
|
|
<i class="fas fa-code me-2"></i>Template Variables
|
|
</h6>
|
|
<p class="small mb-2">The following variables will be replaced when the template is used:</p>
|
|
{% for variable in template.variables %}
|
|
<span class="variable-tag">{{ variable }}</span>
|
|
{% endfor %}
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
|
|
<div id="rawPreview" class="template-preview d-none">
|
|
<pre class="mb-0"><code>{{ template.raw_content|default:"No raw content available." }}</code></pre>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Usage History -->
|
|
<div class="template-content">
|
|
<h5 class="mb-3">
|
|
<i class="fas fa-history me-2"></i>Recent Usage History
|
|
</h5>
|
|
<div class="template-history">
|
|
{% for usage in recent_usage %}
|
|
<div class="history-item">
|
|
<div class="d-flex justify-content-between align-items-start">
|
|
<div>
|
|
<h6 class="mb-1">{{ usage.patient_name }}</h6>
|
|
<p class="text-muted small mb-1">{{ usage.description }}</p>
|
|
<small class="text-muted">
|
|
<i class="fas fa-user me-1"></i>{{ usage.created_by }}
|
|
<i class="fas fa-clock ms-2 me-1"></i>{{ usage.created_at|timesince }} ago
|
|
</small>
|
|
</div>
|
|
<span class="badge bg-{{ usage.status_color }}">{{ usage.status }}</span>
|
|
</div>
|
|
</div>
|
|
{% empty %}
|
|
<div class="text-center py-4">
|
|
<i class="fas fa-history fa-3x text-muted mb-3"></i>
|
|
<h6 class="text-muted">No Usage History</h6>
|
|
<p class="text-muted">This template hasn't been used yet.</p>
|
|
</div>
|
|
{% endfor %}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Sidebar -->
|
|
<div class="col-lg-4">
|
|
<!-- Action Buttons -->
|
|
<div class="action-buttons">
|
|
<div class="card mb-4">
|
|
<div class="card-header">
|
|
<h6 class="mb-0">
|
|
<i class="fas fa-tools me-2"></i>Actions
|
|
</h6>
|
|
</div>
|
|
<div class="card-body">
|
|
<div class="d-grid gap-2">
|
|
<a href="{% url 'patients:consent_template_edit' template.id %}" class="btn btn-primary">
|
|
<i class="fas fa-edit me-2"></i>Edit Template
|
|
</a>
|
|
<button type="button" class="btn btn-success" onclick="useTemplate()">
|
|
<i class="fas fa-play me-2"></i>Use Template
|
|
</button>
|
|
<button type="button" class="btn btn-info" onclick="duplicateTemplate()">
|
|
<i class="fas fa-copy me-2"></i>Duplicate
|
|
</button>
|
|
<button type="button" class="btn btn-warning" onclick="exportTemplate()">
|
|
<i class="fas fa-download me-2"></i>Export
|
|
</button>
|
|
<div class="btn-group">
|
|
<button type="button" class="btn btn-outline-secondary dropdown-toggle" data-bs-toggle="dropdown">
|
|
<i class="fas fa-ellipsis-h me-2"></i>More Actions
|
|
</button>
|
|
<ul class="dropdown-menu w-100">
|
|
<li><a class="dropdown-item" href="#" onclick="shareTemplate()">
|
|
<i class="fas fa-share me-2"></i>Share Template
|
|
</a></li>
|
|
<li><a class="dropdown-item" href="#" onclick="printTemplate()">
|
|
<i class="fas fa-print me-2"></i>Print Preview
|
|
</a></li>
|
|
<li><a class="dropdown-item" href="#" onclick="viewVersionHistory()">
|
|
<i class="fas fa-code-branch me-2"></i>Version History
|
|
</a></li>
|
|
<li><hr class="dropdown-divider"></li>
|
|
{% if template.status != 'archived' %}
|
|
<li><a class="dropdown-item text-danger" href="#" onclick="archiveTemplate()">
|
|
<i class="fas fa-archive me-2"></i>Archive Template
|
|
</a></li>
|
|
{% else %}
|
|
<li><a class="dropdown-item text-success" href="#" onclick="restoreTemplate()">
|
|
<i class="fas fa-undo me-2"></i>Restore Template
|
|
</a></li>
|
|
{% endif %}
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Template Metadata -->
|
|
<div class="card mb-4">
|
|
<div class="card-header">
|
|
<h6 class="mb-0">
|
|
<i class="fas fa-info-circle me-2"></i>Template Information
|
|
</h6>
|
|
</div>
|
|
<div class="card-body">
|
|
<div class="template-meta">
|
|
<div class="row g-3">
|
|
<div class="col-12">
|
|
<label class="form-label small text-muted">Created By</label>
|
|
<div class="fw-bold">{{ template.created_by|default:"System" }}</div>
|
|
</div>
|
|
<div class="col-6">
|
|
<label class="form-label small text-muted">Created</label>
|
|
<div class="fw-bold">{{ template.created_at|date:"M d, Y" }}</div>
|
|
</div>
|
|
<div class="col-6">
|
|
<label class="form-label small text-muted">Updated</label>
|
|
<div class="fw-bold">{{ template.updated_at|date:"M d, Y" }}</div>
|
|
</div>
|
|
<div class="col-6">
|
|
<label class="form-label small text-muted">File Size</label>
|
|
<div class="fw-bold">{{ template.file_size|filesizeformat|default:"N/A" }}</div>
|
|
</div>
|
|
<div class="col-6">
|
|
<label class="form-label small text-muted">Checksum</label>
|
|
<div class="fw-bold small">{{ template.checksum|truncatechars:8|default:"N/A" }}</div>
|
|
</div>
|
|
{% if template.tags %}
|
|
<div class="col-12">
|
|
<label class="form-label small text-muted">Tags</label>
|
|
<div>
|
|
{% for tag in template.tags %}
|
|
<span class="badge bg-secondary me-1">{{ tag }}</span>
|
|
{% endfor %}
|
|
</div>
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Usage Analytics -->
|
|
<div class="card mb-4">
|
|
<div class="card-header">
|
|
<h6 class="mb-0">
|
|
<i class="fas fa-chart-line me-2"></i>Usage Analytics
|
|
</h6>
|
|
</div>
|
|
<div class="card-body">
|
|
<canvas id="usageChart" class="usage-chart"></canvas>
|
|
<div class="mt-3">
|
|
<div class="row text-center">
|
|
<div class="col-4">
|
|
<div class="fw-bold text-primary">{{ analytics.this_week|default:0 }}</div>
|
|
<small class="text-muted">This Week</small>
|
|
</div>
|
|
<div class="col-4">
|
|
<div class="fw-bold text-success">{{ analytics.this_month|default:0 }}</div>
|
|
<small class="text-muted">This Month</small>
|
|
</div>
|
|
<div class="col-4">
|
|
<div class="fw-bold text-info">{{ analytics.total|default:0 }}</div>
|
|
<small class="text-muted">All Time</small>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Related Templates -->
|
|
{% if related_templates %}
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<h6 class="mb-0">
|
|
<i class="fas fa-link me-2"></i>Related Templates
|
|
</h6>
|
|
</div>
|
|
<div class="card-body">
|
|
{% for related in related_templates %}
|
|
<div class="d-flex align-items-center mb-3">
|
|
<div class="me-3">
|
|
<i class="fas fa-file-contract text-primary"></i>
|
|
</div>
|
|
<div class="flex-grow-1">
|
|
<a href="{% url 'patients:consent_template_detail' related.id %}" class="text-decoration-none">
|
|
<div class="fw-bold">{{ related.name }}</div>
|
|
<small class="text-muted">{{ related.category }}</small>
|
|
</a>
|
|
</div>
|
|
<div>
|
|
<span class="badge bg-light text-dark">{{ related.usage_count|default:0 }}</span>
|
|
</div>
|
|
</div>
|
|
{% endfor %}
|
|
</div>
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{% endblock %}
|
|
|
|
{% block extra_js %}
|
|
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
|
|
|
|
<script>
|
|
// Usage Analytics Chart
|
|
const ctx = document.getElementById('usageChart').getContext('2d');
|
|
const usageChart = new Chart(ctx, {
|
|
type: 'line',
|
|
data: {
|
|
labels: {{ analytics.labels|safe|default:"[]" }},
|
|
datasets: [{
|
|
label: 'Usage Count',
|
|
data: {{ analytics.data|safe|default:"[]" }},
|
|
borderColor: '#007bff',
|
|
backgroundColor: 'rgba(0, 123, 255, 0.1)',
|
|
borderWidth: 2,
|
|
fill: true,
|
|
tension: 0.4
|
|
}]
|
|
},
|
|
options: {
|
|
responsive: true,
|
|
maintainAspectRatio: false,
|
|
plugins: {
|
|
legend: {
|
|
display: false
|
|
}
|
|
},
|
|
scales: {
|
|
y: {
|
|
beginAtZero: true,
|
|
ticks: {
|
|
stepSize: 1
|
|
}
|
|
}
|
|
}
|
|
}
|
|
});
|
|
|
|
function togglePreviewMode(mode) {
|
|
if (mode === 'formatted') {
|
|
document.getElementById('formattedPreview').classList.remove('d-none');
|
|
document.getElementById('rawPreview').classList.add('d-none');
|
|
} else {
|
|
document.getElementById('formattedPreview').classList.add('d-none');
|
|
document.getElementById('rawPreview').classList.remove('d-none');
|
|
}
|
|
}
|
|
|
|
function useTemplate() {
|
|
if (confirm('Are you sure you want to use this template for a new consent form?')) {
|
|
window.location.href = '{% url "patients:consent_create" %}?template={{ template.id }}';
|
|
}
|
|
}
|
|
|
|
function duplicateTemplate() {
|
|
if (confirm('Are you sure you want to duplicate this template?')) {
|
|
$.ajax({
|
|
url: '{% url "patients:consent_template_duplicate" template.id %}',
|
|
method: 'POST',
|
|
headers: {
|
|
'X-CSRFToken': $('[name=csrfmiddlewaretoken]').val()
|
|
},
|
|
success: function(response) {
|
|
if (response.success) {
|
|
window.location.href = response.redirect_url;
|
|
} else {
|
|
alert('Error duplicating template: ' + response.error);
|
|
}
|
|
},
|
|
error: function() {
|
|
alert('Error duplicating template.');
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
function exportTemplate() {
|
|
window.location.href = '{% url "patients:consent_template_export" template.id %}';
|
|
}
|
|
|
|
function shareTemplate() {
|
|
const shareUrl = window.location.href;
|
|
if (navigator.share) {
|
|
navigator.share({
|
|
title: '{{ template.name }}',
|
|
text: 'Check out this consent template',
|
|
url: shareUrl
|
|
});
|
|
} else {
|
|
navigator.clipboard.writeText(shareUrl).then(function() {
|
|
alert('Template URL copied to clipboard!');
|
|
});
|
|
}
|
|
}
|
|
|
|
function printTemplate() {
|
|
window.open('{% url "patients:consent_template_print" template.id %}', '_blank');
|
|
}
|
|
|
|
function viewVersionHistory() {
|
|
window.location.href = '{% url "patients:consent_template_versions" template.id %}';
|
|
}
|
|
|
|
function archiveTemplate() {
|
|
if (confirm('Are you sure you want to archive this template? It will no longer be available for use.')) {
|
|
$.ajax({
|
|
url: '{% url "patients:consent_template_archive" template.id %}',
|
|
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 restoreTemplate() {
|
|
if (confirm('Are you sure you want to restore this template?')) {
|
|
$.ajax({
|
|
url: '{% url "patients:consent_template_restore" template.id %}',
|
|
method: 'POST',
|
|
headers: {
|
|
'X-CSRFToken': $('[name=csrfmiddlewaretoken]').val()
|
|
},
|
|
success: function(response) {
|
|
if (response.success) {
|
|
location.reload();
|
|
} else {
|
|
alert('Error restoring template: ' + response.error);
|
|
}
|
|
},
|
|
error: function() {
|
|
alert('Error restoring template.');
|
|
}
|
|
});
|
|
}
|
|
}
|
|
</script>
|
|
{% endblock %}
|
|
|