email templates page

This commit is contained in:
2025-06-01 22:00:45 +02:00
parent 047ad6ef10
commit e8d79cca19
4 changed files with 272 additions and 37 deletions

View File

@@ -5,6 +5,7 @@
{% from "settings/tabs/security.html" import security_tab %}
{% from "settings/tabs/debugging.html" import debugging_tab %}
{% from "settings/tabs/events.html" import events_tab %}
{% from "settings/tabs/email_templates.html" import email_templates_tab %}
{% from "settings/components/reset_colors_modal.html" import reset_colors_modal %}
{% block title %}Settings - DocuPulse{% endblock %}
@@ -37,6 +38,11 @@
<i class="fas fa-building me-2"></i>Company Info
</button>
</li>
<li class="nav-item" role="presentation">
<button class="nav-link {% if active_tab == 'email_templates' %}active{% endif %}" id="email-templates-tab" data-bs-toggle="tab" data-bs-target="#email-templates" type="button" role="tab" aria-controls="email-templates" aria-selected="{{ 'true' if active_tab == 'email_templates' else 'false' }}">
<i class="fas fa-envelope me-2"></i>Email Templates
</button>
</li>
<li class="nav-item" role="presentation">
<button class="nav-link {% if active_tab == 'security' %}active{% endif %}" id="security-tab" data-bs-toggle="tab" data-bs-target="#security" type="button" role="tab" aria-controls="security" aria-selected="{{ 'true' if active_tab == 'security' else 'false' }}">
<i class="fas fa-shield-alt me-2"></i>Security
@@ -66,6 +72,11 @@
{{ company_info_tab(site_settings, form) }}
</div>
<!-- Email Templates Tab -->
<div class="tab-pane fade {% if active_tab == 'email_templates' %}show active{% endif %}" id="email-templates" role="tabpanel" aria-labelledby="email-templates-tab">
{{ email_templates_tab(email_templates) }}
</div>
<!-- Security Tab -->
<div class="tab-pane fade {% if active_tab == 'security' %}show active{% endif %}" id="security" role="tabpanel" aria-labelledby="security-tab">
{{ security_tab() }}

View File

@@ -0,0 +1,191 @@
{% macro email_templates_tab(templates) %}
<div class="row">
<div class="col-12">
<div class="card">
<div class="card-body">
<!-- Template Selection -->
<div class="mb-4">
<label for="templateSelect" class="form-label">Select Template</label>
<select class="form-select" id="templateSelect">
<option value="">Choose a template...</option>
{% for template in templates %}
<option value="{{ template.id }}"
data-subject="{{ template.subject }}"
data-body="{{ template.body }}">
{{ template.name }}
</option>
{% endfor %}
</select>
</div>
<!-- Template Editor -->
<div class="card">
<div class="card-header bg-light">
<h6 class="mb-0">Template Editor</h6>
</div>
<div class="card-body">
<div class="mb-3">
<label for="templateSubject" class="form-label">Subject</label>
<input type="text" class="form-control" id="templateSubject" placeholder="Enter email subject">
</div>
<div class="mb-3">
<label for="templateBody" class="form-label">Body</label>
<textarea id="templateBody" class="form-control"></textarea>
</div>
<div class="text-end">
<button type="button" class="btn btn-primary" id="saveTemplate">
<i class="fas fa-save me-2"></i>Save Template
</button>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Dependencies -->
<script src="https://code.jquery.com/jquery-3.7.1.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/@popperjs/core@2.11.8/dist/umd/popper.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@4.6.2/dist/js/bootstrap.bundle.min.js"></script>
<link href="https://cdn.jsdelivr.net/npm/summernote@0.8.18/dist/summernote-bs4.min.css" rel="stylesheet">
<script src="https://cdn.jsdelivr.net/npm/summernote@0.8.18/dist/summernote-bs4.min.js"></script>
<script>
// Wait for document and jQuery to be ready
document.addEventListener('DOMContentLoaded', function() {
// Initialize Summernote
$('#templateBody').summernote({
height: 300,
toolbar: [
['style', ['style']],
['font', ['bold', 'underline', 'italic', 'clear']],
['color', ['color']],
['para', ['ul', 'ol', 'paragraph']],
['table', ['table']],
['insert', ['link']],
['view', ['fullscreen', 'codeview', 'help']]
],
styleTags: ['p', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6'],
placeholder: 'Enter your email template content here...',
callbacks: {
onImageUpload: function(files) {
// Disable image upload for email templates
return false;
}
},
popover: {
image: [],
link: [],
air: []
}
});
// Handle template selection
$('#templateSelect').on('change', function() {
const selectedOption = this.options[this.selectedIndex];
const subject = selectedOption.dataset.subject || '';
const body = selectedOption.dataset.body || '';
$('#templateSubject').val(subject);
$('#templateBody').summernote('code', body);
});
// Handle template save
$('#saveTemplate').on('click', function() {
const templateId = $('#templateSelect').val();
const subject = $('#templateSubject').val();
const body = $('#templateBody').summernote('code');
if (!templateId) {
alert('Please select a template first');
return;
}
// Show loading state
const saveButton = this;
const originalText = saveButton.innerHTML;
saveButton.disabled = true;
saveButton.innerHTML = '<i class="fas fa-spinner fa-spin me-2"></i>Saving...';
// Send AJAX request
fetch(`/settings/email-templates/${templateId}`, {
method: 'PUT',
headers: {
'Content-Type': 'application/json',
'X-CSRFToken': document.querySelector('meta[name="csrf-token"]').content
},
body: JSON.stringify({
subject: subject,
body: body
})
})
.then(response => response.json())
.then(data => {
if (data.error) {
throw new Error(data.error);
}
// Show success message
const alert = document.createElement('div');
alert.className = 'alert alert-success alert-dismissible fade show mt-3';
alert.innerHTML = `
<i class="fas fa-check-circle me-2"></i>Template saved successfully
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
`;
document.querySelector('.card-body').appendChild(alert);
// Update the select option's data attributes
const option = document.getElementById('templateSelect').options[document.getElementById('templateSelect').selectedIndex];
option.dataset.subject = subject;
option.dataset.body = body;
})
.catch(error => {
// Show error message
const alert = document.createElement('div');
alert.className = 'alert alert-danger alert-dismissible fade show mt-3';
alert.innerHTML = `
<i class="fas fa-exclamation-circle me-2"></i>${error.message || 'Failed to save template'}
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
`;
document.querySelector('.card-body').appendChild(alert);
})
.finally(() => {
// Restore button state
saveButton.disabled = false;
saveButton.innerHTML = originalText;
});
});
});
</script>
<style>
/* Summernote custom styles */
.note-editor {
margin-bottom: 0;
}
.note-editor.note-frame {
border-color: #dee2e6;
border-radius: 0.375rem;
}
.note-editor.note-frame:focus-within {
border-color: #86b7fe;
box-shadow: 0 0 0 0.25rem rgba(13, 110, 253, 0.25);
}
.note-toolbar {
background-color: #f8f9fa;
border-top-left-radius: 0.375rem;
border-top-right-radius: 0.375rem;
border-bottom: 1px solid #dee2e6;
}
.note-editing-area {
background-color: #fff;
}
.note-statusbar {
border-top: 1px solid #dee2e6;
}
.note-placeholder {
color: #6c757d;
}
</style>
{% endmacro %}