This commit is contained in:
Nguyen Ngo
2025-09-17 14:47:02 -04:00
3 changed files with 227 additions and 145 deletions
+12 -5
View File
@@ -3734,10 +3734,11 @@ def qr_checkin(qr_url):
'message': f'Check-in successful! {checkin_sequence_text} for today.',
'data': {
'employee_id': attendance.employee_id,
'location': attendance.location_name,
'location': qr_code.location_address,
'location_event': qr_code.location_event,
'check_in_time': attendance.check_in_time.strftime('%H:%M:%S'),
'check_in_date': attendance.check_in_date.strftime('%m/%d/%Y'),
'event': qr_code.location_event, # Add both for compatibility
'check_in_time': attendance.check_in_time.strftime('%I:%M %p'), # 12-hour format
'check_in_date': attendance.check_in_date.strftime('%B %d, %Y'), # Full date format
'device_info': attendance.device_info,
'ip_address': attendance.ip_address,
'location_accuracy': location_accuracy,
@@ -3752,13 +3753,19 @@ def qr_checkin(qr_url):
if location_data['latitude'] and location_data['longitude']:
response_data['data']['coordinates'] = f"{location_data['latitude']:.10f}, {location_data['longitude']:.10f}"
# Enhanced logging for successful check-in with all details
print(f"✅ Check-in completed successfully")
print(f" Employee: {attendance.employee_id}")
print(f" Time: {attendance.check_in_time}")
print(f" Employee ID: {attendance.employee_id}")
print(f" Time: {attendance.check_in_time.strftime('%I:%M %p')}")
print(f" Date: {attendance.check_in_date.strftime('%B %d, %Y')}")
print(f" Location: {attendance.location_name}")
print(f" Action: {qr_code.location_event}")
print(f" Address: {attendance.address}")
print(f" Today's count: {today_checkin_count}")
# Log to database for audit trail
logger_handler.logger.info(f"Check-in success - Employee: {attendance.employee_id}, Location: {attendance.location_name}, Time: {attendance.check_in_time}, Action: {qr_code.location_event}")
return jsonify(response_data), 200
except Exception as e:
+72 -60
View File
@@ -142,10 +142,11 @@ function initializeForm() {
function disableFormImmediately() {
// Find and disable submit button immediately
const submitButton = document.getElementById("submitCheckin") ||
const submitButton =
document.getElementById("submitCheckin") ||
document.getElementById("submitButton") ||
document.querySelector('button[type="submit"]') ||
document.querySelector('.btn-primary');
document.querySelector(".btn-primary");
const employeeIdInput = document.getElementById("employee_id");
const form = document.getElementById("checkinForm");
@@ -155,11 +156,14 @@ function disableFormImmediately() {
submitButton.style.opacity = "0.5";
submitButton.style.cursor = "not-allowed";
submitButton.style.pointerEvents = "none";
submitButton.setAttribute('data-location-blocked', 'true');
submitButton.setAttribute("data-location-blocked", "true");
// Store original content
if (!submitButton.getAttribute('data-original-content')) {
submitButton.setAttribute('data-original-content', submitButton.innerHTML);
if (!submitButton.getAttribute("data-original-content")) {
submitButton.setAttribute(
"data-original-content",
submitButton.innerHTML
);
}
// Show loading/checking state
@@ -172,7 +176,7 @@ function disableFormImmediately() {
if (employeeIdInput) {
employeeIdInput.disabled = true;
employeeIdInput.style.opacity = "0.7";
employeeIdInput.setAttribute('data-location-blocked', 'true');
employeeIdInput.setAttribute("data-location-blocked", "true");
employeeIdInput.placeholder = "Checking location services...";
}
@@ -257,7 +261,7 @@ function proceedWithCheckin() {
function showLocationServicesBlockedMessage() {
const messages = {
en: "Check-in blocked: Location Services must be enabled to continue.",
es: "Registro bloqueado: Los Servicios de Ubicación deben estar habilitados para continuar."
es: "Registro bloqueado: Los Servicios de Ubicación deben estar habilitados para continuar.",
};
const currentLang = currentLanguage || "en";
@@ -349,6 +353,8 @@ function handleCheckinResponse(data) {
// ENHANCED SUCCESS HANDLING WITH MULTIPLE CHECK-IN INFO
function handleCheckinSuccess(data) {
console.log("✅ Success data received:", data);
const responseData = data.data || data || {};
const checkinCount = responseData.checkin_count_today || 1;
const checkinSequence = responseData.checkin_sequence || "Check-in";
@@ -379,15 +385,36 @@ function handleCheckinSuccess(data) {
}
};
const employeeId = responseData.employee_id || "Unknown";
// Get employee ID from form input if not in response data
const employeeIdInput = document.getElementById("employee_id");
const employeeId =
responseData.employee_id ||
(employeeIdInput
? employeeIdInput.value.trim().toUpperCase()
: "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();
// Format current date and time if not provided in response
const now = new Date();
const checkInTime =
responseData.check_in_time ||
now.toLocaleTimeString("en-US", {
hour: "2-digit",
minute: "2-digit",
hour12: true,
});
const checkInDate =
responseData.check_in_date ||
now.toLocaleDateString("en-US", {
year: "numeric",
month: "short",
day: "numeric",
});
// Update success card elements
updateElement("successEmployeeId", employeeId);
updateElement("successLocation", location);
updateElement("successEvent", event);
@@ -416,36 +443,11 @@ function handleCheckinSuccess(data) {
`${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();
// Log successful check-in with details
console.log(
`✅ Check-in successful for Employee ID: ${employeeId}, Location: ${location}, Time: ${checkInTime}, Date: ${checkInDate}, Action: ${event}`
);
}
}
@@ -874,10 +876,11 @@ function checkLocationServicesStatus() {
function blockCheckInProcess(shouldBlock) {
// Try multiple possible submit button IDs from your codebase
const submitButton = document.getElementById("submitCheckin") ||
const submitButton =
document.getElementById("submitCheckin") ||
document.getElementById("submitButton") ||
document.querySelector('button[type="submit"]') ||
document.querySelector('.btn-primary');
document.querySelector(".btn-primary");
const employeeIdInput = document.getElementById("employee_id");
const form = document.getElementById("checkinForm");
@@ -894,11 +897,14 @@ function blockCheckInProcess(shouldBlock) {
submitButton.style.pointerEvents = "none";
// Add data attribute to track blocking state
submitButton.setAttribute('data-location-blocked', 'true');
submitButton.setAttribute("data-location-blocked", "true");
// Store original button content
if (!submitButton.getAttribute('data-original-content')) {
submitButton.setAttribute('data-original-content', submitButton.innerHTML);
if (!submitButton.getAttribute("data-original-content")) {
submitButton.setAttribute(
"data-original-content",
submitButton.innerHTML
);
}
// Update button text to show it's blocked
@@ -912,7 +918,7 @@ function blockCheckInProcess(shouldBlock) {
submitButton.parentNode.replaceChild(newButton, submitButton);
// Add blocking event listener
newButton.addEventListener('click', function(e) {
newButton.addEventListener("click", function (e) {
e.preventDefault();
e.stopPropagation();
showLocationServicesBlockedMessage();
@@ -923,7 +929,7 @@ function blockCheckInProcess(shouldBlock) {
if (employeeIdInput) {
employeeIdInput.disabled = true;
employeeIdInput.style.opacity = "0.7";
employeeIdInput.setAttribute('data-location-blocked', 'true');
employeeIdInput.setAttribute("data-location-blocked", "true");
}
if (form) {
@@ -931,7 +937,7 @@ function blockCheckInProcess(shouldBlock) {
form.style.pointerEvents = "none";
// Override form submission completely
form.onsubmit = function(e) {
form.onsubmit = function (e) {
e.preventDefault();
e.stopPropagation();
showLocationServicesBlockedMessage();
@@ -952,16 +958,18 @@ function blockCheckInProcess(shouldBlock) {
submitButton.style.pointerEvents = "auto";
// Remove blocking data attribute
submitButton.removeAttribute('data-location-blocked');
submitButton.removeAttribute("data-location-blocked");
// Restore original button content
const originalContent = submitButton.getAttribute('data-original-content');
const originalContent = submitButton.getAttribute(
"data-original-content"
);
if (originalContent) {
submitButton.innerHTML = originalContent;
}
// Re-attach proper event listeners
submitButton.onclick = function(e) {
submitButton.onclick = function (e) {
e.preventDefault();
handleFormSubmit(e);
return false;
@@ -971,7 +979,7 @@ function blockCheckInProcess(shouldBlock) {
if (employeeIdInput) {
employeeIdInput.disabled = false;
employeeIdInput.style.opacity = "1";
employeeIdInput.removeAttribute('data-location-blocked');
employeeIdInput.removeAttribute("data-location-blocked");
}
if (form) {
@@ -979,7 +987,7 @@ function blockCheckInProcess(shouldBlock) {
form.style.pointerEvents = "auto";
// Restore proper form submission handler
form.onsubmit = function(e) {
form.onsubmit = function (e) {
e.preventDefault();
handleFormSubmit(e);
return false;
@@ -1022,7 +1030,7 @@ function showLocationServicesWarning(errorType) {
unknown_error:
"⚠️ Error de Servicios de Ubicación<br><strong>El registro está bloqueado.</strong><br>No se puede acceder a los servicios. Verifique la configuración.",
},
};
};
const currentLang = currentLanguage || "en";
const message =
@@ -1084,14 +1092,17 @@ function initializeLocationServicesCheck() {
console.log("✅ Initial Location Services check passed");
})
.catch(() => {
console.log("❌ Initial Location Services check failed - Check-in blocked");
console.log(
"❌ Initial Location Services check failed - Check-in blocked"
);
});
}, 1000);
// Override form initialization to ensure our handlers are used
setTimeout(() => {
const form = document.getElementById("checkinForm");
const submitButton = document.getElementById("submitCheckin") ||
const submitButton =
document.getElementById("submitCheckin") ||
document.querySelector('button[type="submit"]');
if (form) {
@@ -1100,16 +1111,17 @@ function initializeLocationServicesCheck() {
form.parentNode.replaceChild(newForm, form);
// Add our controlled event listener
newForm.addEventListener('submit', handleFormSubmit);
newForm.addEventListener("submit", handleFormSubmit);
}
if (submitButton) {
// Find the new submit button after form cloning
const newSubmitButton = document.getElementById("submitCheckin") ||
const newSubmitButton =
document.getElementById("submitCheckin") ||
document.querySelector('button[type="submit"]');
if (newSubmitButton) {
newSubmitButton.addEventListener('click', function(e) {
newSubmitButton.addEventListener("click", function (e) {
e.preventDefault();
e.stopPropagation();
handleFormSubmit(e);
+73 -10
View File
@@ -492,6 +492,11 @@
color: white;
}
.status-message.warning {
background: var(--error-color);
color: white;
}
.status-message.error {
background: var(--error-color);
color: white;
@@ -818,18 +823,17 @@
<i class="fas fa-sign-in-alt"></i>
<span class="english-text">Check In</span>
<span class="language-separator">/</span>
<span class="spanish-text">Registrar Entrada</span>
<span class="spanish-text">Entrada</span>
{% else %}
<i class="fas fa-sign-out-alt"></i>
<span class="english-text">Check Out</span>
<span class="language-separator">/</span>
<span class="spanish-text">Registrar Salida</span>
<span class="spanish-text">Salida</span>
{% endif %}
</button>
</form>
</div>
<!-- Success Card -->
<div class="success-card" id="successCard">
<div class="success-icon">
<i class="fas fa-check"></i>
@@ -841,17 +845,76 @@
</h2>
<div class="bilingual-container">
<p>
<span class="english-text"
>Your submission has been recorded successfully.</span
>
<span class="english-text">Your submission has been recorded successfully.</span>
<span class="language-separator">/</span>
<span class="spanish-text"
>Su registro ha sido guardado exitosamente.</span
>
<span class="spanish-text">Su registro ha sido guardado exitosamente.</span>
</p>
</div>
<!-- Enhanced Success Details with Employee Information -->
<div class="success-details" id="successDetails">
<!-- Success details will be populated here -->
<div class="detail-row">
<span class="detail-label">
<span class="english-text">Employee ID</span>
<span class="language-separator">/</span>
<span class="spanish-text">ID del Empleado</span>
</span>
<span class="detail-value" id="successEmployeeId">-</span>
</div>
<div class="detail-row">
<span class="detail-label">
<span class="english-text">Date</span>
<span class="language-separator">/</span>
<span class="spanish-text">Fecha</span>
</span>
<span class="detail-value" id="successCheckInDate">-</span>
</div>
<div class="detail-row">
<span class="detail-label">
<span class="english-text">Time</span>
<span class="language-separator">/</span>
<span class="spanish-text">Hora</span>
</span>
<span class="detail-value" id="successCheckInTime">-</span>
</div>
<div class="detail-row">
<span class="detail-label">
<span class="english-text">Action</span>
<span class="language-separator">/</span>
<span class="spanish-text">Acción</span>
</span>
<span class="detail-value" id="successEvent">-</span>
</div>
<div class="detail-row">
<span class="detail-label">
<span class="english-text">Location</span>
<span class="language-separator">/</span>
<span class="spanish-text">Ubicación</span>
</span>
<span class="detail-value" id="successLocation">-</span>
</div>
<!-- Optional Additional Details (hidden by default) -->
<div class="detail-row" style="display: none;" id="successAddressRow">
<span class="detail-label">
<span class="english-text">Address</span>
<span class="language-separator">/</span>
<span class="spanish-text">Dirección</span>
</span>
<span class="detail-value" id="successAddress">-</span>
</div>
<div class="detail-row" style="display: none;" id="successAccuracyRow">
<span class="detail-label">
<span class="english-text">Accuracy</span>
<span class="language-separator">/</span>
<span class="spanish-text">Precisión</span>
</span>
<span class="detail-value" id="successLocationAccuracy">-</span>
</div>
</div>
</div>