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

939 lines
43 KiB
HTML

{% extends "base.html" %}
{% load static %}
{% block title %}Compose Message - {{ block.super }}{% endblock %}
{% block content %}
<div class="container-fluid">
<!-- Page Header -->
<div class="d-flex justify-content-between align-items-center mb-4">
<div>
<h1 class="h3 mb-1">Compose Message</h1>
<nav aria-label="breadcrumb">
<ol class="breadcrumb mb-0">
<li class="breadcrumb-item"><a href="{% url 'communications:dashboard' %}">Communications</a></li>
<li class="breadcrumb-item"><a href="{% url 'communications:message_inbox' %}">Messages</a></li>
<li class="breadcrumb-item active">Compose</li>
</ol>
</nav>
</div>
<div class="btn-group">
<a href="{% url 'communications:message_inbox' %}" class="btn btn-outline-secondary">
<i class="fas fa-arrow-left me-2"></i>Back to Inbox
</a>
</div>
</div>
<form method="post" id="composeForm" enctype="multipart/form-data" hx-post="{% url 'communications:message_compose' %}" hx-target="#form-container">
{% csrf_token %}
<div class="row">
<!-- Main Compose Area -->
<div class="col-lg-9">
<div id="form-container">
<div class="card">
<div class="card-header d-flex justify-content-between align-items-center">
<h5 class="mb-0">
<i class="fas fa-edit me-2"></i>New Message
</h5>
<div class="btn-group btn-group-sm">
<button type="button" class="btn btn-outline-secondary" onclick="saveDraft()">
<i class="fas fa-save me-1"></i>Save Draft
</button>
<button type="button" class="btn btn-outline-info" onclick="previewMessage()">
<i class="fas fa-eye me-1"></i>Preview
</button>
<button type="button" class="btn btn-outline-warning" onclick="scheduleMessage()">
<i class="fas fa-clock me-1"></i>Schedule
</button>
</div>
</div>
<div class="card-body">
<!-- Message Type Selection -->
<div class="row mb-3">
<div class="col-md-6">
<div class="form-floating">
{{ form.message_type }}
<label for="{{ form.message_type.id_for_label }}">Message Type *</label>
{% if form.message_type.errors %}
<div class="invalid-feedback d-block">
{{ form.message_type.errors.0 }}
</div>
{% endif %}
</div>
</div>
<div class="col-md-6">
<div class="form-floating">
{{ form.priority }}
<label for="{{ form.priority.id_for_label }}">Priority *</label>
{% if form.priority.errors %}
<div class="invalid-feedback d-block">
{{ form.priority.errors.0 }}
</div>
{% endif %}
</div>
</div>
</div>
<!-- Recipients Section -->
<div class="mb-3">
<label class="form-label fw-bold">Recipients *</label>
<div class="card bg-light">
<div class="card-body">
<!-- To Field -->
<div class="mb-3">
<div class="d-flex align-items-center mb-2">
<label class="form-label mb-0 me-3" style="min-width: 60px;">To:</label>
<div class="flex-grow-1">
<input type="text" class="form-control" id="recipientInput" placeholder="Enter names, emails, or select from contacts..." autocomplete="off">
<div id="recipientSuggestions" class="dropdown-menu w-100" style="display: none;"></div>
</div>
<button type="button" class="btn btn-outline-primary btn-sm ms-2" onclick="showContactPicker()">
<i class="fas fa-address-book"></i>
</button>
</div>
<div id="selectedRecipients" class="d-flex flex-wrap gap-2">
<!-- Selected recipients will appear here as badges -->
</div>
</div>
<!-- CC Field -->
<div class="mb-3" id="ccField" style="display: none;">
<div class="d-flex align-items-center mb-2">
<label class="form-label mb-0 me-3" style="min-width: 60px;">CC:</label>
<div class="flex-grow-1">
<input type="text" class="form-control" id="ccInput" placeholder="Carbon copy recipients...">
</div>
</div>
<div id="selectedCcRecipients" class="d-flex flex-wrap gap-2">
<!-- CC recipients will appear here -->
</div>
</div>
<!-- BCC Field -->
<div class="mb-3" id="bccField" style="display: none;">
<div class="d-flex align-items-center mb-2">
<label class="form-label mb-0 me-3" style="min-width: 60px;">BCC:</label>
<div class="flex-grow-1">
<input type="text" class="form-control" id="bccInput" placeholder="Blind carbon copy recipients...">
</div>
</div>
<div id="selectedBccRecipients" class="d-flex flex-wrap gap-2">
<!-- BCC recipients will appear here -->
</div>
</div>
<!-- CC/BCC Toggle -->
<div class="text-end">
<button type="button" class="btn btn-link btn-sm p-0" onclick="toggleCcBcc()">
<i class="fas fa-plus me-1"></i>Add CC/BCC
</button>
</div>
</div>
</div>
</div>
<!-- Subject -->
<div class="mb-3">
<div class="form-floating">
{{ form.subject }}
<label for="{{ form.subject.id_for_label }}">Subject *</label>
{% if form.subject.errors %}
<div class="invalid-feedback d-block">
{{ form.subject.errors.0 }}
</div>
{% endif %}
</div>
</div>
<!-- Message Content -->
<div class="mb-3">
<label for="{{ form.content.id_for_label }}" class="form-label fw-bold">Message Content *</label>
<div class="card">
<div class="card-header p-2">
<!-- Rich Text Editor Toolbar -->
<div class="btn-toolbar" role="toolbar">
<div class="btn-group btn-group-sm me-2" role="group">
<button type="button" class="btn btn-outline-secondary" onclick="formatText('bold')" title="Bold">
<i class="fas fa-bold"></i>
</button>
<button type="button" class="btn btn-outline-secondary" onclick="formatText('italic')" title="Italic">
<i class="fas fa-italic"></i>
</button>
<button type="button" class="btn btn-outline-secondary" onclick="formatText('underline')" title="Underline">
<i class="fas fa-underline"></i>
</button>
</div>
<div class="btn-group btn-group-sm me-2" role="group">
<button type="button" class="btn btn-outline-secondary" onclick="formatText('insertUnorderedList')" title="Bullet List">
<i class="fas fa-list-ul"></i>
</button>
<button type="button" class="btn btn-outline-secondary" onclick="formatText('insertOrderedList')" title="Numbered List">
<i class="fas fa-list-ol"></i>
</button>
</div>
<div class="btn-group btn-group-sm me-2" role="group">
<button type="button" class="btn btn-outline-secondary" onclick="insertLink()" title="Insert Link">
<i class="fas fa-link"></i>
</button>
<button type="button" class="btn btn-outline-secondary" onclick="insertTable()" title="Insert Table">
<i class="fas fa-table"></i>
</button>
</div>
<div class="btn-group btn-group-sm" role="group">
<button type="button" class="btn btn-outline-secondary" onclick="insertTemplate()" title="Insert Template">
<i class="fas fa-file-alt"></i>
</button>
<button type="button" class="btn btn-outline-secondary" onclick="insertSignature()" title="Insert Signature">
<i class="fas fa-signature"></i>
</button>
</div>
</div>
</div>
<div class="card-body p-0">
<div id="messageEditor" contenteditable="true" style="min-height: 300px; padding: 15px; border: none; outline: none;">
<!-- Rich text content will be entered here -->
</div>
<textarea name="content" id="{{ form.content.id_for_label }}" style="display: none;">{{ form.content.value|default:'' }}</textarea>
</div>
</div>
{% if form.content.errors %}
<div class="invalid-feedback d-block">
{{ form.content.errors.0 }}
</div>
{% endif %}
</div>
<!-- Attachments -->
<div class="mb-3">
<label class="form-label fw-bold">Attachments</label>
<div class="card bg-light">
<div class="card-body">
<div class="d-flex align-items-center">
<input type="file" class="form-control" id="attachmentInput" multiple accept=".pdf,.doc,.docx,.xls,.xlsx,.ppt,.pptx,.txt,.jpg,.jpeg,.png,.gif">
<button type="button" class="btn btn-outline-primary ms-2" onclick="addAttachment()">
<i class="fas fa-plus"></i>
</button>
</div>
<div id="attachmentList" class="mt-3">
<!-- Attached files will appear here -->
</div>
<div class="form-text">
Maximum file size: 10MB per file. Allowed types: PDF, DOC, XLS, PPT, TXT, Images
</div>
</div>
</div>
</div>
<!-- Message Options -->
<div class="mb-3">
<label class="form-label fw-bold">Message Options</label>
<div class="card bg-light">
<div class="card-body">
<div class="row">
<div class="col-md-6">
<div class="form-check">
{{ form.is_urgent }}
<label class="form-check-label" for="{{ form.is_urgent.id_for_label }}">
<i class="fas fa-exclamation-triangle text-warning me-1"></i>Mark as Urgent
</label>
</div>
<div class="form-check">
{{ form.requires_acknowledgment }}
<label class="form-check-label" for="{{ form.requires_acknowledgment.id_for_label }}">
<i class="fas fa-check-circle text-info me-1"></i>Require Acknowledgment
</label>
</div>
<div class="form-check">
{{ form.is_confidential }}
<label class="form-check-label" for="{{ form.is_confidential.id_for_label }}">
<i class="fas fa-lock text-danger me-1"></i>Mark as Confidential
</label>
</div>
</div>
<div class="col-md-6">
<div class="form-floating">
{{ form.expires_at }}
<label for="{{ form.expires_at.id_for_label }}">Expiration Date</label>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Action Buttons -->
<div class="d-flex justify-content-between">
<div>
<a href="{% url 'communications:message_inbox' %}" class="btn btn-outline-secondary">
<i class="fas fa-times me-2"></i>Cancel
</a>
</div>
<div class="btn-group">
<button type="submit" name="action" value="draft" class="btn btn-outline-primary">
<i class="fas fa-save me-2"></i>Save Draft
</button>
<button type="submit" name="action" value="send" class="btn btn-success">
<i class="fas fa-paper-plane me-2"></i>Send Message
</button>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Sidebar -->
<div class="col-lg-3">
<!-- Quick Templates -->
<div class="card mb-3">
<div class="card-header">
<h5 class="mb-0">
<i class="fas fa-file-alt me-2"></i>Quick Templates
</h5>
</div>
<div class="card-body p-0">
<div class="list-group list-group-flush" id="templateList">
<button type="button" class="list-group-item list-group-item-action" onclick="loadTemplate('appointment_reminder')">
<i class="fas fa-calendar me-2"></i>Appointment Reminder
</button>
<button type="button" class="list-group-item list-group-item-action" onclick="loadTemplate('lab_results')">
<i class="fas fa-flask me-2"></i>Lab Results Available
</button>
<button type="button" class="list-group-item list-group-item-action" onclick="loadTemplate('medication_reminder')">
<i class="fas fa-pills me-2"></i>Medication Reminder
</button>
<button type="button" class="list-group-item list-group-item-action" onclick="loadTemplate('billing_notice')">
<i class="fas fa-file-invoice me-2"></i>Billing Notice
</button>
<button type="button" class="list-group-item list-group-item-action" onclick="loadTemplate('general_notice')">
<i class="fas fa-info-circle me-2"></i>General Notice
</button>
</div>
</div>
<div class="card-footer">
<a href="{% url 'communications:template_list' %}" class="btn btn-outline-primary btn-sm w-100">
<i class="fas fa-external-link-alt me-1"></i>Manage Templates
</a>
</div>
</div>
<!-- Recent Contacts -->
<div class="card mb-3">
<div class="card-header">
<h5 class="mb-0">
<i class="fas fa-users me-2"></i>Recent Contacts
</h5>
</div>
<div class="card-body p-0">
<div class="list-group list-group-flush" id="recentContactsList" hx-get="{% url 'communications:recent_contacts' %}" hx-trigger="load">
<div class="text-center p-3">
<div class="spinner-border spinner-border-sm text-primary" role="status">
<span class="visually-hidden">Loading...</span>
</div>
</div>
</div>
</div>
</div>
<!-- Message Statistics -->
<div class="card">
<div class="card-header">
<h5 class="mb-0">
<i class="fas fa-chart-bar me-2"></i>Your Statistics
</h5>
</div>
<div class="card-body">
<div class="row text-center">
<div class="col-6">
<div class="border-end">
<div class="h4 text-primary mb-0">{{ user_stats.sent_today|default:0 }}</div>
<div class="small text-muted">Sent Today</div>
</div>
</div>
<div class="col-6">
<div class="h4 text-success mb-0">{{ user_stats.sent_week|default:0 }}</div>
<div class="small text-muted">This Week</div>
</div>
</div>
<hr>
<div class="row text-center">
<div class="col-6">
<div class="border-end">
<div class="h4 text-info mb-0">{{ user_stats.drafts|default:0 }}</div>
<div class="small text-muted">Drafts</div>
</div>
</div>
<div class="col-6">
<div class="h4 text-warning mb-0">{{ user_stats.scheduled|default:0 }}</div>
<div class="small text-muted">Scheduled</div>
</div>
</div>
</div>
</div>
</div>
</div>
</form>
</div>
<!-- Contact Picker Modal -->
<div class="modal fade" id="contactPickerModal" tabindex="-1">
<div class="modal-dialog modal-lg">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Select Contacts</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body">
<div class="mb-3">
<input type="text" class="form-control" id="contactSearch" placeholder="Search contacts...">
</div>
<div id="contactList" style="max-height: 400px; overflow-y: auto;">
<!-- Contact list will be loaded here -->
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
<button type="button" class="btn btn-primary" onclick="addSelectedContacts()">Add Selected</button>
</div>
</div>
</div>
</div>
<!-- Schedule Message Modal -->
<div class="modal fade" id="scheduleModal" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Schedule Message</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body">
<div class="mb-3">
<label for="scheduledDateTime" class="form-label">Send Date & Time</label>
<input type="datetime-local" class="form-control" id="scheduledDateTime" name="scheduled_at">
</div>
<div class="mb-3">
<label for="timezone" class="form-label">Timezone</label>
<select class="form-select" id="timezone">
<option value="UTC">UTC</option>
<option value="America/New_York">Eastern Time</option>
<option value="America/Chicago">Central Time</option>
<option value="America/Denver">Mountain Time</option>
<option value="America/Los_Angeles">Pacific Time</option>
</select>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
<button type="button" class="btn btn-primary" onclick="confirmSchedule()">Schedule Message</button>
</div>
</div>
</div>
</div>
<script>
let selectedRecipients = [];
let selectedCcRecipients = [];
let selectedBccRecipients = [];
let attachments = [];
// Initialize compose form
document.addEventListener('DOMContentLoaded', function() {
initializeEditor();
setupRecipientInput();
setupAutoSave();
// Load draft if editing
const draftId = new URLSearchParams(window.location.search).get('draft');
if (draftId) {
loadDraft(draftId);
}
});
function initializeEditor() {
const editor = document.getElementById('messageEditor');
const hiddenTextarea = document.getElementById('{{ form.content.id_for_label }}');
// Sync editor content with hidden textarea
editor.addEventListener('input', function() {
hiddenTextarea.value = editor.innerHTML;
});
// Load initial content
if (hiddenTextarea.value) {
editor.innerHTML = hiddenTextarea.value;
}
}
function formatText(command, value = null) {
document.execCommand(command, false, value);
document.getElementById('messageEditor').focus();
}
function insertLink() {
const url = prompt('Enter URL:');
if (url) {
formatText('createLink', url);
}
}
function insertTable() {
const rows = prompt('Number of rows:', '3');
const cols = prompt('Number of columns:', '3');
if (rows && cols) {
let tableHtml = '<table class="table table-bordered"><tbody>';
for (let i = 0; i < parseInt(rows); i++) {
tableHtml += '<tr>';
for (let j = 0; j < parseInt(cols); j++) {
tableHtml += '<td>&nbsp;</td>';
}
tableHtml += '</tr>';
}
tableHtml += '</tbody></table>';
formatText('insertHTML', tableHtml);
}
}
function setupRecipientInput() {
const input = document.getElementById('recipientInput');
const suggestions = document.getElementById('recipientSuggestions');
input.addEventListener('input', function() {
const query = this.value;
if (query.length >= 2) {
searchContacts(query);
} else {
suggestions.style.display = 'none';
}
});
input.addEventListener('keydown', function(e) {
if (e.key === 'Enter' || e.key === ',') {
e.preventDefault();
addRecipient(this.value.trim());
this.value = '';
suggestions.style.display = 'none';
}
});
}
function searchContacts(query) {
fetch(`{% url 'communications:search_contacts' %}?q=${encodeURIComponent(query)}`)
.then(response => response.json())
.then(data => {
const suggestions = document.getElementById('recipientSuggestions');
suggestions.innerHTML = '';
data.contacts.forEach(contact => {
const item = document.createElement('a');
item.className = 'dropdown-item';
item.href = '#';
item.innerHTML = `
<div class="d-flex align-items-center">
<div class="me-3">
<i class="fas fa-user-circle fa-lg text-muted"></i>
</div>
<div>
<div class="fw-bold">${contact.name}</div>
<div class="small text-muted">${contact.email}</div>
</div>
</div>
`;
item.onclick = function(e) {
e.preventDefault();
addRecipient(contact);
document.getElementById('recipientInput').value = '';
suggestions.style.display = 'none';
};
suggestions.appendChild(item);
});
suggestions.style.display = data.contacts.length > 0 ? 'block' : 'none';
})
.catch(error => console.error('Error searching contacts:', error));
}
function addRecipient(recipient, type = 'to') {
let recipientObj;
if (typeof recipient === 'string') {
// Parse email string
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
if (emailRegex.test(recipient)) {
recipientObj = { email: recipient, name: recipient };
} else {
return; // Invalid email
}
} else {
recipientObj = recipient;
}
// Add to appropriate list
let targetList, targetContainer;
if (type === 'cc') {
targetList = selectedCcRecipients;
targetContainer = 'selectedCcRecipients';
} else if (type === 'bcc') {
targetList = selectedBccRecipients;
targetContainer = 'selectedBccRecipients';
} else {
targetList = selectedRecipients;
targetContainer = 'selectedRecipients';
}
// Check for duplicates
if (targetList.some(r => r.email === recipientObj.email)) {
return;
}
targetList.push(recipientObj);
updateRecipientDisplay(targetContainer, targetList);
}
function updateRecipientDisplay(containerId, recipients) {
const container = document.getElementById(containerId);
container.innerHTML = '';
recipients.forEach((recipient, index) => {
const badge = document.createElement('span');
badge.className = 'badge bg-primary d-flex align-items-center';
badge.innerHTML = `
${recipient.name || recipient.email}
<button type="button" class="btn-close btn-close-white ms-2" onclick="removeRecipient('${containerId}', ${index})"></button>
`;
container.appendChild(badge);
});
}
function removeRecipient(containerId, index) {
let targetList;
if (containerId === 'selectedCcRecipients') {
targetList = selectedCcRecipients;
} else if (containerId === 'selectedBccRecipients') {
targetList = selectedBccRecipients;
} else {
targetList = selectedRecipients;
}
targetList.splice(index, 1);
updateRecipientDisplay(containerId, targetList);
}
function toggleCcBcc() {
const ccField = document.getElementById('ccField');
const bccField = document.getElementById('bccField');
if (ccField.style.display === 'none') {
ccField.style.display = 'block';
bccField.style.display = 'block';
} else {
ccField.style.display = 'none';
bccField.style.display = 'none';
}
}
function showContactPicker() {
const modal = new bootstrap.Modal(document.getElementById('contactPickerModal'));
modal.show();
// Load contacts
loadContactList();
}
function loadContactList() {
fetch('{% url "communications:contact_list_api" %}')
.then(response => response.json())
.then(data => {
const container = document.getElementById('contactList');
container.innerHTML = '';
data.contacts.forEach(contact => {
const item = document.createElement('div');
item.className = 'form-check';
item.innerHTML = `
<input class="form-check-input" type="checkbox" value="${contact.id}" id="contact_${contact.id}">
<label class="form-check-label d-flex align-items-center" for="contact_${contact.id}">
<div class="me-3">
<i class="fas fa-user-circle fa-lg text-muted"></i>
</div>
<div>
<div class="fw-bold">${contact.name}</div>
<div class="small text-muted">${contact.email}</div>
<div class="small text-muted">${contact.role || ''}</div>
</div>
</label>
`;
container.appendChild(item);
});
})
.catch(error => console.error('Error loading contacts:', error));
}
function addSelectedContacts() {
const checkboxes = document.querySelectorAll('#contactList input[type="checkbox"]:checked');
checkboxes.forEach(checkbox => {
const label = checkbox.nextElementSibling;
const name = label.querySelector('.fw-bold').textContent;
const email = label.querySelector('.small').textContent;
addRecipient({ name, email });
});
bootstrap.Modal.getInstance(document.getElementById('contactPickerModal')).hide();
}
function addAttachment() {
const input = document.getElementById('attachmentInput');
const files = input.files;
for (let file of files) {
if (file.size > 10 * 1024 * 1024) { // 10MB limit
alert(`File "${file.name}" is too large. Maximum size is 10MB.`);
continue;
}
attachments.push(file);
displayAttachment(file);
}
input.value = ''; // Clear input
}
function displayAttachment(file) {
const container = document.getElementById('attachmentList');
const item = document.createElement('div');
item.className = 'attachment-item d-flex align-items-center justify-content-between p-2 border rounded mb-2';
item.innerHTML = `
<div class="d-flex align-items-center">
<i class="fas fa-file me-2"></i>
<div>
<div class="fw-bold">${file.name}</div>
<div class="small text-muted">${formatFileSize(file.size)}</div>
</div>
</div>
<button type="button" class="btn btn-outline-danger btn-sm" onclick="removeAttachment('${file.name}')">
<i class="fas fa-times"></i>
</button>
`;
container.appendChild(item);
}
function removeAttachment(fileName) {
attachments = attachments.filter(file => file.name !== fileName);
// Remove from display
const items = document.querySelectorAll('.attachment-item');
items.forEach(item => {
if (item.querySelector('.fw-bold').textContent === fileName) {
item.remove();
}
});
}
function formatFileSize(bytes) {
if (bytes === 0) return '0 Bytes';
const k = 1024;
const sizes = ['Bytes', 'KB', 'MB', 'GB'];
const i = Math.floor(Math.log(bytes) / Math.log(k));
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
}
function loadTemplate(templateId) {
fetch(`{% url 'communications:template_content' 'TEMPLATE_ID' %}`.replace('TEMPLATE_ID', templateId))
.then(response => response.json())
.then(data => {
if (data.success) {
document.getElementById('{{ form.subject.id_for_label }}').value = data.subject;
document.getElementById('messageEditor').innerHTML = data.content;
document.getElementById('{{ form.content.id_for_label }}').value = data.content;
}
})
.catch(error => console.error('Error loading template:', error));
}
function insertTemplate() {
// Show template picker modal
// Implementation depends on your template system
}
function insertSignature() {
fetch('{% url "communications:user_signature" %}')
.then(response => response.json())
.then(data => {
if (data.signature) {
const editor = document.getElementById('messageEditor');
editor.innerHTML += '<br><br>' + data.signature;
document.getElementById('{{ form.content.id_for_label }}').value = editor.innerHTML;
}
})
.catch(error => console.error('Error loading signature:', error));
}
function saveDraft() {
const formData = new FormData(document.getElementById('composeForm'));
formData.set('action', 'draft');
// Add recipients
formData.set('recipients', JSON.stringify(selectedRecipients));
formData.set('cc_recipients', JSON.stringify(selectedCcRecipients));
formData.set('bcc_recipients', JSON.stringify(selectedBccRecipients));
// Add attachments
attachments.forEach((file, index) => {
formData.append(`attachment_${index}`, file);
});
fetch('{% url "communications:message_compose" %}', {
method: 'POST',
body: formData
})
.then(response => response.json())
.then(data => {
if (data.success) {
showToast('Success', 'Draft saved successfully', 'success');
} else {
showToast('Error', data.error || 'Failed to save draft', 'error');
}
})
.catch(error => {
console.error('Error saving draft:', error);
showToast('Error', 'Failed to save draft', 'error');
});
}
function previewMessage() {
const subject = document.getElementById('{{ form.subject.id_for_label }}').value;
const content = document.getElementById('messageEditor').innerHTML;
const previewWindow = window.open('', '_blank', 'width=800,height=600');
previewWindow.document.write(`
<html>
<head>
<title>Message Preview</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
<div class="container mt-4">
<div class="card">
<div class="card-header">
<h5>Subject: ${subject}</h5>
</div>
<div class="card-body">
${content}
</div>
</div>
</div>
</body>
</html>
`);
}
function scheduleMessage() {
const modal = new bootstrap.Modal(document.getElementById('scheduleModal'));
modal.show();
}
function confirmSchedule() {
const scheduledDateTime = document.getElementById('scheduledDateTime').value;
if (!scheduledDateTime) {
alert('Please select a date and time.');
return;
}
document.getElementById('{{ form.scheduled_at.id_for_label }}').value = scheduledDateTime;
bootstrap.Modal.getInstance(document.getElementById('scheduleModal')).hide();
showToast('Success', 'Message scheduled for ' + new Date(scheduledDateTime).toLocaleString(), 'info');
}
function setupAutoSave() {
// Auto-save draft every 2 minutes
setInterval(function() {
if (document.getElementById('{{ form.subject.id_for_label }}').value.trim() ||
document.getElementById('messageEditor').innerHTML.trim()) {
saveDraft();
}
}, 120000);
}
function loadDraft(draftId) {
fetch(`{% url 'communications:draft_detail' 'DRAFT_ID' %}`.replace('DRAFT_ID', draftId))
.then(response => response.json())
.then(data => {
if (data.success) {
// Populate form with draft data
document.getElementById('{{ form.subject.id_for_label }}').value = data.subject;
document.getElementById('messageEditor').innerHTML = data.content;
document.getElementById('{{ form.content.id_for_label }}').value = data.content;
// Load recipients
selectedRecipients = data.recipients || [];
selectedCcRecipients = data.cc_recipients || [];
selectedBccRecipients = data.bcc_recipients || [];
updateRecipientDisplay('selectedRecipients', selectedRecipients);
updateRecipientDisplay('selectedCcRecipients', selectedCcRecipients);
updateRecipientDisplay('selectedBccRecipients', selectedBccRecipients);
}
})
.catch(error => console.error('Error loading draft:', error));
}
// Form submission handler
document.getElementById('composeForm').addEventListener('submit', function(e) {
e.preventDefault();
// Validate recipients
if (selectedRecipients.length === 0) {
alert('Please add at least one recipient.');
return;
}
// Prepare form data
const formData = new FormData(this);
formData.set('recipients', JSON.stringify(selectedRecipients));
formData.set('cc_recipients', JSON.stringify(selectedCcRecipients));
formData.set('bcc_recipients', JSON.stringify(selectedBccRecipients));
// Add attachments
attachments.forEach((file, index) => {
formData.append(`attachment_${index}`, file);
});
// Submit form
fetch('{% url "communications:message_compose" %}', {
method: 'POST',
body: formData
})
.then(response => response.json())
.then(data => {
if (data.success) {
if (data.action === 'send') {
showToast('Success', 'Message sent successfully', 'success');
setTimeout(() => {
window.location.href = '{% url "communications:message_sent" %}';
}, 1500);
} else {
showToast('Success', 'Draft saved successfully', 'success');
}
} else {
showToast('Error', data.error || 'Failed to process message', 'error');
}
})
.catch(error => {
console.error('Error submitting form:', error);
showToast('Error', 'Failed to process message', 'error');
});
});
function showToast(title, message, type) {
// Implementation depends on your toast system
console.log(`${type.toUpperCase()}: ${title} - ${message}`);
}
</script>
{% endblock %}