Un bloc « Loyer » s'intercale entre les totaux et la chronologie. Il porte le loyer en vigueur, sa date d'effet, le prix au mètre carré et la dernière révision, puis deux graphiques. Le premier suit le loyer mois par mois, charges empilées et prix au m² sur son propre axe. Un mois vacant y reste un blanc, jamais une barre au sol ; un mois de transition prend une couleur distincte et l'infobulle dit lequel des deux on regarde. Ce que la courbe ne peut pas porter — une régularisation à cheval sur plusieurs mois sans en couvrir aucun — est listé dessous plutôt que rattaché de force à un mois. Le second situe le lot dans le parc, surface en abscisse. Il corrige une lecture que les médianes rendent fausse : le loyer au m² décroît fortement avec la taille, si bien qu'un studio de 21 m² affiche +60 % au-dessus de la médiane de son immeuble sans rien devoir à sa gestion. Ce qui se lit n'est donc pas la hauteur d'un point, mais sa position parmi les lots de surface voisine — et une phrase le dit au-dessus du graphe. Les autres lots s'ouvrent d'un clic ; les locaux commerciaux gardent leur place mais changent de forme, leur prix ne suivant pas la même logique. Sans surface saisie, chaque case reste vide et renvoie vers Logements : la fiche montre le trou au lieu de le combler. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
69 lines
2.0 KiB
JavaScript
69 lines
2.0 KiB
JavaScript
/**
|
|
* Mises en forme partagées des montants et des dates.
|
|
*
|
|
* Chaque composant redéfinissait son `formatAmount`, avec des variantes
|
|
* d'arrondi et de séparateur : deux écrans pouvaient écrire différemment le
|
|
* même euro. Les nouveaux affichages passent par ici.
|
|
*/
|
|
|
|
const EUROS = new Intl.NumberFormat('fr-FR', {
|
|
style: 'currency',
|
|
currency: 'EUR',
|
|
maximumFractionDigits: 0
|
|
})
|
|
|
|
const EUROS_PRECIS = new Intl.NumberFormat('fr-FR', {
|
|
style: 'currency',
|
|
currency: 'EUR'
|
|
})
|
|
|
|
const MOIS_COURTS = [
|
|
'janv.', 'févr.', 'mars', 'avr.', 'mai', 'juin',
|
|
'juil.', 'août', 'sept.', 'oct.', 'nov.', 'déc.'
|
|
]
|
|
|
|
/** Montant arrondi à l'euro, pour les chiffres qu'on lit d'un coup d'œil. */
|
|
export function formatMontant(valeur) {
|
|
if (valeur == null) return '—'
|
|
return EUROS.format(valeur)
|
|
}
|
|
|
|
/** Montant au centime, pour les écarts et les rapprochements. */
|
|
export function formatMontantPrecis(valeur) {
|
|
if (valeur == null) return '—'
|
|
return EUROS_PRECIS.format(valeur)
|
|
}
|
|
|
|
/** Montant signé, le « + » explicite pour ce qui revient au propriétaire. */
|
|
export function formatEcart(valeur) {
|
|
if (valeur == null) return '—'
|
|
const signe = valeur > 0 ? '+' : ''
|
|
return `${signe}${EUROS_PRECIS.format(valeur)}`
|
|
}
|
|
|
|
/** "2026-06-22" → "22/06/2026" */
|
|
export function formatDate(chaine) {
|
|
if (!chaine) return '—'
|
|
const [annee, mois, jour] = chaine.split('-')
|
|
return `${jour}/${mois}/${annee}`
|
|
}
|
|
|
|
/** "2026-06" → "juin" (l'année est portée par le titre de la période) */
|
|
export function formatMois(chaine) {
|
|
if (!chaine) return ''
|
|
const [, mois] = chaine.split('-')
|
|
return MOIS_COURTS[parseInt(mois, 10) - 1] ?? chaine
|
|
}
|
|
|
|
/**
|
|
* "2026-06" → "juin 26", pour les séries qui traversent plusieurs années.
|
|
*
|
|
* `formatMois` suffit quand un titre porte déjà l'année ; sur trois ans de
|
|
* comptes rendus, deux mois de juin se confondraient.
|
|
*/
|
|
export function formatMoisAnnee(chaine) {
|
|
if (!chaine) return ''
|
|
const [annee, mois] = chaine.split('-')
|
|
return `${MOIS_COURTS[parseInt(mois, 10) - 1] ?? mois} ${annee.slice(2)}`
|
|
}
|