Repousser un projet en bloc, sauf ce dont on ne décide pas

Glisser la barre résumé décale toutes les phases du même nombre de jours,
écarts conservés : un projet engagé glisse souvent, et le faire phase par
phase les déformait. Restent en place ce qui est terminé — on ne réécrit
pas le passé — et les phases à date imposée, nouveau booléen `fixed` pour
ce qui est délégué ou tenu du dehors.

D'où le format en version 6 : sans l'incrément, un binaire antérieur
effacerait ces dates imposées à la première sauvegarde.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-10 18:07:31 +02:00
parent 55128fd0d4
commit 4a513f9a59
15 changed files with 788 additions and 74 deletions

View File

@@ -22,12 +22,14 @@ import {
STATUTS,
ajouterJours,
ajouterPhase,
ancreDecalage,
aujourdhui,
bornesPlanning,
cleTag,
clotureEcraseDesStatuts,
creerPhase,
creerProjet,
decalerProjet,
ecarterProjet,
etatProjet,
figerReference,
@@ -869,6 +871,17 @@ installerGlisser(refs.couloirs, {
}
majPlanning(remplacerProjet(planning, idProjet, (projet) => ({ ...projet, horizon })));
},
surDecalageProjet(idProjet, jours) {
// Même convention que pour une phase : null veut dire « geste annulé ou sans
// effet », on redessine sans rien sauvegarder.
if (!jours) {
dessiner();
return;
}
majPlanning(
remplacerProjet(planning, idProjet, (projet) => decalerProjet(projet, jours))
);
},
surClicProjet(idProjet) {
const projet = planning.projects.find((p) => p.id === idProjet);
if (projet) ouvrirProjet(projet);
@@ -1315,16 +1328,29 @@ refs.frise.addEventListener(
{ passive: false }
);
// Flèches : ajustement fin de la phase du curseur, au jour près.
// Flèches : ajustement fin, au jour près, de ce que porte le curseur.
document.addEventListener('keydown', (evenement) => {
// Une ligne de projet n'a pas de dates : les flèches n'y ont rien à décaler.
if (!curseur?.phase) return;
if (!curseur) return;
// Pas d'interception pendant une saisie dans le panneau ou un dialogue.
if (['INPUT', 'TEXTAREA', 'SELECT'].includes(document.activeElement?.tagName)) return;
const pas = evenement.key === 'ArrowLeft' ? -1 : evenement.key === 'ArrowRight' ? 1 : 0;
if (pas === 0) return;
// Sur la ligne d'un projet, les flèches repoussent le projet entier — le même
// geste que le glisser de sa barre cumulative, au jour près. Maj n'y ajoute
// rien : un projet ne s'étire pas (docs/decisions.md, section 8).
if (!curseur.phase) {
const projet = planning.projects.find((p) => p.id === curseur.projet);
if (!projet || !ancreDecalage(projet)) return;
evenement.preventDefault();
majPlanning(
remplacerProjet(planning, curseur.projet, (aDecaler) => decalerProjet(aDecaler, pas))
);
return;
}
evenement.preventDefault();
const trouve = localiser(curseur.projet, curseur.phase);
if (!trouve) return;