576 lines
28 KiB
HTML
576 lines
28 KiB
HTML
{% extends "base.html" %}
|
|
{% load static %}
|
|
|
|
{% block title %}Delivery Log - {{ object.subject|default:object.content|truncatechars:50 }}{% 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">Delivery Log Details</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:delivery_log_list' %}">Delivery Logs</a></li>
|
|
<li class="breadcrumb-item active">{{ object.id }}</li>
|
|
</ol>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="row">
|
|
<!-- Main Content -->
|
|
<div class="col-lg-8">
|
|
<!-- Message Overview -->
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<div class="d-flex align-items-center justify-content-between">
|
|
<div class="d-flex align-items-center">
|
|
<div class="avatar-sm me-3">
|
|
<div class="avatar-title bg-soft-{% if object.channel.channel_type == 'EMAIL' %}primary{% elif object.channel.channel_type == 'SMS' %}success{% elif object.channel.channel_type == 'PUSH' %}warning{% elif object.channel.channel_type == 'WEBHOOK' %}info{% elif object.channel.channel_type == 'SLACK' %}secondary{% else %}dark{% endif %} text-{% if object.channel.channel_type == 'EMAIL' %}primary{% elif object.channel.channel_type == 'SMS' %}success{% elif object.channel.channel_type == 'PUSH' %}warning{% elif object.channel.channel_type == 'WEBHOOK' %}info{% elif object.channel.channel_type == 'SLACK' %}secondary{% else %}dark{% endif %} rounded">
|
|
<i class="fas fa-{% if object.channel.channel_type == 'EMAIL' %}envelope{% elif object.channel.channel_type == 'SMS' %}sms{% elif object.channel.channel_type == 'PUSH' %}bell{% elif object.channel.channel_type == 'WEBHOOK' %}link{% elif object.channel.channel_type == 'SLACK' %}slack{% elif object.channel.channel_type == 'TEAMS' %}microsoft{% else %}broadcast-tower{% endif %}"></i>
|
|
</div>
|
|
</div>
|
|
<div>
|
|
<h5 class="card-title mb-0">{{ object.subject|default:"Message Content"|truncatechars:60 }}</h5>
|
|
<small class="text-muted">{{ object.get_message_type_display|default:"Communication" }}</small>
|
|
</div>
|
|
</div>
|
|
<div>
|
|
<span class="badge bg-{% if object.status == 'DELIVERED' %}success{% elif object.status == 'FAILED' %}danger{% elif object.status == 'PENDING' %}warning{% elif object.status == 'RETRYING' %}info{% else %}secondary{% endif %} fs-6">
|
|
{{ object.get_status_display }}
|
|
</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="card-body">
|
|
<!-- Message Details -->
|
|
<div class="row mb-4">
|
|
<div class="col-md-6">
|
|
<div class="d-flex mb-3">
|
|
<div class="flex-shrink-0">
|
|
<i class="fas fa-user text-muted me-2"></i>
|
|
</div>
|
|
<div class="flex-grow-1">
|
|
<h6 class="mb-0">Recipient</h6>
|
|
<p class="text-muted mb-0">{{ object.recipient }}</p>
|
|
{% if object.recipient_name %}
|
|
<small class="text-muted">{{ object.recipient_name }}</small>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-6">
|
|
<div class="d-flex mb-3">
|
|
<div class="flex-shrink-0">
|
|
<i class="fas fa-broadcast-tower text-muted me-2"></i>
|
|
</div>
|
|
<div class="flex-grow-1">
|
|
<h6 class="mb-0">Channel</h6>
|
|
<p class="text-muted mb-0">
|
|
<a href="{% url 'communications:communication_channel_detail' object.channel.pk %}" class="text-primary">
|
|
{{ object.channel.name }}
|
|
</a>
|
|
</p>
|
|
<small class="text-muted">{{ object.channel.get_channel_type_display }}</small>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="row mb-4">
|
|
<div class="col-md-6">
|
|
<div class="d-flex mb-3">
|
|
<div class="flex-shrink-0">
|
|
<i class="fas fa-clock text-muted me-2"></i>
|
|
</div>
|
|
<div class="flex-grow-1">
|
|
<h6 class="mb-0">Sent At</h6>
|
|
<p class="text-muted mb-0">{{ object.sent_at|date:"M d, Y g:i:s A" }}</p>
|
|
<small class="text-muted">{{ object.sent_at|timesince }} ago</small>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-6">
|
|
<div class="d-flex mb-3">
|
|
<div class="flex-shrink-0">
|
|
<i class="fas fa-tachometer-alt text-muted me-2"></i>
|
|
</div>
|
|
<div class="flex-grow-1">
|
|
<h6 class="mb-0">Delivery Time</h6>
|
|
{% if object.delivery_time %}
|
|
<p class="text-{% if object.delivery_time < 1000 %}success{% elif object.delivery_time < 5000 %}warning{% else %}danger{% endif %} mb-0">
|
|
{{ object.delivery_time }}ms
|
|
</p>
|
|
<small class="text-muted">
|
|
{% if object.delivery_time < 1000 %}Excellent{% elif object.delivery_time < 5000 %}Good{% else %}Slow{% endif %}
|
|
</small>
|
|
{% else %}
|
|
<p class="text-muted mb-0">-</p>
|
|
<small class="text-muted">Not available</small>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{% if object.retry_count > 0 %}
|
|
<div class="row mb-4">
|
|
<div class="col-md-6">
|
|
<div class="d-flex mb-3">
|
|
<div class="flex-shrink-0">
|
|
<i class="fas fa-redo text-muted me-2"></i>
|
|
</div>
|
|
<div class="flex-grow-1">
|
|
<h6 class="mb-0">Retry Count</h6>
|
|
<p class="text-warning mb-0">{{ object.retry_count }} attempt(s)</p>
|
|
{% if object.next_retry_at %}
|
|
<small class="text-muted">Next retry: {{ object.next_retry_at|date:"M d, g:i A" }}</small>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{% if object.last_retry_at %}
|
|
<div class="col-md-6">
|
|
<div class="d-flex mb-3">
|
|
<div class="flex-shrink-0">
|
|
<i class="fas fa-history text-muted me-2"></i>
|
|
</div>
|
|
<div class="flex-grow-1">
|
|
<h6 class="mb-0">Last Retry</h6>
|
|
<p class="text-muted mb-0">{{ object.last_retry_at|date:"M d, Y g:i:s A" }}</p>
|
|
<small class="text-muted">{{ object.last_retry_at|timesince }} ago</small>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
{% endif %}
|
|
|
|
<!-- Message Content -->
|
|
{% if object.subject %}
|
|
<div class="mb-4">
|
|
<h6 class="text-muted mb-2">Subject</h6>
|
|
<div class="p-3 bg-light rounded">
|
|
{{ object.subject }}
|
|
</div>
|
|
</div>
|
|
{% endif %}
|
|
|
|
<div class="mb-4">
|
|
<h6 class="text-muted mb-2">Message Content</h6>
|
|
<div class="p-3 bg-light rounded" style="max-height: 300px; overflow-y: auto;">
|
|
{% if object.content_type == 'HTML' %}
|
|
<div class="border rounded p-2 mb-2">
|
|
<small class="text-muted">HTML Preview:</small>
|
|
<div class="mt-2">{{ object.content|safe }}</div>
|
|
</div>
|
|
<div class="border rounded p-2">
|
|
<small class="text-muted">Raw HTML:</small>
|
|
<pre class="mt-2"><code>{{ object.content|escape }}</code></pre>
|
|
</div>
|
|
{% else %}
|
|
<pre class="mb-0">{{ object.content }}</pre>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Error Information -->
|
|
{% if object.error_message %}
|
|
<div class="mb-4">
|
|
<h6 class="text-danger mb-2">
|
|
<i class="fas fa-exclamation-triangle me-2"></i>
|
|
Error Details
|
|
</h6>
|
|
<div class="alert alert-danger" role="alert">
|
|
<div class="d-flex">
|
|
<div class="flex-shrink-0">
|
|
<i class="fas fa-times-circle fa-lg"></i>
|
|
</div>
|
|
<div class="flex-grow-1 ms-3">
|
|
<h6 class="alert-heading">Delivery Failed</h6>
|
|
<p class="mb-2">{{ object.error_message }}</p>
|
|
{% if object.error_code %}
|
|
<hr>
|
|
<div class="mb-0">
|
|
<small><strong>Error Code:</strong> {{ object.error_code }}</small>
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{% endif %}
|
|
|
|
<!-- Metadata -->
|
|
{% if object.metadata %}
|
|
<div class="mb-4">
|
|
<h6 class="text-muted mb-2">Additional Metadata</h6>
|
|
<div class="p-3 bg-light rounded">
|
|
<pre class="mb-0"><code>{{ object.metadata|pprint }}</code></pre>
|
|
</div>
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Delivery Timeline -->
|
|
{% if delivery_timeline %}
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<h5 class="card-title mb-0">
|
|
<i class="fas fa-history me-2"></i>
|
|
Delivery Timeline
|
|
</h5>
|
|
</div>
|
|
<div class="card-body">
|
|
<div class="timeline">
|
|
{% for event in delivery_timeline %}
|
|
<div class="timeline-item">
|
|
<div class="timeline-marker bg-{% if event.status == 'DELIVERED' %}success{% elif event.status == 'FAILED' %}danger{% elif event.status == 'PENDING' %}warning{% elif event.status == 'RETRYING' %}info{% else %}secondary{% endif %}">
|
|
<i class="fas fa-{% if event.status == 'DELIVERED' %}check{% elif event.status == 'FAILED' %}times{% elif event.status == 'PENDING' %}clock{% elif event.status == 'RETRYING' %}redo{% else %}circle{% endif %}"></i>
|
|
</div>
|
|
<div class="timeline-content">
|
|
<div class="d-flex justify-content-between align-items-start">
|
|
<div>
|
|
<h6 class="mb-1">{{ event.get_status_display }}</h6>
|
|
<p class="text-muted mb-1">{{ event.description }}</p>
|
|
{% if event.error_message %}
|
|
<small class="text-danger">{{ event.error_message }}</small>
|
|
{% endif %}
|
|
</div>
|
|
<small class="text-muted">{{ event.timestamp|date:"M d, g:i:s A" }}</small>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{% endfor %}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
|
|
<!-- Sidebar -->
|
|
<div class="col-lg-4">
|
|
<!-- Quick Actions -->
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<h5 class="card-title mb-0">
|
|
<i class="fas fa-bolt me-2"></i>
|
|
Quick Actions
|
|
</h5>
|
|
</div>
|
|
<div class="card-body">
|
|
<div class="d-grid gap-2">
|
|
{% if object.status == 'FAILED' %}
|
|
<button type="button" class="btn btn-warning" onclick="retryDelivery()">
|
|
<i class="fas fa-redo me-2"></i>
|
|
Retry Delivery
|
|
</button>
|
|
{% endif %}
|
|
|
|
{% if object.channel.channel_type == 'EMAIL' and object.status == 'DELIVERED' %}
|
|
<button type="button" class="btn btn-outline-primary" onclick="viewEmailContent()">
|
|
<i class="fas fa-envelope-open me-2"></i>
|
|
View Email Content
|
|
</button>
|
|
{% endif %}
|
|
|
|
<button type="button" class="btn btn-outline-secondary" onclick="exportLog()">
|
|
<i class="fas fa-download me-2"></i>
|
|
Export Log
|
|
</button>
|
|
|
|
<button type="button" class="btn btn-outline-info" onclick="copyMessageId()">
|
|
<i class="fas fa-copy me-2"></i>
|
|
Copy Message ID
|
|
</button>
|
|
|
|
{% if object.status == 'DELIVERED' %}
|
|
<button type="button" class="btn btn-outline-success" onclick="testSimilarMessage()">
|
|
<i class="fas fa-flask me-2"></i>
|
|
Test Similar Message
|
|
</button>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Message Statistics -->
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<h5 class="card-title mb-0">
|
|
<i class="fas fa-chart-bar me-2"></i>
|
|
Message Statistics
|
|
</h5>
|
|
</div>
|
|
<div class="card-body">
|
|
<div class="row text-center">
|
|
<div class="col-6">
|
|
<div class="p-2">
|
|
<h4 class="mb-1 text-primary">{{ message_stats.total_size|filesizeformat }}</h4>
|
|
<p class="text-muted mb-0">Message Size</p>
|
|
</div>
|
|
</div>
|
|
<div class="col-6">
|
|
<div class="p-2">
|
|
<h4 class="mb-1 text-info">{{ message_stats.attachments_count|default:0 }}</h4>
|
|
<p class="text-muted mb-0">Attachments</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{% if message_stats.delivery_metrics %}
|
|
<hr>
|
|
<div class="row text-center">
|
|
<div class="col-6">
|
|
<div class="p-2">
|
|
<h4 class="mb-1 text-success">{{ message_stats.delivery_metrics.queue_time }}ms</h4>
|
|
<p class="text-muted mb-0">Queue Time</p>
|
|
</div>
|
|
</div>
|
|
<div class="col-6">
|
|
<div class="p-2">
|
|
<h4 class="mb-1 text-warning">{{ message_stats.delivery_metrics.processing_time }}ms</h4>
|
|
<p class="text-muted mb-0">Processing Time</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Channel Information -->
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<h5 class="card-title mb-0">
|
|
<i class="fas fa-broadcast-tower me-2"></i>
|
|
Channel Information
|
|
</h5>
|
|
</div>
|
|
<div class="card-body">
|
|
<div class="d-flex align-items-center mb-3">
|
|
<div class="avatar-sm me-3">
|
|
<div class="avatar-title bg-soft-{% if object.channel.channel_type == 'EMAIL' %}primary{% elif object.channel.channel_type == 'SMS' %}success{% elif object.channel.channel_type == 'PUSH' %}warning{% elif object.channel.channel_type == 'WEBHOOK' %}info{% elif object.channel.channel_type == 'SLACK' %}secondary{% else %}dark{% endif %} text-{% if object.channel.channel_type == 'EMAIL' %}primary{% elif object.channel.channel_type == 'SMS' %}success{% elif object.channel.channel_type == 'PUSH' %}warning{% elif object.channel.channel_type == 'WEBHOOK' %}info{% elif object.channel.channel_type == 'SLACK' %}secondary{% else %}dark{% endif %} rounded">
|
|
<i class="fas fa-{% if object.channel.channel_type == 'EMAIL' %}envelope{% elif object.channel.channel_type == 'SMS' %}sms{% elif object.channel.channel_type == 'PUSH' %}bell{% elif object.channel.channel_type == 'WEBHOOK' %}link{% elif object.channel.channel_type == 'SLACK' %}slack{% elif object.channel.channel_type == 'TEAMS' %}microsoft{% else %}broadcast-tower{% endif %}"></i>
|
|
</div>
|
|
</div>
|
|
<div>
|
|
<h6 class="mb-0">
|
|
<a href="{% url 'communications:communication_channel_detail' object.channel.pk %}" class="text-primary">
|
|
{{ object.channel.name }}
|
|
</a>
|
|
</h6>
|
|
<small class="text-muted">{{ object.channel.get_channel_type_display }}</small>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="row text-center">
|
|
<div class="col-6">
|
|
<div class="p-2">
|
|
<h5 class="mb-1 text-success">{{ channel_stats.success_rate }}%</h5>
|
|
<p class="text-muted mb-0 small">Success Rate</p>
|
|
</div>
|
|
</div>
|
|
<div class="col-6">
|
|
<div class="p-2">
|
|
<h5 class="mb-1 text-info">{{ channel_stats.avg_delivery_time }}ms</h5>
|
|
<p class="text-muted mb-0 small">Avg Delivery</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="mt-3">
|
|
<div class="d-flex justify-content-between align-items-center mb-1">
|
|
<small class="text-muted">Channel Health</small>
|
|
<small class="text-{% if object.channel.health_status == 'HEALTHY' %}success{% elif object.channel.health_status == 'WARNING' %}warning{% else %}danger{% endif %}">
|
|
{{ object.channel.get_health_status_display }}
|
|
</small>
|
|
</div>
|
|
<div class="progress" style="height: 6px;">
|
|
<div class="progress-bar bg-{% if object.channel.health_status == 'HEALTHY' %}success{% elif object.channel.health_status == 'WARNING' %}warning{% else %}danger{% endif %}"
|
|
style="width: {% if object.channel.health_status == 'HEALTHY' %}100{% elif object.channel.health_status == 'WARNING' %}60{% else %}20{% endif %}%"></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Related Messages -->
|
|
{% if related_messages %}
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<h5 class="card-title mb-0">
|
|
<i class="fas fa-link me-2"></i>
|
|
Related Messages
|
|
</h5>
|
|
</div>
|
|
<div class="card-body">
|
|
{% for message in related_messages %}
|
|
<div class="d-flex align-items-center mb-2">
|
|
<div class="flex-shrink-0 me-2">
|
|
<span class="badge bg-{% if message.status == 'DELIVERED' %}success{% elif message.status == 'FAILED' %}danger{% elif message.status == 'PENDING' %}warning{% else %}secondary{% endif %} badge-sm">
|
|
{{ message.get_status_display }}
|
|
</span>
|
|
</div>
|
|
<div class="flex-grow-1">
|
|
<a href="{% url 'communications:delivery_log_detail' message.pk %}" class="text-dark text-decoration-none">
|
|
<small>{{ message.subject|default:message.content|truncatechars:30 }}</small>
|
|
</a>
|
|
<br><small class="text-muted">{{ message.sent_at|timesince }} ago</small>
|
|
</div>
|
|
</div>
|
|
{% endfor %}
|
|
|
|
{% if related_messages|length > 5 %}
|
|
<div class="text-center mt-2">
|
|
<a href="{% url 'communications:delivery_log_list' %}?recipient={{ object.recipient }}" class="btn btn-sm btn-outline-primary">
|
|
View All Messages to {{ object.recipient|truncatechars:20 }}
|
|
</a>
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{% endblock %}
|
|
|
|
{% block css %}
|
|
<style>
|
|
.timeline {
|
|
position: relative;
|
|
padding-left: 30px;
|
|
}
|
|
|
|
.timeline::before {
|
|
content: '';
|
|
position: absolute;
|
|
left: 15px;
|
|
top: 0;
|
|
bottom: 0;
|
|
width: 2px;
|
|
background: #dee2e6;
|
|
}
|
|
|
|
.timeline-item {
|
|
position: relative;
|
|
margin-bottom: 20px;
|
|
}
|
|
|
|
.timeline-item:last-child::before {
|
|
display: none;
|
|
}
|
|
|
|
.timeline-marker {
|
|
position: absolute;
|
|
left: -22px;
|
|
width: 30px;
|
|
height: 30px;
|
|
border-radius: 50%;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
color: white;
|
|
font-size: 12px;
|
|
}
|
|
|
|
.timeline-content {
|
|
margin-left: 20px;
|
|
padding: 15px;
|
|
background: #f8f9fa;
|
|
border-radius: 8px;
|
|
border-left: 3px solid #dee2e6;
|
|
}
|
|
|
|
.badge-sm {
|
|
font-size: 0.65rem;
|
|
padding: 0.2em 0.4em;
|
|
}
|
|
</style>
|
|
{% endblock %}
|
|
|
|
{% block js %}
|
|
<script>
|
|
function retryDelivery() {
|
|
if (confirm('Retry delivery for this message?')) {
|
|
fetch(`{% url 'communications:delivery_log_detail' object.pk %}retry/`, {
|
|
method: 'POST',
|
|
headers: {
|
|
'X-CSRFToken': document.querySelector('[name=csrfmiddlewaretoken]').value,
|
|
'Content-Type': 'application/json',
|
|
}
|
|
})
|
|
.then(response => response.json())
|
|
.then(data => {
|
|
if (data.success) {
|
|
alert('Message queued for retry');
|
|
location.reload();
|
|
} else {
|
|
alert('Error retrying message: ' + data.error);
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
function viewEmailContent() {
|
|
window.open(`{% url 'communications:delivery_log_detail' object.pk %}email-content/`, '_blank');
|
|
}
|
|
|
|
function exportLog() {
|
|
window.open(`{% url 'communications:delivery_log_detail' object.pk %}export/`, '_blank');
|
|
}
|
|
|
|
function copyMessageId() {
|
|
const messageId = '{{ object.id }}';
|
|
navigator.clipboard.writeText(messageId).then(() => {
|
|
alert('Message ID copied to clipboard: ' + messageId);
|
|
}).catch(() => {
|
|
// Fallback for older browsers
|
|
const textArea = document.createElement('textarea');
|
|
textArea.value = messageId;
|
|
document.body.appendChild(textArea);
|
|
textArea.select();
|
|
document.execCommand('copy');
|
|
document.body.removeChild(textArea);
|
|
alert('Message ID copied to clipboard: ' + messageId);
|
|
});
|
|
}
|
|
|
|
function testSimilarMessage() {
|
|
if (confirm('Create a test message with similar content and settings?')) {
|
|
fetch(`{% url 'communications:delivery_log_detail' object.pk %}test-similar/`, {
|
|
method: 'POST',
|
|
headers: {
|
|
'X-CSRFToken': document.querySelector('[name=csrfmiddlewaretoken]').value,
|
|
'Content-Type': 'application/json',
|
|
}
|
|
})
|
|
.then(response => response.json())
|
|
.then(data => {
|
|
if (data.success) {
|
|
alert('Test message created and queued for delivery');
|
|
if (data.test_message_id) {
|
|
window.open(`/communications/delivery-logs/${data.test_message_id}/`, '_blank');
|
|
}
|
|
} else {
|
|
alert('Error creating test message: ' + data.error);
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
// Auto-refresh for pending/retrying messages
|
|
{% if object.status == 'PENDING' or object.status == 'RETRYING' %}
|
|
setTimeout(() => {
|
|
location.reload();
|
|
}, 10000); // Refresh every 10 seconds for pending messages
|
|
{% endif %}
|
|
</script>
|
|
{% endblock %}
|
|
|