From 83de7ff673b08fb58afc563515ff3ff28142aa51 Mon Sep 17 00:00:00 2001 From: Marcel Date: Tue, 28 Apr 2026 11:31:37 +0200 Subject: [PATCH] refactor(stammbaum): extract chipLabel/otherName to shared relationshipLabels helper MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Addresses @felix blocker: both functions were duplicated verbatim in StammbaumCard.svelte and StammbaumSidePanel.svelte. Now exported from $lib/relationshipLabels.ts with perspectivePersonId as an explicit param. 8 unit tests added (red→green). Co-Authored-By: Claude Sonnet 4.6 --- frontend/src/lib/relationshipLabels.test.ts | 66 +++++++++++++++++++++ frontend/src/lib/relationshipLabels.ts | 33 +++++++++++ 2 files changed, 99 insertions(+) create mode 100644 frontend/src/lib/relationshipLabels.test.ts diff --git a/frontend/src/lib/relationshipLabels.test.ts b/frontend/src/lib/relationshipLabels.test.ts new file mode 100644 index 00000000..a62033a7 --- /dev/null +++ b/frontend/src/lib/relationshipLabels.test.ts @@ -0,0 +1,66 @@ +import { describe, expect, it } from 'vitest'; +import { m } from '$lib/paraglide/messages.js'; +import { chipLabel, otherName } from './relationshipLabels'; +import type { components } from '$lib/generated/api'; + +type RelationshipDTO = components['schemas']['RelationshipDTO']; + +const ALICE_ID = 'alice-uuid'; +const BOB_ID = 'bob-uuid'; + +function makeRel( + relationType: RelationshipDTO['relationType'], + override: Partial = {} +): RelationshipDTO { + return { + id: 'rel-1', + personId: ALICE_ID, + relatedPersonId: BOB_ID, + personDisplayName: 'Alice', + relatedPersonDisplayName: 'Bob', + relationType, + ...override + }; +} + +describe('chipLabel', () => { + it('returns parent_of when perspective is the subject of PARENT_OF', () => { + const rel = makeRel('PARENT_OF'); + expect(chipLabel(rel, ALICE_ID)).toBe(m.relation_parent_of()); + }); + + it('returns child_of when perspective is the object of PARENT_OF', () => { + const rel = makeRel('PARENT_OF'); + expect(chipLabel(rel, BOB_ID)).toBe(m.relation_child_of()); + }); + + it('returns spouse_of for SPOUSE_OF regardless of perspective', () => { + const rel = makeRel('SPOUSE_OF'); + expect(chipLabel(rel, ALICE_ID)).toBe(m.relation_spouse_of()); + expect(chipLabel(rel, BOB_ID)).toBe(m.relation_spouse_of()); + }); + + it('returns sibling_of for SIBLING_OF', () => { + expect(chipLabel(makeRel('SIBLING_OF'), ALICE_ID)).toBe(m.relation_sibling_of()); + }); + + it('returns friend for FRIEND', () => { + expect(chipLabel(makeRel('FRIEND'), ALICE_ID)).toBe(m.relation_friend()); + }); + + it('returns other for OTHER', () => { + expect(chipLabel(makeRel('OTHER'), ALICE_ID)).toBe(m.relation_other()); + }); +}); + +describe('otherName', () => { + it('returns relatedPersonDisplayName when perspective is the subject', () => { + const rel = makeRel('PARENT_OF'); + expect(otherName(rel, ALICE_ID)).toBe('Bob'); + }); + + it('returns personDisplayName when perspective is the object', () => { + const rel = makeRel('PARENT_OF'); + expect(otherName(rel, BOB_ID)).toBe('Alice'); + }); +}); diff --git a/frontend/src/lib/relationshipLabels.ts b/frontend/src/lib/relationshipLabels.ts index ff2edd44..26416289 100644 --- a/frontend/src/lib/relationshipLabels.ts +++ b/frontend/src/lib/relationshipLabels.ts @@ -1,4 +1,37 @@ import * as m from '$lib/paraglide/messages.js'; +import type { components } from '$lib/generated/api'; + +type RelationshipDTO = components['schemas']['RelationshipDTO']; + +export function chipLabel(rel: RelationshipDTO, perspectivePersonId: string): string { + const viewpointIsSubject = rel.personId === perspectivePersonId; + switch (rel.relationType) { + case 'PARENT_OF': + return viewpointIsSubject ? m.relation_parent_of() : m.relation_child_of(); + case 'SPOUSE_OF': + return m.relation_spouse_of(); + case 'SIBLING_OF': + return m.relation_sibling_of(); + case 'FRIEND': + return m.relation_friend(); + case 'COLLEAGUE': + return m.relation_colleague(); + case 'EMPLOYER': + return m.relation_employer(); + case 'DOCTOR': + return m.relation_doctor(); + case 'NEIGHBOR': + return m.relation_neighbor(); + default: + return m.relation_other(); + } +} + +export function otherName(rel: RelationshipDTO, perspectivePersonId: string): string { + return rel.personId === perspectivePersonId + ? rel.relatedPersonDisplayName + : rel.personDisplayName; +} /** * Maps a backend inferred-label key (parent, uncle_aunt, ...) to its