feat(fiches): / et /agences ouvrent la fiche dans le panneau ; FicheReseauContenu extrait, FicheModal et FicheModalV2 retirées ; Voir sur la carte (B12-M4)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AGNSEFZdoKvbdsGyfSnu5S
This commit is contained in:
Jules Neny
2026-10-03 08:57:16 +02:00
co-authored by Claude Opus 5.5
parent 1bb55538a4
commit 5e0027b861
8 changed files with 427 additions and 598 deletions
+47 -29
View File
@@ -17,7 +17,7 @@
libelle-recherche="Rechercher une structure…"
:pending="pending"
:selected-id="selectedId"
@select="(f) => onSelectOrg(Number(f.id))"
:lien-page="(f) => `/fiche/${f.id}`"
>
<template #carte>
<!-- Métropole -->
@@ -27,6 +27,7 @@
:orgs="metropoleOrgs"
:selectedId="selectedId"
:survolId="survolNum"
:centrage="centrageMetropole"
@select-org="onSelectOrg"
@survol="(id) => (vf.survolId = id)"
/>
@@ -55,13 +56,22 @@
<ChatbotPlaceholder @highlightOrgs="onHighlightOrgs" />
</div>
</template>
</VueFiches>
<!-- ═══════════════════════════════════════ MODAL FICHE (ordinateur) -->
<FicheModal
v-model="ficheModalOpen"
:orgId="ficheModalId"
/>
<!-- Détail (B12) : la fiche complète vient de la liste (même enregistrement NocoDB que /api/fiche/:id) -->
<template #detail="{ fiche, filtrer }">
<template v-if="orgParId.get(String(fiche.id))">
<FicheDetail
:key="fiche.id"
:org="orgParId.get(String(fiche.id))!"
compact
etiquettes-cliquables
@etiquette="(t) => filtrer('fonctions', t)"
/>
<CommentSection :key="`c-${fiche.id}`" :org-id="Number(fiche.id)" :refresh="commentaires" />
<CommentForm :key="`f-${fiche.id}`" :org-id="Number(fiche.id)" @submitted="commentaires++" />
</template>
</template>
</VueFiches>
<!-- ═══════════════════════════════════════ BOUTON CHATBOT FLOTTANT (mobile) -->
<button
@@ -171,41 +181,49 @@ const metropoleOrgs = computed<Org[]>(() => orgsFiltres.value.filter((o) => !o.t
const outremerOrgs = computed<Org[]>(() => orgsFiltres.value.filter((o) => o.territoire && DOM_TOM.includes(o.territoire)))
const survolNum = computed(() => (vf.survolId == null ? null : Number(vf.survolId)))
// ── Sélection ─────────────────────────────────────────────────────────────
const selectedId = ref<number | null>(null)
const ficheModalOpen = ref(false)
const ficheModalId = ref<number | null>(null)
// ── Sélection (B12) : la fiche ouverte vit dans l'URL (`fiche=`), détail en panneau ──
const orgParId = computed(() => new Map(orgs.value.map((o) => [String(o.Id), o])))
// Épingle et carte-fiche allumées : la fiche ouverte, ou 5 s celle que le chatbot désigne
const surligneId = ref<number | null>(null)
const selectedId = computed<number | null>(() => surligneId.value ?? (vf.ficheId != null ? Number(vf.ficheId) : null))
const commentaires = ref(0)
// Ordinateur : FicheModal ; téléphone et tablette : page /fiche/:id (comportement d'avant B11)
function onSelectOrg(id: number) {
if (typeof window !== 'undefined' && window.innerWidth >= 1024) {
selectedId.value = selectedId.value === id ? null : id
ficheModalId.value = id
ficheModalOpen.value = true
return
}
selectedId.value = id
try {
// Le bouton retour de /fiche/:id revient à la même vue, filtres compris
sessionStorage.setItem('nav_back_filters', new URLSearchParams(route.query as Record<string, string>).toString())
} catch {}
router.push(`/fiche/${id}`)
vf.ouvrirFiche(id)
}
// « Voir sur la carte » : la bonne sous-vue (Métropole ou Outre-mer), puis l'épingle
const centrageMetropole = ref<{ id: number; n: number } | null>(null)
watch(() => vf.centrage, (c) => {
if (!c) return
const o = orgParId.value.get(c.id)
const dom = !!o?.territoire && DOM_TOM.includes(o.territoire)
if (vf.etat.sousVue !== (dom ? 'outremer' : 'metropole')) vf.setSousVue(dom ? 'outremer' : 'metropole')
if (!dom) centrageMetropole.value = { id: Number(c.id), n: c.n }
})
// Le bouton retour de /fiche/:id (« Ouvrir la page de la fiche ») revient à la même vue
if (import.meta.client) {
watch(() => route.fullPath, () => {
try {
sessionStorage.setItem('nav_back_filters', new URLSearchParams(route.query as Record<string, string>).toString())
} catch {}
}, { immediate: true })
}
// Surlignage temporaire (5 s) suite à une réponse chatbot
let highlightTimer: ReturnType<typeof setTimeout> | null = null
const prevSelectedId = ref<number | null>(null)
function onHighlightOrgs(ids: (number | string)[]) {
if (!ids.length) return
const firstId = typeof ids[0] === 'string' ? parseInt(ids[0], 10) : ids[0]
if (isNaN(firstId)) return
prevSelectedId.value = selectedId.value
selectedId.value = firstId
surligneId.value = firstId
if (highlightTimer) clearTimeout(highlightTimer)
highlightTimer = setTimeout(() => {
selectedId.value = prevSelectedId.value
prevSelectedId.value = null
surligneId.value = null
highlightTimer = null
}, 5000)
}