From 112ed0ea2bcfdb39eda4d9f09aa1f14727d20958 Mon Sep 17 00:00:00 2001 From: louispaulb Date: Sat, 4 Jul 2026 17:31:44 -0400 Subject: [PATCH] =?UTF-8?q?fix(ui):=20fiche=20client=20=E2=80=94=20en-t?= =?UTF-8?q?=C3=AAte=20ne=20d=C3=A9borde=20plus=20en=20mobile=20;=20assista?= =?UTF-8?q?nt=20=3D=20s=C3=A9lecteur=20recherchable?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - CustomerHeader : sous-ligne (nom/legacy/type/langue) était `no-wrap` → débordait sa colonne et chevauchait le bloc statut/liens en mobile. Fix : wrap + colonne `min-width:0` + le bloc statut/ERPNext/Users passe pleine largeur en dessous ( (recherche typeahead) ; jdTeamOptions = nom · compétences (searchable par nom OU compétence), CAPABLES d'abord (possèdent la compétence du job), value = id ; jdAddAssistant résout le tech par id. Vérifié préview mobile (375px) : sous-ligne wrap, 0 débordement (scrollW==clientW) ; build vert, leak 0. Co-Authored-By: Claude Opus 4.8 --- .../components/customer/CustomerHeader.vue | 8 +++---- apps/ops/src/pages/PlanificationPage.vue | 21 +++++++++++++------ 2 files changed, 19 insertions(+), 10 deletions(-) diff --git a/apps/ops/src/components/customer/CustomerHeader.vue b/apps/ops/src/components/customer/CustomerHeader.vue index 98fc959..996ee99 100644 --- a/apps/ops/src/components/customer/CustomerHeader.vue +++ b/apps/ops/src/components/customer/CustomerHeader.vue @@ -5,12 +5,12 @@
-
-
+
+
-
+
{{ customer.name }} · @@ -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) {