36293-vm/assets/js/forecasting.js
2025-11-25 21:46:02 +00:00

109 lines
4.2 KiB
JavaScript

$(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 = `<span class="spinner-border spinner-border-sm" role="status" aria-hidden="true"></span>`;
$.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
}
});
}
});