feat(vue-fiches): état unique lu et écrit dans l'URL, filtres OU/ET, compteurs, tri, regroupement (B11-M1, M2 adaptateurs)
utils/vueFiches.ts porte la logique pure, composables/useVueFiches.ts la branche sur la route ; MiniFiche gagne etiquettes, coords, date, ville, texte. Test : node scripts/test-vue-fiches.mjs (42/42). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SCt6e8wWiYqRXLJAuKqP5i
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
5ce431d055
commit
38df1d5eb5
@@ -1,12 +1,18 @@
|
||||
/**
|
||||
* useFicheAdapter — B3-M2
|
||||
* useFicheAdapter — B3-M2, étendu en B11-M2
|
||||
* Adaptateur léger Org | StructureV2 → MiniFiche, format commun consommé par
|
||||
* FicheMiniCard / FichesPanel. Aucune mutation des types sources.
|
||||
* FicheMiniCard / VueFiches. Aucune mutation des types sources.
|
||||
*
|
||||
* Repli résumé (brief B3, le champ `resume_court` du batch B2b n'existe pas
|
||||
* encore) : première phrase du premier champ non vide parmi une liste de
|
||||
* candidats ordonnée par type, tronquée proprement à ~110 caractères sur un
|
||||
* mot (jamais au milieu).
|
||||
*
|
||||
* B11 : chaque fiche porte ses `etiquettes` par dimension (clé = paramètre
|
||||
* d'URL de la dimension : `fonctions`, `echelle`, `territoire` pour l'Entraide ;
|
||||
* `tags`, `famille`, `pays` pour Réseaux AEP). C'est ce que filtre, trie et
|
||||
* regroupe `utils/vueFiches.ts` ; `chips` et `echelle` restent l'affichage
|
||||
* de la carte-fiche, inchangé.
|
||||
*/
|
||||
import type { Org } from '~/types/org'
|
||||
import type { StructureV2 } from '~/types/structure-v2'
|
||||
@@ -15,12 +21,25 @@ export interface MiniFiche {
|
||||
id: string | number
|
||||
nom: string
|
||||
resume: string
|
||||
/** Étiquettes affichées sur la carte-fiche (3 au plus). */
|
||||
chips: string[]
|
||||
/** Dimension d'où viennent `chips` : les rend cliquables dans la vue fiches. */
|
||||
chipsDimension?: string
|
||||
echelle?: string
|
||||
geoloc: boolean
|
||||
kind: 'org' | 'structure'
|
||||
/** Étiquettes complètes par dimension (valeurs telles qu'écrites dans l'URL). */
|
||||
etiquettes?: Record<string, string[]>
|
||||
coords?: [number, number]
|
||||
/** Date ISO si la source en a une (tri « récent »). */
|
||||
date?: string
|
||||
ville?: string
|
||||
/** Texte cherchable en plus du nom, de la ville et du résumé. */
|
||||
texte?: string
|
||||
}
|
||||
|
||||
export const DOM_TOM = ['Guadeloupe', 'Martinique', 'Guyane', 'La Réunion', 'Mayotte']
|
||||
|
||||
const RESUME_MAX = 110
|
||||
|
||||
/** Première phrase d'un texte (coupe sur ". "/"! "/"? " ou fin de chaîne). */
|
||||
@@ -53,32 +72,68 @@ function orgFonctions(org: Org): string[] {
|
||||
.split(',')
|
||||
.map((f) => f.trim())
|
||||
.filter(Boolean)
|
||||
.slice(0, 3)
|
||||
}
|
||||
|
||||
export function useFicheAdapter() {
|
||||
function toMini(source: Org | StructureV2): MiniFiche {
|
||||
if (isStructure(source)) {
|
||||
return {
|
||||
id: source.id,
|
||||
nom: source.nom,
|
||||
resume: buildResume([source.description_courte, source.description_longue]),
|
||||
chips: (source.hashtags ?? []).slice(0, 3),
|
||||
geoloc: source.latitude != null && source.longitude != null,
|
||||
kind: 'structure',
|
||||
}
|
||||
}
|
||||
function coordsDe(lat?: number | null, lon?: number | null): [number, number] | undefined {
|
||||
return lat != null && lon != null ? [lat, lon] : undefined
|
||||
}
|
||||
|
||||
/** Hashtag sans son « # » : c'est la forme de l'URL (`tags=reemploi-structurel`). */
|
||||
export function sansDiese(tag: string): string {
|
||||
return tag.replace(/^#/, '')
|
||||
}
|
||||
|
||||
/** Familles d'une structure : principale, secondaires, et 6 pour le badge recherche politique. */
|
||||
function famillesDe(s: StructureV2): string[] {
|
||||
const ids = [s.famille_principale, ...(s.familles_secondaires ?? [])]
|
||||
if (s.badges?.f6_recherche_politique) ids.push(6 as any)
|
||||
return [...new Set(ids.map(String))]
|
||||
}
|
||||
|
||||
export function toMini(source: Org | StructureV2): MiniFiche {
|
||||
if (isStructure(source)) {
|
||||
const hashtags = source.hashtags ?? []
|
||||
return {
|
||||
id: source.Id,
|
||||
id: source.id,
|
||||
nom: source.nom,
|
||||
resume: buildResume([source.description_enrichie, source.description]),
|
||||
chips: orgFonctions(source),
|
||||
echelle: source.echelle,
|
||||
resume: buildResume([source.description_courte, source.description_longue]),
|
||||
chips: hashtags.slice(0, 3),
|
||||
chipsDimension: 'tags',
|
||||
geoloc: source.latitude != null && source.longitude != null,
|
||||
kind: 'org',
|
||||
kind: 'structure',
|
||||
etiquettes: {
|
||||
tags: hashtags.map(sansDiese),
|
||||
famille: famillesDe(source),
|
||||
pays: source.pays ? [source.pays] : [],
|
||||
},
|
||||
coords: coordsDe(source.latitude, source.longitude),
|
||||
ville: source.ville,
|
||||
// Portée de la recherche d'avant B11 sur /agences : description courte et hashtags.
|
||||
texte: [source.description_courte, ...hashtags].filter(Boolean).join(' '),
|
||||
}
|
||||
}
|
||||
|
||||
const fonctions = orgFonctions(source)
|
||||
return {
|
||||
id: source.Id,
|
||||
nom: source.nom,
|
||||
resume: buildResume([source.description_enrichie, source.description]),
|
||||
chips: fonctions.slice(0, 3),
|
||||
chipsDimension: 'fonctions',
|
||||
echelle: source.echelle,
|
||||
geoloc: source.latitude != null && source.longitude != null,
|
||||
kind: 'org',
|
||||
etiquettes: {
|
||||
fonctions,
|
||||
echelle: source.echelle ? [source.echelle] : [],
|
||||
territoire: [source.territoire && DOM_TOM.includes(source.territoire) ? source.territoire : 'Métropole'],
|
||||
},
|
||||
coords: coordsDe(source.latitude, source.longitude),
|
||||
date: source.submitted_at,
|
||||
ville: source.localisation_ville,
|
||||
}
|
||||
}
|
||||
|
||||
export function useFicheAdapter() {
|
||||
return { toMini }
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user