diff --git a/frontend/src/components/PersonCard.jsx b/frontend/src/components/PersonCard.jsx
index 6212942..8b44272 100644
--- a/frontend/src/components/PersonCard.jsx
+++ b/frontend/src/components/PersonCard.jsx
@@ -8,6 +8,7 @@ import ImmichFacePanel from './ImmichFacePanel.jsx'
import './PersonCard.css'
import PzBusy from './PzBusy.jsx'
import { t, dateLocale } from '../i18n/index.js'
+import { formatPlace } from '../utils/place.js'
const TYPE_LABELS = {
photo: t('Photo'),
@@ -305,13 +306,13 @@ const PersonCard = ({
}
// Rendu d'un champ éditable ou lecture seule
- const renderEditableValue = ({ field, value, placeholder, type = 'text' }) => {
+ const renderEditableValue = ({ field, value, placeholder, type = 'text', format = null }) => {
if (!editMode) {
const isEmptyValue = value === null || value === undefined || value === ''
// Une date complète se lit « 22 octobre 1928 », pas « 1928-10-22 »
const shown = type === 'date' && /^\d{4}-\d{2}-\d{2}/.test(String(value))
? new Date(`${String(value).slice(0, 10)}T00:00:00Z`).toLocaleDateString(dateLocale, { day: 'numeric', month: 'long', year: 'numeric', timeZone: 'UTC' })
- : value
+ : format ? format(value) : value
return {isEmptyValue ? '-' : shown}
}
return (
@@ -336,7 +337,7 @@ const PersonCard = ({
const born = person.birthYear || (person.birthDate ? String(person.birthDate).slice(0, 4) : '')
const died = person.deathYear || (person.deathDate ? String(person.deathDate).slice(0, 4) : '')
const parts = []
- if (born) parts.push(`${born}${person.birthPlace ? `, ${person.birthPlace}` : ''}`)
+ if (born) parts.push(`${born}${person.birthPlace ? `, ${formatPlace(person.birthPlace)}` : ''}`)
if (!person.isAlive && died) parts.push(died)
return parts.join(' – ')
})()
@@ -593,7 +594,7 @@ const PersonCard = ({
{(person.birthPlace || editMode) && (
{t('Lieu')}
- {renderEditableValue({ field: 'birthPlace', value: person.birthPlace || '', placeholder: t('Lieu de naissance') })}
+ {renderEditableValue({ field: 'birthPlace', value: person.birthPlace || '', placeholder: t('Lieu de naissance'), format: formatPlace })}
)}
{(person.birthName || editMode) && (
diff --git a/frontend/src/utils/place.js b/frontend/src/utils/place.js
new file mode 100644
index 0000000..ac0a598
--- /dev/null
+++ b/frontend/src/utils/place.js
@@ -0,0 +1,14 @@
+// Lieu lisible à l'affichage : un arbre importé de Heredis avant le nettoyage à l'import garde ses lieux bruts
+// (« La Roche-sur-Yon,85191,Vendée,Pays de la Loire,France, »). On montre les noms seulement, sans case vide,
+// sans code INSEE ni postal, sans répétition. La valeur enregistrée ne change pas (même règle qu'à l'import).
+export function formatPlace(value) {
+ if (!value) return value
+ const parts = []
+ for (const raw of String(value).split(',')) {
+ const part = raw.trim()
+ if (!part || /^(\d{4,5}|2[AB]\d{3})$/i.test(part)) continue
+ if (parts.length && parts[parts.length - 1].toLowerCase() === part.toLowerCase()) continue
+ parts.push(part)
+ }
+ return parts.join(', ')
+}
diff --git a/frontend/src/utils/place.test.js b/frontend/src/utils/place.test.js
new file mode 100644
index 0000000..f2f1c61
--- /dev/null
+++ b/frontend/src/utils/place.test.js
@@ -0,0 +1,17 @@
+import { describe, expect, it } from 'vitest'
+import { formatPlace } from './place.js'
+
+describe('formatPlace', () => {
+ it('lieu brut de Heredis', () => {
+ expect(formatPlace('La Roche-sur-Yon,85191,Vendée,Pays de la Loire,France,')).toBe('La Roche-sur-Yon, Vendée, Pays de la Loire, France')
+ expect(formatPlace('Ménéac,56129,,,,')).toBe('Ménéac')
+ })
+ it('lieu déjà propre inchangé', () => {
+ expect(formatPlace('Limoges')).toBe('Limoges')
+ expect(formatPlace('Lille, Nord, France')).toBe('Lille, Nord, France')
+ })
+ it('vide reste vide', () => {
+ expect(formatPlace(null)).toBe(null)
+ expect(formatPlace('')).toBe('')
+ })
+})