Remove auto-visit GPS tracking

- Removed startVisitTracking / stopVisitTracking functions
- Removed checkProximityToAssets and haversineM
- Removed logAutoVisit and all visit timer logic
- Removed visitTracker UI div on Map tab
- Removed GPS watchPosition call (was polling at ~1-5s intervals)
- Kept server-side /api/visits endpoints (used by heatmap) and one-shot GPS for scan check-in

Reason: GPS auto-tracker can't distinguish machines above/below each other
on different floors.
This commit is contained in:
2026-05-24 17:41:43 -04:00
parent 839fcdedc5
commit 34da18530f
-150
View File
@@ -1353,10 +1353,6 @@
<span class="map-chip" id="chipGeoGPS" onclick="if(navigator.geolocation)navigator.geolocation.getCurrentPosition(p=>{map.setView([p.coords.latitude,p.coords.longitude],15)})">◎ My GPS</span>
</div>
<div id="mapContainer"></div>
<!-- Auto-visit status indicator -->
<div id="visitTracker" style="display:none;margin-top:6px;padding:8px 12px;background:var(--green-bg);border-radius:var(--radius-sm);font-size:12px;color:var(--green);">
📍 Near <span id="vtAssetName"></span> · <span id="vtTimer">0 min</span>
</div>
</div>
<!-- ═══════════════════════════════════════════════════════════════════════
@@ -1904,9 +1900,6 @@
}
setTimeout(() => map.invalidateSize(), 150);
loadAssetPins(); // Refresh pins on every tab visit
startVisitTracking();
} else {
stopVisitTracking();
}
// Dispatch event so child tab implementations can hook in
@@ -4578,149 +4571,6 @@
}
}
// ═══════════════════════════════════════════════════════════════════════
// AUTO-VISIT LOGGING (Client-side GPS tracking)
// ═══════════════════════════════════════════════════════════════════════
let geoWatchId = null;
let visitTimers = {};
function startVisitTracking() {
if (!navigator.geolocation) return;
if (geoWatchId !== null) return; // already tracking
geoWatchId = navigator.geolocation.watchPosition(
pos => {
const lat = pos.coords.latitude;
const lng = pos.coords.longitude;
AppState.gpsLat = lat;
AppState.gpsLng = lng;
// Show GPS center chip on map
const chip = document.getElementById('chipGeoGPS');
if (chip) chip.style.display = '';
checkProximityToAssets(lat, lng);
},
err => { /* silently fail — GPS permission may change */ },
{ enableHighAccuracy: true, maximumAge: 30000, timeout: 20000 }
);
}
function stopVisitTracking() {
if (geoWatchId !== null) {
navigator.geolocation.clearWatch(geoWatchId);
geoWatchId = null;
}
// Clear all timers
Object.keys(visitTimers).forEach(k => clearTimeout(visitTimers[k]?._timer));
visitTimers = {};
document.getElementById('visitTracker').style.display = 'none';
}
// Haversine distance in meters
function haversineM(lat1, lng1, lat2, lng2) {
const R = 6371000;
const dLat = (lat2 - lat1) * Math.PI / 180;
const dLng = (lng2 - lng1) * Math.PI / 180;
const a = Math.sin(dLat / 2) ** 2 +
Math.cos(lat1 * Math.PI / 180) * Math.cos(lat2 * Math.PI / 180) *
Math.sin(dLng / 2) ** 2;
return R * 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
}
function checkProximityToAssets(userLat, userLng) {
let nearestAsset = null;
let nearestDist = Infinity;
assetMarkers.forEach(({ asset }) => {
if (asset.latitude == null || asset.longitude == null) return;
const dist = haversineM(userLat, userLng, asset.latitude, asset.longitude);
if (dist < nearestDist) {
nearestDist = dist;
nearestAsset = asset;
}
// Check if within threshold
if (dist <= VISIT_THRESHOLD_M) {
if (!visitTimers[asset.id]) {
visitTimers[asset.id] = {
startTime: Date.now(),
lastSeen: Date.now(),
asset: asset,
};
} else {
visitTimers[asset.id].lastSeen = Date.now();
}
} else {
// Asset no longer in range — clear timer
if (visitTimers[asset.id]) {
clearTimeout(visitTimers[asset.id]._timer);
delete visitTimers[asset.id];
}
}
});
// Check if any timer has reached threshold
const now = Date.now();
const tracker = document.getElementById('visitTracker');
let trackingAsset = null;
for (const [id, timer] of Object.entries(visitTimers)) {
const elapsed = Math.floor((now - timer.startTime) / 60000);
timer._elapsed = elapsed;
if (timer.asset === nearestAsset) {
trackingAsset = timer;
}
// If 10+ minutes, auto-log a visit via checkin
if (elapsed >= VISIT_DURATION_MIN && !timer._logged) {
timer._logged = true;
logAutoVisit(timer.asset);
}
// Cleanup stale timers (not seen in 2 minutes)
if (now - timer.lastSeen > 120000) {
clearTimeout(timer._timer);
delete visitTimers[id];
}
}
// Update visit tracker UI
if (trackingAsset && trackingAsset._elapsed >= 0) {
tracker.style.display = 'block';
document.getElementById('vtAssetName').textContent = trackingAsset.asset.name;
document.getElementById('vtTimer').textContent = trackingAsset._elapsed + ' min';
if (trackingAsset._elapsed >= VISIT_DURATION_MIN) {
tracker.style.background = 'var(--accent-bg)';
tracker.style.color = 'var(--accent2)';
}
} else {
tracker.style.display = 'none';
}
}
async function logAutoVisit(asset) {
try {
// Log a visit record (auto-detected stay)
await api('/api/visits', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
asset_id: asset.id,
user_id: AppState.currentUser?.id || null,
latitude: AppState.gpsLat,
longitude: AppState.gpsLng,
duration_minutes: VISIT_DURATION_MIN,
}),
});
showToast('📍 Visit auto-logged for ' + asset.name);
// Refresh heatmap if visible
if (heatVisible) await loadHeatmapData();
} catch (e) {
console.error('Auto-visit log failed:', e);
}
}
// =========================================================================
function renderGpsPreview(lat, lng, containerEl) {
if (!containerEl) return;