From da324121bda5b3ee8d7a7fab6e26679f6e4f638b Mon Sep 17 00:00:00 2001 From: Maurice Date: Wed, 4 Nov 2026 17:53:15 +0100 Subject: [PATCH] fix(packing): name everyone who brings a shared item A common item brought by several members showed the first bringer's avatar and a bare "+2". The tooltip only named the first person, and on the phone neither the row nor the item sheet said who else brings it (discussion #2782). packingBringerNames lists the bringer and every contributor; the desktop row, the phone row and the phone item sheet use it for "brought by". --- .../Packing/PackingListPanelItemRow.tsx | 3 ++- .../Packing/packingBringers.test.ts | 22 +++++++++++++++++++ .../src/components/Packing/packingBringers.ts | 14 ++++++++++++ .../screens/trip/tabs/MPackItemSheet.tsx | 3 ++- .../screens/trip/tabs/MPackingListTab.tsx | 3 ++- 5 files changed, 42 insertions(+), 3 deletions(-) create mode 100644 client/src/components/Packing/packingBringers.test.ts create mode 100644 client/src/components/Packing/packingBringers.ts diff --git a/client/src/components/Packing/PackingListPanelItemRow.tsx b/client/src/components/Packing/PackingListPanelItemRow.tsx index 80bf8dff5..528e77db2 100644 --- a/client/src/components/Packing/PackingListPanelItemRow.tsx +++ b/client/src/components/Packing/PackingListPanelItemRow.tsx @@ -18,6 +18,7 @@ import { PopoverItem } from './PackingPopover' import { POPOVER, POPOVER_CAPTION, POPOVER_DIVIDER } from './packingPopoverStyles' import type { TripMember } from './usePackingListPanel' import { NumericInput } from '../shared/NumericInput' +import { packingBringerNames } from './packingBringers' interface ArtikelZeileProps { item: PackingItem @@ -226,7 +227,7 @@ export function ArtikelZeile({ item, tripId, categories, onCategoryChange: _onCa )} {!isPlaceholder && broughtBy && ( - {ownerAvatar ? diff --git a/client/src/components/Packing/packingBringers.test.ts b/client/src/components/Packing/packingBringers.test.ts new file mode 100644 index 000000000..3c34cae5f --- /dev/null +++ b/client/src/components/Packing/packingBringers.test.ts @@ -0,0 +1,22 @@ +// FE-PACK-BRINGERS-001 to FE-PACK-BRINGERS-003 +import { describe, expect, it } from 'vitest'; +import { packingBringerNames } from './packingBringers'; + +describe('packingBringerNames', () => { + it('FE-PACK-BRINGERS-001: names the bringer first, then everyone who also brings it', () => { + const item = { + owner_username: 'anna', + contributors: [{ username: 'ben' }, { username: 'carla' }], + }; + expect(packingBringerNames(item)).toBe('anna, ben, carla'); + }); + + it('FE-PACK-BRINGERS-002: names a person once, even when listed twice', () => { + expect(packingBringerNames({ owner_username: 'anna', contributors: [{ username: 'anna' }] })).toBe('anna'); + }); + + it('FE-PACK-BRINGERS-003: copes with an item without a bringer or contributors', () => { + expect(packingBringerNames({ owner_username: null, contributors: [{ username: 'ben' }] })).toBe('ben'); + expect(packingBringerNames({})).toBe(''); + }); +}); diff --git a/client/src/components/Packing/packingBringers.ts b/client/src/components/Packing/packingBringers.ts new file mode 100644 index 000000000..a0dd70883 --- /dev/null +++ b/client/src/components/Packing/packingBringers.ts @@ -0,0 +1,14 @@ +interface BroughtItem { + owner_username?: string | null; + contributors?: { username: string }[] | null; +} + +/** + * Everyone bringing a common item, as one readable list: its bringer first, then + * whoever also brings it. The row only has room for one avatar and a "+n", so this + * is what its tooltip and the item sheet name (#2782). + */ +export function packingBringerNames(item: BroughtItem): string { + const names = [item.owner_username, ...(item.contributors ?? []).map((c) => c.username)]; + return [...new Set(names.filter((n): n is string => !!n))].join(', '); +} diff --git a/client/src/mobile/screens/trip/tabs/MPackItemSheet.tsx b/client/src/mobile/screens/trip/tabs/MPackItemSheet.tsx index 10854da16..4846b84d2 100644 --- a/client/src/mobile/screens/trip/tabs/MPackItemSheet.tsx +++ b/client/src/mobile/screens/trip/tabs/MPackItemSheet.tsx @@ -6,6 +6,7 @@ import { avatarSrc } from '../../../../utils/avatarSrc' import type { PackingItem, TripMember } from '../../../../types' import type { TripPlanner } from '../MTripShell' import { isPackingPlaceholder } from '../../../../components/Packing/packingListModel' +import { packingBringerNames } from '../../../../components/Packing/packingBringers' export interface MPackItemSheetProps { planner: TripPlanner @@ -160,7 +161,7 @@ export default function MPackItemSheet({
{item.owner_username && (
- {t('packing.broughtBy', { name: item.owner_username })} + {t('packing.broughtBy', { name: packingBringerNames(item) })}
)}
diff --git a/client/src/mobile/screens/trip/tabs/MPackingListTab.tsx b/client/src/mobile/screens/trip/tabs/MPackingListTab.tsx index 309323b75..7adff8294 100644 --- a/client/src/mobile/screens/trip/tabs/MPackingListTab.tsx +++ b/client/src/mobile/screens/trip/tabs/MPackingListTab.tsx @@ -28,6 +28,7 @@ import MPackItemSheet from './MPackItemSheet' import MPackingImportSheet from './MPackingImportSheet' import PackingPrintPreview from '../../../../components/Packing/PackingPrintPreview' import { usePackingExport } from '../../../../components/Packing/usePackingExport' +import { packingBringerNames } from '../../../../components/Packing/packingBringers' type ActionView = 'menu' | 'apply' | 'save' @@ -718,7 +719,7 @@ function PackingItemRow({ item, planner, currentUserId, editMode, canEdit, bagTr )} {!badgeSharedToMe && !badgeSharedByMe && badgeBroughtBy && ( - + {ownerAvatarUrl ? {item.owner_username : {(item.owner_username || '?')[0]?.toUpperCase()}}