diff --git a/apps/ops/src/components/shared/RouteMap.vue b/apps/ops/src/components/shared/RouteMap.vue index aa6db71..0e22235 100644 --- a/apps/ops/src/components/shared/RouteMap.vue +++ b/apps/ops/src/components/shared/RouteMap.vue @@ -12,6 +12,7 @@ import { watch, onMounted, onBeforeUnmount } from 'vue' import { MAPBOX_TOKEN } from 'src/config/erpnext' import * as roster from 'src/api/roster' +import { initials } from 'src/composables/useFormatters' // initiales (source unique, ex-locale dé-dupliquée) const props = defineProps({ routes: { type: Array, default: () => [] }, @@ -72,7 +73,7 @@ async function realLine (r) { // géométrie routière réelle d'une tournée (O function clearMarkers () { for (const m of stopMk) m.mk.remove(); for (const m of homeMk) m.mk.remove(); stopMk = []; homeMk = [] } // ── Positions GPS LIVE des techs (marqueur distinct des arrêts : pastille ronde à initiales + halo pulsé) ── -function initials (n) { return String(n || '?').trim().split(/\s+/).map(w => w[0]).filter(Boolean).slice(0, 2).join('').toUpperCase() } +// initials → composables/useFormatters (source unique) function clearLive () { for (const m of liveMk) m.mk.remove(); liveMk = [] } function clearPins () { for (const m of pinMk) m.mk.remove(); pinMk = [] } function renderLive (list) { diff --git a/apps/ops/src/composables/useFormatters.js b/apps/ops/src/composables/useFormatters.js index 516c4fd..7b16788 100644 --- a/apps/ops/src/composables/useFormatters.js +++ b/apps/ops/src/composables/useFormatters.js @@ -197,3 +197,16 @@ export function formatDateTimeShort (d) { if (isNaN(dt.getTime())) return '' return dt.toLocaleString('fr-CA', { dateStyle: 'short', timeStyle: 'short' }) } + +// ── Formateurs canoniques dé-dupliqués (audit T1 2026-07-04) — remplacent des copies locales IDENTIQUES ── +// Initiales = 2 PREMIERS mots (« Jean Bourdon » → « JB »), '?' si vide. ⚠ DIFFÉRENT de staffInitials (1er + DERNIER mot). +// Version « 2 premiers » = celle de la grille Planif + carte des tournées. +export function initials (name) { + return String(name || '').split(/\s+/).filter(Boolean).map(w => w[0]).slice(0, 2).join('').toUpperCase() || '?' +} +// Heure d'horloge : chaîne « 8:30 » → « 8h30 ». ⚠ DIFFÉRENT de fmtTimeHHhMM (qui prend un Date). +export function fmtClock (t) { if (!t) return ''; const [h, m] = String(t).split(':'); return `${h}h${m}` } +// Date « longue » : « 3 juillet 2026 » ; '' si absent. +export function formatDateLong (d) { + return d ? new Date(d).toLocaleDateString('fr-CA', { day: 'numeric', month: 'long', year: 'numeric' }) : '' +} diff --git a/apps/ops/src/modules/tech/components/JobCard.vue b/apps/ops/src/modules/tech/components/JobCard.vue index d052ca7..3dc99c3 100644 --- a/apps/ops/src/modules/tech/components/JobCard.vue +++ b/apps/ops/src/modules/tech/components/JobCard.vue @@ -36,6 +36,7 @@