diff --git a/app.py b/app.py index ff858b7..02d8cad 100644 --- a/app.py +++ b/app.py @@ -1150,7 +1150,7 @@ def qr_destination(qr_url): @app.route('/qr//checkin', methods=['POST']) def qr_checkin(qr_url): - """Enhanced staff check-in with proper location handling""" + """FIXED: Enhanced staff check-in with guaranteed location saving""" try: # Find QR code by URL qr_code = QRCode.query.filter_by(qr_url=qr_url, active_status=True).first() @@ -1164,7 +1164,7 @@ def qr_checkin(qr_url): # Get form data employee_id = request.form.get('employee_id', '').strip() - # FIXED: Get location data with correct field names and validation + # CRITICAL: Get location data with debug logging latitude = request.form.get('latitude', '').strip() longitude = request.form.get('longitude', '').strip() accuracy = request.form.get('accuracy', '').strip() @@ -1172,18 +1172,20 @@ def qr_checkin(qr_url): location_source = request.form.get('location_source', 'manual').strip() address = request.form.get('address', '').strip() - # CRITICAL DEBUG: Log all received form data - print(f"\n{'='*50}") - print(f"๐Ÿ“ฅ QR CHECK-IN DATA RECEIVED:") - print(f"{'='*50}") + # COMPREHENSIVE DEBUG: Log all received form data + print(f"\n{'='*60}") + print(f"๐Ÿ” QR CHECK-IN DEBUG - FORM DATA RECEIVED") + print(f"{'='*60}") print(f"Employee ID: '{employee_id}'") - print(f"Latitude: '{latitude}' (type: {type(latitude)})") - print(f"Longitude: '{longitude}' (type: {type(longitude)})") - print(f"Accuracy: '{accuracy}' (type: {type(accuracy)})") - print(f"Altitude: '{altitude}' (type: {type(altitude)})") + print(f"Latitude: '{latitude}' (length: {len(latitude)}, type: {type(latitude)})") + print(f"Longitude: '{longitude}' (length: {len(longitude)}, type: {type(longitude)})") + print(f"Accuracy: '{accuracy}' (length: {len(accuracy)}, type: {type(accuracy)})") + print(f"Altitude: '{altitude}' (length: {len(altitude)}, type: {type(altitude)})") print(f"Location Source: '{location_source}' (type: {type(location_source)})") - print(f"Address: '{address}' (type: {type(address)})") - print(f"{'='*50}\n") + print(f"Address: '{address}' (length: {len(address) if address else 0})") + print(f"QR Code ID: {qr_code.id}") + print(f"QR Location: {qr_code.location}") + print(f"{'='*60}\n") if not employee_id: return jsonify({ @@ -1192,7 +1194,7 @@ def qr_checkin(qr_url): }), 400 # Validate employee ID format - if not re.match(r'^[A-Za-z0-9]{3,20}', employee_id): + if not re.match(r'^[A-Za-z0-9]{3,20}$', employee_id): return jsonify({ 'success': False, 'message': 'Invalid employee ID format. Use 3-20 alphanumeric characters.' @@ -1215,7 +1217,6 @@ def qr_checkin(qr_url): # Parse user agent for device info user_agent = request.headers.get('User-Agent', '') try: - from user_agents import parse parsed_agent = parse(user_agent) device_info = f"{parsed_agent.browser.family} on {parsed_agent.os.family}" except: @@ -1226,66 +1227,84 @@ def qr_checkin(qr_url): if client_ip and ',' in client_ip: client_ip = client_ip.split(',')[0].strip() - # FIXED: Process location data with enhanced validation + # CRITICAL FIX: Enhanced location data processing with validation lat_value = None lng_value = None acc_value = None alt_value = None - # Process latitude - if latitude and latitude.strip() and latitude not in ['null', '', 'undefined']: + print(f"๐Ÿ”„ PROCESSING LOCATION DATA:") + + # Process latitude with comprehensive validation + if latitude and latitude.strip() and latitude not in ['null', '', 'undefined', 'NaN']: try: lat_value = float(latitude) - if not (-90 <= lat_value <= 90): - print(f"โš ๏ธ Invalid latitude range: {lat_value}") - lat_value = None - else: + if -90 <= lat_value <= 90: print(f"โœ… Valid latitude: {lat_value}") + else: + print(f"โš ๏ธ Invalid latitude range: {lat_value} (must be -90 to 90)") + lat_value = None except (ValueError, TypeError) as e: - print(f"โš ๏ธ Latitude parsing error: {e}") + print(f"โŒ Latitude parsing error: {e}") + lat_value = None + else: + print(f"๐Ÿ“ No latitude data: '{latitude}'") - # Process longitude - if longitude and longitude.strip() and longitude not in ['null', '', 'undefined']: + # Process longitude with comprehensive validation + if longitude and longitude.strip() and longitude not in ['null', '', 'undefined', 'NaN']: try: lng_value = float(longitude) - if not (-180 <= lng_value <= 180): - print(f"โš ๏ธ Invalid longitude range: {lng_value}") - lng_value = None - else: + if -180 <= lng_value <= 180: print(f"โœ… Valid longitude: {lng_value}") + else: + print(f"โš ๏ธ Invalid longitude range: {lng_value} (must be -180 to 180)") + lng_value = None except (ValueError, TypeError) as e: - print(f"โš ๏ธ Longitude parsing error: {e}") + print(f"โŒ Longitude parsing error: {e}") + lng_value = None + else: + print(f"๐Ÿ“ No longitude data: '{longitude}'") # Process accuracy - if accuracy and accuracy.strip() and accuracy not in ['null', '', 'undefined']: + if accuracy and accuracy.strip() and accuracy not in ['null', '', 'undefined', 'NaN']: try: acc_value = float(accuracy) - if acc_value < 0: + if acc_value >= 0: + print(f"โœ… Valid accuracy: {acc_value}m") + else: print(f"โš ๏ธ Invalid accuracy (negative): {acc_value}") acc_value = None - else: - print(f"โœ… Valid accuracy: {acc_value}m") except (ValueError, TypeError) as e: - print(f"โš ๏ธ Accuracy parsing error: {e}") + print(f"โŒ Accuracy parsing error: {e}") + acc_value = None + else: + print(f"๐Ÿ“ No accuracy data: '{accuracy}'") # Process altitude - if altitude and altitude.strip() and altitude not in ['null', '', 'undefined']: + if altitude and altitude.strip() and altitude not in ['null', '', 'undefined', 'NaN']: try: alt_value = float(altitude) print(f"โœ… Valid altitude: {alt_value}m") except (ValueError, TypeError) as e: - print(f"โš ๏ธ Altitude parsing error: {e}") + print(f"โŒ Altitude parsing error: {e}") + alt_value = None + else: + print(f"๐Ÿ“ No altitude data: '{altitude}'") - # Validate location source + # Validate and clean location source valid_sources = ['gps', 'network', 'manual'] if location_source not in valid_sources: + print(f"โš ๏ธ Invalid location source '{location_source}', defaulting to 'manual'") location_source = 'manual' + else: + print(f"โœ… Valid location source: {location_source}") # Truncate address if too long if address and len(address) > 500: address = address[:500] + print(f"โš ๏ธ Address truncated to 500 characters") - print(f"๐Ÿ“Š PROCESSED LOCATION DATA:") + print(f"\n๐Ÿ“Š FINAL PROCESSED LOCATION DATA:") print(f" Latitude: {lat_value}") print(f" Longitude: {lng_value}") print(f" Accuracy: {acc_value}") @@ -1293,7 +1312,12 @@ def qr_checkin(qr_url): print(f" Source: {location_source}") print(f" Address: {address[:50]}..." if address and len(address) > 50 else f" Address: {address}") - # Create attendance record + has_coordinates = lat_value is not None and lng_value is not None + print(f" Has Valid Coordinates: {has_coordinates}") + + # CRITICAL: Create attendance record with explicit location field assignment + print(f"\n๐Ÿ’พ CREATING ATTENDANCE RECORD:") + attendance = AttendanceData( qr_code_id=qr_code.id, employee_id=employee_id.upper(), @@ -1303,70 +1327,169 @@ def qr_checkin(qr_url): user_agent=user_agent, ip_address=client_ip, location_name=qr_code.location, - status='present', - # FIXED: Add location data directly to constructor - latitude=lat_value, - longitude=lng_value, - accuracy=acc_value, - altitude=alt_value, - location_source=location_source, - address=address + status='present' ) - print(f"๐Ÿ’พ SAVING ATTENDANCE RECORD:") + # EXPLICIT LOCATION FIELD ASSIGNMENT + if lat_value is not None: + attendance.latitude = lat_value + print(f"โœ… Set latitude: {attendance.latitude}") + + if lng_value is not None: + attendance.longitude = lng_value + print(f"โœ… Set longitude: {attendance.longitude}") + + if acc_value is not None: + attendance.accuracy = acc_value + print(f"โœ… Set accuracy: {attendance.accuracy}") + + if alt_value is not None: + attendance.altitude = alt_value + print(f"โœ… Set altitude: {attendance.altitude}") + + if location_source: + attendance.location_source = location_source + print(f"โœ… Set location_source: {attendance.location_source}") + + if address: + attendance.address = address + print(f"โœ… Set address: {attendance.address[:50]}...") + + print(f"\n๐Ÿ’พ SAVING TO DATABASE:") + print(f" Record ID will be generated...") print(f" Employee: {attendance.employee_id}") - print(f" Location: {attendance.location_name}") - print(f" GPS: {attendance.latitude}, {attendance.longitude}") + print(f" Location Name: {attendance.location_name}") + print(f" Coordinates: {attendance.latitude}, {attendance.longitude}") print(f" Accuracy: {attendance.accuracy}") print(f" Source: {attendance.location_source}") + # Add to session and commit db.session.add(attendance) - db.session.commit() + + try: + db.session.commit() + print(f"โœ… Database commit successful!") + except Exception as commit_error: + print(f"โŒ Database commit failed: {commit_error}") + db.session.rollback() + raise # VERIFICATION: Re-fetch the saved record to confirm data persistence - saved_record = AttendanceData.query.get(attendance.id) - has_location = saved_record.latitude is not None and saved_record.longitude is not None + print(f"\n๐Ÿ” VERIFICATION - Re-fetching saved record:") - print(f"โœ… VERIFICATION - Record saved with ID: {saved_record.id}") - print(f"โœ… Has location data: {has_location}") - if has_location: - print(f"โœ… Saved coordinates: {saved_record.latitude}, {saved_record.longitude}") - print(f"โœ… Saved accuracy: {saved_record.accuracy}") - print(f"โœ… Saved source: {saved_record.location_source}") + try: + saved_record = AttendanceData.query.get(attendance.id) + if saved_record: + print(f"โœ… Record found with ID: {saved_record.id}") + print(f"โœ… Employee ID: {saved_record.employee_id}") + print(f"โœ… Saved latitude: {saved_record.latitude}") + print(f"โœ… Saved longitude: {saved_record.longitude}") + print(f"โœ… Saved accuracy: {saved_record.accuracy}") + print(f"โœ… Saved altitude: {saved_record.altitude}") + print(f"โœ… Saved location_source: {saved_record.location_source}") + print(f"โœ… Saved address: {saved_record.address}") + + has_location_final = saved_record.latitude is not None and saved_record.longitude is not None + print(f"โœ… Final has_location status: {has_location_final}") + + # DATABASE VERIFICATION QUERY + verification_query = f""" + SELECT id, employee_id, latitude, longitude, accuracy, altitude, location_source, address + FROM attendance_data + WHERE id = {saved_record.id} + """ + print(f"\n๐Ÿ” Database verification query:") + print(f" {verification_query}") + + else: + print(f"โŒ ERROR: Could not re-fetch saved record!") + saved_record = attendance # Fallback to original object + + except Exception as verify_error: + print(f"โŒ Verification error: {verify_error}") + saved_record = attendance # Fallback to original object - # Enhanced response with location verification + # Build response with verification data response_data = { 'success': True, 'message': 'Check-in successful!', - 'data': { - 'employee_id': employee_id.upper(), - 'location': qr_code.location, - 'event': qr_code.location_event, - 'check_in_time': attendance.check_in_time.strftime('%H:%M'), - 'check_in_date': attendance.check_in_date.strftime('%B %d, %Y'), - 'has_location': has_location, - 'location_info': { - 'coordinates': f"{saved_record.latitude:.6f}, {saved_record.longitude:.6f}" if has_location else "No GPS data", - 'accuracy': f"ยฑ{saved_record.accuracy:.0f}m" if saved_record.accuracy else "Unknown", - 'source': saved_record.location_source.title() if saved_record.location_source else "Manual", - 'address': saved_record.address or "Not available" - } if has_location else None - } + 'employee_id': employee_id.upper(), + 'location': qr_code.location, + 'event': qr_code.location_event, + 'time': attendance.check_in_time.strftime('%H:%M'), + 'date': attendance.check_in_date.strftime('%Y-%m-%d'), + 'has_location': saved_record.latitude is not None and saved_record.longitude is not None, + 'location_coordinates': f"{saved_record.latitude},{saved_record.longitude}" if saved_record.latitude and saved_record.longitude else None, + 'location_accuracy': saved_record.accuracy, + 'location_address': saved_record.address, + 'location_source': saved_record.location_source } - print(f"๐Ÿ“ค SENDING RESPONSE: {response_data['data']['has_location']} location data") + print(f"\n๐Ÿ“ค SENDING RESPONSE:") + print(f" Success: {response_data['success']}") + print(f" Has Location: {response_data['has_location']}") + print(f" Coordinates: {response_data['location_coordinates']}") + print(f" Accuracy: {response_data['location_accuracy']}") + print(f"{'='*60}\n") return jsonify(response_data) + except Exception as e: - print(f"โŒ CHECK-IN ERROR: {str(e)}") + print(f"\nโŒ CRITICAL ERROR in qr_checkin:") + print(f"โŒ Error type: {type(e).__name__}") + print(f"โŒ Error message: {str(e)}") + + # Print full traceback for debugging import traceback - print(f"โŒ TRACEBACK: {traceback.format_exc()}") + print(f"โŒ Full traceback:") + print(traceback.format_exc()) + db.session.rollback() + return jsonify({ 'success': False, - 'message': 'Check-in failed. Please try again.', + 'message': 'Check-in failed due to server error. Please try again.', 'error': str(e) if app.debug else None }), 500 + +# ===================================================================== +# ADDITIONAL DEBUGGING ROUTE (Add this to your app.py for testing) +# ===================================================================== + +@app.route('/debug/attendance/') +@admin_required +def debug_attendance(attendance_id): + """Debug route to inspect a specific attendance record""" + + try: + record = AttendanceData.query.get_or_404(attendance_id) + + debug_info = { + 'id': record.id, + 'employee_id': record.employee_id, + 'location_name': record.location_name, + 'check_in_date': record.check_in_date.isoformat(), + 'check_in_time': record.check_in_time.isoformat(), + 'latitude': record.latitude, + 'longitude': record.longitude, + 'accuracy': record.accuracy, + 'altitude': record.altitude, + 'location_source': record.location_source, + 'address': record.address, + 'has_coordinates': record.latitude is not None and record.longitude is not None, + 'created_timestamp': record.created_timestamp.isoformat() if record.created_timestamp else None + } + + return jsonify({ + 'success': True, + 'attendance_record': debug_info + }) + + except Exception as e: + return jsonify({ + 'success': False, + 'error': str(e) + }), 500 def process_location_data(location_data): """ diff --git a/static/js/qr_destination.js b/static/js/qr_destination.js index 2c7d60a..d747c63 100644 --- a/static/js/qr_destination.js +++ b/static/js/qr_destination.js @@ -1,716 +1,622 @@ /** - * QR Code Destination Page JavaScript - Complete with Geolocation - * Handles staff check-in functionality and form interactions + * 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(); -// NEW: Geolocation variables +// Location tracking variables let userLocation = { - latitude: null, - longitude: null, - accuracy: null, - altitude: null, - timestamp: null, - source: 'manual', - address: null + 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 geolocation'); - - initializePage(); - setupEventListeners(); - startTimeUpdater(); - - // NEW: Initialize geolocation - initializeGeolocation(); +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(); - } + 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'); - if (form) { - form.addEventListener('submit', handleFormSubmit); - } + 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); + } } -// Initialize geolocation functionality -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(); - } - }; - }); +// 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); } -// NEW: Check location permissions -function checkLocationPermission() { - if (navigator.permissions) { - navigator.permissions.query({name: 'geolocation'}).then(function(result) { - console.log('๐Ÿ” Location permission status:', result.state); - - if (result.state === 'granted') { - console.log('โœ… Location permission granted'); - } else if (result.state === 'prompt') { - console.log('โ“ Location permission will be requested'); - } else if (result.state === 'denied') { - console.log('โŒ Location permission denied'); - showLocationStatus('error', 'Location permission denied - enable in browser settings'); - } - }).catch(function(error) { - console.log('โš ๏ธ Permission query failed:', error); - }); - } +// 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); } -// Request user's current location -function requestUserLocation() { - if (locationRequestActive) { - console.log('โญ๏ธ Location request already active'); - return; +// 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"); } - - 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 + } +} + +// 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" ); - - // Set backup timeout - setTimeout(() => { - if (locationRequestActive && !userLocation.latitude) { - console.log('โฐ Location request backup timeout'); - handleLocationError({ code: 3, message: 'Request timed out' }); - } - }, 16000); + return false; + } + + return true; } -// 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}`); +// 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 with validation (keep as numbers, not strings) - userLocation = { - latitude: Number(coords.latitude), // Keep as number for calculations - 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); + 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(); -} + locationRequestActive = false; -// Enhanced form initialization -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(); -}); + let message = "Unable to get location"; -// Start watching location for continuous updates -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'); -} + console.log("โŒ Location error:", error); -// Stop watching location -function stopLocationWatching() { - if (locationWatchId !== null) { - navigator.geolocation.clearWatch(locationWatchId); - locationWatchId = null; - console.log('โน๏ธ Stopped location watching'); - } + 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() { - // CRITICAL FIX: Use correct field names that match server expectations - const fields = { - 'latitude': userLocation.latitude ? userLocation.latitude.toFixed(6) : '', // Convert to string with precision here - 'longitude': userLocation.longitude ? userLocation.longitude.toFixed(6) : '', - 'accuracy': userLocation.accuracy || '', - 'altitude': userLocation.altitude || '', - 'location_source': userLocation.source || 'manual', // FIXED: was 'locationSource' - '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); + 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'); - } + 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(); - }); -} + console.log("๐Ÿ  Getting address from coordinates..."); -function tryGeocodingService(index, services) { - if (index >= services.length) { - console.log('โš ๏ธ All geocoding services failed'); - const displayAddress = document.getElementById('displayAddress'); - if (displayAddress) { - displayAddress.textContent = 'Address not available'; - } - return; - } - - const service = services[index]; - - fetch(service.url) - .then(response => response.json()) - .then(data => { - const address = service.parser(data); - - if (address) { - userLocation.address = address; - document.getElementById('address').value = address; - - const displayAddress = document.getElementById('displayAddress'); - if (displayAddress) { - displayAddress.textContent = address; - } - - console.log(`๐Ÿ  Address found using ${service.name}:`, address); - return; - } - - // Try next service - tryGeocodingService(index + 1, services); - }) - .catch(error => { - console.log(`โš ๏ธ ${service.name} geocoding failed:`, error); - // Try next service - tryGeocodingService(index + 1, services); - }); -} + // Use a free geocoding service + const geocodeUrl = `https://api.bigdatacloud.net/data/reverse-geocode-client?latitude=${lat}&longitude=${lng}&localityLanguage=en`; -// Show location status to user -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}`); -} + 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(", "); -// NEW: Get accuracy level description -function getAccuracyLevel(accuracy) { - if (!accuracy) return 'unknown'; - if (accuracy <= 50) return 'high'; - if (accuracy <= 100) return 'medium'; - return 'low'; -} - -// Toggle location info display -function toggleLocationInfo() { - const locationInfo = document.getElementById('locationInfo'); - if (!locationInfo) return; - - if (locationInfo.style.display === 'none' || !locationInfo.style.display) { + userLocation.address = address; + updateLocationFormFields(); updateLocationDisplay(); - locationInfo.style.display = 'block'; - } else { - locationInfo.style.display = 'none'; - } -} -// Retry location request -function retryLocationRequest() { - console.log('๐Ÿ”„ Retrying location request...'); - - // Stop any existing watch - stopLocationWatching(); - - // Reset location data - userLocation = { - latitude: null, - longitude: null, - accuracy: null, - altitude: null, - timestamp: null, - source: 'manual', - address: null - }; - - // Clear form fields - updateLocationFormFields(); - - // Request location again - requestUserLocation(); -} - -// Get current location data for external use -function getCurrentLocationData() { - return { - hasLocation: !!(userLocation.latitude && userLocation.longitude), - latitude: userLocation.latitude, - longitude: userLocation.longitude, - accuracy: userLocation.accuracy, - altitude: userLocation.altitude, - source: userLocation.source, - timestamp: userLocation.timestamp, - address: userLocation.address - }; -} - -function handleFormSubmit(e) { - e.preventDefault(); - - if (isSubmitting) { - return false; - } - - const employeeId = document.getElementById('employee_id').value.trim(); - - 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; - } - - // Ensure location data is up to date before submission - updateLocationFormFields(); - - submitCheckin(employeeId); -} - -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'); - } - } -} - -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); - } -} - -function validateEmployeeId() { - const employeeInput = document.getElementById('employee_id'); - const employeeId = employeeInput.value.trim(); - - if (!employeeId) { - showValidationError(employeeInput, 'Employee ID is required'); - return false; - } - - if (employeeId.length < 3) { - showValidationError(employeeInput, 'Employee ID must be at least 3 characters'); - return false; - } - - if (employeeId.length > 20) { - showValidationError(employeeInput, 'Employee ID must be 20 characters or less'); - return false; - } - - if (!isValidEmployeeId(employeeId)) { - showValidationError(employeeInput, 'Employee ID can only contain letters and numbers'); - return false; - } - - clearValidationError(employeeInput); - return true; -} - -function isValidEmployeeId(id) { - return /^[A-Za-z0-9]+$/.test(id); -} - -function showValidationError(input, message) { - input.classList.add('error'); - showStatusMessage(message, 'error'); - shakeInput(input); - input.focus(); -} - -function clearValidationError(input) { - input.classList.remove('error'); - input.classList.add('success'); -} - -function shakeInput(input) { - input.style.animation = 'shake 0.5s'; - setTimeout(() => { - input.style.animation = ''; - }, 500); -} - -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 with CORRECT field names - const formData = new FormData(); - formData.append('employee_id', employeeId); - - // CRITICAL FIX: Use exact field names that server expects - 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'); // FIXED: was locationSource - 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' - } + console.log("๐Ÿ  Address found:", address); + } else { + console.log("๐Ÿ  No address found"); + userLocation.address = "Address not available"; + updateLocationFormFields(); + updateLocationDisplay(); + } }) - .then(response => { - console.log('๐Ÿ“ก Server response status:', response.status); - return response.json(); - }) - .then(data => { - isSubmitting = false; - updateSubmitButton(false); - - console.log('๐Ÿ“ฅ Server response:', data); - - 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 { - showStatusMessage(data.message || 'Check-in failed', 'error'); - console.log('โŒ Check-in failed:', data.message); - } - }) - .catch(error => { - isSubmitting = false; - updateSubmitButton(false); - showStatusMessage('Network error. Please check your connection and try again.', 'error'); - console.error('โŒ Network error:', error); + .catch((error) => { + console.log("โš ๏ธ Geocoding error:", error); + userLocation.address = "Address lookup failed"; + updateLocationFormFields(); + updateLocationDisplay(); }); } -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'; - } - - // Show success message - showStatusMessage(`Check-in successful for ${data.data.employee_id}!`, 'success'); - - // You can customize this to show a proper success page - // For now, just show the success message and reload after 3 seconds - setTimeout(() => { - location.reload(); - }, 3000);; -let locationRequestActive = false; -let locationWatchId = null; +// Ensure location form fields exist +function ensureLocationFormFields() { + const form = document.getElementById("checkinForm"); + if (!form) { + console.log("โš ๏ธ Check-in form not found"); + return; + } -// Utility function to show status messages -function showStatusMessage(message, type = 'info') { - // This function should exist in your original code - // If not, here's a simple implementation - 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 = ` + 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%; @@ -719,137 +625,276 @@ function showStatusMessage(message, type = 'info') { 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); + 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); } -// Utility function to hide status messages +// Hide status messages function hideStatusMessage() { - const statusEl = document.getElementById('statusMessage'); - if (statusEl) { - statusEl.style.display = 'none'; + 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}`); } -function getStatusIcon(type) { - switch(type) { - case 'success': return 'fa-check-circle'; - case 'error': return 'fa-exclamation-triangle'; - case 'info': return 'fa-info-circle'; - case 'warning': return 'fa-exclamation-circle'; - default: return 'fa-info-circle'; +// 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"); -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'; - } + // Create a simple success display + const successMessage = document.createElement("div"); + successMessage.innerHTML = ` +
+

โœ… Check-in Successful!

+

Employee: ${employeeId}

+

Location: ${location}

+

Time: ${checkInTime}

+ ${hasLocation ? "

๐Ÿ“ Location data captured

" : ""} + +
+ `; + + // Insert after the form + if (form && form.parentNode) { + form.parentNode.insertBefore(successMessage, form.nextSibling); + } else { + document.body.appendChild(successMessage); } + + // Auto-reload after 10 seconds as fallback + setTimeout(() => { + location.reload(); + }, 10000); + } } -function startTimeUpdater() { - console.log('โฐ Starting time updater...'); - // Update current time display - setInterval(() => { - const timeElement = document.getElementById('currentTime'); - if (timeElement) { - timeElement.textContent = new Date().toLocaleTimeString(); - } - }, 1000); +// INTERACTIVE FUNCTIONS (called from HTML) + +// Retry location request +function retryLocationRequest() { + console.log("๐Ÿ”„ Retrying location request..."); + + // Stop any existing watch + stopLocationWatching(); + + // Reset location data + userLocation = { + latitude: null, + longitude: null, + accuracy: null, + altitude: null, + timestamp: null, + source: "manual", + address: null, + }; + + // Clear form fields + updateLocationFormFields(); + + // Request location again + requestUserLocation(); } -function updateCurrentTime() { - const now = new Date(); - const timeString = now.toLocaleString('en-US', { - weekday: 'long', - year: 'numeric', - month: 'long', - day: 'numeric', - hour: '2-digit', - minute: '2-digit', - second: '2-digit' - }); - - const timeElement = document.getElementById('currentTime'); - if (timeElement) { - timeElement.textContent = timeString; - } - - currentTime = now; -} - -function handleVisibilityChange() { - if (!document.hidden) { - // Page became visible, update time immediately - updateCurrentTime(); - - // NEW: Request location again if we don't have it and haven't submitted yet - if (!userLocation.latitude && !isSubmitting) { - console.log('๐Ÿ”„ Page visible again, retrying location...'); - setTimeout(requestUserLocation, 1000); - } - } +// Toggle location info display +function toggleLocationInfo() { + const locationInfo = document.getElementById("locationInfo"); + if (!locationInfo) return; + + if (locationInfo.style.display === "none" || !locationInfo.style.display) { + updateLocationDisplay(); + locationInfo.style.display = "block"; + } else { + locationInfo.style.display = "none"; + } } +// Check in another employee (reset form) function checkInAnother() { - // Stop location watching - stopLocationWatching(); - - // Reload page - window.location.reload(); + // Reset form + const form = document.getElementById("checkinForm"); + const successCard = document.getElementById("successCard"); + + if (form) { + form.style.display = "block"; + form.reset(); + } + + if (successCard) { + successCard.style.display = "none"; + } + + // Reset location data and restart tracking + userLocation = { + latitude: null, + longitude: null, + accuracy: null, + altitude: null, + timestamp: null, + source: "manual", + address: null, + }; + + // Restart location tracking + initializeGeolocation(); + + // Focus on employee input + const employeeInput = document.getElementById("employee_id"); + if (employeeInput) { + employeeInput.focus(); + } + + hideStatusMessage(); } -// NEW: Cleanup function for page unload -function cleanup() { - stopLocationWatching(); - console.log('๐Ÿงน Cleaned up geolocation resources'); +// Get current location data (for external use) +function getCurrentLocationData() { + return { + hasLocation: !!(userLocation.latitude && userLocation.longitude), + latitude: userLocation.latitude, + longitude: userLocation.longitude, + accuracy: userLocation.accuracy, + altitude: userLocation.altitude, + source: userLocation.source, + timestamp: userLocation.timestamp, + address: userLocation.address, + }; } -// NEW: Setup cleanup handlers -window.addEventListener('beforeunload', cleanup); -window.addEventListener('pagehide', cleanup); - -// NEW: Export geolocation functions for global use -window.requestUserLocation = requestUserLocation; -window.getCurrentLocationData = getCurrentLocationData; -window.retryLocationRequest = retryLocationRequest; -window.toggleLocationInfo = toggleLocationInfo; -window.stopLocationWatching = stopLocationWatching; -window.startLocationWatching = startLocationWatching; - -console.log('๐Ÿ“ QR Destination with Geolocation loaded successfully!'); -console.log('๐Ÿ”ง Available functions: requestUserLocation(), getCurrentLocationData(), retryLocationRequest(), toggleLocationInfo()'); -console.log('๐Ÿ“Š Location tracking ready for check-ins!'); \ No newline at end of file +console.log( + "๐Ÿ“ QR Destination JavaScript loaded successfully with location tracking!" +);