// Station Clock Mobile JavaScript - Complete PHP 5/MySQL 5 Compatible Version // Updated with unified comment system - no duplicate comment boxes // Global Variables var currentEmployee = null; var clockedIn = false; var currentStation = null; var isProduction = false; var validNum = false; var validStat = false; var breakTimeLeft = 0; var orientationRequired = false; var requestTimeout = 15000; // 15 second timeout // Initialize on page load document.addEventListener('DOMContentLoaded', function() { initializeApp(); setupEventListeners(); updateDateTime(); setInterval(updateDateTime, 1000); }); function initializeApp() { hideLoading(); resetInterface(); var empInput = document.getElementById('empInput'); if (empInput) { empInput.focus(); } } var userId = window.injectedUserId || ""; document.addEventListener("DOMContentLoaded", function () { setTimeout(function () { var empInput = document.getElementById("empInput"); if (empInput && userId) { empInput.value = userId; empInput.classList.add('autofilled'); setTimeout(function () { empInput.classList.remove('autofilled'); }, 500); setTimeout(function () { checkEmployee(); }, 300); } }, 100); }); function setupEventListeners() { var empInput = document.getElementById('empInput'); var empSubmit = document.getElementById('empSubmit'); empInput.addEventListener('keypress', function(e) { if (e.key === 'Enter' || e.keyCode === 13) { checkEmployee(); } }); empSubmit.addEventListener('click', checkEmployee); var stationInput = document.getElementById('stationInput'); stationInput.addEventListener('keyup', function(e) { this.value = this.value.toUpperCase(); if (e.key === 'Enter' || e.keyCode === 13) { checkStation(); } else { validateStation(); } checkEnable(); }); var quantityInput = document.getElementById('quantityInput'); quantityInput.addEventListener('keyup', function() { hideError('quantityError'); checkEnable(); }); quantityInput.addEventListener('change', function() { hideError('quantityError'); checkEnable(); }); quantityInput.addEventListener('blur', function() { var val = this.value.trim(); if (val !== '' && !isNaN(val) && parseInt(val) >= 0) { this.value = parseInt(val).toString(); } checkEnable(); }); var repairCheckbox = document.getElementById('repairCheckbox'); repairCheckbox.addEventListener('change', checkEnable); var feelingOptions = document.querySelectorAll('.feeling-option'); for (var i = 0; i < feelingOptions.length; i++) { feelingOptions[i].addEventListener('click', function() { for (var j = 0; j < feelingOptions.length; j++) { feelingOptions[j].classList.remove('selected'); } this.classList.add('selected'); this.querySelector('input').checked = true; }); } var actionButton = document.getElementById('actionButton'); actionButton.addEventListener('click', function() { if (this.disabled && this.textContent === 'Fill Required Fields First') { validateIndirectFields(); checkEnable(); } else if (!this.disabled) { performAction(); } }); var qcSubStation = document.getElementById('qcSubStation'); qcSubStation.addEventListener('change', function() { updateQCStation(currentEmployee.id, this.value); }); } function checkEmployee() { var empId = document.getElementById('empInput').value.trim(); if (!empId) { showError('empError', 'Please enter an employee ID'); return; } showLoading(); if (empId.length > 5) { ajaxRequest('checkEmployeeCardNumber.php', { card_number: empId }, function(response) { if (response.substring(0, 7) === '*Error:') { hideLoading(); showError('empError', response.substring(7)); } else { document.getElementById('empInput').value = response; loadEmployeeData(response); } }); } else { loadEmployeeData(empId); } } function loadEmployeeData(empId) { ajaxRequest('getEmpName.php', { employee: empId, check_ip: 1 }, function(response) { hideLoading(); if (response.indexOf('ERROR') === 0) { showError('empError', response); return; } try { var result = JSON.parse(response); displayEmployeeInfo(empId, result); } catch(e) { displayEmployeeInfoLegacy(empId, response); } }); ajaxRequest('getSchedule.php', { employee: empId }, function(response) { displaySchedule(response); }); ajaxRequest('getNotes.php', { employee: empId }, function(response) { if (response && response.trim() && response.trim() !== '') { var notesDiv = document.getElementById('empNotes'); notesDiv.innerHTML = 'Important Notes: ' + response; notesDiv.style.display = 'block'; } }); } function displayEmployeeInfo(empId, data) { currentEmployee = { id: empId, name: data.employee_name || 'Employee ' + empId }; validNum = true; document.getElementById('empName').textContent = currentEmployee.name; document.getElementById('empId').textContent = '#' + empId; if (data.gift_card_message && data.gift_card_message.trim()) { var giftCardDiv = document.getElementById('giftCardMessage'); giftCardDiv.innerHTML = 'Gift Card/Reward: ' + data.gift_card_message; giftCardDiv.style.display = 'block'; } if (data.orient !== undefined && data.orient == '0') { orientationRequired = true; } document.getElementById('employeeSection').classList.add('hidden'); document.getElementById('employeeInfo').classList.remove('hidden'); checkEmployeeStation(empId); } function checkEmployeeStation(empId) { ajaxRequest('getEmpStation.php', { employee: empId }, function(response) { response = response.trim(); if (response && response.length > 0) { clockedIn = true; currentStation = response; document.getElementById('empStatus').textContent = 'Clocked In: ' + currentStation; document.getElementById('empStatus').className = 'status-badge clocked-in'; showClockOutSection(currentStation); document.getElementById('stationTitle').textContent = 'Switch to Different Station'; var stationSection = document.getElementById('stationSection'); stationSection.classList.remove('hidden'); stationSection.classList.add('switch-mode'); stationSection.classList.remove('has-errors'); hideError('stationSectionError'); document.getElementById('stationInput').value = ''; document.getElementById('repairCheckbox').checked = false; hideStationInfo(); } else { clockedIn = false; currentStation = null; document.getElementById('empStatus').textContent = 'Not Clocked In'; document.getElementById('empStatus').className = 'status-badge clocked-out'; showClockInSection(); } }); } function showClockInSection() { document.getElementById('stationTitle').textContent = 'Select Station to Clock In'; var stationSection = document.getElementById('stationSection'); stationSection.classList.remove('hidden'); stationSection.classList.remove('switch-mode'); stationSection.classList.remove('has-errors'); hideError('stationSectionError'); document.getElementById('clockOutSection').classList.add('hidden'); document.getElementById('actionSection').classList.remove('hidden'); var actionButton = document.getElementById('actionButton'); actionButton.textContent = 'Clock In'; actionButton.className = 'btn btn-primary btn-large'; document.getElementById('stationInput').focus(); checkEnable(); } function showClockOutSection(station) { document.getElementById('currentStation').textContent = station; hideError('indirectFieldsError'); hideError('quantityError'); hideError('stationSectionError'); if (station === 'QC') { document.getElementById('qcStationSelect').classList.remove('hidden'); } else { document.getElementById('qcStationSelect').classList.add('hidden'); } document.getElementById('clockOutSection').classList.remove('hidden'); document.getElementById('quantityGroup').classList.add('hidden'); isProduction = false; ajaxRequest('validIndirectStation.php', { id_num: currentEmployee.id, station: station }, function(response) { response = response.trim(); if (response && response !== '0' && response !== ' 0' && response !== 'what?') { var stationCode = station; if (response.indexOf('-1:') === 0) { stationCode = response.substring(3); } loadIndirectFieldsInline(stationCode); var actionButton = document.getElementById('actionButton'); actionButton.disabled = true; actionButton.textContent = 'Fill Required Fields First'; actionButton.className = 'btn btn-primary btn-large'; actionButton.setAttribute('data-has-errors', 'true'); checkIfProductionStation(station); } else { document.getElementById('indirectFieldsSection').classList.add('hidden'); document.getElementById('actionSection').classList.remove('hidden'); var actionButton = document.getElementById('actionButton'); actionButton.textContent = 'Clock Out'; actionButton.className = 'btn btn-primary btn-large'; checkIfProductionStation(station); } }); } function loadIndirectFieldsInline(station) { ajaxRequest('fillIndirectBody.php', { station: station, id_num: currentEmployee.id }, function(response) { var indirectFields = document.getElementById('indirectFieldsContent'); // Parse the response and filter out comment fields to avoid duplicates var tempDiv = document.createElement('div'); tempDiv.innerHTML = response; // Find and remove comment fields - we'll use the standard comment box instead var commentFields = tempDiv.querySelectorAll('input[name*="comment"], textarea[name*="comment"], input[id*="comment"], textarea[id*="comment"]'); var hasCommentField = commentFields.length > 0; // Remove comment fields and their containers for (var i = 0; i < commentFields.length; i++) { var commentField = commentFields[i]; // Remove the entire table row or container var parentRow = commentField.closest('tr'); if (parentRow) { parentRow.remove(); } else { var parentContainer = commentField.parentElement; if (parentContainer) { parentContainer.remove(); } } } // Set the cleaned HTML (without comment fields) indirectFields.innerHTML = tempDiv.innerHTML; var indirectSection = document.getElementById('indirectFieldsSection'); indirectSection.classList.remove('hidden'); indirectSection.classList.remove('valid'); hideError('indirectFieldsError'); // If there was a comment field in indirect, add helpful text to standard comment box if (hasCommentField) { var standardCommentLabel = document.querySelector('label[for="commentsInput"]'); if (standardCommentLabel && !standardCommentLabel.querySelector('.comment-help-text')) { var helpSpan = document.createElement('small'); helpSpan.className = 'comment-help-text'; helpSpan.textContent = ' (All station comments go here)'; helpSpan.style.color = '#666'; helpSpan.style.fontWeight = 'normal'; helpSpan.style.fontSize = '12px'; standardCommentLabel.appendChild(helpSpan); } } var allFields = document.querySelectorAll('.indirect_input_class'); for (var i = 0; i < allFields.length; i++) { var field = allFields[i]; var isCommentField = false; if (field.id && field.id.toLowerCase().indexOf('comment') > -1) { isCommentField = true; } if (field.name && field.name.toLowerCase().indexOf('comment') > -1) { isCommentField = true; } if (isCommentField) { var label = field.parentElement.previousElementSibling; if (label && label.tagName === 'TD' && label.innerHTML.indexOf('(Optional)') === -1) { label.innerHTML = label.innerHTML + ' (Optional)'; } } } setupIndirectFieldHandlers(); setupIndirectValidation(); document.getElementById('actionSection').classList.remove('hidden'); setTimeout(function() { var firstEmptyField = document.querySelector('.indirect_input_class[value=""], .indirect_input_class[value="-1"], select.indirect_input_class option:checked[value="-1"]'); if (firstEmptyField) { if (firstEmptyField.tagName === 'OPTION') { firstEmptyField.parentElement.focus(); } else { firstEmptyField.focus(); } } }, 100); }); } function setupIndirectValidation() { var fields = document.querySelectorAll('.indirect_input_class'); for (var i = 0; i < fields.length; i++) { var field = fields[i]; field.addEventListener('change', function() { validateIndirectFields(); }); field.addEventListener('keyup', function() { if (this.value !== '' || this.dataset.touched === 'true') { this.dataset.touched = 'true'; validateIndirectFields(); } }); field.addEventListener('blur', function() { this.dataset.touched = 'true'; validateIndirectFields(); }); } validateIndirectFields(); } function validateIndirectFields() { var fields = document.querySelectorAll('.indirect_input_class'); var allValid = true; var indirectData = {}; var missingFields = []; for (var i = 0; i < fields.length; i++) { var field = fields[i]; if (field.id && field.id.substring(0, 9) === 'indirect_') { var fieldName = field.name || ''; var fieldId = field.id || ''; var isCommentField = (fieldId === 'sc_indirect_comments' || fieldName === 'comments' || fieldId.toLowerCase().indexOf('comment') > -1 || fieldName.toLowerCase().indexOf('comment') > -1); if (isCommentField) { // Skip comment fields - they're handled by the standard comment box now continue; } else { if (field.value === '' || field.value === '-1') { allValid = false; var label = field.parentElement.previousElementSibling; if (label && label.tagName === 'TD') { var labelText = label.textContent.replace(':', '').replace('(Optional)', '').trim(); if (labelText) { missingFields.push(labelText); } } } else { indirectData[field.id] = field.value; } } } } var actionButton = document.getElementById('actionButton'); if (allValid) { window.pendingIndirectData = indirectData; actionButton.disabled = false; actionButton.textContent = 'Clock Out'; actionButton.className = 'btn btn-primary btn-large'; actionButton.removeAttribute('data-has-errors'); hideError('indirectFieldsError'); var indirectSection = document.getElementById('indirectFieldsSection'); indirectSection.classList.add('valid'); var stationSection = document.getElementById('stationSection'); stationSection.classList.remove('has-errors'); hideError('stationSectionError'); checkEnable(); } else { window.pendingIndirectData = null; actionButton.disabled = true; actionButton.textContent = 'Fill Required Fields First'; actionButton.className = 'btn btn-primary btn-large'; actionButton.setAttribute('data-has-errors', 'true'); var indirectSection = document.getElementById('indirectFieldsSection'); indirectSection.classList.remove('valid'); if (missingFields.length > 0) { showError('indirectFieldsError', '? Please fill required fields: ' + missingFields.join(', ')); } else { showError('indirectFieldsError', '? Please fill all required fields'); } } } function validateStation() { var station = document.getElementById('stationInput').value.trim().toUpperCase(); if (!station) { hideStationInfo(); return; } ajaxRequest('checkStation.php', { station: station }, function(response) { if (response.substring(0, 3) === '*In') { showError('stationError', 'Invalid station code'); validStat = false; hideStationInfo(); } else if (response.substring(0, 5) === 'error') { showError('stationError', response); validStat = false; hideStationInfo(); } else { hideError('stationError'); validStat = true; if (response.substring(0, 3) === '*WI') { var parts = response.split('^^^^'); showStationInfo(parts[2], parts[1]); } else { showStationInfo(response, null); } getStationSchedule(station); } checkEnable(); }); } function checkStation() { validateStation(); } function showStationInfo(description, workInstruction) { document.getElementById('stationDesc').textContent = description; document.getElementById('stationInfo').classList.remove('hidden'); if (workInstruction) { var wiLink = document.getElementById('workInstruction').querySelector('a'); wiLink.href = 'http://zeus.msivt.com/uploads/record_id/' + workInstruction; document.getElementById('workInstruction').classList.remove('hidden'); } else { document.getElementById('workInstruction').classList.add('hidden'); } } function hideStationInfo() { document.getElementById('stationInfo').classList.add('hidden'); } function checkIfProductionStation(station) { // Enhanced handling for TRAVEL station if (station.toUpperCase() === 'TRAVEL') { isProduction = false; document.getElementById('quantityGroup').classList.add('hidden'); checkEnable(); return; } ajaxRequest('checkActive.php', { station: station }, function(response) { response = response.trim(); if (response === '1') { isProduction = true; document.getElementById('quantityGroup').classList.remove('hidden'); } else { isProduction = false; if (station.toUpperCase() === 'QCINSP') { document.getElementById('quantityGroup').classList.remove('hidden'); } else { document.getElementById('quantityGroup').classList.add('hidden'); } } checkEnable(); }); } function checkEnable() { var actionButton = document.getElementById('actionButton'); var enabled = false; if (!currentEmployee || !validNum) { enabled = false; } else if (clockedIn) { var indirectFieldsSection = document.getElementById('indirectFieldsSection'); var hasUnfilledIndirectFields = false; if (!indirectFieldsSection.classList.contains('hidden')) { var fields = document.querySelectorAll('.indirect_input_class'); var allIndirectValid = true; var missingFields = []; for (var i = 0; i < fields.length; i++) { var field = fields[i]; if (field.id && field.id.substring(0, 9) === 'indirect_') { var fieldName = field.name || ''; var fieldId = field.id || ''; if (fieldId !== 'sc_indirect_comments' && fieldName !== 'comments' && fieldId.toLowerCase().indexOf('comment') === -1 && fieldName.toLowerCase().indexOf('comment') === -1 && (field.value === '' || field.value === '-1')) { allIndirectValid = false; hasUnfilledIndirectFields = true; var label = field.parentElement.previousElementSibling; if (label && label.tagName === 'TD') { var labelText = label.textContent.replace(':', '').replace('(Optional)', '').trim(); if (labelText) { missingFields.push(labelText); } } } } } if (hasUnfilledIndirectFields) { enabled = false; actionButton.textContent = 'Fill Required Fields First'; actionButton.className = 'btn btn-primary btn-large'; actionButton.setAttribute('data-has-errors', 'true'); var stationSection = document.getElementById('stationSection'); if (!stationSection.classList.contains('hidden') && document.getElementById('stationInput').value.trim()) { stationSection.classList.add('has-errors'); showError('stationSectionError', '? Cannot switch stations until required fields below are filled'); } if (missingFields.length > 0) { showError('indirectFieldsError', '? Please fill required fields: ' + missingFields.join(', ')); } else { showError('indirectFieldsError', '? Please fill all required fields'); } return; } else { var stationSection = document.getElementById('stationSection'); stationSection.classList.remove('has-errors'); hideError('stationSectionError'); } } var stationInput = document.getElementById('stationInput').value.trim(); if (stationInput && validStat) { var quantityGroup = document.getElementById('quantityGroup'); var isQuantityRequired = !quantityGroup.classList.contains('hidden'); if (isQuantityRequired) { var quantity = document.getElementById('quantityInput').value.trim(); enabled = quantity !== '' && !isNaN(quantity) && parseInt(quantity) >= 0; if (!enabled) { actionButton.textContent = 'Enter Quantity to Switch'; actionButton.className = 'btn btn-primary btn-large'; actionButton.setAttribute('data-has-errors', 'true'); showError('quantityError', '? Please enter quantity to switch stations'); } else { actionButton.textContent = 'Switch Station'; actionButton.className = 'btn btn-switch btn-large'; actionButton.removeAttribute('data-has-errors'); hideError('quantityError'); } } else { enabled = true; actionButton.textContent = 'Switch Station'; actionButton.className = 'btn btn-switch btn-large'; actionButton.removeAttribute('data-has-errors'); } hideError('indirectFieldsError'); } else { hideError('indirectFieldsError'); var quantityGroup = document.getElementById('quantityGroup'); var isQuantityRequired = !quantityGroup.classList.contains('hidden'); if (isQuantityRequired) { var quantity = document.getElementById('quantityInput').value.trim(); enabled = quantity !== '' && !isNaN(quantity) && parseInt(quantity) >= 0; if (!enabled) { showError('quantityError', '? Please enter quantity completed'); } else { hideError('quantityError'); } } else { enabled = true; } if (enabled) { actionButton.textContent = 'Clock Out'; actionButton.className = 'btn btn-primary btn-large'; actionButton.removeAttribute('data-has-errors'); } } } else { enabled = validStat; if (enabled) { actionButton.textContent = 'Clock In'; actionButton.className = 'btn btn-primary btn-large'; actionButton.removeAttribute('data-has-errors'); } } actionButton.disabled = !enabled; } function performAction() { var actionText = document.getElementById('actionButton').textContent; var actionButton = document.getElementById('actionButton'); if (actionButton.disabled) { if (clockedIn) { checkEnable(); var indirectFieldsSection = document.getElementById('indirectFieldsSection'); if (!indirectFieldsSection.classList.contains('hidden')) { validateIndirectFields(); } } return; } if (actionText === 'Switch Station') { performSwitchStation(); } else if (clockedIn) { performClockOut(); } else { performClockIn(); } } function performClockIn() { var station = document.getElementById('stationInput').value.trim().toUpperCase(); var repair = document.getElementById('repairCheckbox').checked; showLoading(); ajaxRequest('clockIn.php', { emp_num: currentEmployee.id, station: station, repair_box: repair ? 'true' : 'false', feeling: getSelectedFeeling() }, function(response) { hideLoading(); response = response.trim(); if (response.indexOf('success') === 0) { if (window.parent !== window) { window.parent.postMessage({ type: 'clockSuccess' }, '*'); } if (response.indexOf('too_early') > 0) { var message = response.substring(response.indexOf(':') + 1); showMessage(message, 'warning'); } else { showMessage('Successfully clocked in to ' + station + '!', 'success'); } setTimeout(function() { resetInterface(); }, 2000); } else { if (response.indexOf('^^^^') > 0) { var parts = response.split('^^^^'); showBreakTimer(parts[1]); showMessage(parts[0], 'error'); } else { showMessage(response, 'error'); } } }); } function performSwitchStation() { var newStation = document.getElementById('stationInput').value.trim().toUpperCase(); var repair = document.getElementById('repairCheckbox').checked; if (!newStation) { showMessage('Please enter a station to switch to', 'warning'); return; } var indirectFieldsSection = document.getElementById('indirectFieldsSection'); if (!indirectFieldsSection.classList.contains('hidden')) { validateIndirectFields(); if (!window.pendingIndirectData) { showMessage('Please fill all required fields before switching stations', 'error'); return; } } showLoading(); if (window.pendingIndirectData) { var data = { employee: currentEmployee.id, station: currentStation }; // Add indirect field data for (var key in window.pendingIndirectData) { data[key] = window.pendingIndirectData[key]; } // Add comments from standard comment box (unified system) var comments = document.getElementById('commentsInput').value; if (comments && comments.trim()) { data.comments = comments; data.sc_indirect_comments = comments; // For backward compatibility } ajaxRequestWithRetry('insertIndirectData.php', data, function(response) { response = response.trim(); if (response === 'success') { window.pendingIndirectData = null; executeStationSwitch(newStation, repair); } else { hideLoading(); if (response.indexOf('Error:') === 0) { showMessage('Error saving station data: ' + response.substring(6), 'error'); } else { showMessage('Error saving station data: ' + response, 'error'); } } }, function(error) { hideLoading(); // Show the detailed error message if (error.indexOf('Error:') === 0) { showMessage('Station data error: ' + error.substring(6), 'error'); } else { showMessage('Station data error: ' + error, 'error'); } }); } else { executeStationSwitch(newStation, repair); } } function executeStationSwitch(newStation, repair) { var previousStation = currentStation; var switchQuantity = '0'; var quantityGroup = document.getElementById('quantityGroup'); if (!quantityGroup.classList.contains('hidden')) { var enteredQty = document.getElementById('quantityInput').value.trim(); if (enteredQty !== '' && !isNaN(enteredQty) && parseInt(enteredQty) >= 0) { switchQuantity = enteredQty; } } // Enhanced TRAVEL station handling if (previousStation.toUpperCase() === 'TRAVEL') { switchQuantity = '1'; // TRAVEL typically uses 1 as default quantity } ajaxRequest('clockOut.php', { emp_num: currentEmployee.id, station: currentStation, comments: document.getElementById('commentsInput').value || '', // Use unified comment system feeling: getSelectedFeeling(), special_project: '-1', quantity: switchQuantity }, function(response) { response = response.trim(); if (response === 'success' || response.indexOf('success') === 0) { ajaxRequest('clockIn.php', { emp_num: currentEmployee.id, station: newStation, repair_box: repair ? 'true' : 'false', feeling: getSelectedFeeling(), 'switch': 'true' }, function(clockInResponse) { hideLoading(); clockInResponse = clockInResponse.trim(); if (clockInResponse.indexOf('success') === 0) { if (window.parent !== window) { window.parent.postMessage({ type: 'clockSuccess' }, '*'); } // Enhanced performance tracking for TRAVEL if (previousStation.toUpperCase() === 'TRAVEL' || newStation.toUpperCase() === 'TRAVEL') { // TRAVEL stations typically don't show performance metrics if (newStation === previousStation) { showMessage('Successfully refreshed your clock-in at ' + newStation + '!', 'success'); } else { showMessage('Successfully switched from ' + previousStation + ' to ' + newStation + '!', 'success'); } setTimeout(function() { resetInterface(); }, 2000); } else { // Regular station performance check ajaxRequest('checkActive.php', { station: previousStation }, function(activeResponse) { activeResponse = activeResponse.trim(); var wasPreviousStationProduction = (activeResponse === '1' || previousStation.toUpperCase() === 'QCINSP'); if (wasPreviousStationProduction && switchQuantity !== '0') { setTimeout(function() { getLastProductiveSession(currentEmployee.id, previousStation, function(performanceData) { var successMessage = 'Successfully switched from ' + previousStation + ' to ' + newStation + '!'; if (performanceData) { showMessage(successMessage, 'success', performanceData); setTimeout(function() { resetInterface(); }, 5000); } else { showMessage(successMessage, 'success'); setTimeout(function() { resetInterface(); }, 2000); } }); }, 500); } else { if (newStation === previousStation) { showMessage('Successfully refreshed your clock-in at ' + newStation + '!', 'success'); } else { showMessage('Successfully switched from ' + previousStation + ' to ' + newStation + '!', 'success'); } setTimeout(function() { resetInterface(); }, 2000); } }); } } else { showMessage('Error completing station switch: ' + clockInResponse, 'error'); } }); } else { hideLoading(); showMessage('Error leaving current station: ' + response, 'error'); } }); } function performClockOut() { var stationToClockOut = currentStation; if (!stationToClockOut) { showMessage('Error: No station information found. Please refresh and try again.', 'error'); return; } var indirectFieldsSection = document.getElementById('indirectFieldsSection'); if (!indirectFieldsSection.classList.contains('hidden')) { validateIndirectFields(); var actionButton = document.getElementById('actionButton'); if (actionButton.disabled) { return; } } if (stationToClockOut === 'QC') { stationToClockOut = document.getElementById('qcSubStation').value; } if (orientationRequired && stationToClockOut === 'ORIENTATION') { showOrientationSurvey(); return; } if (window.pendingIndirectData) { saveIndirectDataThenClockOut(stationToClockOut); } else { proceedWithClockOut(stationToClockOut); } } function saveIndirectDataThenClockOut(stationToClockOut) { var data = { employee: currentEmployee.id, station: currentStation }; // Add indirect field data for (var key in window.pendingIndirectData) { data[key] = window.pendingIndirectData[key]; } // Add comments from standard comment box (unified system) var comments = document.getElementById('commentsInput').value; if (comments && comments.trim()) { data.comments = comments; data.sc_indirect_comments = comments; // For backward compatibility } showLoading(); ajaxRequestWithRetry('insertIndirectData.php', data, function(response) { response = response.trim(); if (response === 'success') { window.pendingIndirectData = null; proceedWithClockOut(stationToClockOut); } else { hideLoading(); if (response.indexOf('Error:') === 0) { showMessage('Error saving station data: ' + response.substring(6), 'error'); } else { showMessage('Error saving station data: ' + response, 'error'); } } }, function(error) { hideLoading(); // Show the detailed error message if (error.indexOf('Error:') === 0) { showMessage('Station data error: ' + error.substring(6), 'error'); } else { showMessage('Station data error: ' + error, 'error'); } }); } function proceedWithClockOut(stationToClockOut) { var params = { emp_num: currentEmployee.id, station: stationToClockOut, comments: document.getElementById('commentsInput').value || '', // Use unified comment system feeling: getSelectedFeeling(), special_project: '-1' }; var quantityGroup = document.getElementById('quantityGroup'); if (!quantityGroup.classList.contains('hidden')) { var qtyValue = document.getElementById('quantityInput').value.trim(); if (qtyValue === '' || isNaN(qtyValue)) { params.quantity = '0'; } else { params.quantity = parseInt(qtyValue).toString(); } } else { // Enhanced TRAVEL station handling if (stationToClockOut.toUpperCase() === 'TRAVEL') { params.quantity = '1'; // TRAVEL typically uses 1 as default } else { params.quantity = '0'; } } var loadingOverlay = document.getElementById('loadingOverlay'); if (loadingOverlay.classList.contains('hidden')) { showLoading(); } ajaxRequest('clockOut.php', params, function(response) { hideLoading(); response = response.trim(); if (response.indexOf('*****') === 0 || response.indexOf('Error:') === 0 || response.indexOf('* ERRORS **') === 0) { showMessage(response, 'error'); return; } if (response === 'success') { if (window.parent !== window) { window.parent.postMessage({ type: 'clockSuccess' }, '*'); } // Enhanced handling for TRAVEL station if (stationToClockOut.toUpperCase() === 'TRAVEL') { showMessage('Successfully clocked out from ' + stationToClockOut + '!', 'success'); setTimeout(function() { resetInterface(); }, 2000); } else { // Regular station performance check ajaxRequest('checkActive.php', { station: stationToClockOut }, function(activeResponse) { activeResponse = activeResponse.trim(); var isStationProduction = (activeResponse === '1' || stationToClockOut.toUpperCase() === 'QCINSP'); if (isStationProduction && params.quantity !== '0') { setTimeout(function() { getLastProductiveSession(currentEmployee.id, stationToClockOut, function(performanceData) { var successMessage = 'Successfully clocked out from ' + stationToClockOut + '!'; if (performanceData) { showMessage(successMessage, 'success', performanceData); setTimeout(function() { resetInterface(); }, 5000); } else { showMessage(successMessage, 'success'); setTimeout(function() { resetInterface(); }, 2000); } }); }, 500); } else { showMessage('Successfully clocked out from ' + stationToClockOut + '!', 'success'); setTimeout(function() { resetInterface(); }, 2000); } }); } } else { showMessage(response, 'error'); } }); } function getLastProductiveSession(empId, station, callback) { ajaxRequest('getLastProductiveSession.php', { employee: empId, station: station }, function(response) { response = response.trim(); try { var data = JSON.parse(response); if (data.error === false && data.your_avg !== 'N/A - 0 Qty' && data.your_avg !== 'N/A') { callback(data); return; } } catch(e) { // Fallback } ajaxRequest('getStationDetailsMobile.php', { employee: empId, station: station, mobile: '1' }, function(fallbackResponse) { fallbackResponse = fallbackResponse.trim(); try { var fallbackData = JSON.parse(fallbackResponse); if (fallbackData.error === false && fallbackData.your_avg !== 'N/A - 0 Qty' && fallbackData.your_avg !== 'N/A') { callback(fallbackData); } else { callback(null); } } catch(e) { callback(null); } }); }); } function getSelectedFeeling() { var selected = document.querySelector('input[name="feeling"]:checked'); return selected ? selected.value : '5'; } function showBreakTimer(seconds) { breakTimeLeft = parseInt(seconds); document.getElementById('breakTimer').classList.remove('hidden'); updateBreakTimer(); var interval = setInterval(function() { breakTimeLeft--; updateBreakTimer(); if (breakTimeLeft <= 0) { clearInterval(interval); closeBreakTimer(); } }, 1000); } function updateBreakTimer() { var minutes = Math.floor(breakTimeLeft / 60); var seconds = breakTimeLeft % 60; var display = minutes + ':' + (seconds < 10 ? '0' : '') + seconds; document.getElementById('breakTimeRemaining').textContent = display; } function closeBreakTimer() { document.getElementById('breakTimer').classList.add('hidden'); } function setupIndirectFieldHandlers() { window.ChangeCustomer = function(station) { var customer = document.getElementById('indirect_1'); if (!customer) return; var customerId = customer.value; var params = { customer: customerId }; if (station === 'NPR' && document.getElementById('sc_indirect_npr_td')) { ajaxRequest('getNPRList.php', params, function(response) { document.getElementById('sc_indirect_npr_td').innerHTML = response; }); } if (document.getElementById('sc_indirect_product_td')) { ajaxRequest('getProductTD.php', params, function(response) { document.getElementById('sc_indirect_product_td').innerHTML = response; }); } }; window.ChangeVendor = function() { var vendor = document.getElementById('indirect_4'); if (!vendor || vendor.value === '-1') return; ajaxRequest('getPartNumberTD.php', { vendor: vendor.value }, function(response) { var container = document.getElementById('sc_indirect_part_number_td'); if (container) { container.innerHTML = response; } }); }; window.ChangeProduct = function(station) { // Product change handler if needed }; } function showOrientationSurvey() { document.getElementById('orientationSurvey').classList.remove('hidden'); } function submitOrientationSurvey() { var form = document.getElementById('orientForm'); var data = new FormData(form); var questions = ['glasses', 'badge', 'attendance', 'harassment', 'locker']; var allAnswered = true; for (var i = 0; i < questions.length; i++) { var q = questions[i]; if (!form[q] || !form[q].value) { allAnswered = false; } } if (!allAnswered) { showError('orientError', 'Please answer all questions'); return; } document.getElementById('orientationSurvey').classList.add('hidden'); performClockOut(); } function updateQCStation(empId, station) { ajaxRequest('updateQCstation.php', { id_num: empId, station: station }, function(response) { currentStation = station; document.getElementById('currentStation').textContent = station; }); } function displaySchedule(response) { if (!response) return; var scheduleContent = document.getElementById('scheduleContent'); var scheduleSection = document.getElementById('scheduleSection'); var helpText = scheduleSection.querySelector('.help-text'); if (clockedIn) { helpText.textContent = 'Click any station to switch to it'; } else { helpText.textContent = 'Click any station to select it'; } var items = response.split('~~'); var html = ''; for (var i = 0; i < items.length; i++) { var item = items[i]; if (item) { var parts = item.split('^^'); if (parts.length >= 4) { html += '
'; html += '
' + parts[2] + ''; html += '
' + parts[3] + '
'; html += '
Qty: ' + parts[1] + '
'; html += '
'; } } } scheduleContent.innerHTML = html || '

No schedule for today

'; if (html) { scheduleSection.classList.remove('hidden'); } } function selectStationFromSchedule(stationCode) { document.getElementById('stationInput').value = stationCode; validateStation(); document.getElementById('stationSection').scrollIntoView({ behavior: 'smooth', block: 'center' }); var allItems = document.querySelectorAll('.schedule-item'); for (var i = 0; i < allItems.length; i++) { allItems[i].classList.remove('selected'); } if (event && event.currentTarget) { event.currentTarget.classList.add('selected'); } } function getStationSchedule(station) { ajaxRequest('getStationSchedule.php', { station: station }, function(response) { if (response && response.indexOf('Nothing scheduled') === -1) { document.getElementById('stationScheduleContent').innerHTML = response; document.getElementById('stationScheduleSection').classList.remove('hidden'); } else { document.getElementById('stationScheduleSection').classList.add('hidden'); } }); } function resetInterface() { currentEmployee = null; clockedIn = false; currentStation = null; isProduction = false; validNum = false; validStat = false; orientationRequired = false; window.pendingIndirectData = null; document.getElementById('empInput').value = ''; document.getElementById('stationInput').value = ''; document.getElementById('quantityInput').value = ''; document.getElementById('commentsInput').value = ''; document.getElementById('repairCheckbox').checked = false; // Remove comment help text var commentHelp = document.querySelector('.comment-help-text'); if (commentHelp) { commentHelp.remove(); } var feelingOptions = document.querySelectorAll('.feeling-option'); for (var i = 0; i < feelingOptions.length; i++) { feelingOptions[i].classList.remove('selected'); } document.querySelector('input[value="5"]').checked = true; document.querySelector('input[value="5"]').parentElement.classList.add('selected'); var cards = document.querySelectorAll('.card'); for (var i = 0; i < cards.length; i++) { cards[i].classList.add('hidden'); cards[i].classList.remove('switch-mode'); cards[i].classList.remove('has-errors'); } document.getElementById('indirectFieldsContent').innerHTML = ''; var indirectSection = document.getElementById('indirectFieldsSection'); indirectSection.classList.add('hidden'); indirectSection.classList.remove('valid'); hideError('indirectFieldsError'); var giftCardDiv = document.getElementById('giftCardMessage'); if (giftCardDiv) { giftCardDiv.style.display = 'none'; giftCardDiv.innerHTML = ''; } var notesDiv = document.getElementById('empNotes'); if (notesDiv) { notesDiv.style.display = 'none'; notesDiv.innerHTML = ''; } document.getElementById('employeeSection').classList.remove('hidden'); hideAllErrors(); var actionButton = document.getElementById('actionButton'); actionButton.textContent = 'Clock In'; actionButton.className = 'btn btn-primary btn-large'; actionButton.disabled = true; actionButton.removeAttribute('data-has-errors'); document.getElementById('empInput').focus(); } // Enhanced AJAX with retry logic specifically for critical operations function ajaxRequestWithRetry(url, params, successCallback, errorCallback, retryCount) { retryCount = retryCount || 0; var maxRetries = 2; ajaxRequest(url, params, function(response) { // Check if response indicates a server error that should be retried if (response.indexOf('Connection error (500)') === 0 || response.indexOf('Fatal error:') !== -1 || response.indexOf('Parse error:') !== -1 || response.indexOf('No response from server') === 0) { if (retryCount < maxRetries) { setTimeout(function() { ajaxRequestWithRetry(url, params, successCallback, errorCallback, retryCount + 1); }, 1000); } else { if (errorCallback) { errorCallback(response); } else { hideLoading(); showMessage('Server error after ' + maxRetries + ' attempts: ' + response, 'error'); } } } else if (response.indexOf('Error:') === 0) { // This is a specific error from our code - don't retry, just pass it through if (errorCallback) { errorCallback(response); } else { hideLoading(); showMessage(response, 'error'); } } else { // Success - call the success callback successCallback(response); } }); } // AJAX Request Helper with better error handling (Browser compatible) function ajaxRequest(url, params, callback) { var xhr = new XMLHttpRequest(); var filename = url.substring(url.lastIndexOf('/') + 1); // Enhanced timeout handling xhr.timeout = requestTimeout; xhr.addEventListener('timeout', function() { hideLoading(); showMessage('Request timed out. Please check your connection and try again.', 'error'); }); // Use POST for indirect data submissions or requests with many parameters var usePost = (filename === 'insertIndirectData.php' || Object.keys(params).length > 10); if (usePost) { var formData = new FormData(); formData.append('target', filename); for (var key in params) { if (params.hasOwnProperty(key)) { var value = params[key]; if (typeof value === 'number') { value = value.toString(); } formData.append(key, value); } } xhr.open('POST', 'station_clock_mobile_ajax.php?nocache=' + Math.random(), true); xhr.onreadystatechange = function() { if (xhr.readyState === 4) { if (xhr.status === 200) { // Debug logging for insertIndirectData if (filename === 'insertIndirectData.php') { console.log('insertIndirectData response:', xhr.responseText); } callback(xhr.responseText); } else { hideLoading(); showMessage('Connection error (' + xhr.status + ') for ' + filename + '. Please try again.', 'error'); } } }; xhr.send(formData); } else { var queryString = 'target=' + encodeURIComponent(filename); for (var key in params) { if (params.hasOwnProperty(key)) { var value = params[key]; if (typeof value === 'number') { value = value.toString(); } queryString += '&' + encodeURIComponent(key) + '=' + encodeURIComponent(value); } } xhr.open('GET', 'station_clock_mobile_ajax.php?' + queryString + '&nocache=' + Math.random(), true); xhr.onreadystatechange = function() { if (xhr.readyState === 4) { if (xhr.status === 200) { callback(xhr.responseText); } else { hideLoading(); showMessage('Connection error (' + xhr.status + ') for ' + filename + '. Please try again.', 'error'); } } }; xhr.send(); } } function showLoading() { document.getElementById('loadingOverlay').classList.remove('hidden'); } function hideLoading() { document.getElementById('loadingOverlay').classList.add('hidden'); } function showError(elementId, message) { var element = document.getElementById(elementId); if (element) { element.textContent = message; element.classList.add('show'); element.style.display = 'block'; } } function hideError(elementId) { var element = document.getElementById(elementId); if (element) { element.textContent = ''; element.classList.remove('show'); element.style.display = 'none'; } } function hideAllErrors() { var errors = document.querySelectorAll('.error-message'); for (var i = 0; i < errors.length; i++) { var error = errors[i]; error.textContent = ''; error.classList.remove('show'); error.style.display = 'none'; } } function showMessage(text, type, details) { var popup = document.getElementById('messagePopup'); var icon = document.getElementById('messageIcon'); var textElement = document.getElementById('messageText'); var detailsSection = document.getElementById('messageDetails'); textElement.textContent = text; if (type === 'success') { icon.innerHTML = '✔'; icon.className = 'success'; } else if (type === 'error') { icon.innerHTML = '✘'; icon.className = 'error'; } else { icon.innerHTML = '!'; icon.className = 'warning'; } if (details && type === 'success') { detailsSection.classList.remove('hidden'); document.getElementById('popupYourAvg').textContent = details.your_avg; document.getElementById('popupTargetTime').textContent = details.target_time; var yourAvgElement = document.getElementById('popupYourAvg'); if (details.your_avg_color === 'red') { yourAvgElement.className = 'metric-value bad'; } else { yourAvgElement.className = 'metric-value good'; } var perfMessage = document.getElementById('popupPerformance'); if (details.performance_type === 'behind') { perfMessage.innerHTML = 'Behind target by ' + details.performance_diff + ' min'; } else if (details.performance_type === 'beat') { perfMessage.innerHTML = 'Beat target by ' + details.performance_diff + ' min!'; } else if (details.performance_type === 'on_target') { perfMessage.innerHTML = 'Met the target time!'; } else { perfMessage.innerHTML = details.performance_message || ''; } } else { detailsSection.classList.add('hidden'); } popup.classList.remove('hidden'); var displayTime = details ? 5000 : 3000; setTimeout(function() { popup.classList.add('hidden'); }, displayTime); } function updateDateTime() { var now = new Date(); var timeStr = now.toLocaleTimeString('en-US', { hour: '2-digit', minute: '2-digit' }); var dateStr = now.toLocaleDateString('en-US', { weekday: 'short', month: 'short', day: 'numeric' }); document.getElementById('currentTime').textContent = timeStr; document.getElementById('currentDate').textContent = dateStr; } function displayEmployeeInfoLegacy(empId, response) { var parts = response.split('^^^^'); var name = parts[0] || 'Employee ' + empId; currentEmployee = { id: empId, name: name }; validNum = true; document.getElementById('empName').textContent = currentEmployee.name; document.getElementById('empId').textContent = '#' + empId; if (parts[2] && parts[2].trim()) { var giftCardDiv = document.getElementById('giftCardMessage'); giftCardDiv.innerHTML = 'Gift Card/Reward: ' + parts[2]; giftCardDiv.style.display = 'block'; } if (parts[1] && parts[1] === '0') { orientationRequired = true; } document.getElementById('employeeSection').classList.add('hidden'); document.getElementById('employeeInfo').classList.remove('hidden'); checkEmployeeStation(empId); }