/** * QR Code Destination Page JavaScript - Complete with Location Tracking * Handles staff check-in functionality with GPS location support */ // Global variables let isSubmitting = false; let currentTime = new Date(); // Location tracking variables let userLocation = { latitude: null, longitude: null, accuracy: null, altitude: null, timestamp: null, source: "manual", address: null, }; let locationRequestActive = false; let locationWatchId = null; // Initialize page when DOM is loaded document.addEventListener("DOMContentLoaded", function () { console.log( "đ QR Destination page initialized with enhanced location tracking" ); // Initialize the page initializePage(); setupEventListeners(); startTimeUpdater(); // Initialize geolocation initializeGeolocation(); // Add hidden form fields for location data ensureLocationFormFields(); }); // Initialize basic page functionality function initializePage() { console.log("đ Initializing page..."); // Focus on employee ID input const employeeInput = document.getElementById("employee_id"); if (employeeInput) { employeeInput.focus(); } // Add page load animation document.body.classList.add("page-loaded"); } // Set up event listeners function setupEventListeners() { console.log("đ§ Setting up event listeners..."); const form = document.getElementById("checkinForm"); const employeeInput = document.getElementById("employee_id"); if (form) { form.addEventListener("submit", handleFormSubmit); } if (employeeInput) { employeeInput.addEventListener("input", handleInputChange); employeeInput.addEventListener("keypress", handleKeyPress); } } // Start time updater function startTimeUpdater() { console.log("â° Starting time updater..."); // Update current time display setInterval(() => { const timeElement = document.getElementById("currentTime"); if (timeElement) { timeElement.textContent = new Date().toLocaleTimeString(); } currentTime = new Date(); }, 1000); } // Handle form submission function handleFormSubmit(e) { e.preventDefault(); if (isSubmitting) { return false; } const employeeId = document.getElementById("employee_id").value.trim(); if (!validateEmployeeId(employeeId)) { return false; } // Ensure location data is up to date before submission updateLocationFormFields(); submitCheckin(employeeId); } // Handle input changes function handleInputChange(e) { const input = e.target; const value = input.value.trim(); // Clear previous validation states input.classList.remove("error", "success"); hideStatusMessage(); // Real-time validation feedback if (value.length >= 3) { if (isValidEmployeeId(value)) { input.classList.add("success"); } else { input.classList.add("error"); } } } // Handle key press function handleKeyPress(e) { // Allow only alphanumeric characters const char = String.fromCharCode(e.which); if (!/[A-Za-z0-9]/.test(char)) { e.preventDefault(); shakeInput(e.target); } // Submit on Enter key if (e.key === "Enter") { e.preventDefault(); handleFormSubmit(e); } } // Validate employee ID function validateEmployeeId(employeeId) { if (!employeeId) { showStatusMessage("Please enter your Employee ID", "error"); return false; } if (!employeeId.match(/^[A-Za-z0-9]{3,20}$/)) { showStatusMessage( "Invalid Employee ID format. Use 3-20 alphanumeric characters.", "error" ); return false; } return true; } // Check if employee ID is valid format function isValidEmployeeId(employeeId) { return /^[A-Za-z0-9]{3,20}$/.test(employeeId); } // Shake input on invalid character function shakeInput(input) { input.classList.add("shake"); setTimeout(() => { input.classList.remove("shake"); }, 300); } // GEOLOCATION FUNCTIONS // Initialize geolocation with better error handling function initializeGeolocation() { console.log("đ Initializing geolocation system..."); if (!navigator.geolocation) { console.log("â ī¸ Geolocation not supported by this browser"); showLocationStatus("error", "Location services not supported"); return; } console.log("â Geolocation API available"); // Request location immediately requestUserLocation(); // Set up continuous watching for better accuracy if ("permissions" in navigator) { navigator.permissions .query({ name: "geolocation" }) .then(function (result) { console.log("đ Geolocation permission status:", result.state); if (result.state === "granted") { startLocationWatching(); } result.onchange = function () { console.log("đ Geolocation permission changed to:", result.state); if (result.state === "granted") { requestUserLocation(); startLocationWatching(); } else { stopLocationWatching(); } }; }); } } // Request user location function requestUserLocation() { if (locationRequestActive) { console.log("âī¸ Location request already active"); return; } locationRequestActive = true; showLocationStatus("loading", "Getting your location..."); const options = { enableHighAccuracy: true, // Use GPS for better accuracy timeout: 15000, // Wait up to 15 seconds maximumAge: 300000, // Accept cached location up to 5 minutes old }; console.log("đĄ Requesting location with options:", options); navigator.geolocation.getCurrentPosition( handleLocationSuccess, handleLocationError, options ); // Set backup timeout setTimeout(() => { if (locationRequestActive && !userLocation.latitude) { console.log("â° Location request backup timeout"); handleLocationError({ code: 3, message: "Request timed out" }); } }, 16000); } // Handle successful location retrieval function handleLocationSuccess(position) { locationRequestActive = false; const coords = position.coords; console.log("â Location obtained:", { latitude: coords.latitude, longitude: coords.longitude, accuracy: coords.accuracy, altitude: coords.altitude, timestamp: position.timestamp, }); // Validate coordinates if (!coords.latitude || !coords.longitude) { console.log("â ī¸ Invalid coordinates received"); handleLocationError({ code: 2, message: "Invalid coordinates" }); return; } // Store location data (keep as numbers for calculations) userLocation = { latitude: Number(coords.latitude), longitude: Number(coords.longitude), accuracy: coords.accuracy ? Math.round(coords.accuracy) : null, altitude: coords.altitude ? Math.round(coords.altitude) : null, timestamp: position.timestamp, source: "gps", address: null, }; console.log("đž Stored location data:", userLocation); // Update form fields immediately updateLocationFormFields(); // Update display updateLocationDisplay(); // Show success status const accuracyText = coords.accuracy ? `Âą${Math.round(coords.accuracy)}m` : "unknown"; showLocationStatus("success", `Location captured (${accuracyText} accuracy)`); // Try to get address reverseGeocodeLocation(coords.latitude, coords.longitude); } // Handle location errors function handleLocationError(error) { locationRequestActive = false; let message = "Unable to get location"; console.log("â Location error:", error); switch (error.code) { case error.PERMISSION_DENIED: message = "Location access denied - please enable in browser settings"; break; case error.POSITION_UNAVAILABLE: message = "Location unavailable - GPS signal weak"; break; case error.TIMEOUT: message = "Location request timed out"; break; default: message = "Location error occurred"; } showLocationStatus( "error", `${message} - check-in will continue without location` ); userLocation.source = "manual"; updateLocationFormFields(); } // Update form fields with location data function updateLocationFormFields() { const fields = { latitude: userLocation.latitude ? userLocation.latitude.toFixed(6) : "", longitude: userLocation.longitude ? userLocation.longitude.toFixed(6) : "", accuracy: userLocation.accuracy || "", altitude: userLocation.altitude || "", location_source: userLocation.source || "manual", address: userLocation.address || "", }; // Update hidden form fields Object.keys(fields).forEach((fieldId) => { let field = document.getElementById(fieldId); if (!field) { // Create hidden input if it doesn't exist field = document.createElement("input"); field.type = "hidden"; field.id = fieldId; field.name = fieldId; document.getElementById("checkinForm").appendChild(field); } field.value = fields[fieldId]; }); console.log("đ Updated form fields with location data:", fields); } // Update location display function updateLocationDisplay() { if (userLocation.latitude && userLocation.longitude) { const elements = { displayLatitude: userLocation.latitude.toFixed(6), displayLongitude: userLocation.longitude.toFixed(6), displayAccuracy: userLocation.accuracy ? `Âą${Math.round(userLocation.accuracy)}m` : "Unknown", displayAddress: userLocation.address || "Loading...", }; Object.keys(elements).forEach((elementId) => { const element = document.getElementById(elementId); if (element) { element.textContent = elements[elementId]; } }); console.log("đĨī¸ Updated location display"); } } // Start continuous location watching function startLocationWatching() { if (!navigator.geolocation || locationWatchId !== null) { return; } const watchOptions = { enableHighAccuracy: true, timeout: 30000, maximumAge: 600000, // 10 minutes }; locationWatchId = navigator.geolocation.watchPosition( handleLocationSuccess, (error) => { console.log("â ī¸ Location watch error:", error); // Don't show error for watch failures, just log them }, watchOptions ); console.log("đī¸ Started location watching"); } // Stop location watching function stopLocationWatching() { if (locationWatchId !== null) { navigator.geolocation.clearWatch(locationWatchId); locationWatchId = null; console.log("âšī¸ Stopped location watching"); } } // Reverse geocode coordinates to get address function reverseGeocodeLocation(lat, lng) { console.log("đ Getting address from coordinates..."); // Use a free geocoding service const geocodeUrl = `https://api.bigdatacloud.net/data/reverse-geocode-client?latitude=${lat}&longitude=${lng}&localityLanguage=en`; fetch(geocodeUrl) .then((response) => response.json()) .then((data) => { if (data && (data.locality || data.city || data.principalSubdivision)) { const address = [ data.locality || data.city, data.principalSubdivision, data.countryName, ] .filter(Boolean) .join(", "); userLocation.address = address; updateLocationFormFields(); updateLocationDisplay(); console.log("đ Address found:", address); } else { console.log("đ No address found"); userLocation.address = "Address not available"; updateLocationFormFields(); updateLocationDisplay(); } }) .catch((error) => { console.log("â ī¸ Geocoding error:", error); userLocation.address = "Address lookup failed"; updateLocationFormFields(); updateLocationDisplay(); }); } // Ensure location form fields exist function ensureLocationFormFields() { const form = document.getElementById("checkinForm"); if (!form) { console.log("â ī¸ Check-in form not found"); return; } const locationFields = [ "latitude", "longitude", "accuracy", "altitude", "location_source", "address", ]; locationFields.forEach((fieldName) => { if (!document.getElementById(fieldName)) { const input = document.createElement("input"); input.type = "hidden"; input.id = fieldName; input.name = fieldName; input.value = ""; form.appendChild(input); console.log(`â Created hidden field: ${fieldName}`); } }); } // FORM SUBMISSION // Submit check-in with location data function submitCheckin(employeeId) { if (isSubmitting) { console.log("âī¸ Already submitting, ignoring duplicate request"); return false; } isSubmitting = true; updateSubmitButton(true); hideStatusMessage(); console.log("đ¤ Starting check-in submission for:", employeeId); console.log("đ Current location data:", userLocation); // Ensure location data is in the form updateLocationFormFields(); // Prepare form data const formData = new FormData(); formData.append("employee_id", employeeId); // Add location data formData.append( "latitude", userLocation.latitude ? userLocation.latitude.toFixed(6) : "" ); formData.append( "longitude", userLocation.longitude ? userLocation.longitude.toFixed(6) : "" ); formData.append("accuracy", userLocation.accuracy || ""); formData.append("altitude", userLocation.altitude || ""); formData.append("location_source", userLocation.source || "manual"); formData.append("address", userLocation.address || ""); // Debug: Log exactly what we're sending console.log("đ¤ Form data being submitted:"); for (let [key, value] of formData.entries()) { console.log(` ${key}: "${value}"`); } // Get the current URL for the check-in endpoint const currentUrl = window.location.pathname; const checkinUrl = `${currentUrl}/checkin`; console.log("đ¯ Submitting to URL:", checkinUrl); fetch(checkinUrl, { method: "POST", body: formData, headers: { "X-Requested-With": "XMLHttpRequest", }, }) .then((response) => { console.log("đĄ Server response status:", response.status); console.log("đĄ Server response headers:", response.headers); if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`); } return response.json(); }) .then((data) => { isSubmitting = false; updateSubmitButton(false); console.log( "đĨ Complete server response:", JSON.stringify(data, null, 2) ); if (data.success) { showSuccessPage(data); console.log( "â Check-in successful with location:", data.data?.has_location || false ); // Stop location watching after successful check-in stopLocationWatching(); } else { const errorMessage = data.message || data.error || "Check-in failed"; showStatusMessage(errorMessage, "error"); console.log("â Check-in failed:", errorMessage); console.log("â Full error response:", data); } }) .catch((error) => { isSubmitting = false; updateSubmitButton(false); console.error("â Network/Parse error details:", error); console.error("â Error name:", error.name); console.error("â Error message:", error.message); console.error("â Error stack:", error.stack); let errorMessage = "Network error. Please check your connection and try again."; if (error.message.includes("JSON")) { errorMessage = "Server response error. Please try again."; } else if (error.message.includes("HTTP error")) { errorMessage = "Server error. Please contact support if this continues."; } showStatusMessage(errorMessage, "error"); }); } // UTILITY FUNCTIONS // Update submit button state function updateSubmitButton(isLoading) { const submitBtn = document.querySelector( '#checkinForm button[type="submit"]' ); if (submitBtn) { if (isLoading) { submitBtn.disabled = true; submitBtn.innerHTML = ' Processing...'; } else { submitBtn.disabled = false; submitBtn.innerHTML = ' Check In'; } } } // Show status messages function showStatusMessage(message, type = "info") { console.log(`Status: ${type} - ${message}`); // Try to find existing status display element let statusEl = document.getElementById("statusMessage"); if (!statusEl) { statusEl = document.createElement("div"); statusEl.id = "statusMessage"; statusEl.style.cssText = ` position: fixed; top: 20px; left: 50%; transform: translateX(-50%); padding: 12px 24px; border-radius: 8px; z-index: 1000; font-weight: 500; box-shadow: 0 4px 12px rgba(0,0,0,0.15); `; document.body.appendChild(statusEl); } statusEl.textContent = message; statusEl.className = `status-message ${type}`; // Style based on type if (type === "error") { statusEl.style.backgroundColor = "#fee2e2"; statusEl.style.color = "#dc2626"; statusEl.style.border = "1px solid #fecaca"; } else if (type === "success") { statusEl.style.backgroundColor = "#dcfce7"; statusEl.style.color = "#16a34a"; statusEl.style.border = "1px solid #bbf7d0"; } else { statusEl.style.backgroundColor = "#dbeafe"; statusEl.style.color = "#2563eb"; statusEl.style.border = "1px solid #bfdbfe"; } statusEl.style.display = "block"; // Auto-hide after 5 seconds setTimeout(() => { statusEl.style.display = "none"; }, 5000); } // Hide status messages function hideStatusMessage() { const statusEl = document.getElementById("statusMessage"); if (statusEl) { statusEl.style.display = "none"; } } // Enhanced location status display function showLocationStatus(type, message) { const statusElement = document.getElementById("locationStatus"); const messageElement = document.getElementById("locationMessage"); if (statusElement && messageElement) { statusElement.className = `location-status ${type}`; messageElement.textContent = message; // Auto-hide success messages after 3 seconds if (type === "success") { setTimeout(() => { statusElement.style.display = "none"; }, 3000); } else { statusElement.style.display = "block"; } } console.log(`đ Location status: ${type} - ${message}`); } // Show success page with safe data handling function showSuccessPage(data) { console.log("đ Showing success page with data:", data); // Hide the form const form = document.getElementById("checkinForm"); if (form) { form.style.display = "none"; } // Safely extract data with fallbacks const responseData = data.data || 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 hasLocation = responseData.has_location || false; const locationInfo = responseData.location_info || null; console.log("đ Processed success data:", { employeeId, location, event, checkInTime, checkInDate, hasLocation, locationInfo, }); // Show success message showStatusMessage(`Check-in successful for ${employeeId}!`, "success"); // Update success card if it exists const successCard = document.getElementById("successCard"); if (successCard) { successCard.style.display = "block"; // Update success details safely const updateElement = (id, value) => { const el = document.getElementById(id); if (el) { el.textContent = value || "N/A"; console.log(`â Updated ${id}: ${value}`); } else { console.log(`â ī¸ Element not found: ${id}`); } }; updateElement("successEmployeeId", employeeId); updateElement("successLocation", location); updateElement("successEvent", event); updateElement("successTime", checkInTime); updateElement("successDate", checkInDate); // Show location info if available if (hasLocation && locationInfo) { const locationInfoEl = document.getElementById("successLocationInfo"); const gpsInfo = document.getElementById("successGpsInfo"); if (locationInfoEl && gpsInfo) { const coordinates = locationInfo.coordinates || "Unknown coordinates"; const accuracy = locationInfo.accuracy || "Unknown accuracy"; gpsInfo.textContent = `${coordinates} (${accuracy})`; locationInfoEl.style.display = "block"; console.log("â Updated GPS info display"); } } else { console.log("đ No location data to display"); } } else { console.log("â ī¸ Success card element not found, using fallback"); // Create a simple success display const successMessage = document.createElement("div"); successMessage.innerHTML = `
Employee: ${employeeId}
Location: ${location}
Time: ${checkInTime}
${hasLocation ? "đ Location data captured
" : ""}