fix(ops): Suggérer no overdue-sweep in Tournées + mobile review scroll

Deux retours terrain :
1. Jobs « en retard » (date < jour sélectionné) apparaissaient dans la
   répartition auto. Cause : en vue Tournées, suggestWindow suivait les
   chips de date du POOL, pas le jour choisi dans la bande → buildSuggestion
   balayait les jobs antérieurs sur le jour (branche `else cand = win`).
   Fix : en vue Tournées, `suggestWindow = [routesDay]` (la bande de dates
   fait autorité) → seuls les jobs DUS ce jour-là sont répartis, jamais les
   retards. (Le balayage des retards reste dispo hors Tournées via les chips.)
2. Revue « Répartition suggérée » trop dense sur mobile, lignes rognées
   sans défilement. Fix : dialogue MAXIMISÉ <sm + chaque rangée job
   (.suggest-entry) défile horizontalement (nowrap + overflow-x, enfants
   non rétrécis, sujet borné) → toute la ligne (badges/AM-PM/actions)
   accessible au swipe.

Vérifié live 375px : dialogue plein écran ; 25/25 rangées défilent
(scrollW ~698 > clientW 341) ; plan bâti sur le jour choisi ; 0 nouvelle
erreur console.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
louispaulb 2026-07-04 23:42:25 -04:00
parent 80e09c87ba
commit 8b3c97de9d

View File

@ -1021,8 +1021,8 @@
</div>
<!-- D : revue de la répartition suggérée (surcouche) déplacer/retirer avant d'appliquer -->
<q-dialog v-model="suggestDlg.open">
<q-card style="width:940px;max-width:96vw;max-height:90vh;display:flex;flex-direction:column">
<q-dialog v-model="suggestDlg.open" :maximized="$q.screen.lt.sm">
<q-card :style="$q.screen.lt.sm ? 'width:100%;max-width:100%;height:100%;display:flex;flex-direction:column' : 'width:940px;max-width:96vw;max-height:90vh;display:flex;flex-direction:column'">
<q-card-section class="row items-center q-py-sm" style="border-bottom:1px solid #e2e8f0">
<q-icon name="auto_awesome" color="amber-7" size="22px" class="q-mr-sm" />
<div><div class="text-subtitle1 text-weight-bold">{{ suggestDlg.mode === 'config' ? 'Techniciens disponibles' : 'Répartition suggérée' }}</div><div class="text-caption text-grey-7"><template v-if="suggestDlg.mode === 'config'">Choisis qui participe au dispatch auto{{ suggestDlg.fromSel ? ' de la sélection' : '' }} · {{ suggestSelCount }} sélectionné(s)</template><template v-else>{{ suggestDlg.plan.length }} job(s) · {{ suggestGroups.length }} tech(s){{ suggestDlg.fromSel ? ' · sélection' : '' }} proximité + charge + compétence. Ajuste puis applique.</template></div></div>
@ -4445,6 +4445,9 @@ const suggestSelCount = computed(() => Object.values(suggestDlg.techSel).filter(
// Les chips « en retard » / « Sans date » ne sont pas des jours calendaires les jobs correspondants sont placés AUJOURD'HUI. Non destructif (revue avant d'appliquer).
const suggestDay = ref('')
const suggestWindow = computed(() => {
// Vue Tournées : la fenêtre = EXACTEMENT le jour sélectionné (autorité = la bande de dates), jamais les chips de date du pool.
// « Suggérer » ne balaie PAS les jobs en retard (date < jour choisi) sur ce jour ; seuls les jobs DUS ce jour-là sont répartis.
if (boardView.value === 'routes' && routesDay.value) return [routesDay.value]
const f = assignDateFilter.value || []
if (!f.length) return [suggestDay.value || todayISO()] // aucun chip coché sélecteur de jour du dialogue
const days = new Set(f.filter(iso => iso !== '__overdue__' && iso !== 'Sans date')) // jours concrets cochés (auj./futur)
@ -5730,6 +5733,15 @@ onBeforeRouteLeave(() => { if (dirty.value && !window.confirm(DIRTY_MSG)) return
.suggest-entry { display: flex; align-items: center; gap: 2px; padding: 4px 6px; border-radius: 6px; font-size: 12.5px; }
.suggest-entry:hover { background: #f8fafc; }
.suggest-entry-t { flex: 1; min-width: 0; }
/* Mobile : la rangée d'un job (badges, durée, AM/PM, actions) dépasse souvent l'écran
défilement horizontal de la rangée (au lieu de rogner la fin), sujet borné mais lisible. */
@media (max-width: 600px) {
.suggest-entry { flex-wrap: nowrap; overflow-x: auto; overflow-y: hidden; -webkit-overflow-scrolling: touch; }
.suggest-entry > * { flex-shrink: 0; }
.suggest-entry .suggest-entry-t, .suggest-entry .suggest-clk { flex: 0 0 auto; min-width: 130px; max-width: 62vw; }
.suggest-entry::-webkit-scrollbar { height: 5px; }
.suggest-entry::-webkit-scrollbar-thumb { background: #cbd5e1; border-radius: 3px; }
}
.suggest-tech-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(180px, 1fr)); gap: 6px; }
.suggest-tech-chip { display: flex; align-items: center; gap: 6px; padding: 5px 8px; border: 1px solid #e2e8f0; border-radius: 8px; cursor: pointer; font-size: 12.5px; background: #fff; user-select: none; }
.suggest-tech-chip:hover { background: #f8fafc; }