Created
July 9, 2024 12:53
-
-
Save paulwababu/cce6d758934e02cdb844a9d3f0ed8793 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<html lang="en"> | |
<head> | |
<meta charset="UTF-8"> | |
<meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
<title>FriendlyForce BotDash</title> | |
<link rel="icon" type="image/x-icon" href="https://friendlyforce.live/favicon.ico" /> | |
<link rel="stylesheet" href="styles.css"> | |
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0-beta3/css/all.min.css"> | |
<link rel="stylesheet" href="https://cdn.datatables.net/1.11.5/css/jquery.dataTables.min.css"> | |
<link href="https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;500&display=swap" rel="stylesheet"> | |
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script> | |
<script src="https://cdn.datatables.net/1.11.5/js/jquery.dataTables.min.js"></script> | |
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script> | |
<style> | |
/* Modal styles */ | |
.modal { | |
display: none; | |
position: fixed; | |
z-index: 1; | |
padding-top: 60px; | |
left: 0; | |
top: 0; | |
width: 100%; | |
height: 100%; | |
overflow: auto; | |
background-color: rgb(0,0,0); | |
background-color: rgba(0,0,0,0.4); | |
} | |
.modal-content { | |
background-color: #fefefe; | |
margin: 5% auto; | |
padding: 20px; | |
border: 1px solid #888; | |
width: 80%; | |
} | |
.close { | |
color: #aaa; | |
float: right; | |
font-size: 28px; | |
font-weight: bold; | |
} | |
.close:hover, | |
.close:focus { | |
color: black; | |
text-decoration: none; | |
cursor: pointer; | |
} | |
</style> | |
</head> | |
<body> | |
<div class="container"> | |
<header> | |
<img src="https://res.cloudinary.com/prometheusapi/image/upload/v1714431302/7682900d-2e64-408d-9d24-d01e50ee979b_yhnkuk-removebg-preview_sqhoyj.png" alt="Logo" class="logo"> | |
<h1>FriendlyForce Monitor Administrators Only</h1> | |
<div class="user-info"> | |
<i class="fas fa-user"></i> | |
<div class="user-menu"> | |
<a href="/profile">Profile</a> | |
<a href="/logout">Logout</a> | |
</div> | |
</div> | |
</header> | |
<aside class="sidebar"> | |
<nav> | |
<ul> | |
<li><a href="#overview" class="active"><i class="fas fa-tachometer-alt"></i> Overview</a></li> | |
<li><a href="#nasa-events"><i class="fas fa-globe"></i> NASA EONET Events</a></li> | |
<li><a href="#weather-alerts"><i class="fas fa-cloud"></i> Weather Gov Alerts</a></li> | |
<li><a href="#profile"><i class="fas fa-user"></i> User Profile</a></li> | |
<li><a href="#settings"><i class="fas fa-cog"></i> Settings</a></li> | |
<li><a href="/logout"><i class="fas fa-sign-out-alt"></i> Logout</a></li> | |
</ul> | |
</nav> | |
</aside> | |
<main> | |
<section id="overview" class="content-section active"> | |
<h2>Overview</h2> | |
<div class="summary-cards"> | |
<div class="card"> | |
<h3>Map Posts</h3> | |
<p id="total-events">1234</p> | |
</div> | |
<div class="card"> | |
<h3>Feed Posts</h3> | |
<p id="processed-tweets">5678</p> | |
</div> | |
<div class="card"> | |
<h3>Satellite Posts</h3> | |
<p id="scheduled-posts">256</p> | |
</div> | |
<div class="card"> | |
<h3>Scheduled Posts</h3> | |
<p id="sites-down">18</p> | |
</div> | |
<div class="card"> | |
<h3>Critical Users & Sites Down</h3> | |
<p id="new-users">345</p> | |
</div> | |
<div class="card"> | |
<h3>Alerts <br> | |
<span class="small-text">Sent & Received</span> | |
</h3> | |
<style> | |
.small-text { | |
font-size: 0.8em; /* Adjust the size as needed */ | |
} | |
</style> | |
<p id="alerts-sent">789</p> | |
</div> | |
<div class="card"> | |
<h3>Updates <br> | |
<span class="small-text">Sent & Received</span> | |
</h3> | |
<style> | |
.small-text { | |
font-size: 0.8em; /* Adjust the size as needed */ | |
} | |
</style> | |
<p id="alerts-sent">789</p> | |
</div> | |
<div class="card"> | |
<h3>$$$ Transfers <br> | |
<span class="small-text">Sent & Received</span> | |
</h3> | |
<style> | |
.small-text { | |
font-size: 0.8em; /* Adjust the size as needed */ | |
} | |
</style> | |
<p id="alerts-sent">$7,890.00</p> | |
</div> | |
</div> | |
<div class="charts"> | |
<div class="chart"> | |
<h3>Events Over Time</h3> | |
<canvas id="eventsChart"></canvas> | |
</div> | |
<div class="chart"> | |
<h3>Total $ Transfers Over Time</h3> | |
<canvas id="transfersChart"></canvas> | |
</div> | |
<div class="chart"> | |
<h3>Threat-related Posts by Keywords</h3> | |
<canvas id="threatsChart"></canvas> | |
</div> | |
<div class="chart"> | |
<h3>Downtime Reports by Category</h3> | |
<canvas id="downtimeReportsChart"></canvas> | |
</div> | |
</div> | |
<div class="tables"> | |
<div class="table"> | |
<h3>Activity Feed Details</h3> | |
<table id="event-details-table" class="display" cellspacing="0" width="100%"> | |
<thead> | |
<tr> | |
<th>Event ID</th> | |
<th>Time</th> | |
<th>Location</th> | |
<th>Category</th> | |
<th>Message</th> | |
<th>Media</th> | |
<th>Actions</th> | |
</tr> | |
</thead> | |
<tfoot> | |
<tr> | |
<th>Event ID</th> | |
<th>Time</th> | |
<th>Location</th> | |
<th>Category</th> | |
<th>Message</th> | |
<th>Media</th> | |
<th>Actions</th> | |
</tr> | |
</tfoot> | |
<tbody id="event-details-body"> | |
<!-- Placeholder for event details --> | |
</tbody> | |
</table> | |
</div> | |
</div> | |
</section> | |
<section id="nasa-events" class="content-section"> | |
<h2>NASA EONET Events</h2> | |
<!-- NASA EONET events content --> | |
</section> | |
<section id="weather-alerts" class="content-section"> | |
<h2>Weather Gov Alerts</h2> | |
<!-- Weather Gov alerts content --> | |
</section> | |
<section id="profile" class="content-section"> | |
<h2>User Profile</h2> | |
<!-- User profile content --> | |
</section> | |
<section id="settings" class="content-section"> | |
<h2>Settings</h2> | |
<!-- Settings content --> | |
</section> | |
<section id="logout" class="content-section"> | |
<h2>Logout</h2> | |
<!-- Logout content --> | |
</section> | |
</main> | |
</div> | |
<div class="container"> | |
<!-- Header, Sidebar, and Main content --> | |
<div id="myModal" class="modal"> | |
<div class="modal-content"> | |
<span class="close">×</span> | |
<h2>Event Details</h2> | |
<p id="modal-content"></p> | |
</div> | |
</div> | |
<div id="editModal" class="modal"> | |
<div class="modal-content"> | |
<span class="close">×</span> | |
<h2>Edit Event</h2> | |
<form id="editForm"> | |
<input type="hidden" id="edit-event-time"> | |
<input type="hidden" id="edit-event-time" readonly> | |
<label for="edit-event-location">Location:</label> | |
<input type="text" id="edit-event-location"> | |
<label for="edit-event-category">Category:</label> | |
<input type="text" id="edit-event-category"> | |
<label for="edit-event-message">Message:</label> | |
<textarea id="edit-event-message"></textarea> | |
<label for="edit-event-tags">Tags:</label> | |
<input type="text" id="edit-event-tags"> | |
<label for="edit-event-media">Media URLs (comma-separated):</label> | |
<input type="text" id="edit-event-media"> | |
<button type="submit">Save Changes</button> | |
</form> | |
</div> | |
</div> | |
</div> | |
<script> | |
// Example data for Events Over Time chart | |
const eventsLabels = ['January', 'February', 'March', 'April', 'May', 'June', 'July']; | |
const eventsData = { | |
labels: eventsLabels, | |
datasets: [{ | |
label: 'Total Analyzed & Processed', | |
data: [0, 10, 5, 20, 15, 30, 25], | |
fill: false, | |
borderColor: 'rgba(75, 192, 192, 1)', | |
tension: 0.1 | |
}] | |
}; | |
const eventsConfig = { | |
type: 'line', | |
data: eventsData, | |
options: { | |
scales: { | |
y: { | |
beginAtZero: true | |
} | |
} | |
} | |
}; | |
// Example data for Total $ Transfers Over Time chart | |
const transfersLabels = ['January', 'February', 'March', 'April', 'May', 'June', 'July']; | |
const transfersData = { | |
labels: transfersLabels, | |
datasets: [{ | |
label: 'Total Analyzed & Processed', | |
data: [500, 800, 600, 1500, 2000, 2500, 3500], | |
fill: false, | |
borderColor: 'rgba(75, 192, 75, 1)', | |
tension: 0.1 | |
}] | |
}; | |
const transfersConfig = { | |
type: 'line', | |
data: transfersData, | |
options: { | |
scales: { | |
y: { | |
beginAtZero: true | |
} | |
} | |
} | |
}; | |
// Example data for Threat-related Posts by Keywords chart | |
const threatsLabels = ['Weather', 'Air & Water', 'Wildfire', 'Food Supply', 'Biologicals', 'Infrastructure']; | |
const threatsData = { | |
labels: threatsLabels, | |
datasets: [{ | |
label: 'Posts by Keyword', | |
data: [300, 50, 100, 75, 20, 150], | |
backgroundColor: [ | |
'rgba(255, 99, 132, 0.2)', | |
'rgba(54, 162, 235, 0.2)', | |
'rgba(255, 206, 86, 0.2)', | |
'rgba(75, 192, 192, 0.2)', | |
'rgba(153, 102, 255, 0.2)', | |
'rgba(255, 159, 64, 0.2)' | |
], | |
borderColor: [ | |
'rgba(255, 99, 132, 1)', | |
'rgba(54, 162, 235, 1)', | |
'rgba(255, 206, 86, 1)', | |
'rgba(75, 192, 192, 1)', | |
'rgba(153, 102, 255, 1)', | |
'rgba(255, 159, 64, 1)' | |
], | |
borderWidth: 1 | |
}] | |
}; | |
const threatsConfig = { | |
type: 'doughnut', | |
data: threatsData, | |
options: { | |
responsive: true, | |
plugins: { | |
legend: { | |
position: 'top', | |
}, | |
title: { | |
display: true, | |
text: 'Threat-related Posts by Keywords' | |
} | |
} | |
} | |
}; | |
// Example data for Downtime Reports by Category chart | |
const downtimeLabels = ['January', 'February', 'March', 'April', 'May', 'June', 'July']; | |
const downtimeData = { | |
labels: downtimeLabels, | |
datasets: [{ | |
label: 'Downtime Reports', | |
data: [1, 2, 1, 3, 2, 4, 1], | |
backgroundColor: 'rgba(255, 159, 64, 0.2)', | |
borderColor: 'rgba(255, 159, 64, 1)', | |
borderWidth: 1 | |
}] | |
}; | |
const downtimeConfig = { | |
type: 'bar', | |
data: downtimeData, | |
options: { | |
scales: { | |
y: { | |
beginAtZero: true | |
} | |
} | |
} | |
}; | |
// Initialize all charts | |
window.onload = function() { | |
const eventsChart = new Chart( | |
document.getElementById('eventsChart'), | |
eventsConfig | |
); | |
const transfersChart = new Chart( | |
document.getElementById('transfersChart'), | |
transfersConfig | |
); | |
const threatsChart = new Chart( | |
document.getElementById('threatsChart'), | |
threatsConfig | |
); | |
const downtimeChart = new Chart( | |
document.getElementById('downtimeReportsChart'), | |
downtimeConfig | |
); | |
}; | |
</script> | |
<script> | |
// Example function to handle the response and update the UI | |
function handlePostEventResponse(response) { | |
if (response.status === 'success') { | |
alert(response.message); | |
} else if (response.status === 'scheduled') { | |
alert(response.message); | |
} else { | |
alert('An error occurred. Please try again.'); | |
} | |
} | |
// Example AJAX request to post an event | |
function postEvent(eventData) { | |
$.ajax({ | |
url: '/post_event', | |
type: 'POST', | |
contentType: 'application/json', | |
data: JSON.stringify({ event: eventData }), | |
success: handlePostEventResponse, | |
error: function(jqXHR, textStatus, errorThrown) { | |
alert('An error occurred. Please try again.'); | |
} | |
}); | |
} | |
</script> | |
<script src="scripts.js"></script> | |
</body> | |
</html> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
document.addEventListener('DOMContentLoaded', () => { | |
const links = document.querySelectorAll('.sidebar nav ul li a'); | |
const sections = document.querySelectorAll('.content-section'); | |
const modal = document.getElementById('myModal'); | |
const modalContent = document.getElementById('modal-content'); | |
const editModal = document.getElementById('editModal'); | |
const editForm = document.getElementById('editForm'); | |
const span = document.getElementsByClassName('close'); | |
links.forEach(link => { | |
link.addEventListener('click', (e) => { | |
e.preventDefault(); | |
const targetId = e.target.getAttribute('href').substring(1); | |
sections.forEach(section => { | |
section.classList.remove('active'); | |
}); | |
links.forEach(link => { | |
link.classList.remove('active'); | |
}); | |
document.getElementById(targetId).classList.add('active'); | |
e.target.classList.add('active'); | |
}); | |
}); | |
// Activate the first section by default | |
document.getElementById('overview').classList.add('active'); | |
document.querySelector('.sidebar nav ul li a[href="#overview"]').classList.add('active'); | |
// Fetch and display analytics data | |
fetch('https://monitordb.friendlyforce.live/analytics') | |
.then(response => response.json()) | |
.then(data => { | |
document.getElementById('total-events').innerText = data.total_events; | |
document.getElementById('processed-tweets').innerText = data.processed_tweets; | |
document.getElementById('scheduled-posts').innerText = data.scheduled_posts; | |
document.getElementById('sites-down').innerText = data.popular_sites_down; | |
document.getElementById('new-users').innerText = data.new_users; | |
document.getElementById('alerts-sent').innerText = data.alerts_sent; | |
document.getElementById('most-active-users').innerText = data.most_active_users.join(', '); | |
// Update events chart | |
const eventsCtx = document.getElementById('eventsChart').getContext('2d'); | |
const eventsChart = new Chart(eventsCtx, { | |
type: 'line', | |
data: { | |
labels: data.events_over_time.labels, | |
datasets: [{ | |
label: 'Total Events', | |
data: data.events_over_time.data, | |
backgroundColor: 'rgba(75, 192, 192, 0.2)', | |
borderColor: 'rgba(75, 192, 192, 1)', | |
borderWidth: 1 | |
}] | |
}, | |
options: { | |
responsive: true, | |
scales: { | |
x: { | |
beginAtZero: true | |
}, | |
y: { | |
beginAtZero: true | |
} | |
} | |
} | |
}); | |
// Update tweets processed chart | |
const tweetsCtx = document.getElementById('tweetsChart').getContext('2d'); | |
const tweetsChart = new Chart(tweetsCtx, { | |
type: 'line', | |
data: { | |
labels: data.tweets_processed_over_time.labels, | |
datasets: [{ | |
label: 'Processed Tweets', | |
data: data.tweets_processed_over_time.data, | |
backgroundColor: 'rgba(255, 99, 132, 0.2)', | |
borderColor: 'rgba(255, 99, 132, 1)', | |
borderWidth: 1 | |
}] | |
}, | |
options: { | |
responsive: true, | |
scales: { | |
x: { | |
beginAtZero: true | |
}, | |
y: { | |
beginAtZero: true | |
} | |
} | |
} | |
}); | |
// Update types of posts chart | |
const postTypesCtx = document.getElementById('postTypesChart').getContext('2d'); | |
const postTypesChart = new Chart(postTypesCtx, { | |
type: 'doughnut', | |
data: { | |
labels: data.types_of_posts.labels, | |
datasets: [{ | |
label: 'Types of Posts', | |
data: data.types_of_posts.data, | |
backgroundColor: [ | |
'rgba(75, 192, 192, 0.2)', | |
'rgba(255, 206, 86, 0.2)', | |
'rgba(54, 162, 235, 0.2)', | |
'rgba(153, 102, 255, 0.2)' | |
], | |
borderColor: [ | |
'rgba(75, 192, 192, 1)', | |
'rgba(255, 206, 86, 1)', | |
'rgba(54, 162, 235, 1)', | |
'rgba(153, 102, 255, 1)' | |
], | |
borderWidth: 1 | |
}] | |
}, | |
options: { | |
responsive: true, | |
plugins: { | |
legend: { | |
position: 'top' | |
}, | |
title: { | |
display: true, | |
text: 'Types of Posts' | |
} | |
} | |
} | |
}); | |
// Update downtime reports by category chart | |
const downtimeReportsCtx = document.getElementById('downtimeReportsChart').getContext('2d'); | |
const downtimeReportsChart = new Chart(downtimeReportsCtx, { | |
type: 'bar', | |
data: { | |
labels: data.downtime_reports_by_category.labels, | |
datasets: [{ | |
label: 'Downtime Reports', | |
data: data.downtime_reports_by_category.data, | |
backgroundColor: [ | |
'rgba(255, 159, 64, 0.2)', | |
'rgba(75, 192, 192, 0.2)', | |
'rgba(255, 206, 86, 0.2)', | |
'rgba(153, 102, 255, 0.2)' | |
], | |
borderColor: [ | |
'rgba(255, 159, 64, 1)', | |
'rgba(75, 192, 192, 1)', | |
'rgba(255, 206, 86, 1)', | |
'rgba(153, 102, 255, 1)' | |
], | |
borderWidth: 1 | |
}] | |
}, | |
options: { | |
responsive: true, | |
scales: { | |
x: { | |
beginAtZero: true | |
}, | |
y: { | |
beginAtZero: true | |
} | |
} | |
} | |
}); | |
}) | |
.catch(error => console.error('Error fetching analytics data:', error)); | |
// Function to fetch and display event details | |
const fetchAndDisplayEvents = () => { | |
Promise.all([ | |
fetch('https://monitordb.friendlyforce.live/nasaFireAlerts?limit=1000000'), | |
fetch('https://monitordb.friendlyforce.live/weatherGovAlerts?limit=1000000') | |
]) | |
.then(async ([fireResponse, weatherResponse]) => { | |
if (!fireResponse.ok || !weatherResponse.ok) { | |
throw new Error('Network response was not ok'); | |
} | |
const fireEvents = await fireResponse.json(); | |
const weatherEvents = await weatherResponse.json(); | |
return { fireEvents, weatherEvents }; | |
}) | |
.then(({ fireEvents, weatherEvents }) => { | |
const eventTableBody = document.getElementById('event-details-body'); | |
eventTableBody.innerHTML = ''; // Clear existing table rows | |
// Merge and sort events by time | |
const allEvents = [...fireEvents.data, ...weatherEvents.data]; | |
allEvents.sort((a, b) => new Date(b.date ?? b.created_at) - new Date(a.date ?? a.created_at)); | |
const createRow = (event, type, index) => { | |
const row = document.createElement('tr'); | |
const eventIdCell = document.createElement('td'); | |
eventIdCell.textContent = (index + 1).toString().padStart(3, '0'); | |
row.appendChild(eventIdCell); | |
const timeCell = document.createElement('td'); | |
timeCell.textContent = event.date ?? event.created_at ?? 'N/A'; | |
row.appendChild(timeCell); | |
const locationCell = document.createElement('td'); | |
locationCell.textContent = event.title ?? event.event_details ?? 'N/A'; | |
row.appendChild(locationCell); | |
const categoryCell = document.createElement('td'); | |
categoryCell.textContent = type; | |
row.appendChild(categoryCell); | |
const messageCell = document.createElement('td'); | |
messageCell.textContent = event.conversational_message ?? event.tweet_text ?? 'N/A'; | |
row.appendChild(messageCell); | |
const mediaCell = document.createElement('td'); | |
mediaCell.innerHTML = event.media_urls ? event.media_urls.split(',').map(url => `<img src="${url}" alt="media" style="max-width: 100px;">`).join(' ') : 'N/A'; | |
row.appendChild(mediaCell); | |
const actionsCell = document.createElement('td'); | |
try { | |
const eventStr = JSON.stringify(event).replace(/'/g, "'"); | |
actionsCell.innerHTML = ` | |
<button class="action-btn view-btn" data-event='${eventStr}' data-type='${type}'><i class="fas fa-eye"></i></button> | |
<button class="action-btn publish-btn" data-event='${eventStr}'><i class="fas fa-upload"></i></button> | |
<button class="action-btn edit-btn" data-event='${eventStr}' data-type='${type}' data-id='${event.id}'><i class="fas fa-edit"></i></button> | |
<button class="action-btn delete-btn" data-event-time='${event.date ?? event.created_at}' data-type='${type}'><i class="fas fa-trash"></i></button> | |
`; | |
} catch (error) { | |
console.error('Error stringifying event:', error, event); | |
} | |
row.appendChild(actionsCell); | |
return row; | |
}; | |
allEvents.forEach((event, index) => { | |
const type = event.date ? 'Fire Alert' : 'Weather Alert'; | |
eventTableBody.appendChild(createRow(event, type, index)); | |
}); | |
// Destroy existing DataTable instance if it exists | |
if ($.fn.DataTable.isDataTable('#event-details-table')) { | |
$('#event-details-table').DataTable().destroy(); | |
} | |
// Initialize DataTable | |
const dataTable = $('#event-details-table').DataTable({ | |
pageLength: 100000000000000 | |
}); | |
// Function to reattach event listeners | |
const reattachEventListeners = () => { | |
const eventDetailsBody = document.getElementById('event-details-body'); | |
// Remove existing event listeners | |
eventDetailsBody.replaceWith(eventDetailsBody.cloneNode(true)); | |
// Use event delegation to handle clicks on action buttons | |
document.getElementById('event-details-body').addEventListener('click', (e) => { | |
if (e.target.closest('.view-btn')) { | |
const button = e.target.closest('.view-btn'); | |
const event = JSON.parse(button.getAttribute('data-event').replace(/'/g, "'")); | |
const type = button.getAttribute('data-type'); | |
const eventDetails = ` | |
<p><strong>Event ID:</strong> ${(Array.from(eventTableBody.children).indexOf(button.closest('tr')) + 1).toString().padStart(3, '0')}</p> | |
<p><strong>Time:</strong> ${event.date ?? event.created_at ?? 'N/A'}</p> | |
<p><strong>Location:</strong> ${event.title ?? event.event_details ?? 'N/A'}</p> | |
<p><strong>Category:</strong> ${type}</p> | |
<p><strong>Message:</strong> ${event.conversational_message ?? event.tweet_text ?? 'N/A'}</p> | |
<p><strong>Media:</strong> ${event.media_urls ? event.media_urls.split(',').map(url => `<img src="${url}" alt="media" style="max-width: 100px;">`).join(' ') : 'N/A'}</p> | |
`; | |
modalContent.innerHTML = eventDetails; | |
modal.style.display = 'block'; | |
} else if (e.target.closest('.publish-btn')) { | |
const button = e.target.closest('.publish-btn'); | |
const event = JSON.parse(button.getAttribute('data-event').replace(/'/g, "'")); | |
publishEvent(event); | |
} else if (e.target.closest('.delete-btn')) { | |
const button = e.target.closest('.delete-btn'); | |
const eventTime = button.getAttribute('data-event-time'); | |
const type = button.getAttribute('data-type'); | |
const endpoint = type === 'Fire Alert' ? 'nasaFireAlerts' : 'weatherGovAlerts'; | |
fetch(`https://monitordb.friendlyforce.live/${endpoint}?time=${eventTime}`, { | |
method: 'DELETE' | |
}) | |
.then(response => { | |
if (response.ok) { | |
fetchAndDisplayEvents(); // Refresh the events | |
} else { | |
alert('Failed to delete the event.'); | |
} | |
}) | |
.catch(error => console.error('Error deleting event:', error)); | |
} else if (e.target.closest('.edit-btn')) { | |
const button = e.target.closest('.edit-btn'); | |
const event = JSON.parse(button.getAttribute('data-event').replace(/'/g, "'")); | |
const type = button.getAttribute('data-type'); | |
const id = button.getAttribute('data-id'); | |
const propertyName = type === 'Fire Alert' ? 'date' : 'created_at'; | |
if (!(propertyName in event)) { | |
alert('Invalid event time. Please ensure the event object contains the required time property.'); | |
return; | |
} | |
const eventTime = event[propertyName]; | |
document.getElementById('edit-event-time').value = eventTime; | |
document.getElementById('edit-event-location').value = event.title ?? event.event_details ?? ''; | |
document.getElementById('edit-event-category').value = event.category ?? ''; | |
document.getElementById('edit-event-message').value = event.conversational_message ?? event.tweet_text ?? ''; | |
document.getElementById('edit-event-tags').value = (Array.isArray(event.tags) ? event.tags.join(', ') : event.tags); | |
editModal.style.display = 'block'; | |
editForm.onsubmit = (formEvent) => { | |
formEvent.preventDefault(); | |
const updatedEvent = { | |
[propertyName]: document.getElementById('edit-event-time').value, | |
title: document.getElementById('edit-event-location').value, | |
category: document.getElementById('edit-event-category').value, | |
conversational_message: document.getElementById('edit-event-message').value, | |
tweet_text: document.getElementById('edit-event-message').value, | |
tags: document.getElementById('edit-event-tags').value.split(',').map(tag => tag.trim()) | |
}; | |
const endpoint = type === 'Fire Alert' ? 'nasaFireAlerts' : 'weatherGovAlerts'; | |
fetch(`https://monitordb.friendlyforce.live/${endpoint}/${id}`, { | |
method: 'PUT', | |
headers: { | |
'Content-Type': 'application/json' | |
}, | |
body: JSON.stringify(updatedEvent) | |
}) | |
.then(response => { | |
if (response.ok) { | |
editModal.style.display = 'none'; | |
fetchAndDisplayEvents(); // Refresh the events | |
alert('Event updated successfully.'); | |
} else { | |
alert('Failed to update the event. Please check the console for more details.'); | |
console.error('Failed to update event:', response.statusText); | |
} | |
}) | |
.catch(error => { | |
alert('An error occurred while updating the event. Please check the console for more details.'); | |
console.error('Error updating event:', error); | |
}); | |
}; | |
} | |
}); | |
}; | |
// Attach event listeners initially | |
reattachEventListeners(); | |
// Attach event listeners on each DataTable draw (including page changes) | |
dataTable.on('draw', () => { | |
reattachEventListeners(); | |
}); | |
}) | |
.catch(error => console.error('Error fetching event details:', error)); | |
}; | |
fetchAndDisplayEvents(); | |
// Close the modal when the user clicks on <span> (x) | |
span[0].onclick = () => { | |
modal.style.display = 'none'; | |
} | |
span[1].onclick = () => { | |
editModal.style.display = 'none'; | |
} | |
// Close the modal when the user clicks anywhere outside of the modal | |
window.onclick = (event) => { | |
if (event.target == modal) { | |
modal.style.display = 'none'; | |
} | |
if (event.target == editModal) { | |
editModal.style.display = 'none'; | |
} | |
} | |
}); | |
// Function to publish an event | |
const publishEvent = (event) => { | |
fetch('/post_event', { | |
method: 'POST', | |
headers: { | |
'Content-Type': 'application/json' | |
}, | |
body: JSON.stringify({ event }) | |
}) | |
.then(response => response.json()) | |
.then(data => { | |
if (data.status === 'success') { | |
alert('Event published successfully.'); | |
} else if (data.status === 'scheduled') { | |
alert(data.message); | |
} else { | |
alert('Event published successfully.'); | |
} | |
}) | |
.catch(error => { | |
console.error('Error publishing event:', error); | |
alert('Event scheduled to be published successfully.'); | |
}); | |
}; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
:root { | |
--primary-color: #2c3e50; /* Lighter navy blue */ | |
--secondary-color: #f26522; /* Orange from logo */ | |
--background-color: #f4f4f4; | |
--text-color: #333; | |
--card-background: #fff; | |
--sidebar-width: 250px; | |
--header-height: 70px; | |
--transition-speed: 0.3s; | |
} | |
body, html { | |
margin: 0; | |
padding: 0; | |
font-family: 'Roboto', Arial, sans-serif; | |
background-color: var(--background-color); | |
color: var(--text-color); | |
height: 100%; | |
line-height: 1.6; | |
font-size: 16px; | |
} | |
.container { | |
display: flex; | |
height: 100%; | |
} | |
header { | |
background-color: #ffffff; | |
color: var(--primary-color); | |
display: flex; | |
justify-content: space-between; | |
align-items: center; | |
padding: 0 20px; | |
position: fixed; | |
width: 100%; | |
height: var(--header-height); | |
top: 0; | |
z-index: 1000; | |
box-shadow: 0 2px 10px rgba(0,0,0,0.1); | |
} | |
header .logo { | |
height: 40px; | |
margin-right: 15px; | |
transition: transform var(--transition-speed); | |
} | |
header .logo:hover { | |
transform: scale(1.05); | |
} | |
header h1 { | |
margin: 0; | |
font-size: 1.5em; | |
font-weight: 500; | |
} | |
header .user-info { | |
display: flex; | |
align-items: center; | |
} | |
header .user-info i { | |
font-size: 24px; | |
margin-right: 10px; | |
color: var(--secondary-color); | |
} | |
.sidebar { | |
background-color: #ffffff; | |
color: var(--primary-color); | |
width: var(--sidebar-width); | |
padding-top: var(--header-height); | |
position: fixed; | |
top: 0; | |
bottom: 0; | |
overflow-y: auto; | |
transition: transform var(--transition-speed); | |
box-shadow: 2px 0 10px rgba(0,0,0,0.1); | |
} | |
.sidebar nav ul { | |
list-style-type: none; | |
padding: 0; | |
margin: 0; | |
} | |
.sidebar nav ul li { | |
padding: 0; | |
margin-bottom: 5px; | |
} | |
.sidebar nav ul li a { | |
color: var(--primary-color); | |
text-decoration: none; | |
display: flex; | |
align-items: center; | |
padding: 15px 20px; | |
transition: all var(--transition-speed); | |
border-left: 4px solid transparent; | |
} | |
.sidebar nav ul li a i { | |
margin-right: 15px; | |
font-size: 18px; | |
width: 20px; | |
text-align: center; | |
} | |
.sidebar nav ul li a:hover, | |
.sidebar nav ul li a.active { | |
background-color: rgba(44, 62, 80, 0.1); | |
border-left: 4px solid var(--secondary-color); | |
} | |
main { | |
margin-left: var(--sidebar-width); | |
padding: calc(var(--header-height) + 20px) 30px 30px; | |
width: calc(100% - var(--sidebar-width)); | |
transition: margin-left var(--transition-speed); | |
} | |
.content-section { | |
display: none; | |
animation: fadeIn 0.5s; | |
} | |
@keyframes fadeIn { | |
from { opacity: 0; } | |
to { opacity: 1; } | |
} | |
.content-section.active { | |
display: block; | |
} | |
h2 { | |
color: var(--primary-color); | |
font-weight: 500; | |
margin-bottom: 25px; | |
} | |
.summary-cards { | |
display: grid; | |
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); | |
gap: 20px; | |
margin-bottom: 30px; | |
} | |
.card { | |
background-color: var(--card-background); | |
padding: 25px; | |
text-align: center; | |
border-radius: 12px; | |
box-shadow: 0 6px 12px rgba(0, 0, 0, 0.1); | |
transition: transform var(--transition-speed), box-shadow var(--transition-speed); | |
} | |
.card:hover { | |
transform: translateY(-5px); | |
box-shadow: 0 8px 16px rgba(0, 0, 0, 0.15); | |
} | |
.card h3 { | |
margin-top: 0; | |
font-weight: 500; | |
color: var(--primary-color); | |
} | |
.charts { | |
display: grid; | |
grid-template-columns: repeat(auto-fit, minmax(400px, 1fr)); | |
gap: 30px; | |
margin-bottom: 30px; | |
} | |
.chart { | |
background-color: var(--card-background); | |
padding: 25px; | |
border-radius: 12px; | |
box-shadow: 0 6px 12px rgba(0, 0, 0, 0.1); | |
} | |
.tables { | |
margin-bottom: 30px; | |
} | |
.table { | |
background-color: var(--card-background); | |
padding: 25px; | |
border-radius: 12px; | |
box-shadow: 0 6px 12px rgba(0, 0, 0, 0.1); | |
overflow-x: auto; | |
} | |
.table h3 { | |
margin-bottom: 20px; | |
font-weight: 500; | |
color: var(--primary-color); | |
} | |
.table table { | |
width: 100%; | |
border-collapse: separate; | |
border-spacing: 0; | |
} | |
.table table th, | |
.table table td { | |
padding: 12px; | |
text-align: left; | |
border-bottom: 1px solid #e0e0e0; | |
} | |
.table table th { | |
background-color: #f5f5f5; | |
font-weight: 500; | |
color: var(--primary-color); | |
} | |
.table table tr:last-child td { | |
border-bottom: none; | |
} | |
.table table tr:hover { | |
background-color: #f9f9f9; | |
} | |
/* Modal styles */ | |
.modal { | |
display: none; | |
position: fixed; | |
z-index: 1000; | |
left: 0; | |
top: 0; | |
width: 100%; | |
height: 100%; | |
overflow: auto; | |
background-color: rgba(0,0,0,0.5); | |
} | |
.modal-content { | |
background-color: #fefefe; | |
margin: 10% auto; | |
padding: 30px; | |
border: none; | |
border-radius: 8px; | |
width: 50%; | |
max-width: 500px; | |
box-shadow: 0 4px 20px rgba(0,0,0,0.2); | |
} | |
.close { | |
color: #aaa; | |
float: right; | |
font-size: 28px; | |
font-weight: bold; | |
cursor: pointer; | |
} | |
.close:hover, | |
.close:focus { | |
color: #000; | |
text-decoration: none; | |
cursor: pointer; | |
} | |
/* Form styles */ | |
form { | |
display: flex; | |
flex-direction: column; | |
} | |
form label { | |
margin-top: 10px; | |
margin-bottom: 5px; | |
font-weight: 500; | |
} | |
form input[type="text"], | |
form input[type="password"], | |
form textarea { | |
padding: 12px; | |
margin-bottom: 15px; | |
border: 1px solid #ddd; | |
border-radius: 4px; | |
font-size: 14px; | |
} | |
form button { | |
background-color: var(--secondary-color); | |
color: white; | |
border: none; | |
padding: 12px 20px; | |
border-radius: 5px; | |
cursor: pointer; | |
transition: background-color 0.3s; | |
font-size: 16px; | |
font-weight: 500; | |
} | |
form button:hover { | |
background-color: #d54d0d; /* Darker shade of secondary color */ | |
} | |
@media (max-width: 768px) { | |
.sidebar { | |
transform: translateX(-100%); | |
} | |
main { | |
margin-left: 0; | |
width: 100%; | |
} | |
.sidebar-open .sidebar { | |
transform: translateX(0); | |
} | |
.sidebar-open main { | |
margin-left: var(--sidebar-width); | |
} | |
.modal-content { | |
width: 80%; | |
} | |
} | |
/* Add this to your existing CSS */ | |
.user-info { | |
display: flex; | |
align-items: center; | |
margin-left: auto; | |
margin-right: 20px; | |
position: relative; | |
} | |
.user-icon { | |
background-color: var(--secondary-color); | |
color: white; | |
width: 40px; | |
height: 40px; | |
border-radius: 50%; | |
display: flex; | |
align-items: center; | |
justify-content: center; | |
cursor: pointer; | |
transition: background-color 0.3s; | |
} | |
.user-icon:hover { | |
background-color: #d54d0d; | |
} | |
.user-menu { | |
position: absolute; | |
top: 100%; | |
right: 0; | |
background-color: white; | |
border-radius: 4px; | |
box-shadow: 0 2px 10px rgba(0,0,0,0.1); | |
display: none; | |
z-index: 1000; | |
} | |
.user-info:hover .user-menu { | |
display: block; | |
} | |
.user-menu a { | |
display: block; | |
padding: 10px 20px; | |
color: var(--primary-color); | |
text-decoration: none; | |
transition: background-color 0.3s; | |
} | |
.user-menu a:hover { | |
background-color: #f0f0f0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment