| | {% extends "base.html" %} |
| |
|
| | {% block head %} |
| | <link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css" /> |
| | <style> |
| | .container-fluid { |
| | padding: 0; |
| | height: 100vh; |
| | display: flex; |
| | } |
| | |
| | #emergencyMap { |
| | height: 100vh; |
| | width: 70%; |
| | position: relative; |
| | } |
| | |
| | .control-panel { |
| | width: 30%; |
| | height: 100vh; |
| | background: rgba(26, 26, 46, 0.95); |
| | padding: 20px; |
| | overflow-y: auto; |
| | display: flex; |
| | flex-direction: column; |
| | gap: 15px; |
| | } |
| | |
| | .control-section { |
| | background: rgba(255, 255, 255, 0.05); |
| | border-radius: var(--border-radius); |
| | padding: 15px; |
| | margin-bottom: 15px; |
| | } |
| | |
| | .section-title { |
| | color: var(--text-light); |
| | font-size: 1.1rem; |
| | margin-bottom: 10px; |
| | display: flex; |
| | align-items: center; |
| | gap: 8px; |
| | } |
| | |
| | .emergency-button { |
| | background: linear-gradient(135deg, var(--primary-color), var(--accent-color)); |
| | color: white; |
| | border: none; |
| | padding: 12px 20px; |
| | border-radius: var(--border-radius); |
| | font-size: 1rem; |
| | font-weight: bold; |
| | cursor: pointer; |
| | transition: var(--transition); |
| | width: 100%; |
| | margin-bottom: 8px; |
| | display: flex; |
| | align-items: center; |
| | justify-content: center; |
| | gap: 8px; |
| | } |
| | |
| | .emergency-button:hover { |
| | transform: translateY(-2px); |
| | box-shadow: 0 4px 8px rgba(255, 77, 109, 0.3); |
| | } |
| | |
| | .emergency-button.secondary { |
| | background: rgba(255, 255, 255, 0.1); |
| | } |
| | |
| | .emergency-description { |
| | width: 100%; |
| | padding: 10px; |
| | margin-top: 10px; |
| | background: rgba(255, 255, 255, 0.1); |
| | border: 1px solid rgba(255, 255, 255, 0.2); |
| | border-radius: var(--border-radius); |
| | color: var(--text-color); |
| | resize: vertical; |
| | min-height: 60px; |
| | } |
| | |
| | .emergency-timer { |
| | color: var(--primary-color); |
| | font-size: 1.1rem; |
| | text-align: center; |
| | margin-top: 10px; |
| | font-weight: bold; |
| | } |
| | |
| | .medical-form { |
| | display: none; |
| | background: rgba(255, 255, 255, 0.05); |
| | padding: 15px; |
| | border-radius: var(--border-radius); |
| | margin-top: 10px; |
| | } |
| | |
| | .medical-form input, |
| | .medical-form textarea { |
| | width: 100%; |
| | padding: 8px; |
| | margin-bottom: 10px; |
| | background: rgba(255, 255, 255, 0.1); |
| | border: 1px solid rgba(255, 255, 255, 0.2); |
| | border-radius: var(--border-radius); |
| | color: var(--text-color); |
| | } |
| | |
| | .emergency-contacts { |
| | display: flex; |
| | flex-direction: column; |
| | gap: 10px; |
| | } |
| | |
| | .contact-item { |
| | display: flex; |
| | align-items: center; |
| | gap: 10px; |
| | background: rgba(255, 255, 255, 0.05); |
| | padding: 10px; |
| | border-radius: var(--border-radius); |
| | } |
| | |
| | .contact-item button { |
| | background: none; |
| | border: none; |
| | color: var(--text-light); |
| | cursor: pointer; |
| | padding: 5px; |
| | } |
| | |
| | .contact-item button:hover { |
| | color: var(--primary-color); |
| | } |
| | |
| | .hospital-list { |
| | max-height: 200px; |
| | overflow-y: auto; |
| | } |
| | |
| | .hospital-item { |
| | background: rgba(255, 255, 255, 0.05); |
| | padding: 10px; |
| | margin-bottom: 8px; |
| | border-radius: var(--border-radius); |
| | cursor: pointer; |
| | transition: var(--transition); |
| | } |
| | |
| | .hospital-item:hover { |
| | background: rgba(255, 255, 255, 0.1); |
| | } |
| | |
| | .location-request { |
| | position: absolute; |
| | top: 20px; |
| | left: 50%; |
| | transform: translateX(-50%); |
| | background: rgba(26, 26, 46, 0.95); |
| | padding: 15px 25px; |
| | border-radius: var(--border-radius); |
| | z-index: 1000; |
| | text-align: center; |
| | } |
| | |
| | .location-request button { |
| | background: var(--primary-color); |
| | color: white; |
| | border: none; |
| | padding: 8px 20px; |
| | border-radius: var(--border-radius); |
| | margin-top: 10px; |
| | cursor: pointer; |
| | } |
| | |
| | .medical-records-list { |
| | margin-top: 10px; |
| | max-height: 200px; |
| | overflow-y: auto; |
| | } |
| | |
| | .medical-record-item { |
| | display: flex; |
| | justify-content: space-between; |
| | align-items: center; |
| | background: rgba(255, 255, 255, 0.05); |
| | padding: 10px; |
| | margin-bottom: 8px; |
| | border-radius: var(--border-radius); |
| | } |
| | |
| | .record-info { |
| | flex: 1; |
| | } |
| | |
| | .record-actions { |
| | display: flex; |
| | gap: 8px; |
| | } |
| | |
| | .record-actions button { |
| | background: none; |
| | border: none; |
| | color: var(--text-light); |
| | cursor: pointer; |
| | padding: 5px; |
| | } |
| | |
| | .record-actions button:hover { |
| | color: var(--primary-color); |
| | } |
| | |
| | .hospital-header { |
| | display: flex; |
| | justify-content: space-between; |
| | align-items: center; |
| | margin-bottom: 5px; |
| | } |
| | |
| | .hospital-details { |
| | font-size: 0.9em; |
| | color: var(--text-light); |
| | margin-bottom: 10px; |
| | } |
| | |
| | .hospital-details i { |
| | width: 20px; |
| | text-align: center; |
| | margin-right: 5px; |
| | } |
| | |
| | .select-hospital-btn { |
| | background: var(--primary-color); |
| | color: white; |
| | border: none; |
| | padding: 5px 10px; |
| | border-radius: var(--border-radius); |
| | cursor: pointer; |
| | font-size: 0.9em; |
| | } |
| | |
| | .hospital-item.selected { |
| | border: 2px solid var(--primary-color); |
| | } |
| | |
| | .directions-panel { |
| | max-height: 300px; |
| | overflow-y: auto; |
| | } |
| | |
| | .direction-step { |
| | display: flex; |
| | align-items: center; |
| | padding: 5px; |
| | border-bottom: 1px solid rgba(255, 255, 255, 0.1); |
| | } |
| | |
| | .step-number { |
| | background: var(--primary-color); |
| | color: white; |
| | width: 24px; |
| | height: 24px; |
| | border-radius: 50%; |
| | display: flex; |
| | align-items: center; |
| | justify-content: center; |
| | margin-right: 10px; |
| | } |
| | |
| | .step-instruction { |
| | flex: 1; |
| | } |
| | |
| | .step-distance { |
| | color: var(--text-light); |
| | font-size: 0.9em; |
| | } |
| | |
| | .modal { |
| | position: fixed; |
| | top: 0; |
| | left: 0; |
| | width: 100%; |
| | height: 100%; |
| | background: rgba(0, 0, 0, 0.7); |
| | display: flex; |
| | justify-content: center; |
| | align-items: center; |
| | z-index: 1000; |
| | } |
| | |
| | .modal-content { |
| | background: var(--bg-color); |
| | padding: 20px; |
| | border-radius: var(--border-radius); |
| | width: 90%; |
| | max-width: 500px; |
| | } |
| | |
| | .contact-form { |
| | display: flex; |
| | flex-direction: column; |
| | gap: 15px; |
| | } |
| | |
| | .contact-form input { |
| | width: 100%; |
| | padding: 10px; |
| | border: 1px solid rgba(255, 255, 255, 0.2); |
| | border-radius: var(--border-radius); |
| | background: rgba(255, 255, 255, 0.1); |
| | color: var(--text-color); |
| | } |
| | |
| | .contact-form-buttons { |
| | display: flex; |
| | gap: 10px; |
| | justify-content: flex-end; |
| | } |
| | |
| | .file-upload-container { |
| | position: relative; |
| | margin: 10px 0; |
| | } |
| | |
| | .file-upload-input { |
| | display: none; |
| | } |
| | |
| | .file-upload-label { |
| | display: block; |
| | padding: 10px; |
| | background: rgba(255, 255, 255, 0.1); |
| | border: 1px dashed rgba(255, 255, 255, 0.2); |
| | border-radius: var(--border-radius); |
| | text-align: center; |
| | cursor: pointer; |
| | transition: var(--transition); |
| | } |
| | |
| | .file-upload-label:hover { |
| | background: rgba(255, 255, 255, 0.15); |
| | } |
| | |
| | .file-info { |
| | margin-top: 5px; |
| | font-size: 0.9em; |
| | color: var(--text-light); |
| | } |
| | |
| | .ecg-analysis-section { |
| | margin-top: 15px; |
| | } |
| | |
| | .ecg-analysis-section h4 { |
| | margin-bottom: 10px; |
| | color: var(--text-light); |
| | } |
| | |
| | .analysis-result { |
| | margin-top: 15px; |
| | padding: 10px; |
| | background: rgba(255, 255, 255, 0.05); |
| | border-radius: var(--border-radius); |
| | display: none; |
| | } |
| | |
| | .analysis-result.show { |
| | display: block; |
| | } |
| | |
| | .analysis-result h5 { |
| | color: var(--text-light); |
| | margin-bottom: 10px; |
| | } |
| | |
| | .analysis-result pre { |
| | background: rgba(0, 0, 0, 0.2); |
| | padding: 10px; |
| | border-radius: var(--border-radius); |
| | overflow-x: auto; |
| | white-space: pre-wrap; |
| | word-wrap: break-word; |
| | } |
| | </style> |
| | {% endblock %} |
| |
|
| | {% block content %} |
| | <div class="container-fluid"> |
| | <div id="emergencyMap"></div> |
| | |
| | <div class="control-panel"> |
| | <div class="control-section"> |
| | <div class="section-title"> |
| | <i class="fas fa-map-marker-alt"></i> Location |
| | </div> |
| | <div id="currentLocation">Requesting location...</div> |
| | <button id="startJourney" class="emergency-button secondary" disabled> |
| | <i class="fas fa-route"></i> Start Journey to Nearest Hospital |
| | </button> |
| | </div> |
| | |
| | <div class="control-section"> |
| | <div class="section-title"> |
| | <i class="fas fa-hospital"></i> Nearby Hospitals |
| | </div> |
| | <div class="hospital-list" id="hospitalList"> |
| | Loading nearby hospitals... |
| | </div> |
| | </div> |
| | |
| | <div class="control-section"> |
| | <div class="section-title"> |
| | <i class="fas fa-phone-alt"></i> Emergency Contacts |
| | </div> |
| | <div class="emergency-contacts" id="emergencyContacts"> |
| | <button class="emergency-button secondary" id="addContact"> |
| | <i class="fas fa-plus"></i> Add Emergency Contact |
| | </button> |
| | </div> |
| | </div> |
| | |
| | <div class="control-section"> |
| | <div class="section-title"> |
| | <i class="fas fa-file-medical"></i> Medical Records |
| | </div> |
| | <div class="file-upload-container"> |
| | <label class="file-upload-label" for="medicalRecordUpload"> |
| | <i class="fas fa-upload"></i> Upload Medical Records |
| | <div class="file-info">Supported formats: CSV, TXT, JSON</div> |
| | </label> |
| | <input type="file" id="medicalRecordUpload" class="file-upload-input" accept=".csv,.txt,.json"> |
| | </div> |
| | <div class="medical-records-list" id="medicalRecordsList"> |
| | |
| | </div> |
| | <button class="emergency-button secondary" id="editMedicalInfo"> |
| | <i class="fas fa-edit"></i> View Medical Information |
| | </button> |
| | <div class="medical-form" id="medicalForm"> |
| | <input type="text" placeholder="Current Medications" id="currentMedications"> |
| | <textarea placeholder="Allergies" id="allergies"></textarea> |
| | <button class="emergency-button" id="saveMedicalInfo">Save Information</button> |
| | </div> |
| | </div> |
| | </div> |
| | </div> |
| |
|
| | <div class="location-request" id="locationRequest"> |
| | <div>This app needs your location to provide emergency services</div> |
| | <button id="allowLocation">Allow Location Access</button> |
| | </div> |
| |
|
| | <script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"></script> |
| | <script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/4.0.1/socket.io.js"></script> |
| | <script> |
| | |
| | window.selectHospital = function(index) { |
| | selectedHospital = nearbyHospitals[index]; |
| | if (selectedHospital) { |
| | |
| | const startJourneyBtn = document.getElementById('startJourney'); |
| | startJourneyBtn.innerHTML = `<i class="fas fa-route"></i> Start Journey to ${selectedHospital.name}`; |
| | startJourneyBtn.disabled = false; |
| | |
| | |
| | document.querySelectorAll('.hospital-item').forEach((item, i) => { |
| | item.classList.toggle('selected', i === index); |
| | }); |
| | |
| | |
| | map.setView([selectedHospital.lat, selectedHospital.lon], 15); |
| | } |
| | }; |
| | |
| | window.startJourneyToHospital = function(index) { |
| | const hospital = nearbyHospitals[index]; |
| | if (hospital && userLocation) { |
| | |
| | if (currentRoute) { |
| | map.removeLayer(currentRoute); |
| | } |
| | |
| | |
| | fetch(`https://router.project-osrm.org/route/v1/driving/${userLocation.lng},${userLocation.lat};${hospital.lon},${hospital.lat}?overview=full&geometries=geojson&steps=true`) |
| | .then(response => response.json()) |
| | .then(data => { |
| | if (data.routes && data.routes[0]) { |
| | const route = data.routes[0]; |
| | currentRoute = L.geoJSON(route.geometry, { |
| | style: { |
| | color: '#4CAF50', |
| | weight: 5, |
| | opacity: 0.7 |
| | } |
| | }).addTo(map); |
| | |
| | |
| | map.fitBounds(currentRoute.getBounds()); |
| | |
| | |
| | const steps = route.legs[0].steps; |
| | let directions = ` |
| | <div class="directions-panel"> |
| | <h3>Directions to ${hospital.name}</h3> |
| | <div class="directions-list"> |
| | ${steps.map((step, index) => ` |
| | <div class="direction-step"> |
| | <span class="step-number">${index + 1}</span> |
| | <span class="step-instruction">${step.maneuver.instruction}</span> |
| | <span class="step-distance">${Math.round(step.distance)}m</span> |
| | </div> |
| | `).join('')} |
| | </div> |
| | </div> |
| | `; |
| | |
| | |
| | L.popup({maxWidth: 300}) |
| | .setLatLng([hospital.lat, hospital.lon]) |
| | .setContent(directions) |
| | .openOn(map); |
| | } |
| | }); |
| | } |
| | }; |
| | </script> |
| | <script type="module"> |
| | import { initializeApp } from "https://www.gstatic.com/firebasejs/11.6.0/firebase-app.js"; |
| | import { getAuth, onAuthStateChanged, signInWithCustomToken } from "https://www.gstatic.com/firebasejs/11.6.0/firebase-auth.js"; |
| | import { getDatabase, ref, onValue, set, update, remove, push, get } from "https://www.gstatic.com/firebasejs/11.6.0/firebase-database.js"; |
| | |
| | |
| | const firebaseConfig = { |
| | apiKey: "AIzaSyBDr1xcrLxfemIRTydmgjTcG6mHgx919Rs", |
| | authDomain: "help-6661c.firebaseapp.com", |
| | projectId: "help-6661c", |
| | storageBucket: "help-6661c.firebasestorage.app", |
| | messagingSenderId: "2944311795", |
| | appId: "1:2944311795:web:61d2b982c75a446df7f286", |
| | measurementId: "G-H8RJ7C4Z3K", |
| | databaseURL: "https://help-6661c-default-rtdb.firebaseio.com" |
| | }; |
| | |
| | const app = initializeApp(firebaseConfig); |
| | const auth = getAuth(); |
| | const database = getDatabase(); |
| | |
| | |
| | onAuthStateChanged(auth, async (user) => { |
| | if (user) { |
| | console.log('User is signed in:', user.uid); |
| | loadUserData(user.uid); |
| | } else { |
| | console.log('No user is signed in'); |
| | |
| | window.location.href = '/login'; |
| | } |
| | }); |
| | |
| | async function loadUserData(userId) { |
| | try { |
| | |
| | const contactsRef = ref(database, `users/${userId}/emergency_contacts`); |
| | onValue(contactsRef, (snapshot) => { |
| | const contacts = snapshot.val(); |
| | const contactsDiv = document.getElementById('emergencyContacts'); |
| | |
| | |
| | const addButton = document.getElementById('addContact'); |
| | contactsDiv.innerHTML = ''; |
| | contactsDiv.appendChild(addButton); |
| | |
| | if (contacts) { |
| | Object.entries(contacts).forEach(([id, contact]) => { |
| | const contactElement = document.createElement('div'); |
| | contactElement.className = 'contact-item'; |
| | contactElement.dataset.contactId = id; |
| | contactElement.innerHTML = ` |
| | <div class="contact-info"> |
| | <div class="contact-name">${contact.name}</div> |
| | <div class="contact-phone">${contact.phone}</div> |
| | </div> |
| | <div class="contact-actions"> |
| | <button onclick="callContact('${contact.phone}')" title="Call"><i class="fas fa-phone"></i></button> |
| | <button onclick="removeContact('${id}')" title="Remove"><i class="fas fa-trash"></i></button> |
| | </div> |
| | `; |
| | contactsDiv.insertBefore(contactElement, addButton); |
| | }); |
| | } |
| | }); |
| | |
| | |
| | const medicalInfoRef = ref(database, `users/${userId}/medical_info`); |
| | onValue(medicalInfoRef, (snapshot) => { |
| | const medicalInfo = snapshot.val(); |
| | if (medicalInfo) { |
| | document.getElementById('currentMedications').value = medicalInfo.medications || ''; |
| | document.getElementById('allergies').value = medicalInfo.allergies || ''; |
| | } |
| | }); |
| | |
| | |
| | const recordsRef = ref(database, `users/${userId}/medical_records`); |
| | onValue(recordsRef, (snapshot) => { |
| | const records = snapshot.val(); |
| | const recordsList = document.getElementById('medicalRecordsList'); |
| | recordsList.innerHTML = ''; |
| | |
| | if (records) { |
| | Object.entries(records).forEach(([id, record]) => { |
| | const recordElement = document.createElement('div'); |
| | recordElement.className = 'record-item'; |
| | recordElement.dataset.recordId = id; |
| | recordElement.innerHTML = ` |
| | <div class="record-info"> |
| | <div class="record-name">${record.name}</div> |
| | <div class="record-date">${new Date(record.uploaded_at).toLocaleDateString()}</div> |
| | </div> |
| | <div class="record-actions"> |
| | <button onclick="viewRecord('${id}')" title="View"><i class="fas fa-eye"></i></button> |
| | <button onclick="downloadRecord('${id}')" title="Download"><i class="fas fa-download"></i></button> |
| | <button onclick="removeRecord('${id}')" title="Remove"><i class="fas fa-trash"></i></button> |
| | </div> |
| | `; |
| | recordsList.appendChild(recordElement); |
| | }); |
| | } |
| | }); |
| | } catch (error) { |
| | console.error('Error loading user data:', error); |
| | } |
| | } |
| | |
| | function addEmergencyContact(name, phone) { |
| | const user = auth.currentUser; |
| | if (!user) { |
| | alert('You must be logged in to add contacts'); |
| | return; |
| | } |
| | |
| | |
| | const contactsRef = ref(database, `users/${user.uid}/emergency_contacts`); |
| | const newContactRef = push(contactsRef); |
| | |
| | set(newContactRef, { |
| | name: name, |
| | phone: phone, |
| | added_at: new Date().toISOString() |
| | }) |
| | .then(() => { |
| | |
| | document.querySelector('.modal').remove(); |
| | }) |
| | .catch(error => { |
| | console.error('Error saving contact:', error); |
| | alert('Error saving contact. Please try again.'); |
| | }); |
| | } |
| | |
| | function removeContact(contactId) { |
| | if (confirm('Are you sure you want to remove this contact?')) { |
| | const user = auth.currentUser; |
| | if (user) { |
| | const contactRef = ref(database, `users/${user.uid}/emergency_contacts/${contactId}`); |
| | remove(contactRef) |
| | .then(() => { |
| | |
| | const contactElement = document.querySelector(`.contact-item[data-contact-id="${contactId}"]`); |
| | if (contactElement) { |
| | contactElement.remove(); |
| | } |
| | }) |
| | .catch(error => { |
| | console.error('Error removing contact:', error); |
| | alert('Error removing contact. Please try again.'); |
| | }); |
| | } |
| | } |
| | } |
| | |
| | |
| | window.addEmergencyContact = addEmergencyContact; |
| | window.removeContact = removeContact; |
| | window.callContact = function(phone) { |
| | if (phone) { |
| | window.location.href = `tel:${phone}`; |
| | } |
| | }; |
| | window.viewRecord = viewRecord; |
| | window.removeRecord = removeRecord; |
| | window.downloadRecord = downloadRecord; |
| | |
| | |
| | window.showContactForm = function() { |
| | const modal = document.createElement('div'); |
| | modal.className = 'modal'; |
| | modal.innerHTML = ` |
| | <div class="modal-content"> |
| | <h3>Add Emergency Contact</h3> |
| | <div class="contact-form"> |
| | <input type="text" id="contactName" placeholder="Enter contact name" required> |
| | <input type="tel" id="contactPhone" placeholder="Enter contact phone number" required> |
| | <div class="contact-form-buttons"> |
| | <button class="emergency-button secondary" onclick="this.closest('.modal').remove()">Cancel</button> |
| | <button class="emergency-button" id="saveContactBtn">Save Contact</button> |
| | </div> |
| | </div> |
| | </div> |
| | `; |
| | document.body.appendChild(modal); |
| | |
| | |
| | document.getElementById('saveContactBtn').addEventListener('click', function() { |
| | const name = document.getElementById('contactName').value; |
| | const phone = document.getElementById('contactPhone').value; |
| | |
| | if (name && phone) { |
| | addEmergencyContact(name, phone); |
| | document.querySelector('.modal').remove(); |
| | } else { |
| | alert('Please fill in all fields'); |
| | } |
| | }); |
| | }; |
| | |
| | |
| | document.addEventListener('DOMContentLoaded', function() { |
| | const addContactBtn = document.getElementById('addContact'); |
| | if (addContactBtn) { |
| | addContactBtn.addEventListener('click', window.showContactForm); |
| | } |
| | }); |
| | |
| | |
| | window.map = L.map('emergencyMap').setView([0, 0], 15); |
| | |
| | |
| | const streetLayer = L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', { |
| | attribution: '© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors' |
| | }); |
| | |
| | |
| | const satelliteLayer = L.tileLayer('https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}', { |
| | attribution: '© <a href="https://www.esri.com/">Esri</a>' |
| | }); |
| | |
| | |
| | streetLayer.addTo(window.map); |
| | |
| | |
| | const baseMaps = { |
| | "Street Map": streetLayer, |
| | "Satellite": satelliteLayer |
| | }; |
| | L.control.layers(baseMaps).addTo(window.map); |
| | |
| | let isVolunteer = false; |
| | let emergencyTimeout = null; |
| | let currentEmergency = null; |
| | window.userLocation = null; |
| | window.nearbyHospitals = []; |
| | window.currentRoute = null; |
| | window.selectedHospital = null; |
| | |
| | |
| | const socket = io(); |
| | |
| | socket.on('connect', () => { |
| | console.log('Connected to server'); |
| | }); |
| | |
| | socket.on('emergency_alert', (data) => { |
| | if (isVolunteer) { |
| | showEmergencyAlert(data); |
| | } |
| | }); |
| | |
| | |
| | document.getElementById('allowLocation').addEventListener('click', () => { |
| | if (navigator.geolocation) { |
| | navigator.geolocation.getCurrentPosition( |
| | (position) => { |
| | window.userLocation = { |
| | lat: position.coords.latitude, |
| | lng: position.coords.longitude |
| | }; |
| | window.map.setView([window.userLocation.lat, window.userLocation.lng], 15); |
| | document.getElementById('locationRequest').style.display = 'none'; |
| | updateCurrentLocation(); |
| | findNearbyHospitals(); |
| | }, |
| | (error) => { |
| | console.error('Error getting location:', error); |
| | alert('Please enable location access to use emergency features'); |
| | } |
| | ); |
| | } |
| | }); |
| | |
| | function updateCurrentLocation() { |
| | if (window.userLocation) { |
| | document.getElementById('currentLocation').textContent = |
| | `Current Location: ${window.userLocation.lat.toFixed(4)}, ${window.userLocation.lng.toFixed(4)}`; |
| | document.getElementById('startJourney').disabled = false; |
| | } |
| | } |
| | |
| | async function findNearbyHospitals() { |
| | try { |
| | const response = await fetch(`/api/nearby_hospitals?lat=${window.userLocation.lat}&lon=${window.userLocation.lng}`); |
| | const data = await response.json(); |
| | |
| | if (data.hospitals) { |
| | window.nearbyHospitals = data.hospitals; |
| | displayHospitals(data.hospitals); |
| | |
| | |
| | data.hospitals.forEach((hospital, index) => { |
| | L.marker([hospital.lat, hospital.lon], { |
| | icon: L.divIcon({ |
| | className: 'hospital-marker', |
| | html: '<i class="fas fa-hospital" style="color: #4CAF50; font-size: 20px;"></i>' |
| | }) |
| | }) |
| | .bindPopup(`<b>${hospital.name}</b><br>${hospital.address}<br>Distance: ${(hospital.distance).toFixed(0)}m`) |
| | .addTo(window.map); |
| | }); |
| | } |
| | } catch (error) { |
| | console.error('Error finding hospitals:', error); |
| | } |
| | } |
| | |
| | function displayHospitals(hospitals) { |
| | const hospitalList = document.getElementById('hospitalList'); |
| | hospitalList.innerHTML = hospitals.map((hospital, index) => ` |
| | <div class="hospital-item" onclick="selectHospital(${index})"> |
| | <div class="hospital-header"> |
| | <strong>${hospital.name}</strong> |
| | <span class="distance">${(hospital.distance).toFixed(0)}m</span> |
| | </div> |
| | <div class="hospital-details"> |
| | <div>${hospital.address}</div> |
| | ${hospital.phone ? `<div><i class="fas fa-phone"></i> ${hospital.phone}</div>` : ''} |
| | ${hospital.website ? `<div><i class="fas fa-globe"></i> <a href="${hospital.website}" target="_blank">Website</a></div>` : ''} |
| | </div> |
| | <button class="select-hospital-btn" onclick="event.stopPropagation(); startJourneyToHospital(${index})"> |
| | <i class="fas fa-route"></i> Start Journey |
| | </button> |
| | </div> |
| | `).join(''); |
| | } |
| | |
| | |
| | document.getElementById('startJourney').addEventListener('click', function() { |
| | if (window.nearbyHospitals.length > 0) { |
| | |
| | const hospitalIndex = window.selectedHospital ? |
| | window.nearbyHospitals.findIndex(h => h.name === window.selectedHospital.name) : 0; |
| | window.startJourneyToHospital(hospitalIndex); |
| | } |
| | }); |
| | |
| | |
| | document.getElementById('medicalRecordUpload').addEventListener('change', (e) => { |
| | const file = e.target.files[0]; |
| | if (file) { |
| | if (file.type === 'text/csv' || file.type === 'text/plain' || file.type === 'application/json') { |
| | uploadMedicalRecord(file); |
| | } else { |
| | alert('Please upload a CSV, TXT, or JSON file'); |
| | } |
| | } |
| | }); |
| | |
| | document.getElementById('editMedicalInfo').addEventListener('click', () => { |
| | const form = document.getElementById('medicalForm'); |
| | form.style.display = form.style.display === 'none' ? 'block' : 'none'; |
| | }); |
| | |
| | document.getElementById('saveMedicalInfo').addEventListener('click', () => { |
| | const medications = document.getElementById('currentMedications').value; |
| | const allergies = document.getElementById('allergies').value; |
| | saveMedicalInfo(medications, allergies); |
| | }); |
| | |
| | async function saveMedicalInfo(medications, allergies) { |
| | try { |
| | const user = auth.currentUser; |
| | if (user) { |
| | const medicalInfoRef = ref(database, `users/${user.uid}/medical_info`); |
| | await set(medicalInfoRef, { |
| | medications: medications, |
| | allergies: allergies, |
| | updated_at: new Date().toISOString() |
| | }); |
| | alert('Medical information saved successfully'); |
| | document.getElementById('medicalForm').style.display = 'none'; |
| | } else { |
| | alert('You must be logged in to save medical information'); |
| | } |
| | } catch (error) { |
| | console.error('Error saving medical info:', error); |
| | alert('Error saving medical information. Please try again.'); |
| | } |
| | } |
| | |
| | async function uploadMedicalRecord(file) { |
| | try { |
| | const user = auth.currentUser; |
| | if (!user) { |
| | alert('You must be logged in to upload medical records'); |
| | return; |
| | } |
| | |
| | const reader = new FileReader(); |
| | reader.onload = async function(e) { |
| | const content = e.target.result; |
| | const recordsRef = ref(database, `users/${user.uid}/medical_records`); |
| | const newRecordRef = push(recordsRef); |
| | |
| | await set(newRecordRef, { |
| | name: file.name, |
| | content: content, |
| | type: file.type, |
| | uploaded_at: new Date().toISOString() |
| | }); |
| | |
| | alert('Medical record uploaded successfully'); |
| | }; |
| | reader.readAsText(file); |
| | } catch (error) { |
| | console.error('Error uploading record:', error); |
| | alert('Error uploading medical record. Please try again.'); |
| | } |
| | } |
| | |
| | async function viewRecord(recordId) { |
| | try { |
| | const user = auth.currentUser; |
| | if (user) { |
| | const recordRef = ref(database, `users/${user.uid}/medical_records/${recordId}`); |
| | const snapshot = await get(recordRef); |
| | const record = snapshot.val(); |
| | |
| | if (record) { |
| | const modal = document.createElement('div'); |
| | modal.className = 'modal'; |
| | modal.innerHTML = ` |
| | <div class="modal-content"> |
| | <h3>${record.name}</h3> |
| | <div class="record-content"> |
| | <pre>${record.content}</pre> |
| | </div> |
| | <div class="modal-buttons"> |
| | <button class="emergency-button" onclick="this.closest('.modal').remove()">Close</button> |
| | </div> |
| | </div> |
| | `; |
| | document.body.appendChild(modal); |
| | } |
| | } |
| | } catch (error) { |
| | console.error('Error viewing record:', error); |
| | alert('Error viewing record. Please try again.'); |
| | } |
| | } |
| | |
| | async function removeRecord(recordId) { |
| | if (confirm('Are you sure you want to remove this record?')) { |
| | try { |
| | const user = auth.currentUser; |
| | if (user) { |
| | const recordRef = ref(database, `users/${user.uid}/medical_records/${recordId}`); |
| | await remove(recordRef); |
| | alert('Record removed successfully'); |
| | } |
| | } catch (error) { |
| | console.error('Error removing record:', error); |
| | alert('Error removing record. Please try again.'); |
| | } |
| | } |
| | } |
| | |
| | async function downloadRecord(recordId) { |
| | try { |
| | const user = auth.currentUser; |
| | if (user) { |
| | const recordRef = ref(database, `users/${user.uid}/medical_records/${recordId}`); |
| | const snapshot = await get(recordRef); |
| | const record = snapshot.val(); |
| | |
| | if (record) { |
| | |
| | const blob = new Blob([record.content], { type: record.type || 'text/plain' }); |
| | |
| | |
| | const url = URL.createObjectURL(blob); |
| | const a = document.createElement('a'); |
| | a.href = url; |
| | a.download = record.name; |
| | |
| | |
| | document.body.appendChild(a); |
| | a.click(); |
| | |
| | |
| | document.body.removeChild(a); |
| | URL.revokeObjectURL(url); |
| | } |
| | } |
| | } catch (error) { |
| | console.error('Error downloading record:', error); |
| | alert('Error downloading record. Please try again.'); |
| | } |
| | } |
| | |
| | |
| | document.getElementById('ecgDataUpload').addEventListener('change', (e) => { |
| | const file = e.target.files[0]; |
| | if (file) { |
| | if (file.type !== 'application/json' && !file.name.endsWith('.json')) { |
| | alert('Please upload a JSON file'); |
| | e.target.value = ''; |
| | return; |
| | } |
| | uploadECGData(file); |
| | } |
| | }); |
| | |
| | async function uploadECGData(file) { |
| | try { |
| | const user = auth.currentUser; |
| | if (user) { |
| | const timestamp = new Date().toISOString(); |
| | const recordRef = ref(database, `users/${user.uid}/ecg_data/${timestamp}`); |
| | |
| | const reader = new FileReader(); |
| | reader.onload = async (e) => { |
| | try { |
| | |
| | const content = e.target.result; |
| | const jsonData = JSON.parse(content); |
| | |
| | |
| | await set(recordRef, { |
| | filename: file.name, |
| | type: 'application/json', |
| | content: jsonData, |
| | uploaded_at: timestamp |
| | }); |
| | |
| | |
| | const resultDiv = document.getElementById('ecgAnalysisResult'); |
| | resultDiv.innerHTML = ` |
| | <h5>ECG Analysis Result</h5> |
| | <pre>${JSON.stringify(analyzeECGData(jsonData), null, 2)}</pre> |
| | `; |
| | resultDiv.classList.add('show'); |
| | |
| | alert('ECG data uploaded and analyzed successfully'); |
| | } catch (error) { |
| | console.error('Error parsing JSON:', error); |
| | alert('Invalid JSON format. Please upload a valid JSON file.'); |
| | } |
| | }; |
| | reader.readAsText(file); |
| | } |
| | } catch (error) { |
| | console.error('Error uploading ECG data:', error); |
| | alert('Error uploading ECG data'); |
| | } |
| | } |
| | |
| | function analyzeECGData(data) { |
| | |
| | |
| | return { |
| | status: 'success', |
| | timestamp: new Date().toISOString(), |
| | analysis: { |
| | heartRate: calculateHeartRate(data), |
| | anomalies: detectAnomalies(data), |
| | recommendations: generateRecommendations(data) |
| | } |
| | }; |
| | } |
| | |
| | function calculateHeartRate(data) { |
| | |
| | return '60-100 BPM (Normal)'; |
| | } |
| | |
| | function detectAnomalies(data) { |
| | |
| | return ['No significant anomalies detected']; |
| | } |
| | |
| | function generateRecommendations(data) { |
| | |
| | return ['Continue regular monitoring']; |
| | } |
| | |
| | |
| | document.getElementById('triggerEmergency').addEventListener('click', async function() { |
| | if (!window.userLocation) { |
| | alert('Please enable location access first'); |
| | return; |
| | } |
| | |
| | const description = document.getElementById('emergencyDescription').value; |
| | |
| | try { |
| | |
| | const contacts = document.querySelectorAll('.contact-item'); |
| | contacts.forEach(contact => { |
| | const phone = contact.querySelector('.contact-phone').textContent; |
| | callContact(phone, description); |
| | }); |
| | |
| | |
| | const response = await fetch('/api/emergency', { |
| | method: 'POST', |
| | headers: { |
| | 'Content-Type': 'application/json' |
| | }, |
| | body: JSON.stringify({ |
| | lat: window.userLocation.lat, |
| | lng: window.userLocation.lng, |
| | description: description |
| | }) |
| | }); |
| | |
| | const result = await response.json(); |
| | if (result.status === 'success') { |
| | currentEmergency = result.emergency_id; |
| | startEmergencyTimer(); |
| | showEmergencyMarker(window.userLocation.lat, window.userLocation.lng); |
| | } |
| | } catch (error) { |
| | console.error('Error sending emergency alert:', error); |
| | } |
| | }); |
| | |
| | function startEmergencyTimer() { |
| | let timeLeft = 300; |
| | const timerElement = document.getElementById('emergencyTimer'); |
| | |
| | if (emergencyTimeout) { |
| | clearInterval(emergencyTimeout); |
| | } |
| | |
| | emergencyTimeout = setInterval(() => { |
| | timeLeft--; |
| | const minutes = Math.floor(timeLeft / 60); |
| | const seconds = timeLeft % 60; |
| | timerElement.textContent = `Calling emergency services in: ${minutes}:${seconds.toString().padStart(2, '0')}`; |
| | |
| | if (timeLeft <= 0) { |
| | clearInterval(emergencyTimeout); |
| | callEmergencyServices(); |
| | } |
| | }, 1000); |
| | } |
| | |
| | function callEmergencyServices() { |
| | alert('Emergency services have been notified!'); |
| | document.getElementById('emergencyTimer').textContent = 'Emergency services notified'; |
| | } |
| | |
| | function showEmergencyMarker(lat, lng) { |
| | |
| | window.map.eachLayer((layer) => { |
| | if (layer instanceof L.Marker) { |
| | window.map.removeLayer(layer); |
| | } |
| | }); |
| | |
| | |
| | L.marker([lat, lng], { |
| | icon: L.divIcon({ |
| | className: 'emergency-marker', |
| | html: '<i class="fas fa-exclamation-triangle" style="color: red; font-size: 24px;"></i>' |
| | }) |
| | }).addTo(window.map); |
| | } |
| | </script> |
| | {% endblock %} |