fix(vue-fiches): passe /autofeedback — Échap dans un champ garde le brouillon, titre d'onglet = fiche ouverte, contrastes et mini-carte décorative de FicheDetail

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 09:20:16 +02:00
co-authored by Claude Opus 5.5
parent a6dcf48c71
commit a9befb25ab
4 changed files with 28 additions and 18 deletions
+14 -13
View File
@@ -35,14 +35,14 @@
</div> </div>
<!-- Méta : échelle + ville --> <!-- Méta : échelle + ville -->
<div class="flex flex-wrap items-center gap-2 text-sm mb-3" style="color: var(--nav-text-muted);"> <div class="flex flex-wrap items-center gap-2 text-sm mb-3" style="color: color-mix(in srgb, var(--nav-text) 78%, transparent);">
<span <span
v-if="org.echelle" v-if="org.echelle"
class="px-2 py-0.5 rounded-full text-xs font-medium" class="px-2 py-0.5 rounded-full text-xs font-medium"
style="background: var(--nav-primary); color: var(--nav-text-on-primary);" style="background: var(--nav-primary-solid); color: var(--nav-text-on-primary);"
>{{ org.echelle }}</span> >{{ org.echelle }}</span>
<span v-if="org.territoire && org.territoire !== 'Métropole'" class="text-xs" style="color: var(--nav-text-muted);">{{ org.territoire }}</span> <span v-if="org.territoire && org.territoire !== 'Métropole'" class="text-xs" style="color: color-mix(in srgb, var(--nav-text) 78%, transparent);">{{ org.territoire }}</span>
<span v-if="org.localisation_ville" style="color: var(--nav-text-muted);">{{ org.localisation_ville }}</span> <span v-if="org.localisation_ville" style="color: color-mix(in srgb, var(--nav-text) 78%, transparent);">{{ org.localisation_ville }}</span>
</div> </div>
<!-- Tags fonction --> <!-- Tags fonction -->
@@ -73,7 +73,7 @@
<div class="flex-1 min-w-0"> <div class="flex-1 min-w-0">
<!-- Description soumise par le contributeur --> <!-- Description soumise par le contributeur -->
<div v-if="descriptionUser" class="mb-4"> <div v-if="descriptionUser" class="mb-4">
<p class="text-sm leading-relaxed" style="color: var(--nav-text-muted);">Description communauté</p> <p class="text-sm leading-relaxed" style="color: color-mix(in srgb, var(--nav-text) 78%, transparent);">Description communauté</p>
<p class="mt-1 leading-relaxed" style="color: var(--nav-text);">{{ descriptionUser }}</p> <p class="mt-1 leading-relaxed" style="color: var(--nav-text);">{{ descriptionUser }}</p>
</div> </div>
@@ -82,7 +82,7 @@
<!-- Description enrichie IA --> <!-- Description enrichie IA -->
<div v-if="descriptionEnrichie" class="mb-4"> <div v-if="descriptionEnrichie" class="mb-4">
<p class="text-sm leading-relaxed flex items-center gap-1.5" style="color: var(--nav-text-muted);"> <p class="text-sm leading-relaxed flex items-center gap-1.5" style="color: color-mix(in srgb, var(--nav-text) 78%, transparent);">
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true"> <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true">
<path d="M12 2L2 7l10 5 10-5-10-5z"/> <path d="M12 2L2 7l10 5 10-5-10-5z"/>
<path d="M2 17l10 5 10-5"/> <path d="M2 17l10 5 10-5"/>
@@ -100,7 +100,7 @@
<!-- Points clés --> <!-- Points clés -->
<div v-if="pointsCles.length" class="mt-4"> <div v-if="pointsCles.length" class="mt-4">
<p class="text-sm font-medium mb-2" style="color: var(--nav-text-muted);">Points clés</p> <p class="text-sm font-medium mb-2" style="color: color-mix(in srgb, var(--nav-text) 78%, transparent);">Points clés</p>
<ul class="space-y-1"> <ul class="space-y-1">
<li <li
v-for="(point, i) in pointsCles" v-for="(point, i) in pointsCles"
@@ -123,11 +123,11 @@
style="height: 180px; border: 1px solid var(--nav-bg-alt);" style="height: 180px; border: 1px solid var(--nav-bg-alt);"
> >
<ClientOnly> <ClientOnly>
<div ref="mapContainer" class="w-full h-full"></div> <div ref="mapContainer" class="w-full h-full" role="img" :aria-label="`Carte : ${org.nom}${org.localisation_ville ? `, ${org.localisation_ville}` : ''}`"></div>
<template #fallback> <template #fallback>
<div <div
class="w-full h-full flex items-center justify-center text-xs" class="w-full h-full flex items-center justify-center text-xs"
style="background: var(--nav-bg-alt); color: var(--nav-text-muted);" style="background: var(--nav-bg-alt); color: color-mix(in srgb, var(--nav-text) 78%, transparent);"
>Carte…</div> >Carte…</div>
</template> </template>
</ClientOnly> </ClientOnly>
@@ -153,7 +153,7 @@
<Transition name="report-form"> <Transition name="report-form">
<div v-if="reportOpen" class="report-panel"> <div v-if="reportOpen" class="report-panel">
<p class="text-xs mb-3" style="color: var(--nav-text-muted);"> <p class="text-xs mb-3" style="color: color-mix(in srgb, var(--nav-text) 78%, transparent);">
Tes suggestions seront transmises à l'équipe AEP par email. Tes suggestions seront transmises à l'équipe AEP par email.
</p> </p>
<div class="flex flex-col gap-3"> <div class="flex flex-col gap-3">
@@ -184,7 +184,7 @@
</div> </div>
<p v-if="reportError" class="text-xs" style="color: #e53e3e;">{{ reportError }}</p> <p v-if="reportError" class="text-xs" style="color: #e53e3e;">{{ reportError }}</p>
<p v-if="reportSuccess" class="text-xs" style="color: #38a169;">{{ reportSuccess }}</p> <p v-if="reportSuccess" class="text-xs" style="color: #38a169;">{{ reportSuccess }}</p>
<p class="text-xs" style="color: var(--nav-text-muted); opacity: 0.6;"> <p class="text-xs" style="color: color-mix(in srgb, var(--nav-text) 78%, transparent); opacity: 0.6;">
{{ reportMessage.length }}/500 {{ reportMessage.length }}/500
</p> </p>
</div> </div>
@@ -331,7 +331,8 @@ async function initMap(el: HTMLElement) {
iconAnchor: [7, 7], iconAnchor: [7, 7],
}) })
L.marker([lat, lng], { icon }).addTo(map) // Épingle décorative : ni clic ni clavier (la mini-carte est une image, nommée par son conteneur)
L.marker([lat, lng], { icon, interactive: false, keyboard: false }).addTo(map)
} }
</script> </script>
@@ -342,7 +343,7 @@ async function initMap(el: HTMLElement) {
align-items: center; align-items: center;
gap: 6px; gap: 6px;
font-size: 0.78rem; font-size: 0.78rem;
color: var(--nav-text-muted); color: color-mix(in srgb, var(--nav-text) 78%, transparent);
background: none; background: none;
border: none; border: none;
cursor: pointer; cursor: pointer;
+10 -3
View File
@@ -459,11 +459,18 @@ watch(() => props.vf.ficheId, (id, avant) => {
}) })
function clavierDetail(e: KeyboardEvent) { function clavierDetail(e: KeyboardEvent) {
if (e.key === 'Escape') { e.preventDefault(); props.vf.fermerFiche(); return } const t = e.target as HTMLElement
const champ = !!t.closest('input, textarea, select, [contenteditable="true"]')
if (e.key === 'Escape') {
e.preventDefault()
// Échap dans un champ (commentaire, signalement) le quitte d'abord : un brouillon ne se perd pas d'une touche
if (champ) { detail.value?.focus({ preventScroll: true }); return }
props.vf.fermerFiche()
return
}
if (e.key !== 'ArrowLeft' && e.key !== 'ArrowRight') return if (e.key !== 'ArrowLeft' && e.key !== 'ArrowRight') return
if (e.altKey || e.ctrlKey || e.metaKey || e.shiftKey) return if (e.altKey || e.ctrlKey || e.metaKey || e.shiftKey) return
const t = e.target as HTMLElement if (champ) return
if (t.closest('input, textarea, select, [contenteditable="true"]')) return
e.preventDefault() e.preventDefault()
if (e.key === 'ArrowLeft') props.vf.fichePrecedente() if (e.key === 'ArrowLeft') props.vf.fichePrecedente()
else props.vf.ficheSuivante() else props.vf.ficheSuivante()
+2 -1
View File
@@ -250,7 +250,8 @@ onMounted(() => {
} catch {} } catch {}
}) })
useHead({ title: "AEP - Réseaux de bifurcation architecturale" }) // Une fiche ouverte donne son nom à l'onglet (les données arrivent après le rendu serveur ici)
useHead({ title: computed(() => (vf.ficheOuverte ? `${vf.ficheOuverte.nom} — AEP` : 'AEP - Réseaux de bifurcation architecturale')) })
</script> </script>
<style scoped> <style scoped>
+2 -1
View File
@@ -249,7 +249,8 @@ onMounted(() => {
} catch {} } catch {}
}) })
useHead({ title: 'AEP — Cartographie de l\'écologie politique architecturale' }) // Un lien partagé avec fiche= porte le nom de la fiche (onglet, aperçu de lien), dès le rendu serveur
useHead({ title: computed(() => (vf.ficheOuverte ? `${vf.ficheOuverte.nom} — AEP` : 'AEP — Cartographie de l\'écologie politique architecturale')) })
</script> </script>
<style scoped> <style scoped>