aditya-me13's picture
Checkpoint on 9th Oct - Everything Working
39ab179
raw
history blame
16.9 kB
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Plot Gallery - CAMS Air Pollution Dashboard</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
min-height: 100vh;
padding: 20px;
}
.container {
max-width: 1400px;
margin: 0 auto;
}
.header {
background: rgba(255, 255, 255, 0.95);
border-radius: 15px;
padding: 20px;
margin-bottom: 20px;
box-shadow: 0 8px 32px rgba(31, 38, 135, 0.37);
backdrop-filter: blur(4px);
border: 1px solid rgba(255, 255, 255, 0.18);
}
.header h1 {
color: #2c3e50;
text-align: center;
margin-bottom: 10px;
}
h1 {
color: #2c3e50;
text-align: center;
margin-bottom: 30px;
}
h2 {
color: #34495e;
border-bottom: 2px solid #3498db;
padding-bottom: 10px;
margin-bottom: 20px;
display: flex;
align-items: center;
gap: 10px;
}
.section {
background: rgba(255, 255, 255, 0.95);
border-radius: 15px;
padding: 20px;
margin-bottom: 20px;
box-shadow: 0 8px 32px rgba(31, 38, 135, 0.37);
backdrop-filter: blur(4px);
border: 1px solid rgba(255, 255, 255, 0.18);
}
.controls {
display: flex;
gap: 15px;
justify-content: center;
margin-bottom: 20px;
flex-wrap: wrap;
}
.btn {
background: linear-gradient(45deg, #3498db, #2980b9);
color: white;
padding: 12px 24px;
border: none;
border-radius: 25px;
text-decoration: none;
font-weight: 600;
transition: all 0.3s ease;
cursor: pointer;
font-size: 14px;
}
.btn:hover {
transform: translateY(-2px);
box-shadow: 0 8px 25px rgba(52, 152, 219, 0.4);
}
.btn-secondary {
background: linear-gradient(45deg, #95a5a6, #7f8c8d);
}
.btn-danger {
background: linear-gradient(45deg, #e74c3c, #c0392b);
}
.plots-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
gap: 20px;
}
.plot-card {
background: white;
border-radius: 10px;
padding: 15px;
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.1);
transition: all 0.3s ease;
border: 2px solid transparent;
}
.plot-card:hover {
transform: translateY(-5px);
box-shadow: 0 8px 25px rgba(0, 0, 0, 0.15);
border-color: #3498db;
}
.plot-preview {
width: 100%;
height: 200px;
background: #f8f9fa;
border-radius: 8px;
margin-bottom: 15px;
display: flex;
align-items: center;
justify-content: center;
overflow: hidden;
position: relative;
}
.plot-preview img {
max-width: 100%;
max-height: 100%;
object-fit: cover;
border-radius: 8px;
}
.plot-preview.interactive {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
font-size: 48px;
}
.plot-info {
color: #2c3e50;
}
.plot-info h3 {
margin-bottom: 10px;
color: #34495e;
font-size: 16px;
}
.plot-meta {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 8px;
margin-bottom: 15px;
font-size: 12px;
color: #7f8c8d;
}
.plot-meta span {
background: #ecf0f1;
padding: 4px 8px;
border-radius: 4px;
}
.plot-actions {
display: flex;
gap: 8px;
justify-content: space-between;
}
.btn-small {
padding: 6px 12px;
font-size: 12px;
border-radius: 15px;
text-decoration: none;
font-weight: 500;
}
.btn-view {
background: linear-gradient(45deg, #27ae60, #219a52);
color: white;
}
.btn-download {
background: linear-gradient(45deg, #f39c12, #e67e22);
color: white;
}
.btn-delete {
background: linear-gradient(45deg, #e74c3c, #c0392b);
color: white;
}
.stats {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
gap: 15px;
margin-bottom: 20px;
}
.stat-card {
background: rgba(52, 152, 219, 0.1);
padding: 15px;
border-radius: 10px;
text-align: center;
border-left: 4px solid #3498db;
}
.stat-number {
font-size: 24px;
font-weight: bold;
color: #2980b9;
}
.stat-label {
color: #7f8c8d;
font-size: 12px;
margin-top: 5px;
}
.empty-state {
text-align: center;
padding: 40px;
color: #7f8c8d;
}
.empty-state h3 {
margin-bottom: 10px;
color: #95a5a6;
}
@media (max-width: 768px) {
.plots-grid {
grid-template-columns: 1fr;
}
.controls {
justify-content: center;
}
.btn {
padding: 10px 20px;
font-size: 12px;
}
}
</style>
</head>
<body>
<div class="container">
<div class="header">
<h1>πŸ“Š Plot Gallery</h1>
<p style="text-align: center; color: #7f8c8d; margin-top: 10px;">
View and manage all your saved pollution maps
</p>
</div>
<div class="controls">
<a href="{{ url_for('index') }}" class="btn btn-secondary">← Back to Dashboard</a>
<a href="{{ url_for('cleanup') }}" class="btn btn-danger"
onclick="return confirm('This will delete plots older than 24 hours. Continue?')">
🧹 Clean Old Plots
</a>
</div>
<!-- Statistics -->
<div class="section">
<div class="stats">
<div class="stat-card">
<div class="stat-number">{{ total_plots }}</div>
<div class="stat-label">Total Plots</div>
</div>
<div class="stat-card">
<div class="stat-number">{{ static_plots|length }}</div>
<div class="stat-label">Static Plots</div>
</div>
<div class="stat-card">
<div class="stat-number">{{ interactive_plots|length }}</div>
<div class="stat-label">Interactive Plots</div>
</div>
</div>
</div>
<!-- Interactive Plots Section -->
<div class="section">
<h2>🎯 Interactive Plots</h2>
{% if interactive_plots %}
<div class="plots-grid">
{% for plot in interactive_plots %}
<div class="plot-card">
<div class="plot-preview interactive">
🌍
</div>
<div class="plot-info">
<h3>{{ plot.variable|title }} - {{ plot.region|title }}{{ ' (' + plot.plot_type + ')' if plot.plot_type else '' }}</h3>
<div class="plot-meta">
<span>πŸ“… {{ plot.created.strftime('%Y-%m-%d %H:%M') }}</span>
<span>🎨 {{ plot.theme|title }}</span>
<span>πŸ“ {{ "%.1f"|format(plot.size/1024) }} KB</span>
<span>🌐 Interactive</span>
</div>
<div class="plot-actions">
<a href="{{ url_for('view_interactive_plot', filename=plot.filename) }}"
class="btn-small btn-view">πŸ‘οΈ View</a>
<a href="{{ url_for('serve_plot', filename=plot.filename) }}"
class="btn-small btn-download" download>πŸ’Ύ Download</a>
<button onclick="deletePlot('{{ plot.filename }}')"
class="btn-small btn-delete">πŸ—‘οΈ Delete</button>
</div>
</div>
</div>
{% endfor %}
</div>
{% else %}
<div class="empty-state">
<h3>No Interactive Plots Yet</h3>
<p>Create your first interactive plot from the dashboard!</p>
</div>
{% endif %}
</div>
<!-- Static Plots Section -->
<div class="section">
<h2>πŸ“Š Static Plots</h2>
{% if static_plots %}
<div class="plots-grid">
{% for plot in static_plots %}
<div class="plot-card">
<div class="plot-preview">
<img src="{{ url_for('serve_plot', filename=plot.filename) }}"
alt="{{ plot.variable }} plot"
onerror="this.style.display='none'; this.parentElement.innerHTML='<div style=\'color: #e74c3c; text-align: center;\'>πŸ“·<br>Preview unavailable</div>'">
</div>
<div class="plot-info">
<h3>{{ plot.variable|title }} - {{ plot.region|title }}{{ ' (' + plot.plot_type + ')' if plot.plot_type else '' }}</h3>
<div class="plot-meta">
<span>πŸ“… {{ plot.created.strftime('%Y-%m-%d %H:%M') }}</span>
<span>🎨 {{ plot.theme|title }}</span>
<span>πŸ“ {{ "%.1f"|format(plot.size/1024) }} KB</span>
<span>πŸ–ΌοΈ {{ plot.extension.upper() }}</span>
</div>
<div class="plot-actions">
<a href="{{ url_for('serve_plot', filename=plot.filename) }}"
class="btn-small btn-view" target="_blank">πŸ‘οΈ View</a>
<a href="{{ url_for('serve_plot', filename=plot.filename) }}"
class="btn-small btn-download" download>πŸ’Ύ Download</a>
<button onclick="deletePlot('{{ plot.filename }}')"
class="btn-small btn-delete">πŸ—‘οΈ Delete</button>
</div>
</div>
</div>
{% endfor %}
</div>
{% else %}
<div class="empty-state">
<h3>No Static Plots Yet</h3>
<p>Generate your first static plot from the dashboard!</p>
</div>
{% endif %}
</div>
</div>
<script>
// Add loading indicators for actions
document.addEventListener('DOMContentLoaded', function() {
const viewButtons = document.querySelectorAll('.btn-view');
viewButtons.forEach(button => {
button.addEventListener('click', function() {
const originalText = this.textContent;
this.textContent = '⏳ Loading...';
// Reset after a delay if still on page
setTimeout(() => {
this.textContent = originalText;
}, 3000);
});
});
const downloadButtons = document.querySelectorAll('.btn-download');
downloadButtons.forEach(button => {
button.addEventListener('click', function() {
const originalText = this.textContent;
this.textContent = 'πŸ“₯ Downloading...';
setTimeout(() => {
this.textContent = originalText;
}, 2000);
});
});
// Delete plot functionality - fixed to work properly with onclick
window.deletePlot = function(filename) {
if (confirm(`Are you sure you want to delete "${filename}"? This action cannot be undone.`)) {
// Find the button that was clicked by searching for the button with this filename
const buttons = document.querySelectorAll('.btn-delete');
let button = null;
buttons.forEach(btn => {
if (btn.getAttribute('onclick').includes(filename)) {
button = btn;
}
});
if (!button) {
alert('Error: Could not find the delete button');
return;
}
const originalText = button.textContent;
button.textContent = 'πŸ—‘οΈ Deleting...';
button.disabled = true;
fetch(`/delete_plot/${filename}`, {
method: 'DELETE',
headers: {
'Content-Type': 'application/json',
}
})
.then(response => {
console.log('Delete response status:', response.status);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
return response.json();
})
.then(data => {
console.log('Delete response data:', data);
if (data.success) {
// Remove the plot card from the DOM
button.closest('.plot-card').remove();
// Show success message
const successMsg = document.createElement('div');
successMsg.style.cssText = `
position: fixed; top: 20px; right: 20px; z-index: 10000;
background: #27ae60; color: white; padding: 15px 25px;
border-radius: 8px; font-weight: bold; box-shadow: 0 4px 12px rgba(0,0,0,0.3);
`;
successMsg.textContent = `βœ… ${filename} deleted successfully`;
document.body.appendChild(successMsg);
setTimeout(() => {
successMsg.remove();
// Reload page to update statistics
window.location.reload();
}, 2000);
} else {
alert(`Failed to delete plot: ${data.error}`);
button.textContent = originalText;
button.disabled = false;
}
})
.catch(error => {
console.error('Error:', error);
alert(`An error occurred while deleting the plot: ${error.message}`);
button.textContent = originalText;
button.disabled = false;
});
}
}
});
</script>
</body>
</html>