Add mobile responsiveness and persistent reports feature

- Enhanced mobile responsiveness across entire site and admin panel
  - Optimized layouts, font sizes, and spacing for screens ≤768px and ≤480px
  - Made forms, tables, maps, and buttons touch-friendly
  - Added responsive breakpoints for better mobile experience

- Added persistent reports functionality
  - Added 'persistent' column to database with automatic migration
  - Updated cleanup logic to preserve persistent reports (no auto-expiration)
  - Added admin panel toggle for marking reports as persistent
  - Added persistent report count to admin dashboard stats
  - Visual indicators with lock/unlock icons for persistent status

- Improved admin panel UI
  - Standardized header button styling and sizing
  - Added 'Return to Homepage' button for better navigation
  - Enhanced mobile responsiveness for admin interface
  - Fixed table layouts and button arrangements for mobile devices

- Backend API enhancements
  - New PATCH endpoint for toggling persistent status
  - Updated admin routes to include persistent field
  - Backwards compatible database migration
This commit is contained in:
Deco Vander 2025-07-03 01:17:41 -04:00
parent 5e56d59bbd
commit a3b450de1a
4 changed files with 358 additions and 16 deletions

View file

@ -102,7 +102,7 @@ document.addEventListener('DOMContentLoaded', () => {
renderLocationsTable();
} catch (error) {
console.error('Error loading locations:', error);
locationsTableBody.innerHTML = '<tr><td colspan="6">Error loading locations</td></tr>';
locationsTableBody.innerHTML = '<tr><td colspan="7">Error loading locations</td></tr>';
}
}
@ -114,14 +114,17 @@ document.addEventListener('DOMContentLoaded', () => {
new Date(location.created_at) > twentyFourHoursAgo
);
const persistentLocations = allLocations.filter(location => location.persistent);
document.getElementById('total-count').textContent = allLocations.length;
document.getElementById('active-count').textContent = activeLocations.length;
document.getElementById('expired-count').textContent = allLocations.length - activeLocations.length;
document.getElementById('persistent-count').textContent = persistentLocations.length;
}
function renderLocationsTable() {
if (allLocations.length === 0) {
locationsTableBody.innerHTML = '<tr><td colspan="6">No locations found</td></tr>';
locationsTableBody.innerHTML = '<tr><td colspan="7">No locations found</td></tr>';
return;
}
@ -143,6 +146,13 @@ document.addEventListener('DOMContentLoaded', () => {
</td>
<td class="address-cell" title="${location.address}">${location.address}</td>
<td>${location.description || ''}</td>
<td>
<button class="btn ${location.persistent ? 'btn-save' : 'btn-edit'}"
onclick="togglePersistent(${location.id})"
title="${location.persistent ? 'Remove persistent status' : 'Mark as persistent'}">
${location.persistent ? '🔒 Yes' : '🔓 No'}
</button>
</td>
<td title="${formattedDate}">${getTimeAgo(location.created_at)}</td>
<td>
<div class="action-buttons">
@ -178,6 +188,13 @@ document.addEventListener('DOMContentLoaded', () => {
<input type="text" class="edit-input" id="edit-description-${location.id}"
value="${location.description || ''}" placeholder="Description">
</td>
<td>
<button class="btn ${location.persistent ? 'btn-save' : 'btn-edit'}"
onclick="togglePersistent(${location.id})"
title="${location.persistent ? 'Remove persistent status' : 'Mark as persistent'}">
${location.persistent ? '🔒 Yes' : '🔓 No'}
</button>
</td>
<td title="${formattedDate}">${getTimeAgo(location.created_at)}</td>
<td>
<div class="action-buttons">
@ -262,6 +279,41 @@ document.addEventListener('DOMContentLoaded', () => {
}
};
window.togglePersistent = async (id) => {
const location = allLocations.find(loc => loc.id === id);
if (!location) return;
const newPersistentStatus = !location.persistent;
const confirmMessage = newPersistentStatus
? 'Mark this report as persistent? It will not auto-expire after 24 hours.'
: 'Remove persistent status? This report will expire normally after 24 hours.';
if (!confirm(confirmMessage)) {
return;
}
try {
const response = await fetch(`/api/admin/locations/${id}/persistent`, {
method: 'PATCH',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${authToken}`
},
body: JSON.stringify({ persistent: newPersistentStatus })
});
if (response.ok) {
loadLocations(); // Reload to get updated data
} else {
const error = await response.json();
alert(`Error updating persistent status: ${error.error}`);
}
} catch (error) {
console.error('Error toggling persistent status:', error);
alert('Error updating persistent status. Please try again.');
}
};
window.deleteLocation = async (id) => {
if (!confirm('Are you sure you want to delete this location report?')) {
return;