first commit
This commit is contained in:
@@ -0,0 +1,116 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>{% block title %}Email Alerts System{% endblock %}</title>
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css" rel="stylesheet">
|
||||
<style>
|
||||
.sidebar {
|
||||
min-height: 100vh;
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
}
|
||||
.sidebar .nav-link {
|
||||
color: rgba(255,255,255,0.8);
|
||||
border-radius: 8px;
|
||||
margin: 2px 0;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
.sidebar .nav-link:hover {
|
||||
color: white;
|
||||
background-color: rgba(255,255,255,0.1);
|
||||
}
|
||||
.sidebar .nav-link.active {
|
||||
background-color: rgba(255,255,255,0.2);
|
||||
color: white;
|
||||
}
|
||||
.main-content {
|
||||
background-color: #f8f9fa;
|
||||
min-height: 100vh;
|
||||
}
|
||||
.card {
|
||||
border: none;
|
||||
border-radius: 15px;
|
||||
box-shadow: 0 4px 6px rgba(0,0,0,0.1);
|
||||
transition: transform 0.2s ease;
|
||||
}
|
||||
.card:hover {
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
.btn-primary {
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
border: none;
|
||||
border-radius: 8px;
|
||||
}
|
||||
.btn-primary:hover {
|
||||
background: linear-gradient(135deg, #5a6fd8 0%, #6a4190 100%);
|
||||
}
|
||||
.alert {
|
||||
border-radius: 10px;
|
||||
border: none;
|
||||
}
|
||||
.table {
|
||||
border-radius: 10px;
|
||||
overflow: hidden;
|
||||
}
|
||||
.form-control, .form-select {
|
||||
border-radius: 8px;
|
||||
border: 1px solid #e0e0e0;
|
||||
}
|
||||
.form-control:focus, .form-select:focus {
|
||||
border-color: #667eea;
|
||||
box-shadow: 0 0 0 0.2rem rgba(102, 126, 234, 0.25);
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<!-- Sidebar -->
|
||||
<div class="col-md-3 col-lg-2 px-0">
|
||||
<div class="sidebar p-3">
|
||||
<div class="text-center mb-4">
|
||||
<h4 class="text-white">
|
||||
<i class="fas fa-envelope-open-text me-2"></i>
|
||||
Email Alerts
|
||||
</h4>
|
||||
</div>
|
||||
<nav class="nav flex-column">
|
||||
<a class="nav-link {% if request.endpoint == 'index' %}active{% endif %}" href="{{ url_for('index') }}">
|
||||
<i class="fas fa-tachometer-alt me-2"></i>
|
||||
Dashboard
|
||||
</a>
|
||||
<a class="nav-link {% if request.endpoint == 'settings' %}active{% endif %}" href="{{ url_for('settings') }}">
|
||||
<i class="fas fa-cog me-2"></i>
|
||||
Settings
|
||||
</a>
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Main Content -->
|
||||
<div class="col-md-9 col-lg-10">
|
||||
<div class="main-content p-4">
|
||||
{% with messages = get_flashed_messages(with_categories=true) %}
|
||||
{% if messages %}
|
||||
{% for category, message in messages %}
|
||||
<div class="alert alert-{{ 'success' if category == 'success' else 'danger' }} alert-dismissible fade show" role="alert">
|
||||
{{ message }}
|
||||
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{% endwith %}
|
||||
|
||||
{% block content %}{% endblock %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
|
||||
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
|
||||
{% block scripts %}{% endblock %}
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,320 @@
|
||||
{% 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 %}
|
||||
@@ -0,0 +1,265 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block title %}Settings - Email Alerts System{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<h2 class="mb-4">
|
||||
<i class="fas fa-cog me-2"></i>
|
||||
System Settings
|
||||
</h2>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<form method="POST" action="{{ url_for('update_settings') }}">
|
||||
<div class="row">
|
||||
<!-- Email Configuration -->
|
||||
<div class="col-md-6">
|
||||
<div class="card mb-4">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title">
|
||||
<i class="fas fa-envelope me-2"></i>
|
||||
Email Configuration
|
||||
</h5>
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="email_address" class="form-label">Email Address to Monitor</label>
|
||||
<input type="email" class="form-control" id="email_address" name="email_address"
|
||||
value="{{ config.email_address }}" required>
|
||||
<div class="form-text">The email address that will be checked for new messages.</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="zoho_email" class="form-label">Zoho Email Address</label>
|
||||
<input type="email" class="form-control" id="zoho_email" name="zoho_email"
|
||||
value="{{ config.zoho_email }}" required>
|
||||
<div class="form-text">Your Zoho email address for IMAP access.</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="zoho_app_password" class="form-label">Zoho App Password</label>
|
||||
<input type="password" class="form-control" id="zoho_app_password" name="zoho_app_password"
|
||||
value="{{ config.zoho_app_password }}" required>
|
||||
<div class="form-text">App password for Zoho IMAP access (not your regular password).</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="email_days_back" class="form-label">Email Range (Days)</label>
|
||||
<input type="number" class="form-control" id="email_days_back" name="email_days_back"
|
||||
value="{{ config.email_days_back }}" min="1" max="365" required>
|
||||
<div class="form-text">How many days back to check for emails (1-365 days).</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="agency_domains" class="form-label">Agency Domains</label>
|
||||
<textarea class="form-control" id="agency_domains" name="agency_domains" rows="3"
|
||||
placeholder="projects@manaknightdigital.com, support@company.com">{{ config.agency_domains|join(', ') }}</textarea>
|
||||
<div class="form-text">Comma-separated list of email domains that indicate agency responses.</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox" id="auto_process" name="auto_process"
|
||||
{% if config.auto_process %}checked{% endif %}>
|
||||
<label class="form-check-label" for="auto_process">
|
||||
Enable Automatic Email Processing
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-text">Automatically process emails at regular intervals.</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="auto_process_interval" class="form-label">Processing Interval (minutes)</label>
|
||||
<input type="number" class="form-control" id="auto_process_interval" name="auto_process_interval"
|
||||
value="{{ config.auto_process_interval }}" min="5" max="1440">
|
||||
<div class="form-text">How often to automatically process emails (5-1440 minutes).</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Time Frames Configuration -->
|
||||
<div class="col-md-6">
|
||||
<div class="card mb-4">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title">
|
||||
<i class="fas fa-clock me-2"></i>
|
||||
Alert Time Frames
|
||||
</h5>
|
||||
<p class="text-muted">Configure when alerts should be sent based on response time.</p>
|
||||
|
||||
<div id="time-frames-container">
|
||||
{% for frame in config.time_frames %}
|
||||
<div class="time-frame-row mb-3 p-3 border rounded">
|
||||
<div class="row">
|
||||
<div class="col-md-4">
|
||||
<label class="form-label">Name</label>
|
||||
<input type="text" class="form-control" name="frame_name[]"
|
||||
value="{{ frame.name }}" placeholder="e.g., 1-24 hours">
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<label class="form-label">Hours</label>
|
||||
<input type="number" class="form-control" name="frame_hours[]"
|
||||
value="{{ frame.hours }}" min="1" max="720">
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<label class="form-label">Alert Level</label>
|
||||
<select class="form-select" name="frame_level[]">
|
||||
<option value="1" {% if frame.alert_level == 1 %}selected{% endif %}>Level 1 (Normal)</option>
|
||||
<option value="2" {% if frame.alert_level == 2 %}selected{% endif %}>Level 2 (Urgent)</option>
|
||||
<option value="3" {% if frame.alert_level == 3 %}selected{% endif %}>Level 3 (Critical)</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-2 d-flex align-items-end">
|
||||
<button type="button" class="btn btn-outline-danger btn-sm" onclick="removeTimeFrame(this)">
|
||||
<i class="fas fa-trash"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
|
||||
<button type="button" class="btn btn-outline-primary btn-sm" onclick="addTimeFrame()">
|
||||
<i class="fas fa-plus me-2"></i>
|
||||
Add Time Frame
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Save Button -->
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<div class="d-flex justify-content-between align-items-center">
|
||||
<div>
|
||||
<h6 class="mb-0">
|
||||
<i class="fas fa-save me-2"></i>
|
||||
Save Configuration
|
||||
</h6>
|
||||
<small class="text-muted">Click save to update all settings</small>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary">
|
||||
<i class="fas fa-save me-2"></i>
|
||||
Save Settings
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<!-- Configuration Preview -->
|
||||
<div class="row mt-4">
|
||||
<div class="col-12">
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title">
|
||||
<i class="fas fa-eye me-2"></i>
|
||||
Current Configuration Preview
|
||||
</h5>
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<h6>Email Settings</h6>
|
||||
<ul class="list-unstyled">
|
||||
<li><strong>Email:</strong> <span id="preview-email">{{ config.email_address }}</span></li>
|
||||
<li><strong>Range:</strong> <span id="preview-range">{{ config.email_days_back }}</span> days</li>
|
||||
<li><strong>Domains:</strong> <span id="preview-domains">{{ config.agency_domains|join(', ') }}</span></li>
|
||||
<li><strong>Auto Processing:</strong> <span id="preview-auto">{{ 'Enabled' if config.auto_process else 'Disabled' }}</span></li>
|
||||
<li><strong>Interval:</strong> <span id="preview-interval">{{ config.auto_process_interval }}</span> minutes</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<h6>Time Frames</h6>
|
||||
<div id="preview-frames">
|
||||
{% for frame in config.time_frames %}
|
||||
<div class="mb-1">
|
||||
<span class="badge bg-primary me-2">{{ frame.name }}</span>
|
||||
<small>{{ frame.hours }} hours (Level {{ frame.alert_level }})</small>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block scripts %}
|
||||
<script>
|
||||
function addTimeFrame() {
|
||||
const container = document.getElementById('time-frames-container');
|
||||
const newFrame = document.createElement('div');
|
||||
newFrame.className = 'time-frame-row mb-3 p-3 border rounded';
|
||||
newFrame.innerHTML = `
|
||||
<div class="row">
|
||||
<div class="col-md-4">
|
||||
<label class="form-label">Name</label>
|
||||
<input type="text" class="form-control" name="frame_name[]"
|
||||
placeholder="e.g., 1-24 hours">
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<label class="form-label">Hours</label>
|
||||
<input type="number" class="form-control" name="frame_hours[]"
|
||||
value="24" min="1" max="720">
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<label class="form-label">Alert Level</label>
|
||||
<select class="form-select" name="frame_level[]">
|
||||
<option value="1">Level 1 (Normal)</option>
|
||||
<option value="2">Level 2 (Urgent)</option>
|
||||
<option value="3">Level 3 (Critical)</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-2 d-flex align-items-end">
|
||||
<button type="button" class="btn btn-outline-danger btn-sm" onclick="removeTimeFrame(this)">
|
||||
<i class="fas fa-trash"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
container.appendChild(newFrame);
|
||||
}
|
||||
|
||||
function removeTimeFrame(button) {
|
||||
const frameRow = button.closest('.time-frame-row');
|
||||
frameRow.remove();
|
||||
}
|
||||
|
||||
// Update preview when form fields change
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
const emailInput = document.getElementById('email_address');
|
||||
const rangeInput = document.getElementById('email_days_back');
|
||||
const domainsInput = document.getElementById('agency_domains');
|
||||
const autoProcessInput = document.getElementById('auto_process');
|
||||
const intervalInput = document.getElementById('auto_process_interval');
|
||||
|
||||
emailInput.addEventListener('input', function() {
|
||||
document.getElementById('preview-email').textContent = this.value;
|
||||
});
|
||||
|
||||
rangeInput.addEventListener('input', function() {
|
||||
document.getElementById('preview-range').textContent = this.value;
|
||||
});
|
||||
|
||||
domainsInput.addEventListener('input', function() {
|
||||
document.getElementById('preview-domains').textContent = this.value;
|
||||
});
|
||||
|
||||
autoProcessInput.addEventListener('change', function() {
|
||||
document.getElementById('preview-auto').textContent = this.checked ? 'Enabled' : 'Disabled';
|
||||
});
|
||||
|
||||
intervalInput.addEventListener('input', function() {
|
||||
document.getElementById('preview-interval').textContent = this.value;
|
||||
});
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
Reference in New Issue
Block a user