diff --git a/app.py b/app.py index 8b5c6f8..2341963 100644 --- a/app.py +++ b/app.py @@ -90,7 +90,7 @@ class QRCode(db.Model): def coordinates_display(self): """Get formatted coordinates for display""" if self.has_coordinates: - return f"{self.address_latitude:.8f}, {self.address_longitude:.8f}" + return f"{self.address_latitude:.10f}, {self.address_longitude:.10f}" return "Coordinates not available" def update_coordinates(self, latitude, longitude, accuracy='geocoded'): @@ -155,7 +155,7 @@ class AttendanceData(db.Model): def coordinates_display(self): """Get formatted coordinates for display""" if self.has_location_data: - return f"{self.latitude:.8f}, {self.longitude:.8f}" + return f"{self.latitude:.10f}, {self.longitude:.10f}" return "No GPS data" def to_dict(self): @@ -268,7 +268,7 @@ def get_coordinates_from_address_enhanced(address): accuracy = 'poor' print(f"✅ Enhanced geocoding successful:") - print(f" Coordinates: {lat:.8f}, {lng:.8f}") + print(f" Coordinates: {lat:.10f}, {lng:.10f}") print(f" Accuracy: {accuracy}") return lat, lng, accuracy @@ -324,7 +324,7 @@ def geocode_address_enhanced(address): accuracy = 'low' print(f"✅ Geocoded address: {address}") - print(f" Coordinates: {lat:.8f}, {lng:.8f}") + print(f" Coordinates: {lat:.10f}, {lng:.10f}") print(f" Accuracy: {accuracy} ({place_type})") return lat, lng, accuracy @@ -376,8 +376,8 @@ def calculate_distance_miles(lat1, lng1, lat2, lng2): distance = round(distance, 4) print(f"📏 Enhanced distance calculation:") - print(f" Point 1: {lat1*180/3.14159:.8f}, {lng1*180/3.14159:.8f}") - print(f" Point 2: {lat2*180/3.14159:.8f}, {lng2*180/3.14159:.8f}") + print(f" Point 1: {lat1*180/3.14159:.10f}, {lng1*180/3.14159:.10f}") + print(f" Point 2: {lat2*180/3.14159:.10f}, {lng2*180/3.14159:.10f}") print(f" Distance: {distance:.4f} miles") return distance @@ -455,7 +455,7 @@ def calculate_location_accuracy_enhanced(qr_address, checkin_address, checkin_la print(f"❌ Could not geocode QR address: {qr_address}") return None - print(f"✅ QR location coordinates: {qr_lat:.8f}, {qr_lng:.8f} (accuracy: {qr_accuracy})") + print(f"✅ QR location coordinates: {qr_lat:.10f}, {qr_lng:.10f} (accuracy: {qr_accuracy})") # Step 2: Determine check-in coordinates print(f"\n📱 Step 2: Determining check-in coordinates...") @@ -475,7 +475,7 @@ def calculate_location_accuracy_enhanced(qr_address, checkin_address, checkin_la checkin_coords_lat = lat_val checkin_coords_lng = lng_val checkin_source = "gps" - print(f"✅ Using GPS coordinates: {lat_val:.8f}, {lng_val:.8f}") + print(f"✅ Using GPS coordinates: {lat_val:.10f}, {lng_val:.10f}") else: print(f"⚠️ Invalid GPS coordinates: {lat_val}, {lng_val}") except (ValueError, TypeError): @@ -487,7 +487,7 @@ def calculate_location_accuracy_enhanced(qr_address, checkin_address, checkin_la checkin_coords_lat, checkin_coords_lng, checkin_accuracy = get_coordinates_from_address_enhanced(checkin_address) if checkin_coords_lat is not None: checkin_source = "address" - print(f"✅ Using geocoded coordinates: {checkin_coords_lat:.8f}, {checkin_coords_lng:.8f} (accuracy: {checkin_accuracy})") + print(f"✅ Using geocoded coordinates: {checkin_coords_lat:.10f}, {checkin_coords_lng:.10f} (accuracy: {checkin_accuracy})") # Check if we have valid coordinates for both locations if checkin_coords_lat is None or checkin_coords_lng is None: @@ -502,8 +502,8 @@ def calculate_location_accuracy_enhanced(qr_address, checkin_address, checkin_la if distance is not None: print(f"✅ Enhanced location accuracy calculated successfully!") - print(f" QR Location: {qr_lat:.8f}, {qr_lng:.8f}") - print(f" Check-in Location: {checkin_coords_lat:.8f}, {checkin_coords_lng:.8f}") + print(f" QR Location: {qr_lat:.10f}, {qr_lng:.10f}") + print(f" Check-in Location: {checkin_coords_lat:.10f}, {checkin_coords_lng:.10f}") print(f" Source: {checkin_source}") print(f" Distance: {distance:.4f} miles") print(f" Accuracy Level: {get_location_accuracy_level_enhanced(distance)}") @@ -1387,7 +1387,7 @@ def geocode_address(): 'latitude': lat, 'longitude': lng, 'accuracy': accuracy, - 'coordinates_display': f"{lat:.8f}, {lng:.8f}" + 'coordinates_display': f"{lat:.10f}, {lng:.10f}" }, 'message': f'Address geocoded successfully with {accuracy} accuracy' }) @@ -1745,7 +1745,7 @@ def create_qr_code(): new_qr_code.address_longitude = lng new_qr_code.coordinate_accuracy = coordinate_accuracy new_qr_code.coordinates_updated_date = datetime.utcnow() - print(f"✅ Added coordinates to QR code: {lat:.8f}, {lng:.8f}") + print(f"✅ Added coordinates to QR code: {lat:.10f}, {lng:.10f}") except (ValueError, TypeError) as e: print(f"⚠️ Invalid coordinates provided: {e}") @@ -1810,7 +1810,7 @@ def edit_qr_code(qr_id): qr_code.address_longitude = lng qr_code.coordinate_accuracy = coordinate_accuracy qr_code.coordinates_updated_date = datetime.utcnow() - print(f"✅ Updated coordinates for QR code: {lat:.8f}, {lng:.8f}") + print(f"✅ Updated coordinates for QR code: {lat:.10f}, {lng:.10f}") except (ValueError, TypeError) as e: print(f"⚠️ Invalid coordinates provided during edit: {e}") @@ -2347,7 +2347,7 @@ def attendance_report(): 'gps_accuracy': gps_accuracy, 'accuracy_level': get_location_accuracy_level(location_accuracy) if location_accuracy else 'unknown', 'has_location_data': record.latitude is not None and record.longitude is not None, - 'coordinates': f"{record.latitude:.8f}, {record.longitude:.8f}" if record.latitude and record.longitude else "No GPS data", + 'coordinates': f"{record.latitude:.10f}, {record.longitude:.10f}" if record.latitude and record.longitude else "No GPS data", 'has_location_accuracy_feature': has_location_accuracy } processed_records.append(record_dict) diff --git a/static/js/qr_destination.js b/static/js/qr_destination.js index 211c7f2..0d679d6 100644 --- a/static/js/qr_destination.js +++ b/static/js/qr_destination.js @@ -21,58 +21,58 @@ let locationRequestActive = false; let locationWatchId = null; // BILINGUAL FUNCTIONALITY (PRESERVED FROM ORIGINAL) -let currentLanguage = 'en'; +let currentLanguage = "en"; const translations = { en: { - languageText: 'EN', + languageText: "EN", statusMessages: { - processing: 'Processing check-in...', - success: 'Check-in successful!', - error: 'Check-in failed. Please try again.', - duplicate: 'You have already checked in today.', - tooSoon: 'Please wait before checking in again.', - multipleSuccess: 'Submitted successfully!', - invalidId: 'Please enter a valid Employee ID.', - locationError: 'Unable to get location data.', - networkError: 'Network error. Please check your connection.' - } + processing: "Processing check-in...", + success: "Check-in successful!", + error: "Check-in failed. Please try again.", + duplicate: "You have already checked in today.", + tooSoon: "Please wait before checking in again.", + multipleSuccess: "Submitted successfully!", + invalidId: "Please enter a valid Employee ID.", + locationError: "Unable to get location data.", + networkError: "Network error. Please check your connection.", + }, }, es: { - languageText: 'ES', + languageText: "ES", statusMessages: { - processing: 'Procesando registro...', - success: '¡Registro exitoso!', - error: 'Error en el registro. Por favor intente de nuevo.', - duplicate: 'Ya se ha registrado hoy.', - tooSoon: 'Por favor espere antes de registrarse nuevamente.', - multipleSuccess: 'Submitted successful!!', - invalidId: 'Por favor ingrese un ID de empleado válido.', - locationError: 'No se pudo obtener datos de ubicación.', - networkError: 'Error de red. Verifique su conexión.' - } - } + processing: "Procesando registro...", + success: "¡Registro exitoso!", + error: "Error en el registro. Por favor intente de nuevo.", + duplicate: "Ya se ha registrado hoy.", + tooSoon: "Por favor espere antes de registrarse nuevamente.", + multipleSuccess: "Submitted successful!!", + invalidId: "Por favor ingrese un ID de empleado válido.", + locationError: "No se pudo obtener datos de ubicación.", + networkError: "Error de red. Verifique su conexión.", + }, + }, }; // DOM Content Loaded Event (PRESERVED FROM ORIGINAL) -document.addEventListener("DOMContentLoaded", function() { +document.addEventListener("DOMContentLoaded", function () { console.log("🎯 QR Destination page loaded"); - + // Initialize language functionality initializeLanguage(); - + // Initialize form handling initializeForm(); - + // Initialize location services initializeLocation(); - + // Start real-time clock startClock(); - + // Add fade-in animation to elements setTimeout(() => { - document.querySelectorAll('.fade-transition').forEach(el => { - el.classList.add('active'); + document.querySelectorAll(".fade-transition").forEach((el) => { + el.classList.add("active"); }); }, 100); }); @@ -81,16 +81,16 @@ document.addEventListener("DOMContentLoaded", function() { function initializeForm() { const form = document.getElementById("checkinForm"); const submitButton = document.getElementById("submitCheckin"); - + if (form && submitButton) { form.addEventListener("submit", handleFormSubmit); - + // Add real-time Employee ID validation const employeeIdInput = document.getElementById("employee_id"); if (employeeIdInput) { employeeIdInput.addEventListener("input", validateEmployeeId); - employeeIdInput.addEventListener("keypress", function(e) { - if (e.key === 'Enter') { + employeeIdInput.addEventListener("keypress", function (e) { + if (e.key === "Enter") { e.preventDefault(); handleFormSubmit(e); } @@ -101,29 +101,31 @@ function initializeForm() { function handleFormSubmit(event) { event.preventDefault(); - + if (isSubmitting) { - console.log("⏳ Check-in already in progress, ignoring duplicate submission"); + console.log( + "⏳ Check-in already in progress, ignoring duplicate submission" + ); return; } - + console.log("🎯 Form submission triggered"); - + const employeeId = document.getElementById("employee_id")?.value?.trim(); - + if (!employeeId) { - showLocalizedStatusMessage('invalidId', 'error'); + showLocalizedStatusMessage("invalidId", "error"); return; } - + if (employeeId.length < 2) { - showLocalizedStatusMessage('invalidId', 'error'); + showLocalizedStatusMessage("invalidId", "error"); return; } - + // Show processing status - showLocalizedStatusMessage('processing', 'info'); - + showLocalizedStatusMessage("processing", "info"); + // Submit the check-in submitCheckin(); } @@ -131,32 +133,38 @@ function handleFormSubmit(event) { // ENHANCED CHECK-IN SUBMISSION WITH MULTIPLE CHECK-IN SUPPORT function submitCheckin() { console.log("🚀 Starting check-in submission process"); - + if (isSubmitting) { console.log("⏳ Already submitting, aborting"); return; } - + isSubmitting = true; updateSubmitButton(true); - + const employeeId = document.getElementById("employee_id").value.trim(); - + if (!employeeId) { - showLocalizedStatusMessage('invalidId', 'error'); + showLocalizedStatusMessage("invalidId", "error"); isSubmitting = false; updateSubmitButton(false); return; } - + console.log(`👤 Employee ID: ${employeeId}`); console.log(`📍 User location:`, userLocation); - + // Prepare form data const formData = new FormData(); formData.append("employee_id", employeeId); - formData.append("latitude", userLocation.latitude ? userLocation.latitude.toFixed(8) : ""); - formData.append("longitude", userLocation.longitude ? userLocation.longitude.toFixed(8) : ""); + formData.append( + "latitude", + userLocation.latitude ? userLocation.latitude.toFixed(10) : "" + ); + formData.append( + "longitude", + userLocation.longitude ? userLocation.longitude.toFixed(10) : "" + ); formData.append("accuracy", userLocation.accuracy || ""); formData.append("altitude", userLocation.altitude || ""); formData.append("location_source", userLocation.source || "manual"); @@ -197,17 +205,20 @@ function handleCheckinResponse(data) { if (data.success) { handleCheckinSuccess(data); } else { - const errorMsg = data.message || 'Submission failed'; + const errorMsg = data.message || "Submission failed"; console.log("❌ Submission failed:", errorMsg); - + // NEW: Handle different types of check-in failures - if (errorMsg.toLowerCase().includes('already submitted')) { - showLocalizedStatusMessage('duplicate', 'warning'); - } else if (errorMsg.toLowerCase().includes('submit again in') || errorMsg.toLowerCase().includes('minutes')) { + if (errorMsg.toLowerCase().includes("already submitted")) { + showLocalizedStatusMessage("duplicate", "warning"); + } else if ( + errorMsg.toLowerCase().includes("submit again in") || + errorMsg.toLowerCase().includes("minutes") + ) { // Handle 30-minute interval message - showCustomStatusMessage(errorMsg, 'warning'); + showCustomStatusMessage(errorMsg, "warning"); } else { - showLocalizedStatusMessage('error', 'error'); + showLocalizedStatusMessage("error", "error"); } } } @@ -215,18 +226,18 @@ function handleCheckinResponse(data) { // ENHANCED SUCCESS HANDLING WITH MULTIPLE CHECK-IN INFO function handleCheckinSuccess(data) { console.log("✅ Submitted successful!"); - + const responseData = data.data || data || {}; const checkinCount = responseData.checkin_count_today || 1; - const checkinSequence = responseData.checkin_sequence || 'Check-in'; - + const checkinSequence = responseData.checkin_sequence || "Check-in"; + // Show appropriate success message based on check-in count if (checkinCount > 1) { - showLocalizedStatusMessage('multipleSuccess', 'success'); + showLocalizedStatusMessage("multipleSuccess", "success"); } else { - showLocalizedStatusMessage('success', 'success'); + showLocalizedStatusMessage("success", "success"); } - + // Hide form (PRESERVED FROM ORIGINAL) const form = document.getElementById("checkinForm"); if (form) { @@ -237,7 +248,7 @@ function handleCheckinSuccess(data) { const successCard = document.getElementById("successCard"); if (successCard) { successCard.style.display = "block"; - successCard.classList.add('active'); + successCard.classList.add("active"); const updateElement = (id, value) => { const el = document.getElementById(id); @@ -248,34 +259,40 @@ function handleCheckinSuccess(data) { const employeeId = responseData.employee_id || "Unknown"; const location = responseData.location || "Unknown Location"; - const event = responseData.event || responseData.location_event || "Check-in"; - const checkInTime = responseData.check_in_time || new Date().toLocaleTimeString(); - const checkInDate = responseData.check_in_date || new Date().toLocaleDateString(); + const event = + responseData.event || responseData.location_event || "Check-in"; + const checkInTime = + responseData.check_in_time || new Date().toLocaleTimeString(); + const checkInDate = + responseData.check_in_date || new Date().toLocaleDateString(); updateElement("successEmployeeId", employeeId); updateElement("successLocation", location); updateElement("successEvent", event); updateElement("successCheckInTime", checkInTime); updateElement("successCheckInDate", checkInDate); - + // NEW: Add check-in sequence information updateElement("successCheckinSequence", checkinSequence); - + // Update additional info if available if (responseData.device_info) { updateElement("successDeviceInfo", responseData.device_info); } - + if (responseData.coordinates) { updateElement("successCoordinates", responseData.coordinates); } - + if (responseData.address) { updateElement("successAddress", responseData.address); } - + if (responseData.location_accuracy) { - updateElement("successLocationAccuracy", `${responseData.location_accuracy} miles`); + updateElement( + "successLocationAccuracy", + `${responseData.location_accuracy} miles` + ); } } @@ -303,8 +320,8 @@ function addCheckInAgainOption() { `; - successCard.insertAdjacentHTML('beforeend', checkInAgainHtml); - + successCard.insertAdjacentHTML("beforeend", checkInAgainHtml); + // Apply current language translations applyTranslations(); } @@ -313,30 +330,30 @@ function addCheckInAgainOption() { // NEW: Reset form for new check-in function resetForNewCheckin() { console.log("🔄 Resetting for new check-in"); - + // Show form again const form = document.getElementById("checkinForm"); if (form) { form.style.display = "block"; } - + // Hide success card const successCard = document.getElementById("successCard"); if (successCard) { successCard.style.display = "none"; - successCard.classList.remove('active'); + successCard.classList.remove("active"); } - + // Clear previous employee ID const employeeIdInput = document.getElementById("employee_id"); if (employeeIdInput) { - employeeIdInput.value = ''; + employeeIdInput.value = ""; employeeIdInput.focus(); } - + // Clear status messages clearStatusMessages(); - + // Reset location if needed if (!userLocation.latitude || !userLocation.longitude) { requestLocationData(); @@ -344,7 +361,7 @@ function resetForNewCheckin() { } // NEW: Show custom status message (for interval warnings) -function showCustomStatusMessage(message, type = 'info') { +function showCustomStatusMessage(message, type = "info") { const statusContainer = document.getElementById("statusMessage"); if (statusContainer) { statusContainer.className = `status-message ${type}`; @@ -355,7 +372,7 @@ function showCustomStatusMessage(message, type = 'info') { `; statusContainer.style.display = "block"; - + // Auto-hide after 5 seconds setTimeout(() => { statusContainer.style.display = "none"; @@ -366,20 +383,25 @@ function showCustomStatusMessage(message, type = 'info') { // Helper function to get appropriate icon for status type function getStatusIcon(type) { switch (type) { - case 'success': return 'fa-check-circle'; - case 'error': return 'fa-exclamation-circle'; - case 'warning': return 'fa-clock'; - case 'info': - default: return 'fa-info-circle'; + case "success": + return "fa-check-circle"; + case "error": + return "fa-exclamation-circle"; + case "warning": + return "fa-clock"; + case "info": + default: + return "fa-info-circle"; } } // PRESERVED: All other existing functions remain unchanged -function showLocalizedStatusMessage(messageKey, type = 'info') { - const message = translations[currentLanguage].statusMessages[messageKey] || - translations['en'].statusMessages[messageKey] || - 'Status update'; - +function showLocalizedStatusMessage(messageKey, type = "info") { + const message = + translations[currentLanguage].statusMessages[messageKey] || + translations["en"].statusMessages[messageKey] || + "Status update"; + showCustomStatusMessage(message, type); } @@ -392,7 +414,7 @@ function clearStatusMessages() { function handleCheckinError(error) { console.error("❌ Check-in submission error:", error); - showLocalizedStatusMessage('networkError', 'error'); + showLocalizedStatusMessage("networkError", "error"); } function updateSubmitButton(isLoading) { @@ -400,10 +422,12 @@ function updateSubmitButton(isLoading) { if (submitButton) { if (isLoading) { submitButton.disabled = true; - submitButton.innerHTML = ' Processing...'; + submitButton.innerHTML = + ' Processing...'; } else { submitButton.disabled = false; - submitButton.innerHTML = ' Submit'; + submitButton.innerHTML = + ' Submit'; } applyTranslations(); } @@ -412,18 +436,18 @@ function updateSubmitButton(isLoading) { function validateEmployeeId() { const employeeIdInput = document.getElementById("employee_id"); const submitButton = document.getElementById("submitCheckin"); - + if (employeeIdInput && submitButton) { const isValid = employeeIdInput.value.trim().length >= 2; submitButton.disabled = !isValid || isSubmitting; - + if (isValid) { - employeeIdInput.classList.remove('invalid'); - employeeIdInput.classList.add('valid'); + employeeIdInput.classList.remove("invalid"); + employeeIdInput.classList.add("valid"); } else { - employeeIdInput.classList.remove('valid'); + employeeIdInput.classList.remove("valid"); if (employeeIdInput.value.length > 0) { - employeeIdInput.classList.add('invalid'); + employeeIdInput.classList.add("invalid"); } } } @@ -440,22 +464,22 @@ function requestLocationData() { console.log("📍 Location request already active, skipping"); return; } - + if (!navigator.geolocation) { console.log("❌ Geolocation not supported"); userLocation.source = "manual"; return; } - + locationRequestActive = true; console.log("📍 Requesting location data..."); - + const options = { enableHighAccuracy: true, timeout: 10000, - maximumAge: 300000 + maximumAge: 300000, }; - + navigator.geolocation.getCurrentPosition( handleLocationSuccess, handleLocationError, @@ -465,7 +489,7 @@ function requestLocationData() { function handleLocationSuccess(position) { console.log("✅ Location obtained successfully"); - + userLocation = { latitude: position.coords.latitude, longitude: position.coords.longitude, @@ -473,14 +497,14 @@ function handleLocationSuccess(position) { altitude: position.coords.altitude, timestamp: new Date(), source: "gps", - address: null + address: null, }; - + console.log("📍 Location data:", userLocation); - + // Reverse geocode to get address reverseGeocode(userLocation.latitude, userLocation.longitude); - + locationRequestActive = false; } @@ -498,26 +522,26 @@ function reverseGeocode(lat, lng) { // Language functionality remains unchanged function initializeLanguage() { - const languageToggle = document.getElementById('languageToggle'); + const languageToggle = document.getElementById("languageToggle"); if (languageToggle) { - languageToggle.addEventListener('click', toggleLanguage); + languageToggle.addEventListener("click", toggleLanguage); } applyTranslations(); } function toggleLanguage() { - currentLanguage = currentLanguage === 'en' ? 'es' : 'en'; + currentLanguage = currentLanguage === "en" ? "es" : "en"; applyTranslations(); console.log(`🌐 Language switched to: ${currentLanguage}`); } function applyTranslations() { - const languageText = document.getElementById('languageText'); + const languageText = document.getElementById("languageText"); if (languageText) { languageText.textContent = translations[currentLanguage].languageText; } - - document.querySelectorAll(`[data-${currentLanguage}]`).forEach(element => { + + document.querySelectorAll(`[data-${currentLanguage}]`).forEach((element) => { element.textContent = element.getAttribute(`data-${currentLanguage}`); }); } @@ -525,12 +549,12 @@ function applyTranslations() { function startClock() { function updateClock() { currentTime = new Date(); - const timeElements = document.querySelectorAll('.current-time'); - timeElements.forEach(el => { + const timeElements = document.querySelectorAll(".current-time"); + timeElements.forEach((el) => { el.textContent = currentTime.toLocaleTimeString(); }); } - + updateClock(); setInterval(updateClock, 1000); -} \ No newline at end of file +} diff --git a/templates/create_qr_code.html b/templates/create_qr_code.html index 2770de0..c07422a 100644 --- a/templates/create_qr_code.html +++ b/templates/create_qr_code.html @@ -723,8 +723,8 @@ lngDisplay.textContent = coordinatesData.longitude.toFixed(10); clearBtn.style.display = "inline-flex"; } else { - latDisplay.textContent = "---.------"; - lngDisplay.textContent = "---.------"; + latDisplay.textContent = "---.----------"; + lngDisplay.textContent = "---.----------"; clearBtn.style.display = "none"; } } diff --git a/templates/edit_qr_code.html b/templates/edit_qr_code.html index 70bb0a4..0c635f4 100644 --- a/templates/edit_qr_code.html +++ b/templates/edit_qr_code.html @@ -916,12 +916,12 @@ const clearBtn = document.getElementById('clearCoordinatesBtn'); if (coordinatesData.latitude && coordinatesData.longitude) { - latDisplay.textContent = coordinatesData.latitude.toFixed(6); - lngDisplay.textContent = coordinatesData.longitude.toFixed(6); + latDisplay.textContent = coordinatesData.latitude.toFixed(10); + lngDisplay.textContent = coordinatesData.longitude.toFixed(10); clearBtn.style.display = 'inline-flex'; } else { - latDisplay.textContent = '---.------'; - lngDisplay.textContent = '---.------'; + latDisplay.textContent = '---.----------'; + lngDisplay.textContent = '---.----------'; clearBtn.style.display = 'none'; } }