feat(ops): unassigned-jobs strip → horizontal carousel (Tournées)

La bande « N à répartir » sous le sélecteur de jour (vue Tournées)
retournait à la ligne sur 2 rangées (max-height 70px, scroll vertical) et
PLAFONNAIT à 16 jobs (« +N de plus » cachait le reste). Passée en
carrousel horizontal 1 rangée (flex nowrap + overflow-x auto, scroll-snap,
barre fine) qui montre TOUS les jobs du jour — compact au-dessus de la
carte, tactile/tablette, aligné avec la bande de dates au-dessus.

Vérifié live (Tournées, 06/07 = 25 à répartir) : 25 chips rendus (fini le
plafond 16), 1 rangée nowrap, scrollWidth 5870 > 1214 = défile bien.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
louispaulb 2026-07-04 23:03:51 -04:00
parent eb4d098bf0
commit b7b1647ece

View File

@ -506,12 +506,12 @@
<q-space /> <q-space />
<q-btn dense unelevated no-caps size="sm" color="primary" icon="auto_awesome" label="Suggérer" @click="openSuggest"><q-tooltip>Répartir automatiquement les jobs {{ routeSectorSel.size ? 'du secteur' : 'de ce jour' }}</q-tooltip></q-btn> <q-btn dense unelevated no-caps size="sm" color="primary" icon="auto_awesome" label="Suggérer" @click="openSuggest"><q-tooltip>Répartir automatiquement les jobs {{ routeSectorSel.size ? 'du secteur' : 'de ce jour' }}</q-tooltip></q-btn>
</div> </div>
<!-- Carrousel horizontal (1 rangée, défile latéralement) : compact au-dessus de la carte, tactile/tablette, montre TOUS les jobs (pas de plafond à 16). -->
<div class="rt-ua-jobs"> <div class="rt-ua-jobs">
<span v-for="j in routeDayUnassignedView.slice(0, 16)" :key="j.name" class="rt-ua-chip" :style="{ borderLeftColor: prioColor(j.priority) }" @click="openJobDetail(j)"> <span v-for="j in routeDayUnassignedView" :key="j.name" class="rt-ua-chip" :style="{ borderLeftColor: prioColor(j.priority) }" @click="openJobDetail(j)">
<q-icon :name="skillSym(j.required_skill || j.service_type)" size="14px" :style="{ color: getTagColor(j.required_skill || j.service_type) }" />{{ j.subject || j.service_type || j.name }}<template v-if="jobCity(j)"> · {{ jobCity(j) }}</template> <q-icon :name="skillSym(j.required_skill || j.service_type)" size="14px" :style="{ color: getTagColor(j.required_skill || j.service_type) }" />{{ j.subject || j.service_type || j.name }}<template v-if="jobCity(j)"> · {{ jobCity(j) }}</template>
<q-tooltip class="bg-grey-9">{{ j.required_skill || '—' }}<template v-if="j.customer_name"> · {{ j.customer_name }}</template><template v-if="j.address"><br>📍 {{ j.address }}</template></q-tooltip> <q-tooltip class="bg-grey-9">{{ j.required_skill || '—' }}<template v-if="j.customer_name"> · {{ j.customer_name }}</template><template v-if="j.address"><br>📍 {{ j.address }}</template></q-tooltip>
</span> </span>
<span v-if="routeDayUnassignedView.length > 16" class="text-caption text-grey-6 q-ml-xs">+{{ routeDayUnassignedView.length - 16 }} de plus</span>
</div> </div>
</div> </div>
<RouteMap ref="routesMapRef" :routes="dayRoutes" :live="showLivePos ? dayLivePositions : []" :pins="routeUnassignedPins" height="62vh" @metrics="m => Object.assign(dayRouteMetrics, m)" @stop-click="onRoutesStopClick" @pin-click="p => openJobDetail(p.job)" /> <RouteMap ref="routesMapRef" :routes="dayRoutes" :live="showLivePos ? dayLivePositions : []" :pins="routeUnassignedPins" height="62vh" @metrics="m => Object.assign(dayRouteMetrics, m)" @stop-click="onRoutesStopClick" @pin-click="p => openJobDetail(p.job)" />
@ -5685,7 +5685,11 @@ onBeforeRouteLeave(() => { if (dirty.value && !window.confirm(DIRTY_MSG)) return
/* Bande « jobs dus non assignés » (vue Tournées) : chips secteur (filtrent liste+carte) + chips job + Suggérer */ /* Bande « jobs dus non assignés » (vue Tournées) : chips secteur (filtrent liste+carte) + chips job + Suggérer */
.rt-unassigned { margin-bottom: 6px; padding: 6px 8px; background: #fff7ed; border: 1px solid #fed7aa; border-radius: 8px; } .rt-unassigned { margin-bottom: 6px; padding: 6px 8px; background: #fff7ed; border: 1px solid #fed7aa; border-radius: 8px; }
.rt-ua-top { display: flex; align-items: center; flex-wrap: wrap; gap: 5px; } .rt-ua-top { display: flex; align-items: center; flex-wrap: wrap; gap: 5px; }
.rt-ua-jobs { display: flex; align-items: center; flex-wrap: wrap; gap: 5px; margin-top: 5px; max-height: 70px; overflow-y: auto; } /* Carrousel : rangée unique qui défile horizontalement (pas de retour à la ligne). */
.rt-ua-jobs { display: flex; align-items: center; flex-wrap: nowrap; gap: 5px; margin-top: 5px; overflow-x: auto; overflow-y: hidden; padding-bottom: 4px; scroll-snap-type: x proximity; -webkit-overflow-scrolling: touch; overscroll-behavior-x: contain; }
.rt-ua-jobs > .rt-ua-chip { flex: 0 0 auto; scroll-snap-align: start; }
.rt-ua-jobs::-webkit-scrollbar { height: 6px; }
.rt-ua-jobs::-webkit-scrollbar-thumb { background: #fdba74; border-radius: 3px; }
.rt-ua-lbl { font-size: 12px; color: #9a3412; display: inline-flex; align-items: center; gap: 3px; } .rt-ua-lbl { font-size: 12px; color: #9a3412; display: inline-flex; align-items: center; gap: 3px; }
.rt-sect { display: inline-flex; align-items: center; gap: 2px; padding: 2px 8px; border: 1px solid #cbd5e1; border-radius: 12px; background: #fff; font-size: 11.5px; font-weight: 600; color: #475569; cursor: pointer; user-select: none; } .rt-sect { display: inline-flex; align-items: center; gap: 2px; padding: 2px 8px; border: 1px solid #cbd5e1; border-radius: 12px; background: #fff; font-size: 11.5px; font-weight: 600; color: #475569; cursor: pointer; user-select: none; }
.rt-sect.on { background: #6366f1; border-color: #6366f1; color: #fff; } .rt-sect.on { background: #6366f1; border-color: #6366f1; color: #fff; }