diff --git a/apps/ops/src/pages/PlanificationPage.vue b/apps/ops/src/pages/PlanificationPage.vue
index 9f24492..55283fa 100644
--- a/apps/ops/src/pages/PlanificationPage.vue
+++ b/apps/ops/src/pages/PlanificationPage.vue
@@ -1620,19 +1620,29 @@
Choisir le domicile (point de départ) sur la carte
Fixer le départ à la position GPS live actuelle du tech (Traccar)
-
+
-
+
Appareil GPS :
-
onPickDevice(dayEditor.tech, v)">
-
-
-
- {{ scope.opt.label }}{{ scope.opt.uniqueId }}
-
-
- Aucun appareil Traccar
-
+
+
+
+ {{ jdDevice.label }}
+ {{ jdDevice.online ? 'En ligne' : 'Hors ligne' }}{{ jdDevice.uniqueId ? ' · ' + jdDevice.uniqueId : '' }}
+
+ Relier à un autre véhicule
+ {{ o.label }}{{ o.uniqueId }}{{ devicesLoading ? 'Chargement…' : 'Aucun appareil' }}
+
+ Dissocier l'appareil GPS
+
+
+
+ non associé
+ Appareil probable (nom similaire au tech) — cliquer pour relier
+ Relier un véhicule Traccar
+ {{ o.label }}{{ o.uniqueId }}{{ devicesLoading ? 'Chargement…' : 'Aucun appareil' }}
+
+
Aucun job ce jour.
@@ -2676,14 +2686,37 @@ async function loadTraccarDevices () {
devicesLoading.value = true
try { const d = await roster.listTraccarDevices(); traccarDevices.value = Array.isArray(d) ? d : (d && d.devices) || []; _devicesLoaded = true } catch (e) { /* non bloquant */ } finally { devicesLoading.value = false }
}
-function filterDevices (val, update) { update(() => { deviceFilter.value = (val || '').toLowerCase() }) }
const deviceOptions = computed(() => {
- const q = deviceFilter.value
+ const q = deviceFilter.value.toLowerCase().trim()
return traccarDevices.value
.map(d => ({ label: d.name || d.uniqueId || ('#' + d.id), value: String(d.id), uniqueId: d.uniqueId || '', online: d.status === 'online' }))
.filter(o => !q || o.label.toLowerCase().includes(q) || (o.uniqueId && o.uniqueId.toLowerCase().includes(q)))
.sort((a, b) => (b.online ? 1 : 0) - (a.online ? 1 : 0) || a.label.localeCompare(b.label))
})
+// Appareil ACTUELLEMENT relié au tech de l'éditeur (résout l'id → nom réel, ex. « 24 - Stephane M »). missing = id enregistré mais introuvable dans Traccar.
+const jdDevice = computed(() => {
+ const id = dayEditor.tech && dayEditor.tech.traccar_device_id
+ if (!id) return null
+ const d = traccarDevices.value.find(x => String(x.id) === String(id))
+ if (d) return { value: String(d.id), label: d.name || d.uniqueId || ('#' + d.id), online: d.status === 'online', uniqueId: d.uniqueId || '' }
+ return { value: String(id), label: devicesLoading.value ? ('#' + id + '…') : ('#' + id + ' — introuvable'), online: false, missing: !devicesLoading.value }
+})
+// Suggestion AUTO quand non relié : appareil dont le nom/uniqueId contient un mot du nom du tech (≥3 lettres). Facilite « relier ».
+const _normDev = (s) => String(s || '').toLowerCase().normalize('NFD').replace(/\p{Diacritic}/gu, '')
+const jdDeviceSuggest = computed(() => {
+ if (jdDevice.value) return null
+ const t = dayEditor.tech; if (!t) return null
+ const parts = _normDev(t.name).split(/[^a-z]+/).filter(p => p.length >= 3)
+ if (!parts.length) return null
+ let best = null
+ for (const d of traccarDevices.value) {
+ const dn = _normDev(d.name) + ' ' + _normDev(d.uniqueId)
+ const hit = parts.filter(p => dn.includes(p)).length
+ if (hit > (best ? best.hit : 0)) best = { hit, d }
+ }
+ if (best && best.hit >= 1) { const d = best.d; return { value: String(d.id), label: d.name || ('#' + d.id), online: d.status === 'online' } }
+ return null
+})
async function onPickDevice (tech, deviceId) {
if (!tech || !tech.id) return
const prev = tech.traccar_device_id || ''; const v = deviceId == null ? '' : String(deviceId)