674 lines
37 KiB
HTML
674 lines
37 KiB
HTML
{% extends "base.html" %}
|
|
{% load static %}
|
|
|
|
{% block title %}Communication Channels - 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">Communication Channels</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">Channels</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 Channels</span>
|
|
<h4 class="mb-3">
|
|
<span class="counter-value" data-target="{{ total_channels }}">{{ total_channels }}</span>
|
|
</h4>
|
|
</div>
|
|
<div class="flex-shrink-0">
|
|
<div class="avatar-sm rounded-circle bg-primary">
|
|
<span class="avatar-title rounded-circle bg-primary">
|
|
<i class="fas fa-broadcast-tower text-white font-size-16"></i>
|
|
</span>
|
|
</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">Active Channels</span>
|
|
<h4 class="mb-3">
|
|
<span class="counter-value" data-target="{{ active_channels }}">{{ active_channels }}</span>
|
|
</h4>
|
|
</div>
|
|
<div class="flex-shrink-0">
|
|
<div class="avatar-sm rounded-circle bg-success">
|
|
<span class="avatar-title rounded-circle bg-success">
|
|
<i class="fas fa-check-circle text-white font-size-16"></i>
|
|
</span>
|
|
</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">Messages 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 class="avatar-sm rounded-circle bg-info">
|
|
<span class="avatar-title rounded-circle bg-info">
|
|
<i class="fas fa-envelope text-white font-size-16"></i>
|
|
</span>
|
|
</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">Delivery Rate</span>
|
|
<h4 class="mb-3">
|
|
<span class="counter-value" data-target="{{ delivery_rate }}">{{ delivery_rate }}</span>
|
|
<small class="text-muted">%</small>
|
|
</h4>
|
|
</div>
|
|
<div class="flex-shrink-0">
|
|
<div class="avatar-sm rounded-circle bg-warning">
|
|
<span class="avatar-title rounded-circle bg-warning">
|
|
<i class="fas fa-chart-line text-white font-size-16"></i>
|
|
</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Communication Channels 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">Communication Channels</h4>
|
|
</div>
|
|
<div class="col-auto">
|
|
<div class="btn-group" role="group">
|
|
<a href="{% url 'communications:communication_channel_create' %}" class="btn btn-primary">
|
|
<i class="fas fa-plus me-1"></i>
|
|
Add Channel
|
|
</a>
|
|
<button type="button" class="btn btn-outline-secondary" onclick="testAllChannels()">
|
|
<i class="fas fa-flask me-1"></i>
|
|
Test All
|
|
</button>
|
|
</div>
|
|
</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 channels..."
|
|
id="channelSearch">
|
|
<i class="bx bx-search-alt search-icon"></i>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-2">
|
|
<select class="form-select" id="typeFilter">
|
|
<option value="">All Types</option>
|
|
<option value="EMAIL">Email</option>
|
|
<option value="SMS">SMS</option>
|
|
<option value="PUSH">Push Notification</option>
|
|
<option value="WEBHOOK">Webhook</option>
|
|
<option value="SLACK">Slack</option>
|
|
<option value="TEAMS">Microsoft Teams</option>
|
|
</select>
|
|
</div>
|
|
<div class="col-md-2">
|
|
<select class="form-select" id="statusFilter">
|
|
<option value="">All Status</option>
|
|
<option value="true">Active</option>
|
|
<option value="false">Inactive</option>
|
|
</select>
|
|
</div>
|
|
<div class="col-md-2">
|
|
<select class="form-select" id="healthFilter">
|
|
<option value="">All Health</option>
|
|
<option value="HEALTHY">Healthy</option>
|
|
<option value="WARNING">Warning</option>
|
|
<option value="ERROR">Error</option>
|
|
</select>
|
|
</div>
|
|
<div class="col-md-2">
|
|
<div class="btn-group" role="group">
|
|
<button type="button" class="btn btn-outline-secondary" id="refreshChannels">
|
|
<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="exportChannels('csv')">CSV</a></li>
|
|
<li><a class="dropdown-item" href="#" onclick="exportChannels('json')">JSON Config</a></li>
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- View Toggle -->
|
|
<div class="row mb-3">
|
|
<div class="col-md-6">
|
|
<div class="btn-group btn-group-sm" role="group">
|
|
<input type="radio" class="btn-check" name="viewMode" id="tableView" checked>
|
|
<label class="btn btn-outline-secondary" for="tableView">
|
|
<i class="fas fa-table me-1"></i>Table
|
|
</label>
|
|
<input type="radio" class="btn-check" name="viewMode" id="cardView">
|
|
<label class="btn btn-outline-secondary" for="cardView">
|
|
<i class="fas fa-th-large me-1"></i>Cards
|
|
</label>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-6 text-end">
|
|
<small class="text-muted">
|
|
Last updated: <span id="lastUpdated">{{ last_updated|date:"g:i A" }}</span>
|
|
</small>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Channels Table -->
|
|
<div id="channelsList">
|
|
<div class="table-responsive" id="tableViewContent">
|
|
<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">Channel</th>
|
|
<th scope="col">Type</th>
|
|
<th scope="col">Status</th>
|
|
<th scope="col">Health</th>
|
|
<th scope="col">Messages</th>
|
|
<th scope="col">Success Rate</th>
|
|
<th scope="col">Actions</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{% for channel in object_list %}
|
|
<tr class="channel-row">
|
|
<td>
|
|
<div class="form-check">
|
|
<input class="form-check-input" type="checkbox" value="{{ channel.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-{% if channel.channel_type == 'EMAIL' %}primary{% elif channel.channel_type == 'SMS' %}success{% elif channel.channel_type == 'PUSH' %}warning{% elif channel.channel_type == 'WEBHOOK' %}info{% elif channel.channel_type == 'SLACK' %}secondary{% else %}dark{% endif %} text-{% if channel.channel_type == 'EMAIL' %}primary{% elif channel.channel_type == 'SMS' %}success{% elif channel.channel_type == 'PUSH' %}warning{% elif channel.channel_type == 'WEBHOOK' %}info{% elif channel.channel_type == 'SLACK' %}secondary{% else %}dark{% endif %} rounded-circle">
|
|
<i class="fas fa-{% if channel.channel_type == 'EMAIL' %}envelope{% elif channel.channel_type == 'SMS' %}sms{% elif channel.channel_type == 'PUSH' %}bell{% elif channel.channel_type == 'WEBHOOK' %}link{% elif channel.channel_type == 'SLACK' %}slack{% elif channel.channel_type == 'TEAMS' %}microsoft{% else %}broadcast-tower{% endif %}"></i>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="flex-grow-1">
|
|
<h6 class="mb-0">
|
|
<a href="{% url 'communications:communication_channel_detail' channel.pk %}" class="text-dark">
|
|
{{ channel.name }}
|
|
</a>
|
|
</h6>
|
|
<small class="text-muted">{{ channel.description|truncatechars:40 }}</small>
|
|
</div>
|
|
</div>
|
|
</td>
|
|
<td>
|
|
<span class="badge bg-{% if channel.channel_type == 'EMAIL' %}primary{% elif channel.channel_type == 'SMS' %}success{% elif channel.channel_type == 'PUSH' %}warning{% elif channel.channel_type == 'WEBHOOK' %}info{% elif channel.channel_type == 'SLACK' %}secondary{% else %}dark{% endif %}">
|
|
{{ channel.get_channel_type_display }}
|
|
</span>
|
|
</td>
|
|
<td>
|
|
<div class="d-flex align-items-center">
|
|
<span class="badge bg-{% if channel.is_active %}success{% else %}secondary{% endif %} me-2">
|
|
{% if channel.is_active %}Active{% else %}Inactive{% endif %}
|
|
</span>
|
|
{% if channel.is_active %}
|
|
<div class="form-check form-switch">
|
|
<input class="form-check-input" type="checkbox" checked
|
|
onchange="toggleChannel({{ channel.id }}, this.checked)">
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
</td>
|
|
<td>
|
|
<span class="badge bg-{% if channel.health_status == 'HEALTHY' %}success{% elif channel.health_status == 'WARNING' %}warning{% else %}danger{% endif %}">
|
|
{{ channel.get_health_status_display|default:"Unknown" }}
|
|
</span>
|
|
{% if channel.last_health_check %}
|
|
<br><small class="text-muted">{{ channel.last_health_check|timesince }} ago</small>
|
|
{% endif %}
|
|
</td>
|
|
<td>
|
|
<div class="text-center">
|
|
<h6 class="mb-0">{{ channel.message_count|default:0 }}</h6>
|
|
<small class="text-muted">total</small>
|
|
</div>
|
|
</td>
|
|
<td>
|
|
<div class="d-flex align-items-center">
|
|
<div class="flex-grow-1">
|
|
<div class="progress progress-sm">
|
|
<div class="progress-bar bg-{% if channel.success_rate >= 95 %}success{% elif channel.success_rate >= 80 %}warning{% else %}danger{% endif %}"
|
|
style="width: {{ channel.success_rate|default:0 }}%"></div>
|
|
</div>
|
|
</div>
|
|
<div class="flex-shrink-0 ms-2">
|
|
<small class="text-muted">{{ channel.success_rate|default:0 }}%</small>
|
|
</div>
|
|
</div>
|
|
</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:communication_channel_detail' channel.pk %}">
|
|
<i class="fas fa-eye me-2"></i>View Details
|
|
</a>
|
|
<a class="dropdown-item" href="{% url 'communications:communication_channel_update' channel.pk %}">
|
|
<i class="fas fa-edit me-2"></i>Edit Channel
|
|
</a>
|
|
<a class="dropdown-item" href="#" onclick="testChannel({{ channel.id }})">
|
|
<i class="fas fa-flask me-2"></i>Test Channel
|
|
</a>
|
|
<a class="dropdown-item" href="#" onclick="viewLogs({{ channel.id }})">
|
|
<i class="fas fa-list me-2"></i>View Logs
|
|
</a>
|
|
<div class="dropdown-divider"></div>
|
|
{% if channel.is_active %}
|
|
<a class="dropdown-item text-warning" href="#" onclick="toggleChannel({{ channel.id }}, false)">
|
|
<i class="fas fa-pause me-2"></i>Disable
|
|
</a>
|
|
{% else %}
|
|
<a class="dropdown-item text-success" href="#" onclick="toggleChannel({{ channel.id }}, true)">
|
|
<i class="fas fa-play me-2"></i>Enable
|
|
</a>
|
|
{% endif %}
|
|
<a class="dropdown-item text-danger" href="{% url 'communications:communication_channel_delete' channel.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-broadcast-tower fa-3x text-muted mb-3"></i>
|
|
<h5 class="text-muted">No communication channels found</h5>
|
|
<p class="text-muted">Create your first communication channel to start sending messages</p>
|
|
<a href="{% url 'communications:communication_channel_create' %}" class="btn btn-primary">
|
|
<i class="fas fa-plus me-1"></i>Add Channel
|
|
</a>
|
|
</div>
|
|
</td>
|
|
</tr>
|
|
{% endfor %}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
|
|
<!-- Card View (Hidden by default) -->
|
|
<div id="cardViewContent" style="display: none;">
|
|
<div class="row">
|
|
{% for channel in object_list %}
|
|
<div class="col-md-6 col-lg-4 mb-3">
|
|
<div class="card channel-card h-100">
|
|
<div class="card-body">
|
|
<div class="d-flex justify-content-between align-items-start mb-3">
|
|
<div class="d-flex align-items-center">
|
|
<div class="avatar-sm me-3">
|
|
<div class="avatar-title bg-soft-{% if channel.channel_type == 'EMAIL' %}primary{% elif channel.channel_type == 'SMS' %}success{% elif channel.channel_type == 'PUSH' %}warning{% elif channel.channel_type == 'WEBHOOK' %}info{% elif channel.channel_type == 'SLACK' %}secondary{% else %}dark{% endif %} text-{% if channel.channel_type == 'EMAIL' %}primary{% elif channel.channel_type == 'SMS' %}success{% elif channel.channel_type == 'PUSH' %}warning{% elif channel.channel_type == 'WEBHOOK' %}info{% elif channel.channel_type == 'SLACK' %}secondary{% else %}dark{% endif %} rounded">
|
|
<i class="fas fa-{% if channel.channel_type == 'EMAIL' %}envelope{% elif channel.channel_type == 'SMS' %}sms{% elif channel.channel_type == 'PUSH' %}bell{% elif channel.channel_type == 'WEBHOOK' %}link{% elif channel.channel_type == 'SLACK' %}slack{% elif channel.channel_type == 'TEAMS' %}microsoft{% else %}broadcast-tower{% endif %}"></i>
|
|
</div>
|
|
</div>
|
|
<div>
|
|
<h6 class="mb-0">{{ channel.name }}</h6>
|
|
<small class="text-muted">{{ channel.get_channel_type_display }}</small>
|
|
</div>
|
|
</div>
|
|
<div class="text-end">
|
|
<span class="badge bg-{% if channel.is_active %}success{% else %}secondary{% endif %} mb-1">
|
|
{% if channel.is_active %}Active{% else %}Inactive{% endif %}
|
|
</span>
|
|
<br>
|
|
<span class="badge bg-{% if channel.health_status == 'HEALTHY' %}success{% elif channel.health_status == 'WARNING' %}warning{% else %}danger{% endif %}">
|
|
{{ channel.get_health_status_display|default:"Unknown" }}
|
|
</span>
|
|
</div>
|
|
</div>
|
|
|
|
<p class="card-text text-muted">{{ channel.description|truncatechars:80 }}</p>
|
|
|
|
<div class="row text-center mb-3">
|
|
<div class="col-6">
|
|
<h6 class="mb-0">{{ channel.message_count|default:0 }}</h6>
|
|
<small class="text-muted">Messages</small>
|
|
</div>
|
|
<div class="col-6">
|
|
<h6 class="mb-0">{{ channel.success_rate|default:0 }}%</h6>
|
|
<small class="text-muted">Success Rate</small>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="d-flex gap-2">
|
|
<a href="{% url 'communications:communication_channel_detail' channel.pk %}" class="btn btn-sm btn-outline-primary flex-fill">
|
|
View
|
|
</a>
|
|
<button class="btn btn-sm btn-outline-secondary" onclick="testChannel({{ channel.id }})">
|
|
<i class="fas fa-flask"></i>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{% endfor %}
|
|
</div>
|
|
</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>
|
|
// View mode switching
|
|
document.querySelectorAll('input[name="viewMode"]').forEach(radio => {
|
|
radio.addEventListener('change', function() {
|
|
const tableView = document.getElementById('tableViewContent');
|
|
const cardView = document.getElementById('cardViewContent');
|
|
|
|
if (this.id === 'tableView') {
|
|
tableView.style.display = 'block';
|
|
cardView.style.display = 'none';
|
|
} else {
|
|
tableView.style.display = 'none';
|
|
cardView.style.display = 'block';
|
|
}
|
|
});
|
|
});
|
|
|
|
// Search functionality
|
|
document.getElementById('channelSearch').addEventListener('input', function() {
|
|
filterChannels();
|
|
});
|
|
|
|
// Filter functionality
|
|
document.getElementById('typeFilter').addEventListener('change', filterChannels);
|
|
document.getElementById('statusFilter').addEventListener('change', filterChannels);
|
|
document.getElementById('healthFilter').addEventListener('change', filterChannels);
|
|
|
|
function filterChannels() {
|
|
const searchTerm = document.getElementById('channelSearch').value.toLowerCase();
|
|
const type = document.getElementById('typeFilter').value;
|
|
const status = document.getElementById('statusFilter').value;
|
|
const health = document.getElementById('healthFilter').value;
|
|
|
|
const rows = document.querySelectorAll('.channel-row');
|
|
|
|
rows.forEach(row => {
|
|
let show = true;
|
|
const text = row.textContent.toLowerCase();
|
|
|
|
// Search filter
|
|
if (searchTerm && !text.includes(searchTerm)) {
|
|
show = false;
|
|
}
|
|
|
|
// Type filter
|
|
if (type) {
|
|
const typeBadge = row.querySelector('td:nth-child(3) .badge');
|
|
show = show && typeBadge && typeBadge.textContent.trim().toLowerCase().includes(type.toLowerCase());
|
|
}
|
|
|
|
// Status filter
|
|
if (status !== '') {
|
|
const statusBadge = row.querySelector('td:nth-child(4) .badge');
|
|
const isActive = statusBadge && statusBadge.textContent.includes('Active');
|
|
show = show && ((status === 'true' && isActive) || (status === 'false' && !isActive));
|
|
}
|
|
|
|
// Health filter
|
|
if (health) {
|
|
const healthBadge = row.querySelector('td:nth-child(5) .badge');
|
|
show = show && healthBadge && healthBadge.textContent.trim().toLowerCase().includes(health.toLowerCase());
|
|
}
|
|
|
|
row.style.display = show ? '' : 'none';
|
|
});
|
|
}
|
|
|
|
// Channel actions
|
|
function toggleChannel(channelId, activate) {
|
|
const action = activate ? 'enable' : 'disable';
|
|
|
|
fetch(`/communications/channels/${channelId}/toggle/`, {
|
|
method: 'POST',
|
|
headers: {
|
|
'X-CSRFToken': document.querySelector('[name=csrfmiddlewaretoken]').value,
|
|
'Content-Type': 'application/json',
|
|
},
|
|
body: JSON.stringify({ active: activate })
|
|
})
|
|
.then(response => response.json())
|
|
.then(data => {
|
|
if (data.success) {
|
|
location.reload();
|
|
} else {
|
|
alert(`Error ${action}ing channel: ` + data.error);
|
|
}
|
|
});
|
|
}
|
|
|
|
function testChannel(channelId) {
|
|
if (confirm('Send a test message through this channel?')) {
|
|
fetch(`/communications/channels/${channelId}/test/`, {
|
|
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 sent successfully!\n\nDelivery time: ${data.delivery_time}ms\nStatus: ${data.status}`);
|
|
} else {
|
|
alert('Test failed: ' + data.error);
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
function testAllChannels() {
|
|
if (confirm('Send test messages through all active channels?')) {
|
|
fetch('/communications/channels/test-all/', {
|
|
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 completed!\n\nSuccessful: ${data.successful}\nFailed: ${data.failed}\nTotal: ${data.total}`);
|
|
location.reload();
|
|
} else {
|
|
alert('Test failed: ' + data.error);
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
function viewLogs(channelId) {
|
|
window.open(`/communications/channels/${channelId}/logs/`, '_blank');
|
|
}
|
|
|
|
function exportChannels(format) {
|
|
const url = `/communications/export/channels/?format=${format}`;
|
|
window.open(url, '_blank');
|
|
}
|
|
|
|
// Check all functionality
|
|
document.getElementById('checkAll').addEventListener('change', function() {
|
|
const checkboxes = document.querySelectorAll('tbody input[type="checkbox"]');
|
|
checkboxes.forEach(checkbox => {
|
|
checkbox.checked = this.checked;
|
|
});
|
|
});
|
|
|
|
// Refresh channels
|
|
document.getElementById('refreshChannels').addEventListener('click', function() {
|
|
location.reload();
|
|
});
|
|
|
|
// Bulk actions
|
|
function bulkAction(action) {
|
|
const selected = Array.from(document.querySelectorAll('tbody input[type="checkbox"]:checked'))
|
|
.map(cb => cb.value);
|
|
|
|
if (selected.length === 0) {
|
|
alert('Please select at least one channel.');
|
|
return;
|
|
}
|
|
|
|
if (confirm(`${action} ${selected.length} selected channel(s)?`)) {
|
|
fetch(`/communications/channels/bulk-${action}/`, {
|
|
method: 'POST',
|
|
headers: {
|
|
'X-CSRFToken': document.querySelector('[name=csrfmiddlewaretoken]').value,
|
|
'Content-Type': 'application/json',
|
|
},
|
|
body: JSON.stringify({ channel_ids: selected })
|
|
})
|
|
.then(response => response.json())
|
|
.then(data => {
|
|
if (data.success) {
|
|
location.reload();
|
|
} else {
|
|
alert(`Error performing bulk ${action}: ` + data.error);
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
// Auto-refresh health status
|
|
setInterval(function() {
|
|
fetch('/communications/channels/health-status/', {
|
|
method: 'GET',
|
|
headers: {
|
|
'X-CSRFToken': document.querySelector('[name=csrfmiddlewaretoken]').value,
|
|
}
|
|
})
|
|
.then(response => response.json())
|
|
.then(data => {
|
|
if (data.success) {
|
|
// Update health status badges
|
|
data.channels.forEach(channel => {
|
|
const row = document.querySelector(`input[value="${channel.id}"]`).closest('tr');
|
|
const healthBadge = row.querySelector('td:nth-child(5) .badge');
|
|
if (healthBadge) {
|
|
healthBadge.className = `badge bg-${channel.health_status === 'HEALTHY' ? 'success' : channel.health_status === 'WARNING' ? 'warning' : 'danger'}`;
|
|
healthBadge.textContent = channel.health_status;
|
|
}
|
|
});
|
|
|
|
// Update last updated time
|
|
document.getElementById('lastUpdated').textContent = new Date().toLocaleTimeString();
|
|
}
|
|
});
|
|
}, 60000); // Update every minute
|
|
</script>
|
|
{% endblock %}
|
|
|