Allow users to edit the station itself when clicking a station (description, etc). Humanize refill/empty date/time formats. Remove last
updated line on the popup. On the select city page only show the add new city section and heading when logged in, adjust the login link based on logged-in status, and say click to update on each city button when logged in.
This commit is contained in:
parent
429663d80c
commit
275c91f4e1
@ -194,7 +194,7 @@
|
||||
<div class="loading">Loading cities...</div>
|
||||
</div>
|
||||
|
||||
<div class="add-city-form">
|
||||
<div class="add-city-form" id="addCitySection" style="display: none;">
|
||||
<h3>Add New City</h3>
|
||||
<form id="addCityForm">
|
||||
<div class="form-group">
|
||||
@ -227,12 +227,31 @@
|
||||
const data = await response.json();
|
||||
user = data.user;
|
||||
|
||||
if (!user) {
|
||||
document.getElementById('addCityForm').style.display = 'none';
|
||||
}
|
||||
updateUIBasedOnAuth();
|
||||
} catch (error) {
|
||||
console.error('Auth check failed:', error);
|
||||
document.getElementById('addCityForm').style.display = 'none';
|
||||
updateUIBasedOnAuth();
|
||||
}
|
||||
}
|
||||
|
||||
function updateUIBasedOnAuth() {
|
||||
const addCitySection = document.getElementById('addCitySection');
|
||||
const authLinks = document.querySelector('.auth-links');
|
||||
|
||||
if (user) {
|
||||
addCitySection.style.display = 'block';
|
||||
authLinks.innerHTML = `
|
||||
<a href="/dashboard">Dashboard</a>
|
||||
<span>|</span>
|
||||
<a href="/city/salem">View Salem (Default)</a>
|
||||
`;
|
||||
} else {
|
||||
addCitySection.style.display = 'none';
|
||||
authLinks.innerHTML = `
|
||||
<a href="/login">Login</a>
|
||||
<span>|</span>
|
||||
<a href="/city/salem">View Salem (Default)</a>
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
@ -256,10 +275,11 @@
|
||||
return;
|
||||
}
|
||||
|
||||
const clickText = user ? 'Click to update' : 'Click to view';
|
||||
cityList.innerHTML = cities.map(city => `
|
||||
<div class="city-item" onclick="selectCity('${city.name}')">
|
||||
<div class="city-name">${city.display_name}</div>
|
||||
<div class="city-stats">Click to view</div>
|
||||
<div class="city-stats">${clickText}</div>
|
||||
</div>
|
||||
`).join('');
|
||||
}
|
||||
|
@ -322,6 +322,34 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Edit Station Modal -->
|
||||
<div id="editModal" class="modal">
|
||||
<div class="modal-content">
|
||||
<span class="close" onclick="closeEditModal()">×</span>
|
||||
<h2>Edit Station</h2>
|
||||
<div id="edit-message"></div>
|
||||
<form id="editStationForm">
|
||||
<div class="form-group">
|
||||
<label for="editStationName">Station Name</label>
|
||||
<input type="text" id="editStationName" name="name" required>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="editStationDescription">Description</label>
|
||||
<textarea id="editStationDescription" name="description" placeholder="e.g., Public fountain in park"></textarea>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Location</label>
|
||||
<input type="text" id="editCoordinates" readonly>
|
||||
</div>
|
||||
|
||||
<button type="submit" class="btn btn-primary">Save Changes</button>
|
||||
<button type="button" class="btn btn-secondary" onclick="closeEditModal()">Cancel</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"></script>
|
||||
<script>
|
||||
let map;
|
||||
@ -333,6 +361,52 @@
|
||||
let contextMenuPosition = null;
|
||||
let longPressTimer = null;
|
||||
|
||||
// Helper function to format dates in a human-readable way
|
||||
function formatTimeAgo(dateString) {
|
||||
if (!dateString) return 'Never';
|
||||
|
||||
const date = new Date(dateString);
|
||||
const now = new Date();
|
||||
const diffInSeconds = Math.floor((now - date) / 1000);
|
||||
|
||||
if (diffInSeconds < 60) {
|
||||
return 'Just now';
|
||||
} else if (diffInSeconds < 3600) {
|
||||
const minutes = Math.floor(diffInSeconds / 60);
|
||||
return `${minutes} minute${minutes !== 1 ? 's' : ''} ago`;
|
||||
} else if (diffInSeconds < 86400) {
|
||||
const hours = Math.floor(diffInSeconds / 3600);
|
||||
return `${hours} hour${hours !== 1 ? 's' : ''} ago`;
|
||||
} else if (diffInSeconds < 604800) {
|
||||
const days = Math.floor(diffInSeconds / 86400);
|
||||
return `${days} day${days !== 1 ? 's' : ''} ago`;
|
||||
} else {
|
||||
return date.toLocaleDateString() + ' at ' + date.toLocaleTimeString([], {hour: '2-digit', minute:'2-digit'});
|
||||
}
|
||||
}
|
||||
|
||||
// Helper function to format estimated empty time
|
||||
function formatTimeUntil(dateString) {
|
||||
if (!dateString) return 'Unknown';
|
||||
|
||||
const date = new Date(dateString);
|
||||
const now = new Date();
|
||||
const diffInSeconds = Math.floor((date - now) / 1000);
|
||||
|
||||
if (diffInSeconds <= 0) {
|
||||
return 'Already empty';
|
||||
} else if (diffInSeconds < 3600) {
|
||||
const minutes = Math.floor(diffInSeconds / 60);
|
||||
return `${minutes} minute${minutes !== 1 ? 's' : ''}`;
|
||||
} else if (diffInSeconds < 86400) {
|
||||
const hours = Math.floor(diffInSeconds / 3600);
|
||||
return `${hours} hour${hours !== 1 ? 's' : ''}`;
|
||||
} else {
|
||||
const days = Math.floor(diffInSeconds / 86400);
|
||||
return `${days} day${days !== 1 ? 's' : ''}`;
|
||||
}
|
||||
}
|
||||
|
||||
function initDashboard() {
|
||||
// Get city name from URL
|
||||
const pathParts = window.location.pathname.split('/');
|
||||
@ -457,6 +531,8 @@
|
||||
// Expose functions to global scope for onclick handlers (for modal close buttons)
|
||||
window.closeUpdateModal = closeUpdateModal;
|
||||
window.closeAddModal = closeAddModal;
|
||||
window.closeEditModal = closeEditModal;
|
||||
window.openEditModal = openEditModal;
|
||||
|
||||
async function loadStations() {
|
||||
try {
|
||||
@ -522,11 +598,8 @@
|
||||
}
|
||||
|
||||
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';
|
||||
const refillTime = formatTimeAgo(station.last_refill_time);
|
||||
const estimatedEmpty = formatTimeUntil(station.estimated_empty_time);
|
||||
|
||||
return `
|
||||
<div style="min-width: 200px;">
|
||||
@ -534,9 +607,11 @@
|
||||
<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>
|
||||
<p><strong>Last Updated By:</strong> ${station.updated_by_name || 'Unknown'}</p>
|
||||
<button class="popup-btn" onclick="openUpdateModal(${station.id})">Update Status</button>
|
||||
<div style="margin-top: 10px;">
|
||||
<button class="popup-btn" onclick="openEditModal(${station.id})">Edit Station</button>
|
||||
<button class="popup-btn" onclick="openUpdateModal(${station.id})">Update Status</button>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
@ -582,10 +657,32 @@
|
||||
}
|
||||
}
|
||||
|
||||
function openEditModal(stationId) {
|
||||
const station = stations.find(s => s.id === stationId);
|
||||
if (!station) return;
|
||||
|
||||
selectedStation = station;
|
||||
document.getElementById('editModal').style.display = 'block';
|
||||
document.getElementById('editStationName').value = station.name;
|
||||
document.getElementById('editStationDescription').value = station.description || '';
|
||||
document.getElementById('editCoordinates').value = `${station.latitude.toFixed(6)}, ${station.longitude.toFixed(6)}`;
|
||||
|
||||
// Center map on selected station
|
||||
map.setView([station.latitude, station.longitude], 16);
|
||||
}
|
||||
|
||||
function closeEditModal() {
|
||||
selectedStation = null;
|
||||
document.getElementById('editModal').style.display = 'none';
|
||||
document.getElementById('editStationForm').reset();
|
||||
document.getElementById('edit-message').innerHTML = '';
|
||||
}
|
||||
|
||||
// Close modal when clicking outside
|
||||
window.onclick = function(event) {
|
||||
const updateModal = document.getElementById('updateModal');
|
||||
const addModal = document.getElementById('addModal');
|
||||
const editModal = document.getElementById('editModal');
|
||||
const contextMenu = document.getElementById('contextMenu');
|
||||
|
||||
if (event.target === updateModal) {
|
||||
@ -594,6 +691,9 @@
|
||||
if (event.target === addModal) {
|
||||
closeAddModal();
|
||||
}
|
||||
if (event.target === editModal) {
|
||||
closeEditModal();
|
||||
}
|
||||
if (!contextMenu.contains(event.target)) {
|
||||
hideContextMenu();
|
||||
}
|
||||
@ -706,6 +806,44 @@
|
||||
}
|
||||
});
|
||||
|
||||
document.getElementById('editStationForm').addEventListener('submit', async (e) => {
|
||||
e.preventDefault();
|
||||
|
||||
if (!selectedStation) {
|
||||
showMessage('edit-message', 'Please select a station first', 'error');
|
||||
return;
|
||||
}
|
||||
|
||||
const formData = new FormData(e.target);
|
||||
const data = {
|
||||
name: formData.get('name'),
|
||||
description: formData.get('description')
|
||||
};
|
||||
|
||||
try {
|
||||
const response = await fetch(`/api/stations/${selectedStation.id}`, {
|
||||
method: 'PUT',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify(data)
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
showMessage('edit-message', 'Station updated successfully!');
|
||||
setTimeout(() => {
|
||||
closeEditModal();
|
||||
loadStations();
|
||||
}, 1500);
|
||||
} else {
|
||||
const result = await response.json();
|
||||
showMessage('edit-message', result.error || 'Failed to update station', 'error');
|
||||
}
|
||||
} catch (error) {
|
||||
showMessage('edit-message', 'Failed to update station', 'error');
|
||||
}
|
||||
});
|
||||
|
||||
async function logout() {
|
||||
try {
|
||||
await fetch('/auth/logout', { method: 'POST' });
|
||||
|
17
server.js
17
server.js
@ -411,6 +411,23 @@ app.post('/api/stations/:id/update', (req, res) => {
|
||||
);
|
||||
});
|
||||
|
||||
app.put('/api/stations/:id', (req, res) => {
|
||||
if (!req.user) {
|
||||
return res.status(401).json({ error: 'Authentication required' });
|
||||
}
|
||||
|
||||
const { name, description } = req.body;
|
||||
const stationId = req.params.id;
|
||||
|
||||
db.run('UPDATE water_stations SET name = ?, description = ? WHERE id = ?',
|
||||
[name, description, stationId],
|
||||
function(err) {
|
||||
if (err) return res.status(500).json({ error: 'Database error' });
|
||||
res.json({ success: true });
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
app.get('/dashboard', (req, res) => {
|
||||
if (!req.user) {
|
||||
return res.redirect('/login');
|
||||
|
Loading…
x
Reference in New Issue
Block a user