Merge pull request #102 from IliesAllali/fix/vie-sans-date

Onglet Vie : une union sans date à sa place logique
This commit is contained in:
IliesAllali authored and GitHub committed 2026-10-19 01:24:34 +02:00
commit 6fd76ff7a4
3 files changed
+41 -7

No files matched your search

+2
View File
@@ -436,6 +436,8 @@ const PersonCard = ({
unions: personUnions.map((union) => ({
...union,
partner: persons.find((p) => p.id === (union.partner1Id === person.id ? union.partner2Id : union.partner1Id)) || null,
// Enfants du couple : placent dans la frise une union dont la date manque
childIds: filiations.filter((f) => f.unionId && String(f.unionId) === String(union.id)).map((f) => f.childId),
})),
}
+15 -7
View File
@@ -107,7 +107,7 @@ const siblingWord = (p) => {
/**
* @param person personne de la fiche (birthDate, deathDate, birthPlace, birthYear, deathYear)
* @param family { parents, children, siblings, unions: [{ id, partner, startYear, endYear, unionType }] }
* @param family { parents, children, siblings, unions: [{ id, partner, startYear, endYear, unionType, childIds }] }
* @param life réponse de GET /trees/:id/persons/:personId/life, ou null tant qu'elle n'est pas là
* @returns entrées triées { key, year, kind: 'mile' | 'minor' | 'fam', title, meta, note, sources, mediaIds,
* event (moment modifiable), milestone ({ type, unionId } d'une naissance, d'un mariage, d'un décès) }
@@ -144,10 +144,18 @@ export function buildLifeTimeline(person, family = {}, life = null) {
const partnerName = union.partner ? fullName(union.partner) : t('Conjoint inconnu')
const place = formatPlace(detail.startPlace || '')
const extra = sourcesOf('marriage', union.id)
// Union sans date : à sa place logique, juste avant le premier enfant du couple, sinon avant la fin de
// l'union ou le décès du conjoint. Aucune année n'est inventée, la colonne reste vide.
const childIds = new Set((union.childIds || []).map(String))
const childYears = (family.children || []).filter((c) => childIds.has(String(c.id))).map((c) => yearOf(c.birthDate) ?? c.birthYear).filter(within)
const partnerDeath = union.partner ? yearOf(union.partner.deathDate) ?? union.partner.deathYear : null
const anchor = year ? null : (childYears.length ? Math.min(...childYears) : null) ?? union.endYear ?? (within(partnerDeath) ? partnerDeath : null)
out.push({
key: `union-${union.id}`, year, order: 2, kind: 'mile',
key: `union-${union.id}`, year, sortYear: anchor, order: year ? 2 : 1, kind: 'mile',
title: union.unionType === 'union' ? t('Union avec {name}', { name: partnerName }) : t('Épouse {name}', { name: partnerName }),
meta: joinMeta(place, detail.startDatePrecision ? describeDate(detail.startDate, null, detail.startDatePrecision) : '', ageAt(birthYear, year)),
meta: year
? joinMeta(place, detail.startDatePrecision ? describeDate(detail.startDate, null, detail.startDatePrecision) : '', ageAt(birthYear, year))
: joinMeta(place, t('date inconnue')),
note: extra?.notes || '', sources: extra?.sources || '',
...milestoneFields(extra, { type: 'marriage', unionId: union.id }),
})
@@ -171,7 +179,7 @@ export function buildLifeTimeline(person, family = {}, life = null) {
}
out.push({
key: `event-${event.id}`, year, order: 1, kind: 'minor', title,
meta: joinMeta(formatPlace(event.place || ''), describeDate(event.date, event.dateEnd, event.datePrecision), ageAt(birthYear, year)),
meta: joinMeta(formatPlace(event.place || ''), year ? describeDate(event.date, event.dateEnd, event.datePrecision) : t('date inconnue'), ageAt(birthYear, year)),
note: event.notes || '', sources: event.sources || '',
mediaIds: event.mediaIds || [],
// Seuls les moments propres à la personne se modifient depuis l'onglet (pas ceux d'une union)
@@ -206,9 +214,9 @@ export function buildLifeTimeline(person, family = {}, life = null) {
})
}
// Les entrées sans date restent en bas, dans l'ordre où elles sont arrivées
// Une union sans date se range à sa place logique (sortYear) ; le reste sans date va en bas, dans l'ordre d'arrivée
return out
.map((entry, index) => ({ ...entry, index }))
.sort((a, b) => (a.year ?? Infinity) - (b.year ?? Infinity) || a.order - b.order || a.index - b.index)
.map(({ order, index, ...entry }) => entry) // eslint-disable-line no-unused-vars
.sort((a, b) => (a.year ?? a.sortYear ?? Infinity) - (b.year ?? b.sortYear ?? Infinity) || a.order - b.order || a.index - b.index)
.map(({ order, index, sortYear, ...entry }) => entry) // eslint-disable-line no-unused-vars
}
+24
View File
@@ -66,6 +66,30 @@ describe('frise de vie', () => {
expect(entries.map((e) => [e.title, e.year, e.meta])).toEqual([['Naissance', 1900, ''], ['Décès', null, 'date inconnue']])
})
it('union sans date : avant le premier enfant du couple, sinon avant le décès du conjoint, sinon en bas', () => {
const lucie = { id: 1, firstName: 'Lucie', birthYear: 1940 }
const entries = buildLifeTimeline(lucie, {
children: [{ id: 7, firstName: 'Hugo', birthYear: 1966 }, { id: 8, firstName: 'Léa', birthYear: 1981 }],
unions: [
{ id: 'u1', partner: { id: 2, firstName: 'Marc', lastName: 'Roy', deathYear: 2013 }, unionType: 'mariage', childIds: [7] },
{ id: 'u2', partner: { id: 3, firstName: 'Paul', lastName: 'Vidal', deathYear: 2016 }, unionType: 'mariage', childIds: [] },
{ id: 'u3', partner: { id: 4, firstName: 'Yves', lastName: 'Mallet' }, unionType: 'mariage' },
],
}, null)
expect(entries.map((e) => e.title)).toEqual([
'Naissance', 'Épouse Marc Roy', 'Naissance de Hugo', 'Naissance de Léa', 'Décès de Marc', 'Épouse Paul Vidal', 'Décès de Paul', 'Épouse Yves Mallet',
])
// Aucune année inventée : colonne vide, « date inconnue » dit pourquoi
for (const key of ['union-u1', 'union-u2', 'union-u3']) {
expect(entries.find((e) => e.key === key)).toMatchObject({ year: undefined, meta: 'date inconnue' })
}
expect(entries.find((e) => e.key === 'union-u1')).not.toHaveProperty('sortYear')
})
it('un moment sans date dit « date inconnue »', () => {
expect(entries.find((e) => e.title === 'Résidence').meta).toBe('Rue des Écoles · date inconnue')
})
it('dit les dates comme on les dit', () => {
expect(describeDate('1879-01-01', null, 'about')).toBe('vers 1879')
expect(describeDate('1879-01-01', null, 'before')).toBe('avant 1879')