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

588 lines
29 KiB
HTML

{% extends "base.html" %}
{% load static %}
{% block title %}{{ object.rule_name }} - Alert Rule Details{% 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 Rule 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:alert_rule_list' %}">Alert Rules</a></li>
<li class="breadcrumb-item active">Details</li>
</ol>
</div>
</div>
</div>
</div>
<div class="row">
<!-- Main Content -->
<div class="col-lg-8">
<!-- Alert Rule Overview -->
<div class="card">
<div class="card-header">
<div class="d-flex justify-content-between align-items-center">
<h5 class="card-title mb-0">
<i class="fas fa-exclamation-triangle me-2"></i>
{{ object.rule_name }}
</h5>
<div class="d-flex gap-2">
<span class="badge bg-{% if object.category == 'SYSTEM' %}primary{% elif object.category == 'PATIENT' %}success{% elif object.category == 'EQUIPMENT' %}warning{% elif object.category == 'MEDICATION' %}info{% else %}secondary{% endif %} fs-6">
{{ object.get_category_display }}
</span>
<span class="badge bg-{% if object.severity == 'CRITICAL' %}danger{% elif object.severity == 'HIGH' %}warning{% elif object.severity == 'MEDIUM' %}info{% else %}secondary{% endif %} fs-6">
{{ object.get_severity_display }}
</span>
<span class="badge bg-{% if object.is_active %}success{% else %}secondary{% endif %} fs-6">
{% if object.is_active %}Active{% else %}Inactive{% endif %}
</span>
</div>
</div>
</div>
<div class="card-body">
<!-- Rule Information -->
<div class="row mb-4">
<div class="col-md-6">
<div class="table-responsive">
<table class="table table-borderless">
<tr>
<td class="fw-bold text-muted">Rule Name:</td>
<td>{{ object.rule_name }}</td>
</tr>
<tr>
<td class="fw-bold text-muted">Category:</td>
<td>{{ object.get_category_display }}</td>
</tr>
<tr>
<td class="fw-bold text-muted">Severity:</td>
<td>{{ object.get_severity_display }}</td>
</tr>
<tr>
<td class="fw-bold text-muted">Event Type:</td>
<td>{{ object.event_type|default:"Any" }}</td>
</tr>
</table>
</div>
</div>
<div class="col-md-6">
<div class="table-responsive">
<table class="table table-borderless">
<tr>
<td class="fw-bold text-muted">Created:</td>
<td>{{ object.created_at|date:"M d, Y g:i A" }}</td>
</tr>
<tr>
<td class="fw-bold text-muted">Last Updated:</td>
<td>{{ object.updated_at|date:"M d, Y g:i A" }}</td>
</tr>
<tr>
<td class="fw-bold text-muted">Last Triggered:</td>
<td>
{% if object.last_triggered %}
{{ object.last_triggered|date:"M d, Y g:i A" }}
{% else %}
<span class="text-muted">Never</span>
{% endif %}
</td>
</tr>
<tr>
<td class="fw-bold text-muted">Trigger Count:</td>
<td>{{ object.trigger_count|default:0 }} times</td>
</tr>
</table>
</div>
</div>
</div>
<!-- Description -->
{% if object.description %}
<div class="mb-4">
<h6 class="text-muted mb-2">Description:</h6>
<div class="bg-light p-3 rounded">
{{ object.description|linebreaks }}
</div>
</div>
{% endif %}
<!-- Condition Expression -->
<div class="mb-4">
<h6 class="text-muted mb-2">Condition Expression:</h6>
<div class="bg-dark p-3 rounded">
<code class="text-light">{{ object.condition_expression }}</code>
</div>
<small class="text-muted">This condition is evaluated to determine when the alert should trigger</small>
</div>
<!-- Condition Parameters -->
{% if object.condition_parameters %}
<div class="mb-4">
<h6 class="text-muted mb-2">Condition Parameters:</h6>
<div class="bg-light p-3 rounded">
<pre class="mb-0">{{ object.condition_parameters|pprint }}</pre>
</div>
</div>
{% endif %}
<!-- Notification Settings -->
<div class="mb-4">
<h6 class="text-muted mb-2">Notification Settings:</h6>
<div class="row">
<div class="col-md-6">
<div class="table-responsive">
<table class="table table-borderless table-sm">
<tr>
<td class="fw-bold text-muted">Cooldown Period:</td>
<td>{{ object.cooldown_minutes|default:0 }} minutes</td>
</tr>
<tr>
<td class="fw-bold text-muted">Max Triggers/Day:</td>
<td>{{ object.max_triggers_per_day|default:"Unlimited" }}</td>
</tr>
<tr>
<td class="fw-bold text-muted">Auto Resolve:</td>
<td>
{% if object.auto_resolve %}
<span class="text-success"><i class="fas fa-check me-1"></i>Enabled</span>
{% else %}
<span class="text-muted"><i class="fas fa-times me-1"></i>Disabled</span>
{% endif %}
</td>
</tr>
</table>
</div>
</div>
<div class="col-md-6">
<div class="table-responsive">
<table class="table table-borderless table-sm">
<tr>
<td class="fw-bold text-muted">Escalation:</td>
<td>
{% if object.escalation_enabled %}
<span class="text-warning"><i class="fas fa-arrow-up me-1"></i>Enabled</span>
{% else %}
<span class="text-muted"><i class="fas fa-times me-1"></i>Disabled</span>
{% endif %}
</td>
</tr>
<tr>
<td class="fw-bold text-muted">Escalation Time:</td>
<td>{{ object.escalation_minutes|default:"N/A" }} minutes</td>
</tr>
<tr>
<td class="fw-bold text-muted">Snooze Enabled:</td>
<td>
{% if object.snooze_enabled %}
<span class="text-info"><i class="fas fa-clock me-1"></i>Enabled</span>
{% else %}
<span class="text-muted"><i class="fas fa-times me-1"></i>Disabled</span>
{% endif %}
</td>
</tr>
</table>
</div>
</div>
</div>
</div>
<!-- Recipients -->
{% if object.recipients.exists %}
<div class="mb-4">
<h6 class="text-muted mb-2">Alert Recipients:</h6>
<div class="row">
{% for recipient in object.recipients.all %}
<div class="col-md-6 mb-2">
<div class="d-flex align-items-center bg-light p-2 rounded">
<div class="avatar-xs me-2">
<div class="avatar-title bg-soft-primary text-primary rounded-circle">
{{ recipient.first_name.0 }}{{ recipient.last_name.0 }}
</div>
</div>
<div>
<h6 class="mb-0">{{ recipient.get_full_name }}</h6>
<small class="text-muted">{{ recipient.email }}</small>
</div>
</div>
</div>
{% endfor %}
</div>
</div>
{% endif %}
<!-- Metadata -->
{% if object.metadata %}
<div class="mb-4">
<h6 class="text-muted mb-2">Additional Metadata:</h6>
<div class="bg-light p-3 rounded">
<pre class="mb-0">{{ object.metadata|pprint }}</pre>
</div>
</div>
{% endif %}
</div>
</div>
<!-- Recent Alert Instances -->
{% if recent_instances %}
<div class="card">
<div class="card-header">
<h5 class="card-title mb-0">
<i class="fas fa-history me-2"></i>
Recent Alert Instances
</h5>
</div>
<div class="card-body">
<div class="table-responsive">
<table class="table table-nowrap table-hover mb-0">
<thead class="table-light">
<tr>
<th>Triggered</th>
<th>Status</th>
<th>Message</th>
<th>Acknowledged</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
{% for instance in recent_instances %}
<tr>
<td>{{ instance.triggered_at|date:"M d, Y g:i A" }}</td>
<td>
<span class="badge bg-{% if instance.status == 'RESOLVED' %}success{% elif instance.status == 'ACKNOWLEDGED' %}info{% elif instance.status == 'ESCALATED' %}warning{% else %}danger{% endif %}">
{{ instance.get_status_display }}
</span>
</td>
<td>{{ instance.message|truncatechars:50 }}</td>
<td>
{% if instance.acknowledged_at %}
<span class="text-success">
<i class="fas fa-check me-1"></i>
{{ instance.acknowledged_at|date:"M d, g:i A" }}
</span>
{% else %}
<span class="text-muted">-</span>
{% endif %}
</td>
<td>
<a href="{% url 'communications:alert_instance_detail' instance.pk %}" class="btn btn-sm btn-outline-primary">
<i class="fas fa-eye"></i>
</a>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
<div class="mt-3">
<a href="{% url 'communications:alert_instance_list' %}?rule={{ object.id }}" class="btn btn-outline-secondary">
View All Instances
</a>
</div>
</div>
</div>
{% endif %}
<!-- Rule Performance -->
<div class="card">
<div class="card-header">
<h5 class="card-title mb-0">
<i class="fas fa-chart-line me-2"></i>
Performance Metrics
</h5>
</div>
<div class="card-body">
<div class="row">
<div class="col-md-3">
<div class="text-center p-3 bg-light rounded">
<i class="fas fa-bell fa-2x text-primary mb-2"></i>
<h6>{{ object.trigger_count|default:0 }}</h6>
<small class="text-muted">Total Triggers</small>
</div>
</div>
<div class="col-md-3">
<div class="text-center p-3 bg-light rounded">
<i class="fas fa-calendar-week fa-2x text-success mb-2"></i>
<h6>{{ weekly_triggers|default:0 }}</h6>
<small class="text-muted">This Week</small>
</div>
</div>
<div class="col-md-3">
<div class="text-center p-3 bg-light rounded">
<i class="fas fa-percentage fa-2x text-info mb-2"></i>
<h6>{{ accuracy_rate|default:0 }}%</h6>
<small class="text-muted">Accuracy Rate</small>
</div>
</div>
<div class="col-md-3">
<div class="text-center p-3 bg-light rounded">
<i class="fas fa-clock fa-2x text-warning mb-2"></i>
<h6>{{ avg_response_time|default:0 }}s</h6>
<small class="text-muted">Avg Response</small>
</div>
</div>
</div>
</div>
</div>
</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">
<button class="btn btn-success" onclick="testRule()">
<i class="fas fa-flask me-1"></i>
Test Rule
</button>
<a href="{% url 'communications:alert_rule_update' object.pk %}" class="btn btn-primary">
<i class="fas fa-edit me-1"></i>
Edit Rule
</a>
<hr>
{% if object.is_active %}
<button class="btn btn-outline-warning" onclick="toggleRule(false)">
<i class="fas fa-pause me-1"></i>
Disable Rule
</button>
{% else %}
<button class="btn btn-outline-success" onclick="toggleRule(true)">
<i class="fas fa-play me-1"></i>
Enable Rule
</button>
{% endif %}
<button class="btn btn-outline-secondary" onclick="duplicateRule()">
<i class="fas fa-copy me-1"></i>
Duplicate Rule
</button>
<button class="btn btn-outline-info" onclick="exportRule()">
<i class="fas fa-download me-1"></i>
Export Rule
</button>
<hr>
{% if object.snooze_enabled %}
<button class="btn btn-outline-secondary" onclick="snoozeRule()">
<i class="fas fa-clock me-1"></i>
Snooze Rule
</button>
{% endif %}
<a href="{% url 'communications:alert_rule_delete' object.pk %}" class="btn btn-outline-danger">
<i class="fas fa-trash me-1"></i>
Delete Rule
</a>
</div>
</div>
</div>
<!-- Rule Status -->
<div class="card">
<div class="card-header">
<h5 class="card-title mb-0">
<i class="fas fa-info-circle me-2"></i>
Rule Status
</h5>
</div>
<div class="card-body">
<div class="table-responsive">
<table class="table table-borderless table-sm">
<tr>
<td class="fw-bold text-muted">Current Status:</td>
<td>
{% if object.is_active %}
<span class="text-success"><i class="fas fa-check-circle me-1"></i>Active</span>
{% else %}
<span class="text-secondary"><i class="fas fa-pause-circle me-1"></i>Inactive</span>
{% endif %}
</td>
</tr>
<tr>
<td class="fw-bold text-muted">Health Status:</td>
<td>
<span class="text-success"><i class="fas fa-heart me-1"></i>Healthy</span>
</td>
</tr>
<tr>
<td class="fw-bold text-muted">Last Check:</td>
<td>{{ last_evaluation|default:"Never"|date:"M d, g:i A" }}</td>
</tr>
<tr>
<td class="fw-bold text-muted">Next Check:</td>
<td>{{ next_evaluation|default:"N/A"|date:"M d, g:i A" }}</td>
</tr>
</table>
</div>
</div>
</div>
<!-- Rule Configuration -->
<div class="card">
<div class="card-header">
<h5 class="card-title mb-0">
<i class="fas fa-cog me-2"></i>
Configuration Summary
</h5>
</div>
<div class="card-body">
<div class="table-responsive">
<table class="table table-borderless table-sm">
<tr>
<td class="fw-bold text-muted">Evaluation Frequency:</td>
<td>{{ object.evaluation_frequency|default:"Real-time" }}</td>
</tr>
<tr>
<td class="fw-bold text-muted">Notification Channels:</td>
<td>{{ object.notification_channels.count|default:0 }}</td>
</tr>
<tr>
<td class="fw-bold text-muted">Recipients:</td>
<td>{{ object.recipients.count|default:0 }}</td>
</tr>
<tr>
<td class="fw-bold text-muted">Template:</td>
<td>
{% if object.notification_template %}
<a href="{% url 'communications:notification_template_detail' object.notification_template.pk %}">
{{ object.notification_template.template_name }}
</a>
{% else %}
<span class="text-muted">Default</span>
{% endif %}
</td>
</tr>
</table>
</div>
</div>
</div>
<!-- Related Rules -->
{% if related_rules %}
<div class="card">
<div class="card-header">
<h5 class="card-title mb-0">
<i class="fas fa-link me-2"></i>
Related Rules
</h5>
</div>
<div class="card-body">
{% for rule in related_rules %}
<div class="d-flex align-items-center mb-2">
<div class="avatar-xs me-2">
<div class="avatar-title bg-soft-primary text-primary rounded">
<i class="fas fa-exclamation-triangle"></i>
</div>
</div>
<div class="flex-grow-1">
<h6 class="mb-0">
<a href="{% url 'communications:alert_rule_detail' rule.pk %}">
{{ rule.rule_name|truncatechars:25 }}
</a>
</h6>
<small class="text-muted">{{ rule.get_category_display }}</small>
</div>
</div>
{% endfor %}
</div>
</div>
{% endif %}
</div>
</div>
</div>
{% endblock %}
{% block js %}
<script>
function testRule() {
if (confirm('Test this alert rule with current system data?')) {
fetch(`{% url 'communications:alert_rule_detail' object.pk %}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 completed!\n\nResult: ${data.result}\nCondition met: ${data.condition_met ? 'Yes' : 'No'}\nExecution time: ${data.execution_time}ms`);
} else {
alert('Error testing rule: ' + data.error);
}
});
}
}
function toggleRule(activate) {
const action = activate ? 'enable' : 'disable';
if (confirm(`${action.charAt(0).toUpperCase() + action.slice(1)} this alert rule?`)) {
fetch(`{% url 'communications:alert_rule_detail' object.pk %}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 rule: ` + data.error);
}
});
}
}
function duplicateRule() {
if (confirm('Create a copy of this alert rule?')) {
window.location.href = '{% url "communications:alert_rule_create" %}?duplicate={{ object.id }}';
}
}
function exportRule() {
window.open(`{% url 'communications:alert_rule_detail' object.pk %}export/`, '_blank');
}
function snoozeRule() {
const minutes = prompt('Snooze this rule for how many minutes?', '60');
if (minutes && !isNaN(minutes)) {
fetch(`{% url 'communications:alert_rule_detail' object.pk %}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) {
alert(`Rule snoozed for ${minutes} minutes`);
location.reload();
} else {
alert('Error snoozing rule: ' + data.error);
}
});
}
}
</script>
{% endblock %}