85 lines
2.5 KiB
TypeScript
85 lines
2.5 KiB
TypeScript
/**
|
|
* useFicheAdapter — B3-M2
|
|
* Adaptateur léger Org | StructureV2 → MiniFiche, format commun consommé par
|
|
* FicheMiniCard / FichesPanel. 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).
|
|
*/
|
|
import type { Org } from '~/types/org'
|
|
import type { StructureV2 } from '~/types/structure-v2'
|
|
|
|
export interface MiniFiche {
|
|
id: string | number
|
|
nom: string
|
|
resume: string
|
|
chips: string[]
|
|
echelle?: string
|
|
geoloc: boolean
|
|
kind: 'org' | 'structure'
|
|
}
|
|
|
|
const RESUME_MAX = 110
|
|
|
|
/** Première phrase d'un texte (coupe sur ". "/"! "/"? " ou fin de chaîne). */
|
|
function firstSentence(text: string): string {
|
|
const match = text.match(/^.*?[.!?](?=\s|$)/)
|
|
return (match ? match[0] : text).trim()
|
|
}
|
|
|
|
/** Tronque proprement sur un mot, jamais au milieu. */
|
|
function truncateOnWord(text: string, max = RESUME_MAX): string {
|
|
if (text.length <= max) return text
|
|
const slice = text.slice(0, max)
|
|
const lastSpace = slice.lastIndexOf(' ')
|
|
const cut = lastSpace > max * 0.4 ? slice.slice(0, lastSpace) : slice
|
|
return `${cut.trimEnd()}…`
|
|
}
|
|
|
|
function buildResume(candidates: Array<string | null | undefined>): string {
|
|
const source = candidates.find((c) => !!c && c.trim().length > 0)
|
|
if (!source) return ''
|
|
return truncateOnWord(firstSentence(source.trim()))
|
|
}
|
|
|
|
function isStructure(source: Org | StructureV2): source is StructureV2 {
|
|
return 'famille_principale' in source
|
|
}
|
|
|
|
function orgFonctions(org: Org): string[] {
|
|
return (org.tags_fonction ?? '')
|
|
.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',
|
|
}
|
|
}
|
|
|
|
return {
|
|
id: source.Id,
|
|
nom: source.nom,
|
|
resume: buildResume([source.description_enrichie, source.description]),
|
|
chips: orgFonctions(source),
|
|
echelle: source.echelle,
|
|
geoloc: source.latitude != null && source.longitude != null,
|
|
kind: 'org',
|
|
}
|
|
}
|
|
|
|
return { toMini }
|
|
}
|