chore: remove accidentally committed gps backup file

This commit is contained in:
2026-05-29 23:17:48 -04:00
parent 17b870e4cc
commit 9ae0f271ea
3 changed files with 354 additions and 60 deletions
+351 -57
View File
@@ -827,16 +827,6 @@
.map-search-row input {
flex: 1; min-width: 0;
}
.map-filter-chips {
display: flex; gap: 4px; flex-wrap: wrap; margin-bottom: 6px;
}
.map-filter-chip {
font-size: 10px; font-weight: 600; padding: 3px 8px;
border-radius: 12px; border: 1px solid var(--border);
background: var(--card2); color: var(--text2); cursor: pointer;
transition: all 0.12s;
}
.map-filter-chip.active { background: var(--accent); border-color: var(--accent); color: #fff; }
/* Asset list below map */
.map-asset-list {
@@ -1281,6 +1271,22 @@
<div id="detailFields"></div>
</div>
<!-- Photo card -->
<div class="card" id="detailPhotoCard" style="display:none;">
<div class="card-title">📷 Photo</div>
<div style="margin-top:8px;text-align:center;">
<img id="detailPhotoImg" style="max-width:100%;max-height:60vh;border-radius:8px;cursor:pointer;"
onclick="openPhotoModal()" alt="Asset photo">
</div>
</div>
<!-- Photo modal (fullscreen) -->
<div id="photoModal" style="display:none;position:fixed;top:0;left:0;width:100%;height:100%;background:rgba(0,0,0,0.9);z-index:9999;cursor:pointer;justify-content:center;align-items:center;"
onclick="closePhotoModal(event)">
<img id="photoModalImg" style="max-width:95%;max-height:95%;border-radius:4px;object-fit:contain;">
<span style="position:absolute;top:16px;right:24px;color:#fff;font-size:32px;font-weight:bold;cursor:pointer;" onclick="closePhotoModal()"></span>
</div>
<!-- Directions card -->
<div class="card" id="detailDirections" style="display:none;">
<div class="card-title">📍 Directions &amp; Access</div>
@@ -1378,8 +1384,86 @@
oninput="mapDebounceFilter()">
<button class="btn btn-secondary btn-sm" onclick="document.getElementById('mapSearchInput').value='';mapDebounceFilter()" style="white-space:nowrap;"></button>
</div>
<!-- Filter chips -->
<div class="map-filter-chips" id="mapFilterChips"></div>
<!-- Filter toggle + panel (same pattern as assets tab) -->
<div style="display:flex;gap:6px;align-items:flex-start;margin-bottom:8px;">
<button class="filter-toggle" id="mapFilterToggle" onclick="toggleMapFilterPanel()" title="Toggle filter panel">
🎯 Filters <span id="mapFilterCount" class="ft-count" style="display:none;">0</span>
</button>
</div>
<div class="filter-panel" id="mapFilterPanel">
<div class="fp-row">
<div style="flex:1">
<div class="fp-label">Category</div>
<select id="mapFilterCategory" class="input-field" onchange="onMapFilterChange()">
<option value="">All categories</option>
</select>
</div>
<div style="flex:1">
<div class="fp-label">Make</div>
<select id="mapFilterMake" class="input-field" onchange="onMapFilterChange()">
<option value="">All makes</option>
</select>
</div>
</div>
<div class="fp-row">
<div style="flex:1">
<div class="fp-label">Location Type</div>
<select id="mapFilterDisneyFilter" class="input-field" onchange="onMapFilterChange()">
<option value="">All locations</option>
<option value="disney">🏰 Disney</option>
<option value="non_disney">🏢 Non-Disney</option>
</select>
</div>
<div style="flex:1">
<div class="fp-label">Disney Park</div>
<select id="mapFilterDisneyPark" class="input-field" onchange="onMapFilterChange()">
<option value="">All parks</option>
<option disabled>──────────</option>
<option value="magic-kingdom">🏰 Magic Kingdom</option>
<option value="epcot">🌍 Epcot</option>
<option value="hollywood-studios">🎬 Hollywood Studios</option>
<option value="animal-kingdom">🌿 Animal Kingdom</option>
<option value="disney-springs">🛍️ Disney Springs</option>
<option value="resort">🏨 Resort</option>
<option value="office">🏢 Office</option>
<option value="other">📍 Other</option>
</select>
</div>
</div>
<div class="fp-row">
<div style="flex:1">
<div class="fp-label">Customer</div>
<select id="mapFilterCustomer" class="input-field" onchange="onMapFilterChange()">
<option value="">All customers</option>
</select>
</div>
<div style="flex:1">
<div class="fp-label">Location</div>
<select id="mapFilterLocation" class="input-field" onchange="onMapFilterChange()" disabled>
<option value="">All locations</option>
</select>
</div>
</div>
<div class="fp-row">
<div style="flex:1">
<div class="fp-label">Branch</div>
<select id="mapFilterBranch" class="input-field" onchange="onMapFilterChange()">
<option value="">All branches</option>
</select>
</div>
<div style="flex:1">
<div class="fp-label">Assigned To</div>
<select id="mapFilterAssignedTo" class="input-field" onchange="onMapFilterChange()">
<option value="">Anyone</option>
</select>
</div>
</div>
<div class="fp-actions">
<button class="btn btn-outline btn-xs" onclick="clearMapFilters()" style="flex:1;">✕ Clear all filters</button>
<button class="btn btn-outline btn-xs" onclick="toggleMapFilterPanel()" style="flex:1;">✓ Done</button>
</div>
</div>
<div class="active-filters" id="mapActiveFilters"></div>
<!-- Map -->
<div id="mapContainer"></div>
<!-- Asset list -->
@@ -3831,6 +3915,18 @@
df('Created', formatDate(a.created_at)) +
df('Updated', formatDate(a.updated_at));
// Photo
const photoCard = document.getElementById('detailPhotoCard');
const photoImg = document.getElementById('detailPhotoImg');
if (a.photo_path) {
photoCard.style.display = 'block';
photoImg.src = a.photo_path;
photoImg.onload = () => { photoImg.style.display = 'inline'; };
photoImg.onerror = () => { photoCard.style.display = 'none'; };
} else {
photoCard.style.display = 'none';
}
// Location / Directions
const hasDir = a.address || a.building_name || a.building_number || a.floor || a.room || a.trailer_number || a.walking_directions || a.map_link || a.parking_location;
const dirCard = document.getElementById('detailDirections');
@@ -4016,6 +4112,34 @@
}).catch(() => {});
}
// ── Photo viewer ──────────────────────────────────────────────────
function openPhotoModal() {
const img = document.getElementById('detailPhotoImg');
const modal = document.getElementById('photoModal');
const modalImg = document.getElementById('photoModalImg');
if (!img || !modal || !modalImg) return;
modalImg.src = img.src;
modal.style.display = 'flex';
document.body.style.overflow = 'hidden';
}
function closePhotoModal(event) {
if (event && event.target && event.target.id !== 'photoModal' && event.target.id !== 'photoModalImg' && event.target.tagName !== 'SPAN') return;
const modal = document.getElementById('photoModal');
if (modal) {
modal.style.display = 'none';
document.body.style.overflow = '';
}
}
document.addEventListener('keydown', function(e) {
if (e.key === 'Escape') {
const modal = document.getElementById('photoModal');
if (modal && modal.style.display === 'flex') {
modal.style.display = 'none';
document.body.style.overflow = '';
}
}
});
async function navigateToAsset(mode) {
mode = mode || 'driving';
if (!AppState.currentAssetId) {
@@ -4497,81 +4621,252 @@
}
// ── Map filter state ──────────────────────────────────────────────────
let mapFilters = { category: null, status: null, make: null, disney_park: null, disney_filter: null, q: '',
customer_id: null, location_id: null, assigned_to: null, branch: null };
let mapFilterDropdownsLoaded = false;
function toggleMapFilterPanel() {
const panel = document.getElementById('mapFilterPanel');
const toggle = document.getElementById('mapFilterToggle');
panel.classList.toggle('open');
toggle.classList.toggle('active', panel.classList.contains('open'));
if (panel.classList.contains('open') && !mapFilterDropdownsLoaded) {
loadMapFilterDropdowns();
}
}
async function loadMapFilterDropdowns() {
if (mapFilterDropdownsLoaded) return;
mapFilterDropdownsLoaded = true;
var panel = document.getElementById('mapFilterPanel');
var loadingMsg = document.createElement('div');
loadingMsg.className = 'loading-spinner';
loadingMsg.innerHTML = '<span class="spinner"></span> Loading filters...';
panel.appendChild(loadingMsg);
try {
var cats = await api('/api/categories');
document.getElementById('mapFilterCategory').innerHTML = '<option value="">All categories</option>' +
cats.map(function(c) { return '<option value="' + esc(c) + '">' + esc(c) + '</option>'; }).join('');
var makes = await api('/api/makes');
document.getElementById('mapFilterMake').innerHTML = '<option value="">All makes</option>' +
makes.map(function(m) { return '<option value="' + esc(m) + '">' + esc(m) + '</option>'; }).join('');
var customers = await api('/api/customers');
document.getElementById('mapFilterCustomer').innerHTML = '<option value="">All customers</option>' +
customers.map(function(c) { return '<option value="' + c.id + '">' + esc(c.name) + '</option>'; }).join('');
var users = await api('/api/users');
document.getElementById('mapFilterAssignedTo').innerHTML = '<option value="">Anyone</option>' +
users.filter(function(u) { return u.role === 'technician' || u.role === 'admin'; })
.map(function(u) { return '<option value="' + esc(u.username) + '">' + esc(u.username) + '</option>'; }).join('');
var branches = await api('/api/branches');
document.getElementById('mapFilterBranch').innerHTML = '<option value="">All branches</option>' +
branches.branches.map(function(b) { return '<option value="' + esc(b.name) + '">' + esc(b.name) + ' (' + b.machine_count + ')</option>'; }).join('');
} catch (e) {
showToast('Failed to load filter options', true);
} finally {
if (loadingMsg && loadingMsg.parentNode) loadingMsg.parentNode.removeChild(loadingMsg);
}
}
function onMapFilterChange() {
mapFilters.category = document.getElementById('mapFilterCategory').value || null;
mapFilters.make = document.getElementById('mapFilterMake').value || null;
var disneyFilterVal = document.getElementById('mapFilterDisneyFilter').value;
if (disneyFilterVal) {
mapFilters.disney_filter = disneyFilterVal;
mapFilters.disney_park = null;
} else {
var parkVal = document.getElementById('mapFilterDisneyPark').value;
mapFilters.disney_park = parkVal || null;
mapFilters.disney_filter = null;
}
mapFilters.customer_id = document.getElementById('mapFilterCustomer').value || null;
var locEl = document.getElementById('mapFilterLocation');
var prevCust = locEl.dataset.customerId;
var custEl = document.getElementById('mapFilterCustomer');
if (custEl.value !== prevCust) {
locEl.innerHTML = '<option value="">All locations</option>';
locEl.disabled = true;
mapFilters.location_id = null;
if (custEl.value) {
loadMapLocationsForCustomer(custEl.value);
}
} else {
mapFilters.location_id = locEl.value || null;
}
locEl.dataset.customerId = custEl.value;
mapFilters.assigned_to = document.getElementById('mapFilterAssignedTo').value || null;
mapFilters.branch = document.getElementById('mapFilterBranch').value || null;
renderMapActiveFilterTags();
applyMapFilter();
}
async function loadMapLocationsForCustomer(customerId) {
try {
var locations = await api('/api/locations?customer_id=' + customerId);
var locEl = document.getElementById('mapFilterLocation');
locEl.innerHTML = '<option value="">All locations</option>' +
locations.map(function(l) { return '<option value="' + l.id + '">' + esc(l.name) + '</option>'; }).join('');
locEl.disabled = false;
} catch (e) { showToast('Failed to load locations', true); }
}
function getMapActiveFilterCount() {
var count = 0;
if (mapFilters.category) count++;
if (mapFilters.make) count++;
if (mapFilters.disney_park) count++;
if (mapFilters.disney_filter) count++;
if (mapFilters.customer_id) count++;
if (mapFilters.location_id) count++;
if (mapFilters.assigned_to) count++;
if (mapFilters.branch) count++;
if (mapFilters.q) count++;
return count;
}
function renderMapActiveFilterTags() {
var el = document.getElementById('mapActiveFilters');
var countEl = document.getElementById('mapFilterCount');
var toggle = document.getElementById('mapFilterToggle');
var count = getMapActiveFilterCount();
countEl.textContent = count;
countEl.style.display = count > 0 ? 'inline' : 'none';
if (toggle) toggle.classList.toggle('active', count > 0);
var tags = [];
if (mapFilters.q) tags.push({key:'q', label:'🔍 "' + mapFilters.q + '"'});
if (mapFilters.category) tags.push({key:'category', label:'📂 ' + mapFilters.category});
if (mapFilters.make) tags.push({key:'make', label:'🏭 ' + mapFilters.make});
if (mapFilters.disney_park) tags.push({key:'disney_park', label: parkIcon(mapFilters.disney_park) + ' ' + parkName(mapFilters.disney_park)});
if (mapFilters.disney_filter === 'disney') tags.push({key:'disney_filter', label:'🏰 Disney only'});
if (mapFilters.disney_filter === 'non_disney') tags.push({key:'disney_filter', label:'🏢 Non-Disney'});
if (mapFilters.customer_id) {
var custSel = document.getElementById('mapFilterCustomer');
var txt = custSel.options[custSel.selectedIndex] ? custSel.options[custSel.selectedIndex].text : 'Customer';
tags.push({key:'customer_id', label:'🟢 ' + txt});
}
if (mapFilters.location_id) {
var locSel = document.getElementById('mapFilterLocation');
var txt = locSel.options[locSel.selectedIndex] ? locSel.options[locSel.selectedIndex].text : 'Location';
tags.push({key:'location_id', label:'📍 ' + txt});
}
if (mapFilters.assigned_to) tags.push({key:'assigned_to', label:'👤 ' + mapFilters.assigned_to});
if (mapFilters.branch) tags.push({key:'branch', label:'🏢 ' + mapFilters.branch});
el.innerHTML = tags.map(function(t) {
return '<span class="filter-tag">' + esc(t.label) + ' <span class="ft-remove" onclick="removeMapFilter(\'' + t.key + '\')"></span></span>';
}).join('');
}
function removeMapFilter(key) {
if (key === 'q') {
document.getElementById('mapSearchInput').value = '';
mapFilters.q = '';
} else if (key === 'category') {
document.getElementById('mapFilterCategory').value = '';
mapFilters.category = null;
} else if (key === 'make') {
document.getElementById('mapFilterMake').value = '';
mapFilters.make = null;
} else if (key === 'disney_park') {
document.getElementById('mapFilterDisneyPark').value = '';
mapFilters.disney_park = null;
} else if (key === 'disney_filter') {
document.getElementById('mapFilterDisneyFilter').value = '';
mapFilters.disney_filter = null;
} else if (key === 'customer_id') {
document.getElementById('mapFilterCustomer').value = '';
document.getElementById('mapFilterLocation').innerHTML = '<option value="">All locations</option>';
document.getElementById('mapFilterLocation').disabled = true;
mapFilters.customer_id = null;
mapFilters.location_id = null;
} else if (key === 'location_id') {
document.getElementById('mapFilterLocation').value = '';
mapFilters.location_id = null;
} else if (key === 'assigned_to') {
document.getElementById('mapFilterAssignedTo').value = '';
mapFilters.assigned_to = null;
} else if (key === 'branch') {
document.getElementById('mapFilterBranch').value = '';
mapFilters.branch = null;
}
renderMapActiveFilterTags();
applyMapFilter();
}
function clearMapFilters() {
mapFilters = { category: null, status: null, make: null, disney_park: null, disney_filter: null, q: '',
customer_id: null, location_id: null, assigned_to: null, branch: null };
document.getElementById('mapSearchInput').value = '';
document.getElementById('mapFilterCategory').value = '';
document.getElementById('mapFilterMake').value = '';
document.getElementById('mapFilterDisneyPark').value = '';
document.getElementById('mapFilterDisneyFilter').value = '';
document.getElementById('mapFilterCustomer').value = '';
document.getElementById('mapFilterLocation').innerHTML = '<option value="">All locations</option>';
document.getElementById('mapFilterLocation').disabled = true;
document.getElementById('mapFilterAssignedTo').value = '';
document.getElementById('mapFilterBranch').value = '';
renderMapActiveFilterTags();
applyMapFilter();
}
async function loadAssetPins() {
// Store markers on a namespace so we can clear them
if (!window._assetPins) window._assetPins = [];
// Clear existing markers
window._assetPins.forEach(function(m) { if (map) map.removeLayer(m); });
window._assetPins = [];
try {
const assets = await api('/api/assets?has_gps=true&limit=2000');
_mapAllAssets = assets;
buildMapFilterChips(assets);
// Apply current filter
applyMapFilter();
} catch (e) {
console.error('Failed to load asset pins:', e);
showToast('Failed to load map assets — ' + (e.message || 'connection error'), true);
}
}
function buildMapFilterChips(assets) {
var cats = {}; var statuses = {};
assets.forEach(function(a) {
if (a.category) cats[a.category] = (cats[a.category] || 0) + 1;
if (a.status) statuses[a.status] = (statuses[a.status] || 0) + 1;
});
var catKeys = Object.keys(cats).sort();
var statusKeys = Object.keys(statuses).sort();
var html = '';
html += '<span class="map-filter-chip' + (!_mapActiveCategory ? ' active' : '') + '" onclick="mapSetCategory(\'\')">All</span>';
catKeys.forEach(function(c) {
html += '<span class="map-filter-chip' + (_mapActiveCategory === c ? ' active' : '') + '" onclick="mapSetCategory(\'' + c.replace(/'/g, "\\'") + '\')">' + esc(c) + ' (' + cats[c] + ')</span>';
});
html += '<span class="map-filter-chip' + (!_mapActiveStatus ? ' active' : '') + '" onclick="mapSetStatus(\'\')">All Status</span>';
statusKeys.forEach(function(s) {
html += '<span class="map-filter-chip' + (_mapActiveStatus === s ? ' active' : '') + '" onclick="mapSetStatus(\'' + s.replace(/'/g, "\\'") + '\')">' + esc(s) + '</span>';
});
document.getElementById('mapFilterChips').innerHTML = html;
}
function mapSetCategory(cat) {
_mapActiveCategory = _mapActiveCategory === cat ? '' : cat;
_mapActiveStatus = '';
applyMapFilter();
buildMapFilterChips(_mapAllAssets);
}
function mapSetStatus(status) {
_mapActiveStatus = _mapActiveStatus === status ? '' : status;
_mapActiveCategory = '';
applyMapFilter();
buildMapFilterChips(_mapAllAssets);
}
function mapDebounceFilter() {
if (_mapFilterTimer) clearTimeout(_mapFilterTimer);
mapFilters.q = (document.getElementById('mapSearchInput').value || '').toLowerCase().trim();
renderMapActiveFilterTags();
_mapFilterTimer = setTimeout(applyMapFilter, 200);
}
function applyMapFilter() {
if (_mapFilterTimer) { clearTimeout(_mapFilterTimer); _mapFilterTimer = null; }
var q = (document.getElementById('mapSearchInput').value || '').toLowerCase().trim();
var q = mapFilters.q;
// Clear existing pins
window._assetPins.forEach(function(m) { if (map) map.removeLayer(m); });
window._assetPins = [];
var matched = [];
_mapAllAssets.forEach(function(a) {
var allAssets = _mapAllAssets || [];
allAssets.forEach(function(a) {
// Category filter
if (_mapActiveCategory && a.category !== _mapActiveCategory) return;
// Status filter
if (_mapActiveStatus && a.status !== _mapActiveStatus) return;
if (mapFilters.category && a.category !== mapFilters.category) return;
// Make filter
if (mapFilters.make && a.make !== mapFilters.make) return;
// Disney/non-disney filter
if (mapFilters.disney_filter === 'disney' && !a.is_disney) return;
if (mapFilters.disney_filter === 'non_disney' && a.is_disney) return;
// Disney park filter
if (mapFilters.disney_park && a.disney_park !== mapFilters.disney_park) return;
// Text search
if (q) {
var haystack = ((a.name || '') + ' ' + (a.machine_id || '') + ' ' + (a.category || '') + ' ' + (a.description || '')).toLowerCase();
var haystack = ((a.name || '') + ' ' + (a.machine_id || '') + ' ' + (a.category || '') + ' ' + (a.company || '') + ' ' + (a.description || '')).toLowerCase();
if (haystack.indexOf(q) === -1) return;
}
matched.push(a);
@@ -4619,7 +4914,6 @@
el.innerHTML = '<div class="map-asset-empty">📦 No assets match your search</div>';
return;
}
// Sort by name
var sorted = assets.slice().sort(function(a, b) { return (a.name || '').localeCompare(b.name || ''); });
var html = '';
sorted.forEach(function(a) {
+1 -1
View File
@@ -3,7 +3,7 @@
// Caches app shell + CDN deps. Network-first for API, cache fallback for static.
// ═══════════════════════════════════════════════════════════════════════════
const CACHE_NAME = 'canteen-v6';
const CACHE_NAME = 'canteen-v7';
// App shell — core resources needed to boot the PWA
const APP_SHELL = [
+2 -2
View File
@@ -142,8 +142,8 @@ class TestGPSControls:
assert "GPS location not available" in body()
def test_pins_chip_ui(self):
"""Pin toggle chip exists."""
assert "chipPins" in body()
"""🎯 Filters toggle button exists in map tab."""
assert "mapFilterToggle" in body()
class TestHeatmap: