mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-11-13 15:51:00 +00:00
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".
This commit is contained in:
@@ -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
|
||||
</span>
|
||||
)}
|
||||
{!isPlaceholder && broughtBy && (
|
||||
<span className="packing-row-badge" title={t('packing.broughtBy', { name: broughtBy })} aria-label={t('packing.broughtBy', { name: broughtBy })}
|
||||
<span className="packing-row-badge" title={t('packing.broughtBy', { name: packingBringerNames(item) })} aria-label={t('packing.broughtBy', { name: packingBringerNames(item) })}
|
||||
style={{ display: 'inline-flex', alignItems: 'center', gap: 3, flexShrink: 0 }}>
|
||||
{ownerAvatar
|
||||
? <img src={ownerAvatar} alt="" style={{ width: 20, height: 20, borderRadius: '50%', objectFit: 'cover', display: 'block' }} />
|
||||
|
||||
@@ -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('');
|
||||
});
|
||||
});
|
||||
@@ -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(', ');
|
||||
}
|
||||
@@ -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({
|
||||
<div className="rounded-[14px] border border-[color:var(--m-rowbr)] bg-[color:var(--m-ic)] p-3">
|
||||
{item.owner_username && (
|
||||
<div className="mb-[9px] font-geist text-[0.6875rem] text-m-muted">
|
||||
{t('packing.broughtBy', { name: item.owner_username })}
|
||||
{t('packing.broughtBy', { name: packingBringerNames(item) })}
|
||||
</div>
|
||||
)}
|
||||
<div className="flex gap-2">
|
||||
|
||||
@@ -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
|
||||
</span>
|
||||
)}
|
||||
{!badgeSharedToMe && !badgeSharedByMe && badgeBroughtBy && (
|
||||
<span className="flex flex-none items-center gap-[3px]" title={item.owner_username || undefined}>
|
||||
<span className="flex flex-none items-center gap-[3px]" title={t('packing.broughtBy', { name: packingBringerNames(item) })} aria-label={t('packing.broughtBy', { name: packingBringerNames(item) })}>
|
||||
{ownerAvatarUrl
|
||||
? <img src={ownerAvatarUrl} alt={item.owner_username || ''} className="h-5 w-5 flex-none rounded-full object-cover" />
|
||||
: <span className="flex h-5 w-5 flex-none items-center justify-center rounded-full bg-[color:var(--m-ic)] font-geist text-[0.5625rem] font-bold text-m-muted">{(item.owner_username || '?')[0]?.toUpperCase()}</span>}
|
||||
|
||||
Reference in New Issue
Block a user