320 lines
10 KiB
HTML
320 lines
10 KiB
HTML
|
|
{% extends "base.html" %}
|
||
|
|
|
||
|
|
{% block title %}Dashboard - Email Alerts System{% endblock %}
|
||
|
|
|
||
|
|
{% block content %}
|
||
|
|
<div class="row">
|
||
|
|
<div class="col-12">
|
||
|
|
<h2 class="mb-4">
|
||
|
|
<i class="fas fa-tachometer-alt me-2"></i>
|
||
|
|
Dashboard
|
||
|
|
</h2>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<!-- Status Cards -->
|
||
|
|
<div class="row mb-4">
|
||
|
|
<div class="col-md-3">
|
||
|
|
<div class="card">
|
||
|
|
<div class="card-body text-center">
|
||
|
|
<i class="fas fa-envelope fa-2x text-primary mb-2"></i>
|
||
|
|
<h5 class="card-title">Email Address</h5>
|
||
|
|
<p class="card-text">{{ config.email_address }}</p>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
<div class="col-md-3">
|
||
|
|
<div class="card">
|
||
|
|
<div class="card-body text-center">
|
||
|
|
<i class="fas fa-clock fa-2x text-warning mb-2"></i>
|
||
|
|
<h5 class="card-title">Time Frames</h5>
|
||
|
|
<p class="card-text">{{ config.time_frames|length }} configured</p>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
<div class="col-md-3">
|
||
|
|
<div class="card">
|
||
|
|
<div class="card-body text-center">
|
||
|
|
<i class="fas fa-calendar-day fa-2x text-info mb-2"></i>
|
||
|
|
<h5 class="card-title">Email Range</h5>
|
||
|
|
<p class="card-text">Last {{ config.email_days_back }} days</p>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
<div class="col-md-3">
|
||
|
|
<div class="card">
|
||
|
|
<div class="card-body text-center">
|
||
|
|
<i class="fas fa-building fa-2x text-success mb-2"></i>
|
||
|
|
<h5 class="card-title">Agency Domains</h5>
|
||
|
|
<p class="card-text">{{ config.agency_domains|length }} domains</p>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<!-- Action Buttons -->
|
||
|
|
<div class="row mb-4">
|
||
|
|
<div class="col-12">
|
||
|
|
<div class="card">
|
||
|
|
<div class="card-body">
|
||
|
|
<h5 class="card-title mb-3">
|
||
|
|
<i class="fas fa-play-circle me-2"></i>
|
||
|
|
System Actions
|
||
|
|
</h5>
|
||
|
|
<div class="row">
|
||
|
|
<div class="col-md-4">
|
||
|
|
<button class="btn btn-primary w-100 mb-2" onclick="testConnection()">
|
||
|
|
<i class="fas fa-wifi me-2"></i>
|
||
|
|
Test Connection
|
||
|
|
</button>
|
||
|
|
</div>
|
||
|
|
<div class="col-md-4">
|
||
|
|
<button class="btn btn-success w-100 mb-2" onclick="processEmails()">
|
||
|
|
<i class="fas fa-envelope-open me-2"></i>
|
||
|
|
Process Emails
|
||
|
|
</button>
|
||
|
|
</div>
|
||
|
|
<div class="col-md-4">
|
||
|
|
<button class="btn btn-info w-100 mb-2" onclick="refreshThreads()">
|
||
|
|
<i class="fas fa-sync-alt me-2"></i>
|
||
|
|
Refresh Threads
|
||
|
|
</button>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<!-- Results Section -->
|
||
|
|
<div class="row">
|
||
|
|
<div class="col-12">
|
||
|
|
<div class="card">
|
||
|
|
<div class="card-body">
|
||
|
|
<h5 class="card-title mb-3">
|
||
|
|
<i class="fas fa-list me-2"></i>
|
||
|
|
Processing Results
|
||
|
|
</h5>
|
||
|
|
<div id="results-container">
|
||
|
|
<div class="text-center text-muted">
|
||
|
|
<i class="fas fa-info-circle fa-2x mb-2"></i>
|
||
|
|
<p>Click "Process Emails" to start processing and view results here.</p>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<!-- Threads Table -->
|
||
|
|
<div class="row mt-4">
|
||
|
|
<div class="col-12">
|
||
|
|
<div class="card">
|
||
|
|
<div class="card-body">
|
||
|
|
<h5 class="card-title mb-3">
|
||
|
|
<i class="fas fa-exclamation-triangle me-2"></i>
|
||
|
|
Threads Needing Alerts
|
||
|
|
</h5>
|
||
|
|
<div id="threads-container">
|
||
|
|
<div class="text-center">
|
||
|
|
<div class="spinner-border text-primary" role="status">
|
||
|
|
<span class="visually-hidden">Loading...</span>
|
||
|
|
</div>
|
||
|
|
<p class="mt-2">Loading threads...</p>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
{% endblock %}
|
||
|
|
|
||
|
|
{% block scripts %}
|
||
|
|
<script>
|
||
|
|
function testConnection() {
|
||
|
|
const button = event.target;
|
||
|
|
const originalText = button.innerHTML;
|
||
|
|
button.innerHTML = '<i class="fas fa-spinner fa-spin me-2"></i>Testing...';
|
||
|
|
button.disabled = true;
|
||
|
|
|
||
|
|
fetch('/test_connection')
|
||
|
|
.then(response => response.json())
|
||
|
|
.then(data => {
|
||
|
|
if (data.status === 'success') {
|
||
|
|
showAlert('success', data.message);
|
||
|
|
} else {
|
||
|
|
showAlert('danger', data.message);
|
||
|
|
}
|
||
|
|
})
|
||
|
|
.catch(error => {
|
||
|
|
showAlert('danger', 'Connection test failed: ' + error.message);
|
||
|
|
})
|
||
|
|
.finally(() => {
|
||
|
|
button.innerHTML = originalText;
|
||
|
|
button.disabled = false;
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
function processEmails() {
|
||
|
|
const button = event.target;
|
||
|
|
const originalText = button.innerHTML;
|
||
|
|
button.innerHTML = '<i class="fas fa-spinner fa-spin me-2"></i>Processing...';
|
||
|
|
button.disabled = true;
|
||
|
|
|
||
|
|
fetch('/process_emails', {
|
||
|
|
method: 'POST',
|
||
|
|
headers: {
|
||
|
|
'Content-Type': 'application/json',
|
||
|
|
}
|
||
|
|
})
|
||
|
|
.then(response => response.json())
|
||
|
|
.then(data => {
|
||
|
|
if (data.status === 'success') {
|
||
|
|
showAlert('success', data.message);
|
||
|
|
updateResults(data.data);
|
||
|
|
} else {
|
||
|
|
showAlert('danger', data.message);
|
||
|
|
}
|
||
|
|
})
|
||
|
|
.catch(error => {
|
||
|
|
showAlert('danger', 'Processing failed: ' + error.message);
|
||
|
|
})
|
||
|
|
.finally(() => {
|
||
|
|
button.innerHTML = originalText;
|
||
|
|
button.disabled = false;
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
function refreshThreads() {
|
||
|
|
const container = document.getElementById('threads-container');
|
||
|
|
container.innerHTML = `
|
||
|
|
<div class="text-center">
|
||
|
|
<div class="spinner-border text-primary" role="status">
|
||
|
|
<span class="visually-hidden">Loading...</span>
|
||
|
|
</div>
|
||
|
|
<p class="mt-2">Loading threads...</p>
|
||
|
|
</div>
|
||
|
|
`;
|
||
|
|
|
||
|
|
fetch('/get_threads')
|
||
|
|
.then(response => response.json())
|
||
|
|
.then(data => {
|
||
|
|
if (data.status === 'success') {
|
||
|
|
updateThreadsTable(data.threads);
|
||
|
|
} else {
|
||
|
|
container.innerHTML = `
|
||
|
|
<div class="alert alert-danger">
|
||
|
|
<i class="fas fa-exclamation-triangle me-2"></i>
|
||
|
|
${data.message}
|
||
|
|
</div>
|
||
|
|
`;
|
||
|
|
}
|
||
|
|
})
|
||
|
|
.catch(error => {
|
||
|
|
container.innerHTML = `
|
||
|
|
<div class="alert alert-danger">
|
||
|
|
<i class="fas fa-exclamation-triangle me-2"></i>
|
||
|
|
Error loading threads: ${error.message}
|
||
|
|
</div>
|
||
|
|
`;
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
function updateResults(data) {
|
||
|
|
const container = document.getElementById('results-container');
|
||
|
|
container.innerHTML = `
|
||
|
|
<div class="row">
|
||
|
|
<div class="col-md-4">
|
||
|
|
<div class="text-center">
|
||
|
|
<h4 class="text-primary">${data.total_emails}</h4>
|
||
|
|
<p class="text-muted">Total Emails</p>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
<div class="col-md-4">
|
||
|
|
<div class="text-center">
|
||
|
|
<h4 class="text-warning">${data.actionable_emails}</h4>
|
||
|
|
<p class="text-muted">Actionable Emails</p>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
<div class="col-md-4">
|
||
|
|
<div class="text-center">
|
||
|
|
<h4 class="text-success">${data.sent_alerts}</h4>
|
||
|
|
<p class="text-muted">Alerts Sent</p>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
`;
|
||
|
|
}
|
||
|
|
|
||
|
|
function updateThreadsTable(threads) {
|
||
|
|
const container = document.getElementById('threads-container');
|
||
|
|
|
||
|
|
if (threads.length === 0) {
|
||
|
|
container.innerHTML = `
|
||
|
|
<div class="text-center text-muted">
|
||
|
|
<i class="fas fa-check-circle fa-2x mb-2"></i>
|
||
|
|
<p>No threads currently need alerts.</p>
|
||
|
|
</div>
|
||
|
|
`;
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
let tableHtml = `
|
||
|
|
<div class="table-responsive">
|
||
|
|
<table class="table table-hover">
|
||
|
|
<thead class="table-light">
|
||
|
|
<tr>
|
||
|
|
<th>Subject</th>
|
||
|
|
<th>Last Message</th>
|
||
|
|
<th>Hours Since</th>
|
||
|
|
<th>Alert Level</th>
|
||
|
|
</tr>
|
||
|
|
</thead>
|
||
|
|
<tbody>
|
||
|
|
`;
|
||
|
|
|
||
|
|
threads.forEach(thread => {
|
||
|
|
const alertClass = thread.alert_level === 3 ? 'danger' :
|
||
|
|
thread.alert_level === 2 ? 'warning' : 'info';
|
||
|
|
const alertText = thread.alert_level === 3 ? 'CRITICAL' :
|
||
|
|
thread.alert_level === 2 ? 'URGENT' : 'NORMAL';
|
||
|
|
|
||
|
|
tableHtml += `
|
||
|
|
<tr>
|
||
|
|
<td><strong>${thread.subject}</strong></td>
|
||
|
|
<td>${thread.last_message}</td>
|
||
|
|
<td>${thread.hours_since} hours</td>
|
||
|
|
<td><span class="badge bg-${alertClass}">${alertText}</span></td>
|
||
|
|
</tr>
|
||
|
|
`;
|
||
|
|
});
|
||
|
|
|
||
|
|
tableHtml += `
|
||
|
|
</tbody>
|
||
|
|
</table>
|
||
|
|
</div>
|
||
|
|
`;
|
||
|
|
|
||
|
|
container.innerHTML = tableHtml;
|
||
|
|
}
|
||
|
|
|
||
|
|
function showAlert(type, message) {
|
||
|
|
const alertHtml = `
|
||
|
|
<div class="alert alert-${type} alert-dismissible fade show" role="alert">
|
||
|
|
${message}
|
||
|
|
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
|
||
|
|
</div>
|
||
|
|
`;
|
||
|
|
|
||
|
|
const container = document.querySelector('.main-content');
|
||
|
|
const alertDiv = document.createElement('div');
|
||
|
|
alertDiv.innerHTML = alertHtml;
|
||
|
|
container.insertBefore(alertDiv.firstElementChild, container.firstChild);
|
||
|
|
}
|
||
|
|
|
||
|
|
// Load threads on page load
|
||
|
|
document.addEventListener('DOMContentLoaded', function() {
|
||
|
|
refreshThreads();
|
||
|
|
});
|
||
|
|
</script>
|
||
|
|
{% endblock %}
|