377 lines
20 KiB
HTML
377 lines
20 KiB
HTML
{% extends "base.html" %}
|
|
{% load static %}
|
|
|
|
{% block title %}Messages - 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">Messages</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 active">Messages</li>
|
|
</ol>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Statistics Cards -->
|
|
<div class="row">
|
|
<div class="col-xl-3 col-md-6">
|
|
<div class="card card-h-100">
|
|
<div class="card-body">
|
|
<div class="d-flex align-items-center">
|
|
<div class="flex-grow-1">
|
|
<span class="text-muted mb-3 lh-1 d-block text-truncate">Total Messages</span>
|
|
<h4 class="mb-3">
|
|
<span class="counter-value" data-target="{{ total_messages }}">{{ total_messages }}</span>
|
|
</h4>
|
|
</div>
|
|
<div class="flex-shrink-0">
|
|
<div id="mini-chart1" data-colors='["--bs-primary"]' class="apex-charts mb-2"></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="col-xl-3 col-md-6">
|
|
<div class="card card-h-100">
|
|
<div class="card-body">
|
|
<div class="d-flex align-items-center">
|
|
<div class="flex-grow-1">
|
|
<span class="text-muted mb-3 lh-1 d-block text-truncate">Sent Today</span>
|
|
<h4 class="mb-3">
|
|
<span class="counter-value" data-target="{{ messages_today }}">{{ messages_today }}</span>
|
|
</h4>
|
|
</div>
|
|
<div class="flex-shrink-0">
|
|
<div id="mini-chart2" data-colors='["--bs-success"]' class="apex-charts mb-2"></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="col-xl-3 col-md-6">
|
|
<div class="card card-h-100">
|
|
<div class="card-body">
|
|
<div class="d-flex align-items-center">
|
|
<div class="flex-grow-1">
|
|
<span class="text-muted mb-3 lh-1 d-block text-truncate">Pending</span>
|
|
<h4 class="mb-3">
|
|
<span class="counter-value" data-target="{{ pending_messages }}">{{ pending_messages }}</span>
|
|
</h4>
|
|
</div>
|
|
<div class="flex-shrink-0">
|
|
<div id="mini-chart3" data-colors='["--bs-warning"]' class="apex-charts mb-2"></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="col-xl-3 col-md-6">
|
|
<div class="card card-h-100">
|
|
<div class="card-body">
|
|
<div class="d-flex align-items-center">
|
|
<div class="flex-grow-1">
|
|
<span class="text-muted mb-3 lh-1 d-block text-truncate">Failed</span>
|
|
<h4 class="mb-3">
|
|
<span class="counter-value" data-target="{{ failed_messages }}">{{ failed_messages }}</span>
|
|
</h4>
|
|
</div>
|
|
<div class="flex-shrink-0">
|
|
<div id="mini-chart4" data-colors='["--bs-danger"]' class="apex-charts mb-2"></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Messages List -->
|
|
<div class="row">
|
|
<div class="col-12">
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<div class="row align-items-center">
|
|
<div class="col">
|
|
<h4 class="card-title">Messages</h4>
|
|
</div>
|
|
<div class="col-auto">
|
|
<a href="{% url 'communications:message_create' %}" class="btn btn-primary">
|
|
<i class="fas fa-plus me-1"></i>
|
|
Compose Message
|
|
</a>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="card-body">
|
|
<!-- Search and Filters -->
|
|
<div class="row mb-3">
|
|
<div class="col-md-4">
|
|
<div class="search-box">
|
|
<div class="position-relative">
|
|
<input type="text" class="form-control search"
|
|
placeholder="Search messages..."
|
|
id="messageSearch"
|
|
hx-get="{% url 'communications:message_search' %}"
|
|
hx-trigger="keyup changed delay:300ms"
|
|
hx-target="#messageList">
|
|
<i class="bx bx-search-alt search-icon"></i>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-2">
|
|
<select class="form-select" id="messageTypeFilter">
|
|
<option value="">All Types</option>
|
|
<option value="INTERNAL">Internal</option>
|
|
<option value="EMAIL">Email</option>
|
|
<option value="SMS">SMS</option>
|
|
<option value="PUSH">Push</option>
|
|
<option value="SYSTEM">System</option>
|
|
</select>
|
|
</div>
|
|
<div class="col-md-2">
|
|
<select class="form-select" id="statusFilter">
|
|
<option value="">All Status</option>
|
|
<option value="DRAFT">Draft</option>
|
|
<option value="PENDING">Pending</option>
|
|
<option value="SENT">Sent</option>
|
|
<option value="DELIVERED">Delivered</option>
|
|
<option value="FAILED">Failed</option>
|
|
</select>
|
|
</div>
|
|
<div class="col-md-2">
|
|
<select class="form-select" id="priorityFilter">
|
|
<option value="">All Priority</option>
|
|
<option value="LOW">Low</option>
|
|
<option value="NORMAL">Normal</option>
|
|
<option value="HIGH">High</option>
|
|
<option value="URGENT">Urgent</option>
|
|
<option value="CRITICAL">Critical</option>
|
|
</select>
|
|
</div>
|
|
<div class="col-md-2">
|
|
<div class="btn-group" role="group">
|
|
<button type="button" class="btn btn-outline-secondary" id="refreshMessages">
|
|
<i class="fas fa-sync-alt"></i>
|
|
</button>
|
|
<button type="button" class="btn btn-outline-secondary dropdown-toggle" data-bs-toggle="dropdown">
|
|
<i class="fas fa-download"></i>
|
|
</button>
|
|
<ul class="dropdown-menu">
|
|
<li><a class="dropdown-item" href="#" onclick="exportMessages('csv')">CSV</a></li>
|
|
<li><a class="dropdown-item" href="#" onclick="exportMessages('excel')">Excel</a></li>
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Messages Table -->
|
|
<div id="messageList">
|
|
<div class="table-responsive">
|
|
<table class="table table-nowrap table-hover mb-0">
|
|
<thead class="table-light">
|
|
<tr>
|
|
<th scope="col">
|
|
<div class="form-check">
|
|
<input class="form-check-input" type="checkbox" id="checkAll">
|
|
</div>
|
|
</th>
|
|
<th scope="col">Subject</th>
|
|
<th scope="col">Type</th>
|
|
<th scope="col">Priority</th>
|
|
<th scope="col">Recipients</th>
|
|
<th scope="col">Status</th>
|
|
<th scope="col">Created</th>
|
|
<th scope="col">Actions</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{% for message in messages %}
|
|
<tr>
|
|
<td>
|
|
<div class="form-check">
|
|
<input class="form-check-input" type="checkbox" value="{{ message.id }}">
|
|
</div>
|
|
</td>
|
|
<td>
|
|
<div class="d-flex align-items-center">
|
|
<div class="flex-shrink-0 me-2">
|
|
<div class="avatar-xs">
|
|
<div class="avatar-title bg-soft-primary text-primary rounded-circle">
|
|
<i class="fas fa-envelope"></i>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="flex-grow-1">
|
|
<h6 class="mb-0">
|
|
<a href="{% url 'communications:message_detail' message.pk %}" class="text-dark">
|
|
{{ message.subject|truncatechars:50 }}
|
|
</a>
|
|
</h6>
|
|
<small class="text-muted">{{ message.body|truncatechars:80 }}</small>
|
|
</div>
|
|
</div>
|
|
</td>
|
|
<td>
|
|
<span class="badge bg-{% if message.message_type == 'EMAIL' %}info{% elif message.message_type == 'SMS' %}success{% elif message.message_type == 'PUSH' %}warning{% else %}secondary{% endif %}">
|
|
{{ message.get_message_type_display }}
|
|
</span>
|
|
</td>
|
|
<td>
|
|
<span class="badge bg-{% if message.priority == 'CRITICAL' %}danger{% elif message.priority == 'URGENT' %}warning{% elif message.priority == 'HIGH' %}info{% else %}secondary{% endif %}">
|
|
{{ message.get_priority_display }}
|
|
</span>
|
|
</td>
|
|
<td>
|
|
<span class="text-muted">{{ message.messagerecipient_set.count }} recipient{{ message.messagerecipient_set.count|pluralize }}</span>
|
|
</td>
|
|
<td>
|
|
<span class="badge bg-{% if message.status == 'SENT' %}success{% elif message.status == 'DELIVERED' %}primary{% elif message.status == 'FAILED' %}danger{% elif message.status == 'PENDING' %}warning{% else %}secondary{% endif %}">
|
|
{{ message.get_status_display }}
|
|
</span>
|
|
</td>
|
|
<td>
|
|
<span class="text-muted">{{ message.created_at|date:"M d, Y g:i A" }}</span>
|
|
</td>
|
|
<td>
|
|
<div class="dropdown">
|
|
<a href="#" class="dropdown-toggle btn btn-light btn-sm" data-bs-toggle="dropdown">
|
|
<i class="fas fa-ellipsis-v"></i>
|
|
</a>
|
|
<div class="dropdown-menu dropdown-menu-end">
|
|
<a class="dropdown-item" href="{% url 'communications:message_detail' message.pk %}">
|
|
<i class="fas fa-eye me-2"></i>View
|
|
</a>
|
|
{% if message.status == 'DRAFT' %}
|
|
<a class="dropdown-item" href="{% url 'communications:message_update' message.pk %}">
|
|
<i class="fas fa-edit me-2"></i>Edit
|
|
</a>
|
|
<a class="dropdown-item" href="#" onclick="sendMessage({{ message.id }})">
|
|
<i class="fas fa-paper-plane me-2"></i>Send
|
|
</a>
|
|
{% endif %}
|
|
<div class="dropdown-divider"></div>
|
|
<a class="dropdown-item text-danger" href="{% url 'communications:message_delete' message.pk %}">
|
|
<i class="fas fa-trash me-2"></i>Delete
|
|
</a>
|
|
</div>
|
|
</div>
|
|
</td>
|
|
</tr>
|
|
{% empty %}
|
|
<tr>
|
|
<td colspan="8" class="text-center py-4">
|
|
<div class="d-flex flex-column align-items-center">
|
|
<i class="fas fa-inbox fa-3x text-muted mb-3"></i>
|
|
<h5 class="text-muted">No messages found</h5>
|
|
<p class="text-muted">Start by composing your first message</p>
|
|
<a href="{% url 'communications:message_create' %}" class="btn btn-primary">
|
|
<i class="fas fa-plus me-1"></i>Compose Message
|
|
</a>
|
|
</div>
|
|
</td>
|
|
</tr>
|
|
{% endfor %}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
|
|
<!-- Pagination -->
|
|
{% if is_paginated %}
|
|
<div class="row">
|
|
<div class="col-lg-12">
|
|
<ul class="pagination pagination-rounded justify-content-center mt-3 mb-4 pb-1">
|
|
{% if page_obj.has_previous %}
|
|
<li class="page-item">
|
|
<a href="?page={{ page_obj.previous_page_number }}" class="page-link">
|
|
<i class="mdi mdi-chevron-left"></i>
|
|
</a>
|
|
</li>
|
|
{% endif %}
|
|
|
|
{% for num in page_obj.paginator.page_range %}
|
|
{% if page_obj.number == num %}
|
|
<li class="page-item active">
|
|
<span class="page-link">{{ num }}</span>
|
|
</li>
|
|
{% elif num > page_obj.number|add:'-3' and num < page_obj.number|add:'3' %}
|
|
<li class="page-item">
|
|
<a href="?page={{ num }}" class="page-link">{{ num }}</a>
|
|
</li>
|
|
{% endif %}
|
|
{% endfor %}
|
|
|
|
{% if page_obj.has_next %}
|
|
<li class="page-item">
|
|
<a href="?page={{ page_obj.next_page_number }}" class="page-link">
|
|
<i class="mdi mdi-chevron-right"></i>
|
|
</a>
|
|
</li>
|
|
{% endif %}
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{% endblock %}
|
|
|
|
{% block js %}
|
|
<script>
|
|
// Send message function
|
|
function sendMessage(messageId) {
|
|
if (confirm('Send this message now?')) {
|
|
fetch(`/communications/messages/${messageId}/send/`, {
|
|
method: 'POST',
|
|
headers: {
|
|
'X-CSRFToken': document.querySelector('[name=csrfmiddlewaretoken]').value,
|
|
'Content-Type': 'application/json',
|
|
}
|
|
})
|
|
.then(response => response.json())
|
|
.then(data => {
|
|
if (data.success) {
|
|
location.reload();
|
|
} else {
|
|
alert('Error sending message: ' + data.error);
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
// Export messages function
|
|
function exportMessages(format) {
|
|
const url = `/communications/export/messages/?format=${format}`;
|
|
window.open(url, '_blank');
|
|
}
|
|
|
|
// Refresh messages
|
|
document.getElementById('refreshMessages').addEventListener('click', function() {
|
|
location.reload();
|
|
});
|
|
|
|
// Check all functionality
|
|
document.getElementById('checkAll').addEventListener('change', function() {
|
|
const checkboxes = document.querySelectorAll('tbody input[type="checkbox"]');
|
|
checkboxes.forEach(checkbox => {
|
|
checkbox.checked = this.checked;
|
|
});
|
|
});
|
|
</script>
|
|
{% endblock %}
|
|
|