femydatagent commited on
Commit
faead47
·
verified ·
1 Parent(s): aa4f8dd

undefined - Initial Deployment

Browse files
Files changed (2) hide show
  1. README.md +7 -5
  2. index.html +582 -19
README.md CHANGED
@@ -1,10 +1,12 @@
1
  ---
2
- title: Gps Tracker For Salesman
3
- emoji: 🐠
4
- colorFrom: indigo
5
- colorTo: indigo
6
  sdk: static
7
  pinned: false
 
 
8
  ---
9
 
10
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
1
  ---
2
+ title: gps-tracker-for-salesman
3
+ emoji: 🐳
4
+ colorFrom: gray
5
+ colorTo: yellow
6
  sdk: static
7
  pinned: false
8
+ tags:
9
+ - deepsite
10
  ---
11
 
12
+ Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
index.html CHANGED
@@ -1,19 +1,582 @@
1
- <!doctype html>
2
- <html>
3
- <head>
4
- <meta charset="utf-8" />
5
- <meta name="viewport" content="width=device-width" />
6
- <title>My static Space</title>
7
- <link rel="stylesheet" href="style.css" />
8
- </head>
9
- <body>
10
- <div class="card">
11
- <h1>Welcome to your static Space!</h1>
12
- <p>You can modify this app directly by editing <i>index.html</i> in the Files and versions tab.</p>
13
- <p>
14
- Also don't forget to check the
15
- <a href="https://huggingface.co/docs/hub/spaces" target="_blank">Spaces documentation</a>.
16
- </p>
17
- </div>
18
- </body>
19
- </html>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>GPS Tracker App</title>
7
+ <script src="https://cdn.tailwindcss.com"></script>
8
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
9
+ <link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css" />
10
+ <script src="https://maps.googleapis.com/maps/api/js?key=YOUR_API_KEY&libraries=places"></script>
11
+ <style>
12
+ #map { height: 300px; }
13
+ .device-card.active {
14
+ border-color: #3b82f6;
15
+ background-color: #eff6ff;
16
+ }
17
+ .battery-low {
18
+ color: #ef4444;
19
+ }
20
+ .battery-medium {
21
+ color: #f59e0b;
22
+ }
23
+ .battery-high {
24
+ color: #10b981;
25
+ }
26
+ .connection-dot {
27
+ width: 10px;
28
+ height: 10px;
29
+ border-radius: 50%;
30
+ display: inline-block;
31
+ }
32
+ .connected {
33
+ background-color: #10b981;
34
+ }
35
+ .disconnected {
36
+ background-color: #ef4444;
37
+ }
38
+ .connecting {
39
+ background-color: #f59e0b;
40
+ animation: pulse 2s infinite;
41
+ }
42
+ @keyframes pulse {
43
+ 0% { opacity: 1; }
44
+ 50% { opacity: 0.5; }
45
+ 100% { opacity: 1; }
46
+ }
47
+ </style>
48
+ </head>
49
+ <body class="bg-gray-100 font-sans">
50
+ <div class="max-w-md mx-auto bg-white min-h-screen shadow-lg overflow-hidden">
51
+ <!-- Header -->
52
+ <header class="bg-blue-600 text-white p-4 shadow-md">
53
+ <div class="flex justify-between items-center">
54
+ <h1 class="text-xl font-bold">GPS Tracker</h1>
55
+ <div class="flex space-x-4">
56
+ <button id="refreshBtn" class="text-white">
57
+ <i class="fas fa-sync-alt"></i>
58
+ </button>
59
+ <button id="settingsBtn" class="text-white">
60
+ <i class="fas fa-cog"></i>
61
+ </button>
62
+ </div>
63
+ </div>
64
+ </header>
65
+
66
+ <!-- Main Content -->
67
+ <main class="p-4">
68
+ <!-- Connection Status -->
69
+ <div class="bg-white rounded-lg shadow p-4 mb-4">
70
+ <div class="flex justify-between items-center">
71
+ <h2 class="font-semibold text-lg">Connection Status</h2>
72
+ <span id="connectionStatus" class="connection-dot disconnected"></span>
73
+ </div>
74
+ <div class="mt-2">
75
+ <select id="connectionType" class="w-full p-2 border rounded">
76
+ <option value="usb">USB Device</option>
77
+ <option value="wifi">WiFi Device</option>
78
+ <option value="bluetooth">Bluetooth Device</option>
79
+ </select>
80
+ </div>
81
+ <button id="connectBtn" class="mt-2 w-full bg-blue-600 text-white py-2 rounded-lg">
82
+ Connect Device
83
+ </button>
84
+ </div>
85
+
86
+ <!-- Map View -->
87
+ <div class="bg-white rounded-lg shadow p-4 mb-4">
88
+ <h2 class="font-semibold text-lg mb-2">Live Location</h2>
89
+ <div id="map" class="rounded-lg"></div>
90
+ <div class="mt-2 flex justify-between items-center">
91
+ <div>
92
+ <span class="text-sm text-gray-600">Last updated: <span id="lastUpdate">Never</span></span>
93
+ </div>
94
+ <button id="centerMapBtn" class="text-blue-600 text-sm">
95
+ <i class="fas fa-crosshairs"></i> Center
96
+ </button>
97
+ </div>
98
+ </div>
99
+
100
+ <!-- Device Info -->
101
+ <div id="deviceInfo" class="bg-white rounded-lg shadow p-4 mb-4 hidden">
102
+ <h2 class="font-semibold text-lg mb-2">Device Information</h2>
103
+ <div class="grid grid-cols-2 gap-4">
104
+ <div>
105
+ <p class="text-sm text-gray-500">Device ID</p>
106
+ <p id="deviceId" class="font-medium">-</p>
107
+ </div>
108
+ <div>
109
+ <p class="text-sm text-gray-500">Battery Level</p>
110
+ <p id="batteryLevel" class="font-medium">-</p>
111
+ </div>
112
+ <div>
113
+ <p class="text-sm text-gray-500">Signal Strength</p>
114
+ <p id="signalStrength" class="font-medium">-</p>
115
+ </div>
116
+ <div>
117
+ <p class="text-sm text-gray-500">Connection Type</p>
118
+ <p id="connectionTypeDisplay" class="font-medium">-</p>
119
+ </div>
120
+ </div>
121
+ </div>
122
+
123
+ <!-- Location History -->
124
+ <div class="bg-white rounded-lg shadow p-4">
125
+ <div class="flex justify-between items-center mb-2">
126
+ <h2 class="font-semibold text-lg">Location History</h2>
127
+ <button id="showHistoryBtn" class="text-blue-600 text-sm">
128
+ <i class="fas fa-history"></i> View All
129
+ </button>
130
+ </div>
131
+ <div id="historyList" class="space-y-2">
132
+ <p class="text-gray-500 text-center py-4">No history available yet</p>
133
+ </div>
134
+ </div>
135
+ </main>
136
+
137
+ <!-- Bottom Navigation -->
138
+ <nav class="bg-white border-t border-gray-200 flex justify-around p-3">
139
+ <button class="text-blue-600 p-2">
140
+ <i class="fas fa-map-marker-alt text-xl"></i>
141
+ <span class="block text-xs">Map</span>
142
+ </button>
143
+ <button class="text-gray-500 p-2" onclick="showStationaryLocations()">
144
+ <i class="fas fa-clock text-xl"></i>
145
+ <span class="block text-xs">Stays</span>
146
+ </button>
147
+ <button class="text-gray-500 p-2">
148
+ <i class="fas fa-chart-line text-xl"></i>
149
+ <span class="block text-xs">Analytics</span>
150
+ </button>
151
+ <button class="text-gray-500 p-2">
152
+ <i class="fas fa-user text-xl"></i>
153
+ <span class="block text-xs">Account</span>
154
+ </button>
155
+ </nav>
156
+ </div>
157
+
158
+ <!-- Settings Modal -->
159
+ <div id="settingsModal" class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center hidden">
160
+ <div class="bg-white rounded-lg p-6 w-full max-w-md mx-4">
161
+ <div class="flex justify-between items-center mb-4">
162
+ <h3 class="text-lg font-semibold">Settings</h3>
163
+ <button id="closeSettingsBtn" class="text-gray-500">
164
+ <i class="fas fa-times"></i>
165
+ </button>
166
+ </div>
167
+ <div class="space-y-4">
168
+ <div>
169
+ <label class="block text-sm font-medium text-gray-700 mb-1">Update Interval</label>
170
+ <select class="w-full p-2 border rounded">
171
+ <option>1 second</option>
172
+ <option selected>5 seconds</option>
173
+ <option>10 seconds</option>
174
+ <option>30 seconds</option>
175
+ <option>1 minute</option>
176
+ </select>
177
+ </div>
178
+ <div>
179
+ <label class="block text-sm font-medium text-gray-700 mb-1">Map Type</label>
180
+ <select class="w-full p-2 border rounded">
181
+ <option>Standard</option>
182
+ <option>Satellite</option>
183
+ <option>Terrain</option>
184
+ </select>
185
+ </div>
186
+ <div class="flex items-center">
187
+ <input type="checkbox" id="notifications" class="mr-2" checked>
188
+ <label for="notifications" class="text-sm font-medium text-gray-700">Enable Notifications</label>
189
+ </div>
190
+ <div class="flex items-center">
191
+ <input type="checkbox" id="batteryAlerts" class="mr-2" checked>
192
+ <label for="batteryAlerts" class="text-sm font-medium text-gray-700">Battery Alerts</label>
193
+ </div>
194
+ </div>
195
+ <div class="mt-6 pt-4 border-t">
196
+ <button class="w-full bg-blue-600 text-white py-2 rounded-lg">Save Settings</button>
197
+ </div>
198
+ </div>
199
+ </div>
200
+
201
+ <!-- History Modal -->
202
+ <div id="historyModal" class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center hidden">
203
+ <div class="bg-white rounded-lg p-6 w-full max-w-md mx-4 max-h-[80vh] overflow-y-auto">
204
+ <div class="flex justify-between items-center mb-4">
205
+ <h3 class="text-lg font-semibold">Location History</h3>
206
+ <button id="closeHistoryBtn" class="text-gray-500">
207
+ <i class="fas fa-times"></i>
208
+ </button>
209
+ </div>
210
+ <div id="fullHistoryList" class="space-y-2">
211
+ <!-- History items will be added here -->
212
+ </div>
213
+ </div>
214
+ </div>
215
+
216
+ <script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"></script>
217
+ <script>
218
+ // Initialize map
219
+ let map;
220
+ let marker;
221
+ let deviceConnected = false;
222
+ let connectionInterval;
223
+ let updateInterval = 300000; // 5 minutes
224
+ let locationHistory = [];
225
+ let stationaryLocations = [];
226
+ let history = [];
227
+
228
+ // Mock device data for demonstration
229
+ const mockDevices = [
230
+ { id: 'GPS-001', battery: 85, signal: 'Strong', lastLocation: [51.505, -0.09] },
231
+ { id: 'GPS-002', battery: 42, signal: 'Medium', lastLocation: [51.51, -0.1] },
232
+ { id: 'GPS-003', battery: 12, signal: 'Weak', lastLocation: [51.515, -0.095] }
233
+ ];
234
+
235
+ document.addEventListener('DOMContentLoaded', function() {
236
+ initMap();
237
+ setupEventListeners();
238
+ });
239
+
240
+ function initMap() {
241
+ // Default to London coordinates
242
+ map = L.map('map').setView([51.505, -0.09], 13);
243
+
244
+ L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
245
+ attribution: '&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
246
+ }).addTo(map);
247
+
248
+ // Add a default marker
249
+ marker = L.marker([51.505, -0.09]).addTo(map)
250
+ .bindPopup('Device location will appear here');
251
+ }
252
+
253
+ function setupEventListeners() {
254
+ // Connect button
255
+ document.getElementById('connectBtn').addEventListener('click', toggleConnection);
256
+
257
+ // Center map button
258
+ document.getElementById('centerMapBtn').addEventListener('click', centerMap);
259
+
260
+ // Settings buttons
261
+ document.getElementById('settingsBtn').addEventListener('click', () => {
262
+ document.getElementById('settingsModal').classList.remove('hidden');
263
+ });
264
+
265
+ document.getElementById('closeSettingsBtn').addEventListener('click', () => {
266
+ document.getElementById('settingsModal').classList.add('hidden');
267
+ });
268
+
269
+ // History buttons
270
+ document.getElementById('showHistoryBtn').addEventListener('click', showHistoryModal);
271
+
272
+ document.getElementById('closeHistoryBtn').addEventListener('click', () => {
273
+ document.getElementById('historyModal').classList.add('hidden');
274
+ });
275
+
276
+ // Refresh button
277
+ document.getElementById('refreshBtn').addEventListener('click', refreshData);
278
+ }
279
+
280
+ function toggleConnection() {
281
+ const connectBtn = document.getElementById('connectBtn');
282
+ const connectionStatus = document.getElementById('connectionStatus');
283
+ const connectionType = document.getElementById('connectionType').value;
284
+
285
+ if (deviceConnected) {
286
+ // Disconnect
287
+ clearInterval(connectionInterval);
288
+ deviceConnected = false;
289
+ connectBtn.textContent = 'Connect Device';
290
+ connectBtn.classList.remove('bg-red-600');
291
+ connectBtn.classList.add('bg-blue-600');
292
+ connectionStatus.classList.remove('connected');
293
+ connectionStatus.classList.add('disconnected');
294
+
295
+ // Hide device info
296
+ document.getElementById('deviceInfo').classList.add('hidden');
297
+
298
+ // Show notification
299
+ showNotification('Device disconnected', 'info');
300
+ } else {
301
+ // Connect
302
+ connectBtn.textContent = 'Disconnect';
303
+ connectBtn.classList.remove('bg-blue-600');
304
+ connectBtn.classList.add('bg-red-600');
305
+ connectionStatus.classList.remove('disconnected');
306
+ connectionStatus.classList.add('connecting');
307
+
308
+ // Simulate connection delay
309
+ setTimeout(() => {
310
+ deviceConnected = true;
311
+ connectionStatus.classList.remove('connecting');
312
+ connectionStatus.classList.add('connected');
313
+
314
+ // Show device info
315
+ const deviceInfo = document.getElementById('deviceInfo');
316
+ deviceInfo.classList.remove('hidden');
317
+
318
+ // Start updating data
319
+ updateDeviceData();
320
+ connectionInterval = setInterval(updateDeviceData, updateInterval);
321
+
322
+ // Show notification
323
+ showNotification('Device connected successfully', 'success');
324
+ }, 2000);
325
+ }
326
+ }
327
+
328
+ function updateDeviceData() {
329
+ if (!deviceConnected) return;
330
+
331
+ // For demo purposes, we'll use mock data
332
+ const randomDevice = mockDevices[Math.floor(Math.random() * mockDevices.length)];
333
+
334
+ // Check if device stayed in same location for more than 5 minutes
335
+ if (locationHistory.length > 0) {
336
+ const lastLocation = locationHistory[locationHistory.length - 1];
337
+ const distance = getDistanceBetweenPoints(
338
+ lastLocation.coords[0], lastLocation.coords[1],
339
+ randomDevice.lastLocation[0], randomDevice.lastLocation[1]
340
+ );
341
+
342
+ // If distance is less than 50 meters (consider same location)
343
+ if (distance < 50) {
344
+ const timeDiff = (new Date() - lastLocation.timestamp) / (1000 * 60);
345
+ if (timeDiff >= 5) {
346
+ // Check if we already have this stationary location
347
+ const existingLocation = stationaryLocations.find(loc =>
348
+ getDistanceBetweenPoints(
349
+ loc.coords[0], loc.coords[1],
350
+ randomDevice.lastLocation[0], randomDevice.lastLocation[1]
351
+ ) < 50
352
+ );
353
+
354
+ if (!existingLocation) {
355
+ // Get address using Google Maps API
356
+ getAddressFromCoords(randomDevice.lastLocation[0], randomDevice.lastLocation[1])
357
+ .then(address => {
358
+ stationaryLocations.push({
359
+ coords: randomDevice.lastLocation,
360
+ address: address,
361
+ startTime: lastLocation.timestamp,
362
+ endTime: new Date(),
363
+ deviceId: randomDevice.id
364
+ });
365
+ showNotification(`Device stayed at ${address} for more than 5 minutes`, 'info');
366
+ });
367
+ }
368
+ }
369
+ }
370
+ }
371
+ const now = new Date();
372
+
373
+ // Update device info
374
+ document.getElementById('deviceId').textContent = randomDevice.id;
375
+ document.getElementById('connectionTypeDisplay').textContent =
376
+ document.getElementById('connectionType').value.toUpperCase();
377
+ document.getElementById('signalStrength').textContent = randomDevice.signal;
378
+
379
+ // Update battery level with appropriate color
380
+ const batteryElement = document.getElementById('batteryLevel');
381
+ batteryElement.textContent = `${randomDevice.battery}%`;
382
+ batteryElement.className = 'font-medium';
383
+
384
+ if (randomDevice.battery < 20) {
385
+ batteryElement.classList.add('battery-low');
386
+ } else if (randomDevice.battery < 50) {
387
+ batteryElement.classList.add('battery-medium');
388
+ } else {
389
+ batteryElement.classList.add('battery-high');
390
+ }
391
+
392
+ // Update map with new location
393
+ updateMap(randomDevice.lastLocation);
394
+
395
+ // Update last updated time
396
+ document.getElementById('lastUpdate').textContent = now.toLocaleTimeString();
397
+
398
+ // Add to history
399
+ const historyItem = {
400
+ time: now,
401
+ location: randomDevice.lastLocation,
402
+ deviceId: randomDevice.id,
403
+ battery: randomDevice.battery,
404
+ coords: randomDevice.lastLocation,
405
+ timestamp: now
406
+ };
407
+ locationHistory.push(historyItem);
408
+ updateHistoryDisplay();
409
+
410
+ // Show low battery notification if needed
411
+ if (randomDevice.battery < 15) {
412
+ showNotification(`Low battery (${randomDevice.battery}%) on device ${randomDevice.id}`, 'warning');
413
+ }
414
+ }
415
+
416
+ function updateMap(coords) {
417
+ // Remove old marker if exists
418
+ if (marker) {
419
+ map.removeLayer(marker);
420
+ }
421
+
422
+ // Add new marker
423
+ marker = L.marker(coords).addTo(map)
424
+ .bindPopup(`Device location<br>${coords[0].toFixed(4)}, ${coords[1].toFixed(4)}`);
425
+
426
+ // Center map on new location
427
+ map.setView(coords, 13);
428
+ }
429
+
430
+ function centerMap() {
431
+ if (marker) {
432
+ map.setView(marker.getLatLng(), 13);
433
+ }
434
+ }
435
+
436
+ function updateHistoryDisplay() {
437
+ const historyList = document.getElementById('historyList');
438
+ const fullHistoryList = document.getElementById('fullHistoryList');
439
+
440
+ // Clear existing items
441
+ historyList.innerHTML = '';
442
+ fullHistoryList.innerHTML = '';
443
+
444
+ if (history.length === 0) {
445
+ historyList.innerHTML = '<p class="text-gray-500 text-center py-4">No history available yet</p>';
446
+ fullHistoryList.innerHTML = '<p class="text-gray-500 text-center py-4">No history available yet</p>';
447
+ return;
448
+ }
449
+
450
+ // Show only last 3 items in main view
451
+ const recentHistory = history.slice(0, 3);
452
+
453
+ recentHistory.forEach(item => {
454
+ const historyItem = createHistoryItemElement(item);
455
+ historyList.appendChild(historyItem);
456
+ });
457
+
458
+ // Show all items in modal
459
+ history.forEach(item => {
460
+ const historyItem = createHistoryItemElement(item);
461
+ fullHistoryList.appendChild(historyItem);
462
+ });
463
+ }
464
+
465
+ function createHistoryItemElement(item) {
466
+ const element = document.createElement('div');
467
+ element.className = 'bg-gray-50 p-3 rounded-lg';
468
+
469
+ const time = document.createElement('p');
470
+ time.className = 'text-sm font-medium';
471
+ time.textContent = item.time.toLocaleTimeString();
472
+
473
+ const coords = document.createElement('p');
474
+ coords.className = 'text-xs text-gray-500';
475
+ coords.textContent = `${item.location[0].toFixed(4)}, ${item.location[1].toFixed(4)}`;
476
+
477
+ const deviceInfo = document.createElement('p');
478
+ deviceInfo.className = 'text-xs mt-1';
479
+ deviceInfo.innerHTML = `<span class="font-medium">${item.deviceId}</span> • Battery: <span class="${getBatteryClass(item.battery)}">${item.battery}%</span>`;
480
+
481
+ element.appendChild(time);
482
+ element.appendChild(coords);
483
+ element.appendChild(deviceInfo);
484
+
485
+ return element;
486
+ }
487
+
488
+ function getBatteryClass(level) {
489
+ if (level < 20) return 'battery-low';
490
+ if (level < 50) return 'battery-medium';
491
+ return 'battery-high';
492
+ }
493
+
494
+ function showHistoryModal() {
495
+ document.getElementById('historyModal').classList.remove('hidden');
496
+ }
497
+
498
+ function refreshData() {
499
+ if (deviceConnected) {
500
+ updateDeviceData();
501
+ showNotification('Data refreshed', 'info');
502
+ } else {
503
+ showNotification('Connect a device first', 'warning');
504
+ }
505
+ }
506
+
507
+ function showNotification(message, type) {
508
+ // In a real app, this would show a proper notification
509
+ console.log(`${type.toUpperCase()}: ${message}`);
510
+ }
511
+
512
+ function getDistanceBetweenPoints(lat1, lon1, lat2, lon2) {
513
+ // Haversine formula to calculate distance between two points
514
+ const R = 6371; // Radius of the earth in km
515
+ const dLat = deg2rad(lat2 - lat1);
516
+ const dLon = deg2rad(lon2 - lon1);
517
+ const a =
518
+ Math.sin(dLat/2) * Math.sin(dLat/2) +
519
+ Math.cos(deg2rad(lat1)) * Math.cos(deg2rad(lat2)) *
520
+ Math.sin(dLon/2) * Math.sin(dLon/2);
521
+ const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));
522
+ const d = R * c * 1000; // Distance in meters
523
+ return d;
524
+ }
525
+
526
+ function deg2rad(deg) {
527
+ return deg * (Math.PI/180);
528
+ }
529
+
530
+ function getAddressFromCoords(lat, lng) {
531
+ return new Promise((resolve) => {
532
+ const geocoder = new google.maps.Geocoder();
533
+ const latlng = { lat: lat, lng: lng };
534
+
535
+ geocoder.geocode({ location: latlng }, (results, status) => {
536
+ if (status === 'OK' && results[0]) {
537
+ resolve(results[0].formatted_address);
538
+ } else {
539
+ resolve(`${lat.toFixed(4)}, ${lng.toFixed(4)}`);
540
+ }
541
+ });
542
+ });
543
+ }
544
+
545
+ // Add a new function to display stationary locations
546
+ function showStationaryLocations() {
547
+ const modal = document.createElement('div');
548
+ modal.className = 'fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50';
549
+ modal.innerHTML = `
550
+ <div class="bg-white rounded-lg p-6 w-full max-w-md mx-4 max-h-[80vh] overflow-y-auto">
551
+ <div class="flex justify-between items-center mb-4">
552
+ <h3 class="text-lg font-semibold">Stationary Locations</h3>
553
+ <button onclick="this.parentElement.parentElement.parentElement.remove()" class="text-gray-500">
554
+ <i class="fas fa-times"></i>
555
+ </button>
556
+ </div>
557
+ <div class="space-y-3">
558
+ ${stationaryLocations.length > 0 ?
559
+ stationaryLocations.map(loc => `
560
+ <div class="bg-gray-50 p-3 rounded-lg">
561
+ <p class="font-medium">${loc.address}</p>
562
+ <p class="text-sm text-gray-500">${new Date(loc.startTime).toLocaleString()} - ${new Date(loc.endTime).toLocaleString()}</p>
563
+ <p class="text-xs mt-1">Device: ${loc.deviceId}</p>
564
+ </div>
565
+ `).join('') :
566
+ '<p class="text-gray-500 text-center py-4">No stationary locations recorded</p>'
567
+ }
568
+ </div>
569
+ </div>
570
+ `;
571
+ document.body.appendChild(modal);
572
+ }
573
+
574
+ // For demo purposes, simulate a connection after 3 seconds
575
+ setTimeout(() => {
576
+ if (!deviceConnected) {
577
+ document.getElementById('connectBtn').click();
578
+ }
579
+ }, 3000);
580
+ </script>
581
+ <p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=femydatagent/gps-tracker-for-salesman" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body>
582
+ </html>