2025-07-16 12:24:27 -07:00

287 lines
8.9 KiB
HTML

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Water Station</title>
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css" />
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
background-color: #f5f5f5;
}
.header {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
padding: 1rem;
text-align: center;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
}
.header h1 {
font-size: 1.5rem;
margin-bottom: 0.5rem;
}
.header p {
opacity: 0.9;
font-size: 0.9rem;
}
.auth-button {
position: fixed;
top: 1rem;
right: 1rem;
background: rgba(255,255,255,0.2);
color: white;
border: 1px solid rgba(255,255,255,0.3);
padding: 0.5rem 1rem;
border-radius: 20px;
text-decoration: none;
font-size: 0.9rem;
z-index: 1000;
}
#map {
height: calc(100vh - 120px);
width: 100%;
}
.station-popup {
min-width: 250px;
max-width: 300px;
}
.station-popup h3 {
color: #333;
margin-bottom: 0.5rem;
font-size: 1.1rem;
}
.station-info {
font-size: 0.9rem;
line-height: 1.4;
}
.station-info p {
margin-bottom: 0.3rem;
}
.status-indicator {
display: inline-block;
width: 12px;
height: 12px;
border-radius: 50%;
margin-right: 0.5rem;
}
.status-green { background-color: #4CAF50; }
.status-yellow { background-color: #FFC107; }
.status-red { background-color: #f44336; }
.status-black { background-color: #333; }
.legend {
position: fixed;
bottom: 1rem;
left: 1rem;
background: white;
padding: 1rem;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
font-size: 0.8rem;
z-index: 1000;
}
.legend h4 {
margin-bottom: 0.5rem;
color: #333;
}
.legend-item {
display: flex;
align-items: center;
margin-bottom: 0.3rem;
}
@media (max-width: 480px) {
.header h1 {
font-size: 1.2rem;
}
.legend {
bottom: 0.5rem;
left: 0.5rem;
right: 0.5rem;
padding: 0.8rem;
}
}
</style>
</head>
<body>
<div class="header">
<h1>💧 Water Stations</h1>
<p id="cityName">Loading...</p>
</div>
<a href="/login" class="auth-button">Login</a>
<a href="/city-select" class="auth-button" style="top: 1rem; right: 8rem;">All Cities</a>
<div id="map"></div>
<div class="legend">
<h4>Station Status</h4>
<div class="legend-item">
<span class="status-indicator status-green"></span>
Recently refilled
</div>
<div class="legend-item">
<span class="status-indicator status-yellow"></span>
Needs refill soon
</div>
<div class="legend-item">
<span class="status-indicator status-red"></span>
Empty
</div>
<div class="legend-item">
<span class="status-indicator status-black"></span>
Not updated (7+ days)
</div>
</div>
<script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"></script>
<script>
let map;
let stations = [];
let currentCity = null;
function initMap() {
// Get city name from URL
const pathParts = window.location.pathname.split('/');
const cityName = pathParts[2];
if (!cityName) {
// If no city in URL, redirect to city selection
window.location.href = '/city-select';
return;
}
currentCity = cityName;
document.getElementById('cityName').textContent = cityName.charAt(0).toUpperCase() + cityName.slice(1);
map = L.map('map').setView([37.7749, -122.4194], 13);
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
attribution: '© OpenStreetMap contributors'
}).addTo(map);
loadStations();
}
function loadStations() {
fetch(`/api/cities/${currentCity}/stations`)
.then(response => response.json())
.then(data => {
stations = data;
if (data.length > 0) {
document.getElementById('cityName').textContent = data[0].city_name;
}
displayStations();
fitMapToStations();
})
.catch(error => {
console.error('Error loading stations:', error);
document.getElementById('cityName').textContent = 'City Not Found';
});
}
function getStationColor(station) {
if (!station.last_refill_time) {
return '#333'; // Black for no updates
}
const now = new Date();
const refillTime = new Date(station.last_refill_time);
const timeSinceRefill = now - refillTime;
const daysSinceRefill = timeSinceRefill / (1000 * 60 * 60 * 24);
if (daysSinceRefill > 7) {
return '#333'; // Black for old data
}
if (!station.estimated_empty_time) {
return '#4CAF50'; // Green if no empty time estimate
}
const emptyTime = new Date(station.estimated_empty_time);
const timeUntilEmpty = emptyTime - now;
const hoursUntilEmpty = timeUntilEmpty / (1000 * 60 * 60);
if (hoursUntilEmpty <= 0) {
return '#f44336'; // Red for empty
} else if (hoursUntilEmpty <= 3) {
return '#FFC107'; // Yellow for needs refill soon
} else {
return '#4CAF50'; // Green for good
}
}
function displayStations() {
stations.forEach(station => {
const color = getStationColor(station);
const marker = L.circleMarker([station.latitude, station.longitude], {
color: color,
fillColor: color,
fillOpacity: 0.8,
radius: 8,
weight: 2
}).addTo(map);
const popupContent = createPopupContent(station);
marker.bindPopup(popupContent);
});
}
function createPopupContent(station) {
const refillTime = station.last_refill_time ?
new Date(station.last_refill_time).toLocaleString() : 'Never';
const estimatedEmpty = station.estimated_empty_time ?
new Date(station.estimated_empty_time).toLocaleString() : 'Unknown';
return `
<div class="station-popup">
<h3>${station.name}</h3>
<div class="station-info">
<p><strong>Description:</strong> ${station.latest_description || 'No description'}</p>
<p><strong>Last Refill:</strong> ${refillTime}</p>
<p><strong>Estimated Empty:</strong> ${estimatedEmpty}</p>
<p><strong>Last Updated:</strong> ${station.last_updated ? new Date(station.last_updated).toLocaleString() : 'Never'}</p>
</div>
</div>
`;
}
function fitMapToStations() {
if (stations.length === 0) return;
const bounds = L.latLngBounds();
stations.forEach(station => {
bounds.extend([station.latitude, station.longitude]);
});
map.fitBounds(bounds, { padding: [20, 20] });
}
// Initialize map when page loads
document.addEventListener('DOMContentLoaded', initMap);
</script>
</body>
</html>