Modify checkin page
This commit is contained in:
+374
-490
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
* QR Code Destination Page JavaScript - Enhanced with Bilingual Support
|
||||
* Handles staff check-in functionality with GPS location support and language switching
|
||||
* QR Code Destination Page JavaScript - Enhanced with Multiple Check-ins Support
|
||||
* Handles staff check-in functionality with GPS location support, language switching, and 30-minute interval validation
|
||||
*/
|
||||
|
||||
// Global variables (PRESERVED FROM ORIGINAL)
|
||||
@@ -20,7 +20,7 @@ let userLocation = {
|
||||
let locationRequestActive = false;
|
||||
let locationWatchId = null;
|
||||
|
||||
// BILINGUAL FUNCTIONALITY - NEW FEATURE
|
||||
// BILINGUAL FUNCTIONALITY (PRESERVED FROM ORIGINAL)
|
||||
let currentLanguage = 'en';
|
||||
const translations = {
|
||||
en: {
|
||||
@@ -30,6 +30,8 @@ const translations = {
|
||||
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.'
|
||||
@@ -42,6 +44,8 @@ const translations = {
|
||||
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.'
|
||||
@@ -49,448 +53,106 @@ const translations = {
|
||||
}
|
||||
};
|
||||
|
||||
// Initialize page when DOM is loaded (ENHANCED VERSION)
|
||||
document.addEventListener("DOMContentLoaded", function () {
|
||||
console.log(
|
||||
"🚀 QR Destination page initialized with enhanced location tracking and bilingual support"
|
||||
);
|
||||
|
||||
// Initialize the page
|
||||
initializePage();
|
||||
setupEventListeners();
|
||||
startTimeUpdater();
|
||||
|
||||
// Initialize geolocation
|
||||
initializeGeolocation();
|
||||
|
||||
// Add hidden form fields for location data
|
||||
ensureLocationFormFields();
|
||||
|
||||
// Initialize language system
|
||||
initializeLanguageSystem();
|
||||
|
||||
// NEW: Start location watching for continuous updates
|
||||
startLocationWatching();
|
||||
// DOM Content Loaded Event (PRESERVED FROM ORIGINAL)
|
||||
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');
|
||||
});
|
||||
}, 100);
|
||||
});
|
||||
|
||||
// BILINGUAL FUNCTIONS - NEW FEATURE
|
||||
function initializeLanguageSystem() {
|
||||
console.log("🌐 Initializing bilingual system...");
|
||||
|
||||
// Check for stored language preference
|
||||
const storedLanguage = localStorage.getItem('preferredLanguage');
|
||||
if (storedLanguage && ['en', 'es'].includes(storedLanguage)) {
|
||||
currentLanguage = storedLanguage;
|
||||
}
|
||||
|
||||
// Apply initial language immediately
|
||||
setTimeout(() => {
|
||||
updateLanguage();
|
||||
// Initialize transitions after language is set
|
||||
const elements = document.querySelectorAll('.fade-transition');
|
||||
elements.forEach(el => el.classList.add('active'));
|
||||
}, 100);
|
||||
}
|
||||
|
||||
// Language toggle function - FIXED VERSION
|
||||
window.toggleLanguage = function() {
|
||||
console.log("🌐 Language toggle clicked");
|
||||
|
||||
// Switch language
|
||||
currentLanguage = currentLanguage === 'en' ? 'es' : 'en';
|
||||
console.log("🌐 Switching to:", currentLanguage);
|
||||
|
||||
// Store preference
|
||||
localStorage.setItem('preferredLanguage', currentLanguage);
|
||||
|
||||
// Update language immediately without animations interfering
|
||||
updateLanguage();
|
||||
|
||||
console.log("✅ Language switched successfully to:", currentLanguage);
|
||||
};
|
||||
|
||||
// Update all text content based on current language - FIXED VERSION
|
||||
function updateLanguage() {
|
||||
console.log("🔄 Updating language to:", currentLanguage);
|
||||
|
||||
// Update all translatable elements
|
||||
const langAttr = currentLanguage === 'en' ? 'data-en' : 'data-es';
|
||||
const elements = document.querySelectorAll(`[${langAttr}]`);
|
||||
|
||||
elements.forEach(element => {
|
||||
const text = element.getAttribute(langAttr);
|
||||
if (text) {
|
||||
element.textContent = text;
|
||||
}
|
||||
});
|
||||
|
||||
// Update placeholder text
|
||||
const employeeInput = document.getElementById('employee_id');
|
||||
if (employeeInput) {
|
||||
const placeholderAttr = currentLanguage === 'en' ? 'data-placeholder-en' : 'data-placeholder-es';
|
||||
const placeholder = employeeInput.getAttribute(placeholderAttr);
|
||||
if (placeholder) {
|
||||
employeeInput.placeholder = placeholder;
|
||||
}
|
||||
}
|
||||
|
||||
// Update language toggle button text
|
||||
const languageText = document.getElementById('languageText');
|
||||
if (languageText) {
|
||||
languageText.textContent = translations[currentLanguage].languageText;
|
||||
}
|
||||
|
||||
// Update HTML lang attribute
|
||||
document.documentElement.lang = currentLanguage;
|
||||
|
||||
// Force a repaint to ensure changes are visible
|
||||
document.body.style.display = 'none';
|
||||
document.body.offsetHeight; // Trigger reflow
|
||||
document.body.style.display = '';
|
||||
}
|
||||
|
||||
// Enhanced status message function with translation support
|
||||
function showLocalizedStatusMessage(messageKey, type = 'info') {
|
||||
const message = translations[currentLanguage].statusMessages[messageKey] || messageKey;
|
||||
const statusElement = document.getElementById('statusMessage');
|
||||
|
||||
if (statusElement) {
|
||||
statusElement.textContent = message;
|
||||
statusElement.className = `status-message ${type}`;
|
||||
statusElement.style.display = 'block';
|
||||
|
||||
// Auto-hide success messages after 5 seconds
|
||||
if (type === 'success') {
|
||||
setTimeout(() => {
|
||||
statusElement.style.display = 'none';
|
||||
}, 5000);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize basic page functionality (PRESERVED FROM ORIGINAL)
|
||||
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 (ENHANCED VERSION)
|
||||
function setupEventListeners() {
|
||||
console.log("🎧 Setting up event listeners...");
|
||||
|
||||
// ENHANCED FORM HANDLING FOR MULTIPLE CHECK-INS
|
||||
function initializeForm() {
|
||||
const form = document.getElementById("checkinForm");
|
||||
const employeeInput = document.getElementById("employee_id");
|
||||
const languageToggle = document.getElementById("languageToggle");
|
||||
|
||||
if (form) {
|
||||
const submitButton = document.getElementById("submitCheckin");
|
||||
|
||||
if (form && submitButton) {
|
||||
form.addEventListener("submit", handleFormSubmit);
|
||||
}
|
||||
|
||||
if (employeeInput) {
|
||||
employeeInput.addEventListener("input", handleInputChange);
|
||||
employeeInput.addEventListener("keypress", handleKeyPress);
|
||||
}
|
||||
|
||||
// Set up language toggle button
|
||||
if (languageToggle) {
|
||||
languageToggle.addEventListener("click", function(e) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
window.toggleLanguage();
|
||||
});
|
||||
console.log("✅ Language toggle button event listener attached");
|
||||
}
|
||||
}
|
||||
|
||||
// Start time updater (PRESERVED FROM ORIGINAL)
|
||||
function startTimeUpdater() {
|
||||
console.log("⏰ Starting time updater...");
|
||||
|
||||
// Update current time display
|
||||
setInterval(() => {
|
||||
const timeElement = document.getElementById("currentTime");
|
||||
if (timeElement) {
|
||||
timeElement.textContent = new Date().toLocaleTimeString();
|
||||
|
||||
// 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') {
|
||||
e.preventDefault();
|
||||
handleFormSubmit(e);
|
||||
}
|
||||
});
|
||||
}
|
||||
currentTime = new Date();
|
||||
}, 1000);
|
||||
}
|
||||
}
|
||||
|
||||
// Handle form submission (PRESERVED FROM ORIGINAL)
|
||||
function handleFormSubmit(e) {
|
||||
e.preventDefault();
|
||||
|
||||
function handleFormSubmit(event) {
|
||||
event.preventDefault();
|
||||
|
||||
if (isSubmitting) {
|
||||
return false;
|
||||
console.log("⏳ Check-in already in progress, ignoring duplicate submission");
|
||||
return;
|
||||
}
|
||||
|
||||
const employeeId = document.getElementById("employee_id").value.trim();
|
||||
|
||||
if (!validateEmployeeId(employeeId)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
submitCheckin(employeeId);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Validate employee ID (ENHANCED WITH TRANSLATION)
|
||||
function validateEmployeeId(employeeId) {
|
||||
|
||||
console.log("🎯 Form submission triggered");
|
||||
|
||||
const employeeId = document.getElementById("employee_id")?.value?.trim();
|
||||
|
||||
if (!employeeId) {
|
||||
showLocalizedStatusMessage('invalidId', 'error');
|
||||
return false;
|
||||
return;
|
||||
}
|
||||
|
||||
if (employeeId.length < 3 || employeeId.length > 20) {
|
||||
|
||||
if (employeeId.length < 2) {
|
||||
showLocalizedStatusMessage('invalidId', 'error');
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Handle input change (PRESERVED FROM ORIGINAL)
|
||||
function handleInputChange(e) {
|
||||
const value = e.target.value.trim();
|
||||
|
||||
// Clear any existing error messages
|
||||
hideStatusMessage();
|
||||
}
|
||||
|
||||
// Handle key press (PRESERVED FROM ORIGINAL)
|
||||
function handleKeyPress(e) {
|
||||
if (e.key === 'Enter') {
|
||||
e.preventDefault();
|
||||
const form = document.getElementById('checkinForm');
|
||||
if (form) {
|
||||
form.dispatchEvent(new Event('submit'));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Update submit button state (ENHANCED WITH TRANSLATION)
|
||||
function updateSubmitButton(loading) {
|
||||
const button = document.querySelector('button[type="submit"]');
|
||||
const content = button?.querySelector('.btn-content');
|
||||
const loader = button?.querySelector('.btn-loader');
|
||||
|
||||
if (!button) return;
|
||||
|
||||
if (loading) {
|
||||
button.disabled = true;
|
||||
if (content) content.style.display = 'none';
|
||||
if (loader) loader.style.display = 'flex';
|
||||
showLocalizedStatusMessage('processing', 'info');
|
||||
} else {
|
||||
button.disabled = false;
|
||||
if (content) content.style.display = 'flex';
|
||||
if (loader) loader.style.display = 'none';
|
||||
}
|
||||
}
|
||||
|
||||
// Hide status message (PRESERVED FROM ORIGINAL)
|
||||
function hideStatusMessage() {
|
||||
const statusElement = document.getElementById('statusMessage');
|
||||
if (statusElement) {
|
||||
statusElement.style.display = 'none';
|
||||
}
|
||||
}
|
||||
|
||||
// Enhanced showStatusMessage with translation support
|
||||
window.showStatusMessage = function(message, type) {
|
||||
// Try to find translation key, fallback to original message
|
||||
const messageKeys = Object.keys(translations.en.statusMessages);
|
||||
const foundKey = messageKeys.find(key =>
|
||||
translations.en.statusMessages[key].toLowerCase().includes(message.toLowerCase()) ||
|
||||
message.toLowerCase().includes(translations.en.statusMessages[key].toLowerCase())
|
||||
);
|
||||
|
||||
if (foundKey) {
|
||||
showLocalizedStatusMessage(foundKey, type);
|
||||
} else {
|
||||
// Fallback to original functionality
|
||||
const statusElement = document.getElementById('statusMessage');
|
||||
if (statusElement) {
|
||||
statusElement.textContent = message;
|
||||
statusElement.className = `status-message ${type}`;
|
||||
statusElement.style.display = 'block';
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// GEOLOCATION FUNCTIONS (PRESERVED FROM ORIGINAL)
|
||||
function initializeGeolocation() {
|
||||
console.log("📍 Initializing geolocation...");
|
||||
|
||||
if (!navigator.geolocation) {
|
||||
console.log("❌ Geolocation not supported");
|
||||
showLocalizedStatusMessage('locationError', 'warning');
|
||||
return;
|
||||
}
|
||||
|
||||
requestLocationPermission();
|
||||
}
|
||||
|
||||
// NEW: 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();
|
||||
|
||||
console.log("🏠 Address found:", address);
|
||||
} else {
|
||||
console.log("🏠 No address found");
|
||||
userLocation.address = "Address not available";
|
||||
updateLocationFormFields();
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
console.log("⚠️ Geocoding error:", error);
|
||||
userLocation.address = "Address lookup failed";
|
||||
updateLocationFormFields();
|
||||
});
|
||||
}
|
||||
|
||||
function requestLocationPermission() {
|
||||
if (locationRequestActive) {
|
||||
console.log("⏭️ Location request already active, skipping...");
|
||||
return;
|
||||
}
|
||||
|
||||
locationRequestActive = true;
|
||||
console.log("📍 Requesting location permission...");
|
||||
|
||||
const options = {
|
||||
enableHighAccuracy: true,
|
||||
timeout: 10000,
|
||||
maximumAge: 300000 // 5 minutes
|
||||
};
|
||||
|
||||
navigator.geolocation.getCurrentPosition(
|
||||
handleLocationSuccess,
|
||||
handleLocationError,
|
||||
options
|
||||
);
|
||||
}
|
||||
|
||||
function handleLocationSuccess(position) {
|
||||
console.log("✅ Location acquired successfully!");
|
||||
|
||||
userLocation = {
|
||||
latitude: position.coords.latitude,
|
||||
longitude: position.coords.longitude,
|
||||
accuracy: position.coords.accuracy,
|
||||
altitude: position.coords.altitude,
|
||||
timestamp: new Date().toISOString(),
|
||||
source: "gps"
|
||||
};
|
||||
|
||||
locationRequestActive = false;
|
||||
updateLocationFormFields();
|
||||
// Show processing status
|
||||
showLocalizedStatusMessage('processing', 'info');
|
||||
|
||||
// NEW: Convert coordinates to address
|
||||
reverseGeocodeLocation(userLocation.latitude, userLocation.longitude);
|
||||
// Submit the check-in
|
||||
submitCheckin();
|
||||
}
|
||||
|
||||
// ENHANCED CHECK-IN SUBMISSION WITH MULTIPLE CHECK-IN SUPPORT
|
||||
function submitCheckin() {
|
||||
console.log("🚀 Starting check-in submission process");
|
||||
|
||||
console.log("📍 Location data:", userLocation);
|
||||
}
|
||||
|
||||
function handleLocationError(error) {
|
||||
console.log("❌ Location error:", error.message);
|
||||
locationRequestActive = false;
|
||||
|
||||
// Don't show error message - just continue without location
|
||||
userLocation.source = "manual";
|
||||
userLocation.timestamp = new Date().toISOString();
|
||||
}
|
||||
|
||||
// REST OF YOUR ORIGINAL FUNCTIONS (PRESERVED)
|
||||
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}`);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function updateLocationFormFields() {
|
||||
const fields = {
|
||||
latitude: userLocation.latitude ? userLocation.latitude.toFixed(8) : "",
|
||||
longitude: userLocation.longitude ? userLocation.longitude.toFixed(8) : "",
|
||||
accuracy: userLocation.accuracy || "",
|
||||
altitude: userLocation.altitude || "",
|
||||
locationSource: userLocation.source || "manual",
|
||||
address: userLocation.address || "" // RESTORED: Address field population
|
||||
};
|
||||
|
||||
Object.keys(fields).forEach(fieldName => {
|
||||
const field = document.getElementById(fieldName);
|
||||
if (field) {
|
||||
field.value = fields[fieldName];
|
||||
console.log(`📝 Updated field ${fieldName}: ${fields[fieldName]}`);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function submitCheckin(employeeId) {
|
||||
if (isSubmitting) {
|
||||
console.log("⏭️ Already submitting, ignoring duplicate request");
|
||||
return false;
|
||||
console.log("⏳ Already submitting, aborting");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
isSubmitting = true;
|
||||
updateSubmitButton(true);
|
||||
hideStatusMessage();
|
||||
|
||||
console.log("📤 Starting check-in submission for:", employeeId);
|
||||
console.log("📍 Current location data:", userLocation);
|
||||
|
||||
updateLocationFormFields();
|
||||
|
||||
|
||||
const employeeId = document.getElementById("employee_id").value.trim();
|
||||
|
||||
if (!employeeId) {
|
||||
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) : "");
|
||||
@@ -530,36 +192,48 @@ function submitCheckin(employeeId) {
|
||||
});
|
||||
}
|
||||
|
||||
// ENHANCED RESPONSE HANDLING FOR MULTIPLE CHECK-INS
|
||||
function handleCheckinResponse(data) {
|
||||
if (data.success) {
|
||||
handleCheckinSuccess(data);
|
||||
} else {
|
||||
const errorMsg = data.message || 'Check-in failed';
|
||||
if (errorMsg.toLowerCase().includes('already checked in')) {
|
||||
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')) {
|
||||
// Handle 30-minute interval message
|
||||
showCustomStatusMessage(errorMsg, 'warning');
|
||||
} else {
|
||||
showLocalizedStatusMessage('error', 'error');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ENHANCED SUCCESS HANDLING WITH MULTIPLE CHECK-IN INFO
|
||||
function handleCheckinSuccess(data) {
|
||||
console.log("✅ Check-in successful!");
|
||||
console.log("✅ Submitted successful!");
|
||||
|
||||
const responseData = data.data || data || {};
|
||||
const checkinCount = responseData.checkin_count_today || 1;
|
||||
const checkinSequence = responseData.checkin_sequence || 'Check-in';
|
||||
|
||||
// Show appropriate success message based on check-in count
|
||||
if (checkinCount > 1) {
|
||||
showLocalizedStatusMessage('multipleSuccess', 'success');
|
||||
} else {
|
||||
showLocalizedStatusMessage('success', 'success');
|
||||
}
|
||||
|
||||
// Hide form (PRESERVED FROM ORIGINAL)
|
||||
const form = document.getElementById("checkinForm");
|
||||
if (form) {
|
||||
form.style.display = "none";
|
||||
}
|
||||
|
||||
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();
|
||||
|
||||
showLocalizedStatusMessage('success', 'success');
|
||||
|
||||
// Update success card with enhanced information
|
||||
const successCard = document.getElementById("successCard");
|
||||
if (successCard) {
|
||||
successCard.style.display = "block";
|
||||
@@ -572,81 +246,291 @@ 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();
|
||||
|
||||
updateElement("successEmployeeId", employeeId);
|
||||
updateElement("successLocation", location);
|
||||
updateElement("successEvent", event);
|
||||
updateElement("successTime", checkInTime);
|
||||
updateElement("successDate", checkInDate);
|
||||
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`);
|
||||
}
|
||||
}
|
||||
|
||||
// NEW: Add option to check in again after success
|
||||
//setTimeout(() => {
|
||||
// addCheckInAgainOption();
|
||||
//}, 3000);
|
||||
}
|
||||
|
||||
// NEW: Add option to check in again
|
||||
function addCheckInAgainOption() {
|
||||
const successCard = document.getElementById("successCard");
|
||||
if (successCard && !document.getElementById("checkInAgainButton")) {
|
||||
const checkInAgainHtml = `
|
||||
<div class="check-in-again-section" style="margin-top: 1.5rem; padding-top: 1.5rem; border-top: 1px solid #e0e4e7;">
|
||||
<p class="check-in-again-text" style="margin-bottom: 1rem; color: #64748b; font-size: 0.9rem;">
|
||||
<span data-en="Need to check in again? You can do so after 30 minutes."
|
||||
data-es="¿Necesita registrarse nuevamente? Puede hacerlo después de 30 minutos.">
|
||||
Need to check in again? You can do so after 30 minutes.
|
||||
</span>
|
||||
</p>
|
||||
<button id="checkInAgainButton" class="btn btn-outline" style="width: 100%;" onclick="resetForNewCheckin()">
|
||||
<i class="fas fa-redo"></i>
|
||||
<span data-en="Check In Again" data-es="Registrarse Nuevamente">Check In Again</span>
|
||||
</button>
|
||||
</div>
|
||||
`;
|
||||
successCard.insertAdjacentHTML('beforeend', checkInAgainHtml);
|
||||
|
||||
// Apply current language translations
|
||||
applyTranslations();
|
||||
}
|
||||
}
|
||||
|
||||
// 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');
|
||||
}
|
||||
|
||||
// Clear previous employee ID
|
||||
const employeeIdInput = document.getElementById("employee_id");
|
||||
if (employeeIdInput) {
|
||||
employeeIdInput.value = '';
|
||||
employeeIdInput.focus();
|
||||
}
|
||||
|
||||
// Clear status messages
|
||||
clearStatusMessages();
|
||||
|
||||
// Reset location if needed
|
||||
if (!userLocation.latitude || !userLocation.longitude) {
|
||||
requestLocationData();
|
||||
}
|
||||
}
|
||||
|
||||
// NEW: Show custom status message (for interval warnings)
|
||||
function showCustomStatusMessage(message, type = 'info') {
|
||||
const statusContainer = document.getElementById("statusMessage");
|
||||
if (statusContainer) {
|
||||
statusContainer.className = `status-message ${type}`;
|
||||
statusContainer.innerHTML = `
|
||||
<div class="status-content">
|
||||
<i class="fas ${getStatusIcon(type)}"></i>
|
||||
<span>${message}</span>
|
||||
</div>
|
||||
`;
|
||||
statusContainer.style.display = "block";
|
||||
|
||||
// Auto-hide after 5 seconds
|
||||
setTimeout(() => {
|
||||
statusContainer.style.display = "none";
|
||||
}, 5000);
|
||||
}
|
||||
}
|
||||
|
||||
// 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';
|
||||
}
|
||||
}
|
||||
|
||||
// PRESERVED: All other existing functions remain unchanged
|
||||
function showLocalizedStatusMessage(messageKey, type = 'info') {
|
||||
const message = translations[currentLanguage].statusMessages[messageKey] ||
|
||||
translations['en'].statusMessages[messageKey] ||
|
||||
'Status update';
|
||||
|
||||
showCustomStatusMessage(message, type);
|
||||
}
|
||||
|
||||
function clearStatusMessages() {
|
||||
const statusContainer = document.getElementById("statusMessage");
|
||||
if (statusContainer) {
|
||||
statusContainer.style.display = "none";
|
||||
}
|
||||
}
|
||||
|
||||
function handleCheckinError(error) {
|
||||
console.error("❌ Check-in submission failed:", error);
|
||||
console.error("❌ Check-in submission error:", error);
|
||||
showLocalizedStatusMessage('networkError', 'error');
|
||||
}
|
||||
|
||||
// Check in another employee function (PRESERVED WITH TRANSLATION)
|
||||
window.checkInAnother = function() {
|
||||
const form = document.getElementById("checkinForm");
|
||||
const successCard = document.getElementById("successCard");
|
||||
const employeeInput = document.getElementById("employee_id");
|
||||
|
||||
if (form) form.style.display = "block";
|
||||
if (successCard) successCard.style.display = "none";
|
||||
if (employeeInput) {
|
||||
employeeInput.value = "";
|
||||
employeeInput.focus();
|
||||
function updateSubmitButton(isLoading) {
|
||||
const submitButton = document.getElementById("submitCheckin");
|
||||
if (submitButton) {
|
||||
if (isLoading) {
|
||||
submitButton.disabled = true;
|
||||
submitButton.innerHTML = '<i class="fas fa-spinner fa-spin"></i> <span data-en="Processing..." data-es="Procesando...">Processing...</span>';
|
||||
} else {
|
||||
submitButton.disabled = false;
|
||||
submitButton.innerHTML = '<i class="fas fa-user-check"></i> <span data-en="Submit" data-es="Someter">Submit</span>';
|
||||
}
|
||||
applyTranslations();
|
||||
}
|
||||
}
|
||||
|
||||
hideStatusMessage();
|
||||
console.log("🔄 Ready for another check-in");
|
||||
};
|
||||
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');
|
||||
} else {
|
||||
employeeIdInput.classList.remove('valid');
|
||||
if (employeeIdInput.value.length > 0) {
|
||||
employeeIdInput.classList.add('invalid');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// NEW: Start continuous location watching
|
||||
function startLocationWatching() {
|
||||
if (!navigator.geolocation || locationWatchId !== null) {
|
||||
// All location and language functions remain unchanged from original
|
||||
function initializeLocation() {
|
||||
console.log("📍 Initializing location services");
|
||||
requestLocationData();
|
||||
}
|
||||
|
||||
function requestLocationData() {
|
||||
if (locationRequestActive) {
|
||||
console.log("📍 Location request already active, skipping");
|
||||
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");
|
||||
}
|
||||
|
||||
// NEW: Stop location watching
|
||||
function stopLocationWatching() {
|
||||
if (locationWatchId !== null) {
|
||||
navigator.geolocation.clearWatch(locationWatchId);
|
||||
locationWatchId = null;
|
||||
console.log("⏹️ Stopped location watching");
|
||||
|
||||
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
|
||||
};
|
||||
|
||||
navigator.geolocation.getCurrentPosition(
|
||||
handleLocationSuccess,
|
||||
handleLocationError,
|
||||
options
|
||||
);
|
||||
}
|
||||
|
||||
// NEW: Show location status with translations
|
||||
function showLocationStatus(type, messageKey) {
|
||||
const message = translations[currentLanguage].statusMessages[messageKey] || messageKey;
|
||||
function handleLocationSuccess(position) {
|
||||
console.log("✅ Location obtained successfully");
|
||||
|
||||
let icon = '📡';
|
||||
if (type === 'success') icon = '✅';
|
||||
if (type === 'error') icon = '⚠️';
|
||||
userLocation = {
|
||||
latitude: position.coords.latitude,
|
||||
longitude: position.coords.longitude,
|
||||
accuracy: position.coords.accuracy,
|
||||
altitude: position.coords.altitude,
|
||||
timestamp: new Date(),
|
||||
source: "gps",
|
||||
address: null
|
||||
};
|
||||
|
||||
console.log(`${icon} Location Status: ${message}`);
|
||||
console.log("📍 Location data:", userLocation);
|
||||
|
||||
// You can enhance this to show a visual status indicator if needed
|
||||
showLocalizedStatusMessage(messageKey, type);
|
||||
// Reverse geocode to get address
|
||||
reverseGeocode(userLocation.latitude, userLocation.longitude);
|
||||
|
||||
locationRequestActive = false;
|
||||
}
|
||||
|
||||
console.log("📍 QR Destination JavaScript loaded successfully with location tracking and bilingual support!");
|
||||
function handleLocationError(error) {
|
||||
console.log("❌ Location error:", error.message);
|
||||
userLocation.source = "manual";
|
||||
locationRequestActive = false;
|
||||
}
|
||||
|
||||
function reverseGeocode(lat, lng) {
|
||||
// This would typically use a geocoding service
|
||||
// For now, just set a placeholder
|
||||
userLocation.address = `${lat.toFixed(10)}, ${lng.toFixed(10)}`;
|
||||
}
|
||||
|
||||
// Language functionality remains unchanged
|
||||
function initializeLanguage() {
|
||||
const languageToggle = document.getElementById('languageToggle');
|
||||
if (languageToggle) {
|
||||
languageToggle.addEventListener('click', toggleLanguage);
|
||||
}
|
||||
applyTranslations();
|
||||
}
|
||||
|
||||
function toggleLanguage() {
|
||||
currentLanguage = currentLanguage === 'en' ? 'es' : 'en';
|
||||
applyTranslations();
|
||||
console.log(`🌐 Language switched to: ${currentLanguage}`);
|
||||
}
|
||||
|
||||
function applyTranslations() {
|
||||
const languageText = document.getElementById('languageText');
|
||||
if (languageText) {
|
||||
languageText.textContent = translations[currentLanguage].languageText;
|
||||
}
|
||||
|
||||
document.querySelectorAll(`[data-${currentLanguage}]`).forEach(element => {
|
||||
element.textContent = element.getAttribute(`data-${currentLanguage}`);
|
||||
});
|
||||
}
|
||||
|
||||
function startClock() {
|
||||
function updateClock() {
|
||||
currentTime = new Date();
|
||||
const timeElements = document.querySelectorAll('.current-time');
|
||||
timeElements.forEach(el => {
|
||||
el.textContent = currentTime.toLocaleTimeString();
|
||||
});
|
||||
}
|
||||
|
||||
updateClock();
|
||||
setInterval(updateClock, 1000);
|
||||
}
|
||||
Reference in New Issue
Block a user