| 
									
										
										
										
											2025-09-21 16:57:24 +02:00
										 |  |  | <!DOCTYPE html> | 
					
						
							|  |  |  | <html lang="en"> | 
					
						
							|  |  |  | <head> | 
					
						
							|  |  |  |     <meta charset="UTF-8"> | 
					
						
							|  |  |  |     <meta name="viewport" content="width=device-width, initial-scale=1.0"> | 
					
						
							|  |  |  |     <title>View Saved Events - OpenEventDatabase</title> | 
					
						
							|  |  |  |     <script src="https://unpkg.com/maplibre-gl@3.0.0/dist/maplibre-gl.js"></script> | 
					
						
							|  |  |  |     <link href="https://unpkg.com/maplibre-gl@3.0.0/dist/maplibre-gl.css" rel="stylesheet" /> | 
					
						
							|  |  |  |     <link rel="stylesheet" href="/static/demo/demo_styles.css"> | 
					
						
							|  |  |  |     <script defer src="https://use.fontawesome.com/releases/v5.15.4/js/all.js"></script> | 
					
						
							|  |  |  |     <script src="/static/demo/demo_auth.js"></script> | 
					
						
							|  |  |  |     <style> | 
					
						
							|  |  |  |         #map {  | 
					
						
							|  |  |  |             position: absolute;  | 
					
						
							|  |  |  |             top: 0;  | 
					
						
							|  |  |  |             bottom: 0;  | 
					
						
							|  |  |  |             width: 100%;  | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     </style> | 
					
						
							|  |  |  | </head> | 
					
						
							|  |  |  | <body> | 
					
						
							|  |  |  |     <div id="map"></div> | 
					
						
							|  |  |  |      | 
					
						
							|  |  |  |     <!-- Hidden OAuth2 configuration for the JavaScript module --> | 
					
						
							|  |  |  |     <input type="hidden" id="osmClientId" value="{{ client_id }}"> | 
					
						
							|  |  |  |     <input type="hidden" id="osmClientSecret" value="{{ client_secret }}"> | 
					
						
							|  |  |  |     <input type="hidden" id="osmRedirectUri" value="{{ client_redirect }}"> | 
					
						
							|  |  |  |      | 
					
						
							|  |  |  |     <div class="map-overlay"> | 
					
						
							|  |  |  |         <h2>Your Saved Events</h2> | 
					
						
							| 
									
										
										
										
											2025-09-22 11:44:25 +02:00
										 |  |  |         {% include 'partials/demo_nav.html' %} | 
					
						
							| 
									
										
										
										
											2025-09-21 16:57:24 +02:00
										 |  |  |          | 
					
						
							|  |  |  |         <!-- Authentication section --> | 
					
						
							|  |  |  |         <div id="auth-section" class="auth-section"> | 
					
						
							|  |  |  |             <h3>OpenStreetMap Authentication</h3> | 
					
						
							|  |  |  |             <p>Authenticate with your OpenStreetMap account to include your username in reports.</p> | 
					
						
							|  |  |  |             <a href="https://www.openstreetmap.org/oauth2/authorize?client_id={{ client_id }}&redirect_uri={{ client_redirect }}&response_type=code&scope=read_prefs" class="osm-login-btn"> | 
					
						
							|  |  |  |                 <span class="osm-logo"></span> | 
					
						
							|  |  |  |                 Login with OpenStreetMap | 
					
						
							|  |  |  |             </a> | 
					
						
							|  |  |  |             <script> | 
					
						
							|  |  |  |                 // Replace server-side auth section with JavaScript-rendered version if available | 
					
						
							|  |  |  |                 document.addEventListener('DOMContentLoaded', function() { | 
					
						
							|  |  |  |                     if (window.osmAuth) { | 
					
						
							|  |  |  |                         const clientId = document.getElementById('osmClientId').value; | 
					
						
							|  |  |  |                         const redirectUri = document.getElementById('osmRedirectUri').value; | 
					
						
							|  |  |  |                         const authSection = document.getElementById('auth-section'); | 
					
						
							|  |  |  |                          | 
					
						
							|  |  |  |                         // Only replace if osmAuth is loaded and has renderAuthSection method | 
					
						
							|  |  |  |                         if (osmAuth.renderAuthSection) { | 
					
						
							|  |  |  |                             authSection.innerHTML = osmAuth.renderAuthSection(clientId, redirectUri); | 
					
						
							|  |  |  |                         } | 
					
						
							|  |  |  |                     } | 
					
						
							|  |  |  |                 }); | 
					
						
							|  |  |  |             </script> | 
					
						
							|  |  |  |         </div> | 
					
						
							|  |  |  |          | 
					
						
							|  |  |  |         <div id="event-count"></div> | 
					
						
							|  |  |  |          | 
					
						
							|  |  |  |         <div id="event-list" class="event-list"></div> | 
					
						
							|  |  |  |          | 
					
						
							|  |  |  |         <div class="controls"> | 
					
						
							|  |  |  |             <button id="refresh-btn"> | 
					
						
							|  |  |  |                 <i class="fas fa-sync-alt"></i> Refresh | 
					
						
							|  |  |  |             </button> | 
					
						
							|  |  |  |             <button id="clear-btn" class="danger"> | 
					
						
							|  |  |  |                 <i class="fas fa-trash-alt"></i> Clear All | 
					
						
							|  |  |  |             </button> | 
					
						
							|  |  |  |         </div> | 
					
						
							|  |  |  |     </div> | 
					
						
							|  |  |  |      | 
					
						
							|  |  |  |     <script> | 
					
						
							|  |  |  |         // Initialize the map | 
					
						
							|  |  |  |         const map = new maplibregl.Map({ | 
					
						
							|  |  |  |             container: 'map', | 
					
						
							| 
									
										
										
										
											2025-09-23 11:26:44 +02:00
										 |  |  |             style: 'https://tiles.openfreemap.org/styles/liberty', | 
					
						
							| 
									
										
										
										
											2025-09-21 16:57:24 +02:00
										 |  |  |             center: [2.2137, 46.2276], // Default center (center of metropolitan France) | 
					
						
							|  |  |  |             zoom: 5 | 
					
						
							|  |  |  |         }); | 
					
						
							|  |  |  |          | 
					
						
							|  |  |  |         // Add navigation controls | 
					
						
							|  |  |  |         map.addControl(new maplibregl.NavigationControl()); | 
					
						
							|  |  |  |          | 
					
						
							|  |  |  |         // Add attribution control with OpenStreetMap attribution | 
					
						
							|  |  |  |         map.addControl(new maplibregl.AttributionControl({ | 
					
						
							|  |  |  |             customAttribution: '© <a href="https://www.openstreetmap.org/copyright" target="_blank">OpenStreetMap</a> contributors' | 
					
						
							|  |  |  |         })); | 
					
						
							|  |  |  |          | 
					
						
							|  |  |  |         // Store markers for later reference | 
					
						
							|  |  |  |         let markers = []; | 
					
						
							|  |  |  |          | 
					
						
							|  |  |  |         // Load events when the map is loaded | 
					
						
							|  |  |  |         map.on('load', function() { | 
					
						
							|  |  |  |             loadEvents(); | 
					
						
							|  |  |  |         }); | 
					
						
							|  |  |  |          | 
					
						
							|  |  |  |         // Function to load events from localStorage | 
					
						
							|  |  |  |         function loadEvents() { | 
					
						
							|  |  |  |             // Clear existing markers | 
					
						
							|  |  |  |             markers.forEach(marker => marker.remove()); | 
					
						
							|  |  |  |             markers = []; | 
					
						
							|  |  |  |              | 
					
						
							|  |  |  |             // Get events from localStorage | 
					
						
							|  |  |  |             const savedEvents = JSON.parse(localStorage.getItem('oedb_events') || '[]'); | 
					
						
							|  |  |  |              | 
					
						
							|  |  |  |             // Update event count | 
					
						
							|  |  |  |             document.getElementById('event-count').textContent =  | 
					
						
							|  |  |  |                 `${savedEvents.length} event${savedEvents.length !== 1 ? 's' : ''} found`; | 
					
						
							|  |  |  |              | 
					
						
							|  |  |  |             // Clear event list | 
					
						
							|  |  |  |             const eventList = document.getElementById('event-list'); | 
					
						
							|  |  |  |             eventList.innerHTML = ''; | 
					
						
							|  |  |  |              | 
					
						
							|  |  |  |             if (savedEvents.length === 0) { | 
					
						
							|  |  |  |                 // Show message if no events | 
					
						
							|  |  |  |                 eventList.innerHTML = ` | 
					
						
							|  |  |  |                     <div class="no-events"> | 
					
						
							|  |  |  |                         <i class="fas fa-info-circle"></i> | 
					
						
							|  |  |  |                         <p>No saved events found.</p> | 
					
						
							|  |  |  |                         <p>Report road issues using the <a href="/demo/traffic">Report Issue</a> page.</p> | 
					
						
							|  |  |  |                     </div> | 
					
						
							|  |  |  |                 `; | 
					
						
							|  |  |  |                 return; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |              | 
					
						
							|  |  |  |             // Sort events by timestamp (newest first) | 
					
						
							|  |  |  |             savedEvents.sort((a, b) => { | 
					
						
							|  |  |  |                 return new Date(b.timestamp) - new Date(a.timestamp); | 
					
						
							|  |  |  |             }); | 
					
						
							|  |  |  |              | 
					
						
							|  |  |  |             // Create bounds object for fitting map to events | 
					
						
							|  |  |  |             const bounds = new maplibregl.LngLatBounds(); | 
					
						
							|  |  |  |              | 
					
						
							|  |  |  |             // Add each event to the map and list | 
					
						
							|  |  |  |             savedEvents.forEach((event, index) => { | 
					
						
							|  |  |  |                 // Get event properties | 
					
						
							|  |  |  |                 const properties = event.properties; | 
					
						
							|  |  |  |                 const coordinates = event.geometry.coordinates; | 
					
						
							|  |  |  |                 const label = properties.label || 'Unnamed Event'; | 
					
						
							|  |  |  |                 const what = properties.what || 'unknown'; | 
					
						
							|  |  |  |                 const timestamp = event.timestamp ? new Date(event.timestamp) : new Date(); | 
					
						
							|  |  |  |                 const formattedDate = timestamp.toLocaleString(); | 
					
						
							|  |  |  |                  | 
					
						
							|  |  |  |                 // Determine event type and color | 
					
						
							|  |  |  |                 let eventType = 'other'; | 
					
						
							|  |  |  |                 let markerColor = '#777'; | 
					
						
							|  |  |  |                  | 
					
						
							|  |  |  |                 if (what.includes('pothole')) { | 
					
						
							|  |  |  |                     eventType = 'pothole'; | 
					
						
							|  |  |  |                     markerColor = '#ff9800'; | 
					
						
							|  |  |  |                 } else if (what.includes('obstacle')) { | 
					
						
							|  |  |  |                     eventType = 'obstacle'; | 
					
						
							|  |  |  |                     markerColor = '#f44336'; | 
					
						
							|  |  |  |                 } else if (what.includes('vehicle')) { | 
					
						
							|  |  |  |                     eventType = 'vehicle'; | 
					
						
							|  |  |  |                     markerColor = '#2196f3'; | 
					
						
							|  |  |  |                 } else if (what.includes('danger')) { | 
					
						
							|  |  |  |                     eventType = 'danger'; | 
					
						
							|  |  |  |                     markerColor = '#9c27b0'; | 
					
						
							|  |  |  |                 } else if (what.includes('traffic')) { | 
					
						
							|  |  |  |                     eventType = 'traffic'; | 
					
						
							|  |  |  |                     markerColor = '#ff3860'; | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |                  | 
					
						
							|  |  |  |                 // Create marker | 
					
						
							|  |  |  |                 const marker = new maplibregl.Marker({ | 
					
						
							|  |  |  |                     color: markerColor | 
					
						
							|  |  |  |                 }) | 
					
						
							|  |  |  |                 .setLngLat(coordinates) | 
					
						
							|  |  |  |                 .addTo(map); | 
					
						
							|  |  |  |                  | 
					
						
							|  |  |  |                 // Store marker reference | 
					
						
							|  |  |  |                 markers.push(marker); | 
					
						
							|  |  |  |                  | 
					
						
							|  |  |  |                 // Extend bounds with event coordinates | 
					
						
							|  |  |  |                 bounds.extend(coordinates); | 
					
						
							|  |  |  |                  | 
					
						
							|  |  |  |                 // Create popup content | 
					
						
							|  |  |  |                 let popupContent = `<div class="popup-content"> | 
					
						
							|  |  |  |                     <h3>${label}</h3> | 
					
						
							|  |  |  |                     <table>`; | 
					
						
							|  |  |  |                  | 
					
						
							|  |  |  |                 // Add event properties to popup | 
					
						
							|  |  |  |                 for (const key in properties) { | 
					
						
							|  |  |  |                     if (key === 'label') continue; // Skip label as it's already in the title | 
					
						
							|  |  |  |                      | 
					
						
							|  |  |  |                     let displayValue = properties[key]; | 
					
						
							|  |  |  |                     let displayKey = key; | 
					
						
							|  |  |  |                      | 
					
						
							|  |  |  |                     // Format key for display | 
					
						
							|  |  |  |                     displayKey = displayKey.replace(/:/g, ' ').replace(/([A-Z])/g, ' $1'); | 
					
						
							|  |  |  |                     displayKey = displayKey.charAt(0).toUpperCase() + displayKey.slice(1); | 
					
						
							|  |  |  |                      | 
					
						
							|  |  |  |                     // Format value for display | 
					
						
							|  |  |  |                     if (key === 'start' || key === 'stop') { | 
					
						
							|  |  |  |                         try { | 
					
						
							|  |  |  |                             displayValue = new Date(displayValue).toLocaleString(); | 
					
						
							|  |  |  |                         } catch (e) { | 
					
						
							|  |  |  |                             // Keep original value if date parsing fails | 
					
						
							|  |  |  |                         } | 
					
						
							|  |  |  |                     } | 
					
						
							|  |  |  |                      | 
					
						
							|  |  |  |                     popupContent += ` | 
					
						
							|  |  |  |                         <tr> | 
					
						
							|  |  |  |                             <td>${displayKey}</td> | 
					
						
							|  |  |  |                             <td>${displayValue}</td> | 
					
						
							|  |  |  |                         </tr>`; | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |                  | 
					
						
							|  |  |  |                 // Add timestamp to popup | 
					
						
							|  |  |  |                 popupContent += ` | 
					
						
							|  |  |  |                     <tr> | 
					
						
							|  |  |  |                         <td>Reported</td> | 
					
						
							|  |  |  |                         <td>${formattedDate}</td> | 
					
						
							|  |  |  |                     </tr>`; | 
					
						
							|  |  |  |                  | 
					
						
							|  |  |  |                 popupContent += `</table></div>`; | 
					
						
							|  |  |  |                  | 
					
						
							|  |  |  |                 // Create popup | 
					
						
							|  |  |  |                 const popup = new maplibregl.Popup({ | 
					
						
							|  |  |  |                     closeButton: true, | 
					
						
							|  |  |  |                     closeOnClick: true | 
					
						
							|  |  |  |                 }).setHTML(popupContent); | 
					
						
							|  |  |  |                  | 
					
						
							|  |  |  |                 // Attach popup to marker | 
					
						
							|  |  |  |                 marker.setPopup(popup); | 
					
						
							|  |  |  |                  | 
					
						
							|  |  |  |                 // Add event to list | 
					
						
							|  |  |  |                 const eventItem = document.createElement('div'); | 
					
						
							|  |  |  |                 eventItem.className = 'event-item'; | 
					
						
							|  |  |  |                 eventItem.innerHTML = ` | 
					
						
							|  |  |  |                     <span class="event-type ${eventType}">${eventType}</span> | 
					
						
							|  |  |  |                     <h3>${label}</h3> | 
					
						
							|  |  |  |                     <p>${properties.where || 'Unknown location'}</p> | 
					
						
							|  |  |  |                     <p class="event-date">${formattedDate}</p> | 
					
						
							|  |  |  |                 `; | 
					
						
							|  |  |  |                  | 
					
						
							|  |  |  |                 // Add click event to list item | 
					
						
							|  |  |  |                 eventItem.addEventListener('click', () => { | 
					
						
							|  |  |  |                     // Fly to event location | 
					
						
							|  |  |  |                     map.flyTo({ | 
					
						
							|  |  |  |                         center: coordinates, | 
					
						
							|  |  |  |                         zoom: 14 | 
					
						
							|  |  |  |                     }); | 
					
						
							|  |  |  |                      | 
					
						
							|  |  |  |                     // Open popup | 
					
						
							|  |  |  |                     marker.togglePopup(); | 
					
						
							|  |  |  |                 }); | 
					
						
							|  |  |  |                  | 
					
						
							|  |  |  |                 // Add to list | 
					
						
							|  |  |  |                 eventList.appendChild(eventItem); | 
					
						
							|  |  |  |             }); | 
					
						
							|  |  |  |              | 
					
						
							|  |  |  |             // Fit map to bounds if there are events | 
					
						
							|  |  |  |             if (!bounds.isEmpty()) { | 
					
						
							|  |  |  |                 map.fitBounds(bounds, { | 
					
						
							|  |  |  |                     padding: 50, | 
					
						
							|  |  |  |                     maxZoom: 12 | 
					
						
							|  |  |  |                 }); | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |          | 
					
						
							|  |  |  |         // Handle refresh button click | 
					
						
							|  |  |  |         document.getElementById('refresh-btn').addEventListener('click', function() { | 
					
						
							|  |  |  |             loadEvents(); | 
					
						
							|  |  |  |         }); | 
					
						
							|  |  |  |          | 
					
						
							|  |  |  |         // Handle clear button click | 
					
						
							|  |  |  |         document.getElementById('clear-btn').addEventListener('click', function() { | 
					
						
							|  |  |  |             if (confirm('Are you sure you want to clear all saved events? This cannot be undone.')) { | 
					
						
							|  |  |  |                 // Clear localStorage | 
					
						
							|  |  |  |                 localStorage.removeItem('oedb_events'); | 
					
						
							|  |  |  |                  | 
					
						
							|  |  |  |                 // Reload events | 
					
						
							|  |  |  |                 loadEvents(); | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         }); | 
					
						
							|  |  |  |     </script> | 
					
						
							|  |  |  | </body> | 
					
						
							|  |  |  | </html> |