judu/ex-egopublic Fork
⑂ 5745456
Commits
⬇ Clone ▾
git clone https://git.rickub.com/judu/ex-ego.git
git clone ssh://git@rickub.com/judu/ex-ego.git

Host key fingerprint (ed25519): SHA256:iycHnxEyq0Q7uyVpB7JlznP0G7JrTPXLYRcAU5CSLhc — verify it before your first connect.

Download ZIP

feat(minuteur): la case « Répartir » reste cochée une fois la répartition entaméeUnverified

Quand une mesure a déjà été partiellement répartie, revenir sur l'étape 2
pré-coche « Répartir le temps restant » dès que la durée saisie laisse un
reste : commencer à répartir engage par défaut à aller jusqu'au bout.

Le serveur pose data-want (1 si timers.split n'est pas vide) sur le bloc ;
le JS l'applique quand la case redevient active et y mémorise le dernier
choix explicite de l'utilisateur, au lieu de repartir décoché à chaque
variation de durée. Sans JS, rien ne change (bloc masqué).
Julien Durillon committed 2026-09-13T21:21:15+02:00 Browse files
5745456 parent: ac0c9d7
modified src/routes/entries.rs +6 -0
@@ -110,6 +110,10 @@ pub struct AddStep2Page {
110110 /// durée en est préremplie et le toggle « Répartir » existe. None pour
111111 /// une saisie manuelle.
112112 pub remaining: Option<i64>,
113+ /// Une répartition est entamée (au moins une saisie créditée sur la
114+ /// mesure) : la case « Répartir » est alors cochée d'office — qui a
115+ /// commencé à répartir va par défaut jusqu'au bout.
116+ pub split_started: bool,
113117 }
114118
115119 #[derive(Deserialize)]
@@ -212,6 +216,7 @@ pub async fn entry_new(
212216 .and_then(|id| all_tasks.iter().find(|t| t.id == id && !t.archived))
213217 {
214218 let remaining = pending.as_ref().and_then(|t| t.remaining_minutes());
219+ let split_started = pending.as_ref().is_some_and(|t| t.split_started());
215220 let minutes = q
216221 .minutes
217222 .or(remaining.map(|m| m.min(i32::MAX as i64) as i32))
@@ -244,6 +249,7 @@ pub async fn entry_new(
244249 day_next: (date < paris_today())
245250 .then(|| step2_href(picked.id, minutes, date + Duration::days(1))),
246251 remaining,
252+ split_started,
247253 })
248254 .into_response());
249255 }
@@ -110,6 +110,10 @@ pub struct AddStep2Page {
110 /// durée en est préremplie et le toggle « Répartir » existe. None pour110 /// durée en est préremplie et le toggle « Répartir » existe. None pour
111 /// une saisie manuelle.111 /// une saisie manuelle.
112 pub remaining: Option<i64>,112 pub remaining: Option<i64>,
113+ /// Une répartition est entamée (au moins une saisie créditée sur la
114+ /// mesure) : la case « Répartir » est alors cochée d'office — qui a
115+ /// commencé à répartir va par défaut jusqu'au bout.
116+ pub split_started: bool,
113 }117 }
114 118
115 #[derive(Deserialize)]119 #[derive(Deserialize)]
@@ -212,6 +216,7 @@ pub async fn entry_new(
212 .and_then(|id| all_tasks.iter().find(|t| t.id == id && !t.archived))216 .and_then(|id| all_tasks.iter().find(|t| t.id == id && !t.archived))
213 {217 {
214 let remaining = pending.as_ref().and_then(|t| t.remaining_minutes());218 let remaining = pending.as_ref().and_then(|t| t.remaining_minutes());
219+ let split_started = pending.as_ref().is_some_and(|t| t.split_started());
215 let minutes = q220 let minutes = q
216 .minutes221 .minutes
217 .or(remaining.map(|m| m.min(i32::MAX as i64) as i32))222 .or(remaining.map(|m| m.min(i32::MAX as i64) as i32))
@@ -244,6 +249,7 @@ pub async fn entry_new(
244 day_next: (date < paris_today())249 day_next: (date < paris_today())
245 .then(|| step2_href(picked.id, minutes, date + Duration::days(1))),250 .then(|| step2_href(picked.id, minutes, date + Duration::days(1))),
246 remaining,251 remaining,
252+ split_started,
247 })253 })
248 .into_response());254 .into_response());
249 }255 }
modified templates/add_step2.html +6 -3
@@ -35,11 +35,14 @@
3535 {#- Répartition d'une mesure (0018) — réservée au JS, écart assumé à la
3636 règle zéro-JS : rendu hidden + case disabled, le JS révèle le bloc et
3737 pilote la case d'après la durée saisie (app.html, form[data-duration]).
38- Sans JS le bloc n'existe pas et la mesure est consommée comme avant. -#}
39- <div class="split-row" hidden data-split data-remaining="{{ r }}">
38+ Sans JS le bloc n'existe pas et la mesure est consommée comme avant.
39+ data-want = choix par défaut de la case quand elle s'active : cochée
40+ dès qu'une répartition est entamée (aller jusqu'au bout), puis le JS y
41+ mémorise le dernier choix explicite de l'utilisateur. -#}
42+ <div class="split-row" hidden data-split data-remaining="{{ r }}" data-want="{% if split_started %}1{% else %}0{% endif %}">
4043 <label class="swt-check split-check">
4144 <span class="split-label" data-pat="{{ shell.tr.split_pat }}" data-idle="{{ shell.tr.split_idle }}">{{ shell.tr.split_idle }}</span>
42- <input type="checkbox" name="repartir" value="1" disabled>
45+ <input type="checkbox" name="repartir" value="1" disabled{% if split_started %} checked{% endif %}>
4346 <span class="swt"><span class="knob"></span></span>
4447 </label>
4548 </div>
@@ -35,11 +35,14 @@
35 {#- Répartition d'une mesure (0018) — réservée au JS, écart assumé à la35 {#- Répartition d'une mesure (0018) — réservée au JS, écart assumé à la
36 règle zéro-JS : rendu hidden + case disabled, le JS révèle le bloc et36 règle zéro-JS : rendu hidden + case disabled, le JS révèle le bloc et
37 pilote la case d'après la durée saisie (app.html, form[data-duration]).37 pilote la case d'après la durée saisie (app.html, form[data-duration]).
38- Sans JS le bloc n'existe pas et la mesure est consommée comme avant. -#}38+ Sans JS le bloc n'existe pas et la mesure est consommée comme avant.
39- <div class="split-row" hidden data-split data-remaining="{{ r }}">39+ data-want = choix par défaut de la case quand elle s'active : cochée
40+ dès qu'une répartition est entamée (aller jusqu'au bout), puis le JS y
41+ mémorise le dernier choix explicite de l'utilisateur. -#}
42+ <div class="split-row" hidden data-split data-remaining="{{ r }}" data-want="{% if split_started %}1{% else %}0{% endif %}">
40 <label class="swt-check split-check">43 <label class="swt-check split-check">
41 <span class="split-label" data-pat="{{ shell.tr.split_pat }}" data-idle="{{ shell.tr.split_idle }}">{{ shell.tr.split_idle }}</span>44 <span class="split-label" data-pat="{{ shell.tr.split_pat }}" data-idle="{{ shell.tr.split_idle }}">{{ shell.tr.split_idle }}</span>
42- <input type="checkbox" name="repartir" value="1" disabled>45+ <input type="checkbox" name="repartir" value="1" disabled{% if split_started %} checked{% endif %}>
43 <span class="swt"><span class="knob"></span></span>46 <span class="swt"><span class="knob"></span></span>
44 </label>47 </label>
45 </div>48 </div>
modified templates/app.html +9 -1
@@ -181,6 +181,10 @@
181181 // Répartition d'une mesure de minuteur (réservée au JS) : le toggle vit
182182 // quand la durée saisie laisse un reste, se décoche sinon, et le bouton
183183 // d'envoi annonce la suite. Les textes viennent des data-* du serveur.
184+ // Quand il redevient actif, il reprend data-want : le dernier choix
185+ // explicite de l'utilisateur, sinon « coché » si une répartition est
186+ // déjà entamée (le serveur le pose) — commencer à répartir engage à
187+ // aller jusqu'au bout.
184188 function refreshSplit(root, v) {
185189 var box = root.querySelector('[data-split]');
186190 if (!box) return;
@@ -188,6 +192,7 @@
188192 var label = box.querySelector('.split-label');
189193 var rest = (+box.dataset.remaining) - v;
190194 var on = v > 0 && rest > 0;
195+ if (on && cb.disabled) cb.checked = box.dataset.want === '1';
191196 cb.disabled = !on;
192197 if (!on) cb.checked = false;
193198 label.textContent = on ? label.dataset.pat.replace('{}', fmtMinutes(rest)) : label.dataset.idle;
@@ -268,7 +273,10 @@
268273 if (!root) return;
269274 if (e.target.id === 'minutes') refreshMinutes(root);
270275 else if (e.target.type === 'date') refreshDate(root, e.target);
271- else if (e.target.closest('[data-split]')) refreshSubmit(root);
276+ else if (e.target.closest('[data-split]')) {
277+ if (ev === 'change') e.target.closest('[data-split]').dataset.want = e.target.checked ? '1' : '0';
278+ refreshSubmit(root);
279+ }
272280 });
273281 });
274282 })();
@@ -181,6 +181,10 @@
181 // Répartition d'une mesure de minuteur (réservée au JS) : le toggle vit181 // Répartition d'une mesure de minuteur (réservée au JS) : le toggle vit
182 // quand la durée saisie laisse un reste, se décoche sinon, et le bouton182 // quand la durée saisie laisse un reste, se décoche sinon, et le bouton
183 // d'envoi annonce la suite. Les textes viennent des data-* du serveur.183 // d'envoi annonce la suite. Les textes viennent des data-* du serveur.
184+ // Quand il redevient actif, il reprend data-want : le dernier choix
185+ // explicite de l'utilisateur, sinon « coché » si une répartition est
186+ // déjà entamée (le serveur le pose) — commencer à répartir engage à
187+ // aller jusqu'au bout.
184 function refreshSplit(root, v) {188 function refreshSplit(root, v) {
185 var box = root.querySelector('[data-split]');189 var box = root.querySelector('[data-split]');
186 if (!box) return;190 if (!box) return;
@@ -188,6 +192,7 @@
188 var label = box.querySelector('.split-label');192 var label = box.querySelector('.split-label');
189 var rest = (+box.dataset.remaining) - v;193 var rest = (+box.dataset.remaining) - v;
190 var on = v > 0 && rest > 0;194 var on = v > 0 && rest > 0;
195+ if (on && cb.disabled) cb.checked = box.dataset.want === '1';
191 cb.disabled = !on;196 cb.disabled = !on;
192 if (!on) cb.checked = false;197 if (!on) cb.checked = false;
193 label.textContent = on ? label.dataset.pat.replace('{}', fmtMinutes(rest)) : label.dataset.idle;198 label.textContent = on ? label.dataset.pat.replace('{}', fmtMinutes(rest)) : label.dataset.idle;
@@ -268,7 +273,10 @@
268 if (!root) return;273 if (!root) return;
269 if (e.target.id === 'minutes') refreshMinutes(root);274 if (e.target.id === 'minutes') refreshMinutes(root);
270 else if (e.target.type === 'date') refreshDate(root, e.target);275 else if (e.target.type === 'date') refreshDate(root, e.target);
271- else if (e.target.closest('[data-split]')) refreshSubmit(root);276+ else if (e.target.closest('[data-split]')) {
277+ if (ev === 'change') e.target.closest('[data-split]').dataset.want = e.target.checked ? '1' : '0';
278+ refreshSubmit(root);
279+ }
272 });280 });
273 });281 });
274 })();282 })();