$(document).ready(function() { // Version selector $('#versionSelector').on('change', function() { const projectId = new URLSearchParams(window.location.search).get('projectId'); const version = $(this).val(); window.location.href = `forecasting.php?projectId=${projectId}&version=${version}`; }); // Roster search with Select2 $('#rosterSearch').select2({ theme: 'bootstrap-5', placeholder: 'Search by name or SAP code...', allowClear: true }); // --- Inline editing for allocated days --- const table = document.querySelector('.table'); let originalValue = null; // Use event delegation on the table body const tableBody = table.querySelector('tbody'); tableBody.addEventListener('click', function(event) { const cell = event.target.closest('.editable'); if (!cell) return; // Clicked outside an editable cell // If the cell is already being edited, do nothing if (cell.isContentEditable) return; originalValue = cell.textContent.trim(); cell.setAttribute('contenteditable', 'true'); // Select all text in the cell const range = document.createRange(); range.selectNodeContents(cell); const selection = window.getSelection(); selection.removeAllRanges(); selection.addRange(range); cell.focus(); }); tableBody.addEventListener('focusout', function(event) { const cell = event.target.closest('.editable'); if (!cell) return; cell.removeAttribute('contenteditable'); const newValue = cell.textContent.trim(); // Only update if the value has changed and is a valid number if (newValue !== originalValue && !isNaN(newValue)) { updateAllocation(cell, newValue); } else if (newValue !== originalValue) { // Revert if the new value is not a number cell.textContent = originalValue; } }); tableBody.addEventListener('keydown', function(event) { if (event.key === 'Enter' && event.target.classList.contains('editable')) { event.preventDefault(); event.target.blur(); // Triggers focusout to save } else if (event.key === 'Escape' && event.target.classList.contains('editable')) { event.target.textContent = originalValue; // Revert changes event.target.blur(); // Trigger focusout } }); function updateAllocation(cell, allocatedDays) { const rosterId = cell.dataset.rosterId; const month = cell.dataset.month; const forecastingId = cell.dataset.forecastingId; const projectId = new URLSearchParams(window.location.search).get('projectId'); const originalContent = cell.innerHTML; // Add loading indicator cell.innerHTML = ``; $.ajax({ url: 'forecasting_actions.php', type: 'POST', dataType: 'json', data: { action: 'update_allocation', forecastingId: forecastingId, rosterId: rosterId, month: month, allocatedDays: allocatedDays, projectId: projectId }, success: function(response) { if (response.success) { // The server-side action reloads the page, so no need to update the cell manually. // If the reload were disabled, we'd do: cell.textContent = allocatedDays; window.location.reload(); // Ensure data consistency } else { console.error('Update failed:', response.error); alert('Error: ' + response.error); cell.innerHTML = originalContent; // Revert on failure } }, error: function(xhr, status, error) { console.error('AJAX error:', error); alert('An unexpected error occurred. Please try again.'); cell.innerHTML = originalContent; // Revert on AJAX error } }); } });