109 lines
4.2 KiB
JavaScript
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
|
|
}
|
|
});
|
|
}
|
|
}); |