hospital-management/templates/communications/alert_instance_list.html
2025-08-12 13:33:25 +03:00

813 lines
40 KiB
HTML

{% extends "base.html" %}
{% load static %}
{% block title %}Alert Instances - 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">Alert Instances</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">Alert Instances</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 Instances</span>
<h4 class="mb-3">
<span class="counter-value" data-target="{{ total_instances }}">{{ total_instances }}</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-bell 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 Alerts</span>
<h4 class="mb-3">
<span class="counter-value" data-target="{{ active_instances }}">{{ active_instances }}</span>
</h4>
</div>
<div class="flex-shrink-0">
<div class="avatar-sm rounded-circle bg-danger">
<span class="avatar-title rounded-circle bg-danger">
<i class="fas fa-exclamation-triangle 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">Critical Alerts</span>
<h4 class="mb-3">
<span class="counter-value" data-target="{{ critical_instances }}">{{ critical_instances }}</span>
</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-exclamation-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">Avg Response Time</span>
<h4 class="mb-3">
<span class="counter-value" data-target="{{ avg_response_time }}">{{ avg_response_time }}</span>
<small class="text-muted">min</small>
</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-clock text-white font-size-16"></i>
</span>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Alert Instances 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">Alert Instances</h4>
</div>
<div class="col-auto">
<div class="btn-group" role="group">
<button type="button" class="btn btn-outline-primary" onclick="acknowledgeSelected()">
<i class="fas fa-check me-1"></i>
Acknowledge Selected
</button>
<button type="button" class="btn btn-outline-success" onclick="resolveSelected()">
<i class="fas fa-check-circle me-1"></i>
Resolve Selected
</button>
</div>
</div>
</div>
</div>
<div class="card-body">
<!-- Search and Filters -->
<div class="row mb-3">
<div class="col-md-3">
<div class="search-box">
<div class="position-relative">
<input type="text" class="form-control search"
placeholder="Search alerts..."
id="alertSearch">
<i class="bx bx-search-alt search-icon"></i>
</div>
</div>
</div>
<div class="col-md-2">
<select class="form-select" id="statusFilter">
<option value="">All Status</option>
<option value="ACTIVE">Active</option>
<option value="ACKNOWLEDGED">Acknowledged</option>
<option value="RESOLVED">Resolved</option>
<option value="ESCALATED">Escalated</option>
<option value="SNOOZED">Snoozed</option>
</select>
</div>
<div class="col-md-2">
<select class="form-select" id="severityFilter">
<option value="">All Severity</option>
<option value="CRITICAL">Critical</option>
<option value="HIGH">High</option>
<option value="MEDIUM">Medium</option>
<option value="LOW">Low</option>
</select>
</div>
<div class="col-md-2">
<select class="form-select" id="categoryFilter">
<option value="">All Categories</option>
<option value="SYSTEM">System</option>
<option value="PATIENT">Patient</option>
<option value="EQUIPMENT">Equipment</option>
<option value="MEDICATION">Medication</option>
<option value="APPOINTMENT">Appointment</option>
</select>
</div>
<div class="col-md-2">
<select class="form-select" id="timeFilter">
<option value="">All Time</option>
<option value="today">Today</option>
<option value="yesterday">Yesterday</option>
<option value="week">This Week</option>
<option value="month">This Month</option>
</select>
</div>
<div class="col-md-1">
<div class="btn-group" role="group">
<button type="button" class="btn btn-outline-secondary" id="refreshAlerts">
<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="exportAlerts('csv')">CSV</a></li>
<li><a class="dropdown-item" href="#" onclick="exportAlerts('pdf')">PDF Report</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>
<input type="radio" class="btn-check" name="viewMode" id="timelineView">
<label class="btn btn-outline-secondary" for="timelineView">
<i class="fas fa-stream me-1"></i>Timeline
</label>
</div>
</div>
<div class="col-md-6 text-end">
<small class="text-muted">
Auto-refresh: <span id="autoRefreshStatus">ON</span>
<button class="btn btn-sm btn-outline-secondary ms-2" onclick="toggleAutoRefresh()">
<i class="fas fa-pause" id="refreshIcon"></i>
</button>
</small>
</div>
</div>
<!-- Alert Instances Table -->
<div id="alertInstancesList">
<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">Alert</th>
<th scope="col">Rule</th>
<th scope="col">Severity</th>
<th scope="col">Status</th>
<th scope="col">Triggered</th>
<th scope="col">Response Time</th>
<th scope="col">Actions</th>
</tr>
</thead>
<tbody>
{% for instance in object_list %}
<tr class="alert-row" data-severity="{{ instance.alert_rule.severity }}" data-status="{{ instance.status }}">
<td>
<div class="form-check">
<input class="form-check-input" type="checkbox" value="{{ instance.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 instance.alert_rule.severity == 'CRITICAL' %}danger{% elif instance.alert_rule.severity == 'HIGH' %}warning{% elif instance.alert_rule.severity == 'MEDIUM' %}info{% else %}secondary{% endif %} text-{% if instance.alert_rule.severity == 'CRITICAL' %}danger{% elif instance.alert_rule.severity == 'HIGH' %}warning{% elif instance.alert_rule.severity == 'MEDIUM' %}info{% else %}secondary{% endif %} rounded-circle">
<i class="fas fa-{% if instance.alert_rule.severity == 'CRITICAL' %}exclamation-circle{% elif instance.alert_rule.severity == 'HIGH' %}exclamation-triangle{% else %}bell{% endif %}"></i>
</div>
</div>
</div>
<div class="flex-grow-1">
<h6 class="mb-0">
<a href="{% url 'communications:alert_instance_detail' instance.pk %}" class="text-dark">
{{ instance.message|truncatechars:40 }}
</a>
</h6>
<small class="text-muted">{{ instance.alert_rule.get_category_display }}</small>
</div>
</div>
</td>
<td>
<a href="{% url 'communications:alert_rule_detail' instance.alert_rule.pk %}" class="text-primary">
{{ instance.alert_rule.rule_name|truncatechars:25 }}
</a>
</td>
<td>
<span class="badge bg-{% if instance.alert_rule.severity == 'CRITICAL' %}danger{% elif instance.alert_rule.severity == 'HIGH' %}warning{% elif instance.alert_rule.severity == 'MEDIUM' %}info{% else %}secondary{% endif %}">
{{ instance.alert_rule.get_severity_display }}
</span>
</td>
<td>
<span class="badge bg-{% if instance.status == 'RESOLVED' %}success{% elif instance.status == 'ACKNOWLEDGED' %}info{% elif instance.status == 'ESCALATED' %}warning{% elif instance.status == 'SNOOZED' %}secondary{% else %}danger{% endif %}">
{{ instance.get_status_display }}
</span>
{% if instance.status == 'SNOOZED' and instance.snoozed_until %}
<br><small class="text-muted">Until {{ instance.snoozed_until|date:"M d, g:i A" }}</small>
{% endif %}
</td>
<td>
<span class="text-muted">{{ instance.triggered_at|timesince }} ago</span>
<br><small class="text-muted">{{ instance.triggered_at|date:"M d, g:i A" }}</small>
</td>
<td>
{% if instance.acknowledged_at %}
<span class="text-success">{{ instance.response_time_minutes|default:0 }} min</span>
{% else %}
<span class="text-muted">-</span>
{% endif %}
</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:alert_instance_detail' instance.pk %}">
<i class="fas fa-eye me-2"></i>View Details
</a>
{% if instance.status == 'ACTIVE' %}
<a class="dropdown-item" href="#" onclick="acknowledgeAlert({{ instance.id }})">
<i class="fas fa-check me-2"></i>Acknowledge
</a>
<a class="dropdown-item" href="#" onclick="resolveAlert({{ instance.id }})">
<i class="fas fa-check-circle me-2"></i>Resolve
</a>
<a class="dropdown-item" href="#" onclick="snoozeAlert({{ instance.id }})">
<i class="fas fa-clock me-2"></i>Snooze
</a>
<a class="dropdown-item" href="#" onclick="escalateAlert({{ instance.id }})">
<i class="fas fa-arrow-up me-2"></i>Escalate
</a>
{% elif instance.status == 'ACKNOWLEDGED' %}
<a class="dropdown-item" href="#" onclick="resolveAlert({{ instance.id }})">
<i class="fas fa-check-circle me-2"></i>Resolve
</a>
<a class="dropdown-item" href="#" onclick="escalateAlert({{ instance.id }})">
<i class="fas fa-arrow-up me-2"></i>Escalate
</a>
{% elif instance.status == 'SNOOZED' %}
<a class="dropdown-item" href="#" onclick="unsnoozeAlert({{ instance.id }})">
<i class="fas fa-play me-2"></i>Unsnooze
</a>
{% endif %}
<div class="dropdown-divider"></div>
<a class="dropdown-item" href="#" onclick="addComment({{ instance.id }})">
<i class="fas fa-comment me-2"></i>Add Comment
</a>
<a class="dropdown-item" href="#" onclick="viewHistory({{ instance.id }})">
<i class="fas fa-history me-2"></i>View History
</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-bell-slash fa-3x text-muted mb-3"></i>
<h5 class="text-muted">No alert instances found</h5>
<p class="text-muted">No alerts match your current filters</p>
</div>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
<!-- Card View (Hidden by default) -->
<div id="cardViewContent" style="display: none;">
<div class="row">
{% for instance in object_list %}
<div class="col-md-6 col-lg-4 mb-3">
<div class="card alert-card h-100 border-start border-4 border-{% if instance.alert_rule.severity == 'CRITICAL' %}danger{% elif instance.alert_rule.severity == 'HIGH' %}warning{% elif instance.alert_rule.severity == 'MEDIUM' %}info{% else %}secondary{% endif %}">
<div class="card-body">
<div class="d-flex justify-content-between align-items-start mb-2">
<span class="badge bg-{% if instance.status == 'RESOLVED' %}success{% elif instance.status == 'ACKNOWLEDGED' %}info{% elif instance.status == 'ESCALATED' %}warning{% elif instance.status == 'SNOOZED' %}secondary{% else %}danger{% endif %}">
{{ instance.get_status_display }}
</span>
<span class="badge bg-{% if instance.alert_rule.severity == 'CRITICAL' %}danger{% elif instance.alert_rule.severity == 'HIGH' %}warning{% elif instance.alert_rule.severity == 'MEDIUM' %}info{% else %}secondary{% endif %}">
{{ instance.alert_rule.get_severity_display }}
</span>
</div>
<h6 class="card-title">{{ instance.message|truncatechars:50 }}</h6>
<p class="card-text text-muted">{{ instance.alert_rule.rule_name }}</p>
<div class="d-flex justify-content-between align-items-center">
<small class="text-muted">{{ instance.triggered_at|timesince }} ago</small>
<a href="{% url 'communications:alert_instance_detail' instance.pk %}" class="btn btn-sm btn-outline-primary">
View
</a>
</div>
</div>
</div>
</div>
{% endfor %}
</div>
</div>
<!-- Timeline View (Hidden by default) -->
<div id="timelineViewContent" style="display: none;">
<div class="timeline">
{% for instance in object_list %}
<div class="timeline-item">
<div class="timeline-marker bg-{% if instance.alert_rule.severity == 'CRITICAL' %}danger{% elif instance.alert_rule.severity == 'HIGH' %}warning{% elif instance.alert_rule.severity == 'MEDIUM' %}info{% else %}secondary{% endif %}">
<i class="fas fa-bell"></i>
</div>
<div class="timeline-content">
<div class="card">
<div class="card-body">
<div class="d-flex justify-content-between align-items-start">
<div>
<h6 class="mb-1">{{ instance.message }}</h6>
<p class="text-muted mb-1">{{ instance.alert_rule.rule_name }}</p>
<small class="text-muted">{{ instance.triggered_at|date:"M d, Y g:i A" }}</small>
</div>
<div class="text-end">
<span class="badge bg-{% if instance.status == 'RESOLVED' %}success{% elif instance.status == 'ACKNOWLEDGED' %}info{% elif instance.status == 'ESCALATED' %}warning{% elif instance.status == 'SNOOZED' %}secondary{% else %}danger{% endif %}">
{{ instance.get_status_display }}
</span>
<br>
<span class="badge bg-{% if instance.alert_rule.severity == 'CRITICAL' %}danger{% elif instance.alert_rule.severity == 'HIGH' %}warning{% elif instance.alert_rule.severity == 'MEDIUM' %}info{% else %}secondary{% endif %} mt-1">
{{ instance.alert_rule.get_severity_display }}
</span>
</div>
</div>
</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 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-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;
}
.alert-row.table-danger {
background-color: rgba(220, 53, 69, 0.1);
}
.alert-row.table-warning {
background-color: rgba(255, 193, 7, 0.1);
}
</style>
{% endblock %}
{% block js %}
<script>
let autoRefresh = true;
let refreshInterval;
// View mode switching
document.querySelectorAll('input[name="viewMode"]').forEach(radio => {
radio.addEventListener('change', function() {
const tableView = document.getElementById('tableViewContent');
const cardView = document.getElementById('cardViewContent');
const timelineView = document.getElementById('timelineViewContent');
// Hide all views
tableView.style.display = 'none';
cardView.style.display = 'none';
timelineView.style.display = 'none';
// Show selected view
if (this.id === 'tableView') {
tableView.style.display = 'block';
} else if (this.id === 'cardView') {
cardView.style.display = 'block';
} else if (this.id === 'timelineView') {
timelineView.style.display = 'block';
}
});
});
// Search functionality
document.getElementById('alertSearch').addEventListener('input', function() {
const searchTerm = this.value.toLowerCase();
filterAlerts();
});
// Filter functionality
document.getElementById('statusFilter').addEventListener('change', filterAlerts);
document.getElementById('severityFilter').addEventListener('change', filterAlerts);
document.getElementById('categoryFilter').addEventListener('change', filterAlerts);
document.getElementById('timeFilter').addEventListener('change', filterAlerts);
function filterAlerts() {
const searchTerm = document.getElementById('alertSearch').value.toLowerCase();
const status = document.getElementById('statusFilter').value;
const severity = document.getElementById('severityFilter').value;
const category = document.getElementById('categoryFilter').value;
const timeFilter = document.getElementById('timeFilter').value;
const rows = document.querySelectorAll('.alert-row');
rows.forEach(row => {
let show = true;
const text = row.textContent.toLowerCase();
// Search filter
if (searchTerm && !text.includes(searchTerm)) {
show = false;
}
// Status filter
if (status) {
const statusBadge = row.querySelector('td:nth-child(5) .badge');
show = show && statusBadge && statusBadge.textContent.trim().toLowerCase().includes(status.toLowerCase());
}
// Severity filter
if (severity) {
show = show && row.dataset.severity === severity;
}
// Category filter
if (category) {
const categoryText = row.querySelector('td:nth-child(2) small');
show = show && categoryText && categoryText.textContent.trim().toLowerCase().includes(category.toLowerCase());
}
row.style.display = show ? '' : 'none';
});
}
// Alert actions
function acknowledgeAlert(instanceId) {
if (confirm('Acknowledge this alert?')) {
updateAlertStatus(instanceId, 'ACKNOWLEDGED');
}
}
function resolveAlert(instanceId) {
if (confirm('Resolve this alert?')) {
updateAlertStatus(instanceId, 'RESOLVED');
}
}
function snoozeAlert(instanceId) {
const minutes = prompt('Snooze this alert for how many minutes?', '60');
if (minutes && !isNaN(minutes)) {
fetch(`/communications/alert-instances/${instanceId}/snooze/`, {
method: 'POST',
headers: {
'X-CSRFToken': document.querySelector('[name=csrfmiddlewaretoken]').value,
'Content-Type': 'application/json',
},
body: JSON.stringify({ minutes: parseInt(minutes) })
})
.then(response => response.json())
.then(data => {
if (data.success) {
location.reload();
} else {
alert('Error snoozing alert: ' + data.error);
}
});
}
}
function escalateAlert(instanceId) {
if (confirm('Escalate this alert?')) {
updateAlertStatus(instanceId, 'ESCALATED');
}
}
function unsnoozeAlert(instanceId) {
if (confirm('Unsnooze this alert?')) {
updateAlertStatus(instanceId, 'ACTIVE');
}
}
function updateAlertStatus(instanceId, status) {
fetch(`/communications/alert-instances/${instanceId}/update-status/`, {
method: 'POST',
headers: {
'X-CSRFToken': document.querySelector('[name=csrfmiddlewaretoken]').value,
'Content-Type': 'application/json',
},
body: JSON.stringify({ status: status })
})
.then(response => response.json())
.then(data => {
if (data.success) {
location.reload();
} else {
alert('Error updating alert: ' + data.error);
}
});
}
// Bulk actions
function acknowledgeSelected() {
const selected = getSelectedAlerts();
if (selected.length === 0) {
alert('Please select at least one alert.');
return;
}
if (confirm(`Acknowledge ${selected.length} selected alert(s)?`)) {
bulkUpdateStatus(selected, 'ACKNOWLEDGED');
}
}
function resolveSelected() {
const selected = getSelectedAlerts();
if (selected.length === 0) {
alert('Please select at least one alert.');
return;
}
if (confirm(`Resolve ${selected.length} selected alert(s)?`)) {
bulkUpdateStatus(selected, 'RESOLVED');
}
}
function getSelectedAlerts() {
return Array.from(document.querySelectorAll('tbody input[type="checkbox"]:checked'))
.map(cb => cb.value);
}
function bulkUpdateStatus(alertIds, status) {
fetch('/communications/alert-instances/bulk-update-status/', {
method: 'POST',
headers: {
'X-CSRFToken': document.querySelector('[name=csrfmiddlewaretoken]').value,
'Content-Type': 'application/json',
},
body: JSON.stringify({ alert_ids: alertIds, status: status })
})
.then(response => response.json())
.then(data => {
if (data.success) {
location.reload();
} else {
alert('Error updating alerts: ' + data.error);
}
});
}
// Auto-refresh functionality
function toggleAutoRefresh() {
autoRefresh = !autoRefresh;
const status = document.getElementById('autoRefreshStatus');
const icon = document.getElementById('refreshIcon');
if (autoRefresh) {
status.textContent = 'ON';
icon.className = 'fas fa-pause';
startAutoRefresh();
} else {
status.textContent = 'OFF';
icon.className = 'fas fa-play';
stopAutoRefresh();
}
}
function startAutoRefresh() {
refreshInterval = setInterval(() => {
if (autoRefresh) {
location.reload();
}
}, 30000); // Refresh every 30 seconds
}
function stopAutoRefresh() {
if (refreshInterval) {
clearInterval(refreshInterval);
}
}
// Export functionality
function exportAlerts(format) {
const url = `/communications/export/alert-instances/?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 alerts
document.getElementById('refreshAlerts').addEventListener('click', function() {
location.reload();
});
// Additional actions
function addComment(instanceId) {
const comment = prompt('Add a comment to this alert:');
if (comment && comment.trim()) {
fetch(`/communications/alert-instances/${instanceId}/add-comment/`, {
method: 'POST',
headers: {
'X-CSRFToken': document.querySelector('[name=csrfmiddlewaretoken]').value,
'Content-Type': 'application/json',
},
body: JSON.stringify({ comment: comment.trim() })
})
.then(response => response.json())
.then(data => {
if (data.success) {
alert('Comment added successfully');
} else {
alert('Error adding comment: ' + data.error);
}
});
}
}
function viewHistory(instanceId) {
window.open(`/communications/alert-instances/${instanceId}/history/`, '_blank');
}
// Initialize
document.addEventListener('DOMContentLoaded', function() {
if (autoRefresh) {
startAutoRefresh();
}
});
// Cleanup
window.addEventListener('beforeunload', function() {
stopAutoRefresh();
});
</script>
{% endblock %}