+
{{ customer.name }}
· Legacy: {{ customer.legacy_customer_id }}
·
@@ -27,7 +27,7 @@
+
Aucun assistant. Ajoutez un renfort → un bloc hachuré sera réservé dans son horaire (anti double‑booking).
-
+
@@ -2186,16 +2186,25 @@ const geoTimeline = computed(() => {
const curIdx = order[(gf && gf.state) || 'assigned'] ?? 0
return GEO_STEPS.map((s, i) => ({ ...s, at: s.k === 'assigned' ? null : atOf(s.k), done: i <= curIdx, current: i === curIdx }))
})
-// Options du sélecteur d'assistant : tous les techs sauf le lead courant + ceux déjà dans l'équipe.
-const jdTeamOptions = computed(() => { const taken = new Set([jobDetail.techId, ...(jobDetail.team || []).map(a => a.tech_id)]); return (techs.value || []).filter(t => !taken.has(t.id)).map(t => ({ label: t.name, value: { id: t.id, name: t.name } })) })
+// Options du sélecteur d'assistant (TechSelect, recherche par nom OU compétence) : tous les techs sauf le lead + l'équipe déjà là.
+// Label = nom · compétences (searchable) ; CAPABLES d'abord (possèdent la compétence du job) ; value = id (scalaire).
+const jdTeamOptions = computed(() => {
+ const taken = new Set([jobDetail.techId, ...(jobDetail.team || []).map(a => a.tech_id)])
+ const req = jobDetail.required_skill || jobDetail.skill || ''
+ return (techs.value || []).filter(t => !taken.has(t.id))
+ .map(t => { const sk = (t.skills || []); return { capable: !!(req && sk.includes(req)), label: t.name + (sk.length ? ' · ' + sk.slice(0, 4).join(', ') : ''), value: t.id } })
+ .sort((a, b) => (b.capable - a.capable) || a.label.localeCompare(b.label))
+ .map(({ label, value }) => ({ label, value }))
+})
async function jdAddAssistant () {
- const t = jobDetail.teamAdd; if (!t || !jobDetail.name) return
+ const id = jobDetail.teamAdd; if (!id || !jobDetail.name) return
+ const tech = (techs.value || []).find(t => t.id === id); if (!tech) return
try {
- await roster.addAssistant(jobDetail.name, { tech_id: t.id, tech_name: t.name, duration_h: 0, pinned: 1 })
+ await roster.addAssistant(jobDetail.name, { tech_id: tech.id, tech_name: tech.name, duration_h: 0, pinned: 1 })
jobDetail.teamAdd = null
const r = await roster.getJobTeam(jobDetail.name); jobDetail.team = r.assistants || []
await reloadOccupancy()
- $q.notify({ type: 'positive', icon: 'group_add', message: t.name + ' ajouté en renfort · bloc hachuré réservé dans son horaire', timeout: 2600 })
+ $q.notify({ type: 'positive', icon: 'group_add', message: tech.name + ' ajouté en renfort · bloc hachuré réservé dans son horaire', timeout: 2600 })
} catch (e) { err(e) }
}
async function jdRemoveAssistant (techId) {