loko/streetup/projects/static/projects/timeline.js
2026-07-22 14:48:40 +02:00

145 lines
4.7 KiB
JavaScript

const statusTimeline = document.getElementById("statusTimeline");
// Récupère la liste des statuts autorisés (inline script ou propriété globale)
const allowedStatusList = Array.isArray(window.allowedStatuses)
? window.allowedStatuses
: (typeof allowedStatuses !== 'undefined' && Array.isArray(allowedStatuses) ? allowedStatuses : []);
// Helper CSRF sans redéclarer l'identifiant global s'il existe déjà
const getCsrfCookie = (typeof window.getCookie === 'function') ? window.getCookie : function(name) {
let cookieValue = null;
if (document.cookie && document.cookie !== '') {
const cookies = document.cookie.split(';');
for (let i = 0; i < cookies.length; i++) {
const cookie = cookies[i].trim();
if (cookie.substring(0, name.length + 1) === (name + '=')) {
cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
break;
}
}
}
return cookieValue;
};
let currentIndex = -1;
STATUS_CHOICES.forEach((status, index) => {
if (status.key === currentStatus) {
currentIndex = index;
}
});
let nextActionsIndex = [];
let pastActionsIndex = [];
if (!['closed', 'canceled'].includes(currentStatus)) {
nextActionsIndex.push(currentIndex + 1);
}
// Annulation accessible tant que le projet n'est pas clôturé/comptabilisé
if (!['finished', 'invoiced', 'closed', 'canceled'].includes(currentStatus)) {
const canceledIndex = STATUS_CHOICES.findIndex(status => status.key === 'canceled');
if (canceledIndex !== -1) {
nextActionsIndex.push(canceledIndex);
}
}
STATUS_CHOICES.forEach((status, index) => {
let statusClass = "hide";
let actionClass = "";
let dashedClass = "";
let buttonClass = "";
let label = status.label;
let icon = "";
const isAllowed = allowedStatusList.includes(status.key);
if (index === currentIndex) {
statusClass = "current";
dashedClass = "next-step-dashed";
} else if (index < currentIndex) {
statusClass = "past";
if (pastActionsIndex.includes(index) && isAllowed) {
actionClass = "action";
buttonClass = "button";
icon = "bi-arrow-left";
} else {
label = "";
}
} else if (nextActionsIndex.includes(index)) {
statusClass = "next";
if (isAllowed) {
actionClass = "action";
buttonClass = "button";
dashedClass = "next-step-dashed";
icon = "bi-arrow-right";
}
}
if (index === (STATUS_CHOICES.length - 1)) {
dashedClass = "";
}
if (statusClass !== "hide") {
const step = document.createElement("div");
step.className = `step ${statusClass} ${dashedClass}`;
step.innerHTML = `
<div id="status_${status.key}" status="${status.key}" data-status="${status.key}" class="circle ${actionClass} ${buttonClass} ${status.key}">
${icon ? '<i class="bi '+icon+'"></i>' : (index + 1)}
</div>
<div class="steplabel">${label}</div>
`;
statusTimeline.appendChild(step);
}
});
document.addEventListener('click', (event) => {
const actionTarget = event.target.closest('.circle.action');
if (!actionTarget) {
return;
}
const newStatus = actionTarget.getAttribute('data-status') || actionTarget.getAttribute('status');
if (!newStatus) {
return;
}
if (newStatus === 'canceled') {
const confirmCancel = typeof gettext === 'function'
? gettext("Êtes-vous sûr de vouloir annuler ce projet ?")
: "Êtes-vous sûr de vouloir annuler ce projet ?";
if (!window.confirm(confirmCancel)) {
return;
}
}
updateStatus(newStatus);
});
function updateStatus(newStatus) {
const errorMsg = typeof gettext === 'function'
? gettext("Erreur lors de la mise à jour du statut.")
: "Erreur lors de la mise à jour du statut.";
fetch(`/projects/${project_id}/update-status/`, {
method: 'POST',
headers: {
'X-CSRFToken': getCsrfCookie('csrftoken'),
'X-Requested-With': 'XMLHttpRequest',
'Content-Type': 'application/x-www-form-urlencoded',
},
body: new URLSearchParams({ new_status: newStatus }),
})
.then(response => response.json().then(data => ({ ok: response.ok, data })))
.then(({ ok, data }) => {
if (ok && data && data.success) {
window.location.reload();
return;
}
const message = data && data.message ? data.message : errorMsg;
window.alert(message);
})
.catch(() => window.alert(errorMsg));
}