From 104f0eef386536d1a2ac72e3be817ed0171447c7 Mon Sep 17 00:00:00 2001 From: Maurice Date: Thu, 1 Oct 2026 09:34:52 +0200 Subject: [PATCH] feat(costs): each expense row shows who it is split between, with their part Under the payers, a Split line lists every member of the expense with their share and a tick once they marked it paid, so checking that the right people are in no longer means opening each expense. More than six fold into a +N chip with the rest in its tooltip. (#1763) --- .../src/components/Budget/CostsPanel.test.tsx | 43 +++++++++++++- client/src/components/Budget/CostsPanel.tsx | 58 +++++++++++++++---- shared/src/i18n/ar/budget.ts | 1 + shared/src/i18n/az/budget.ts | 1 + shared/src/i18n/br/budget.ts | 1 + shared/src/i18n/ca/budget.ts | 1 + shared/src/i18n/cs/budget.ts | 1 + shared/src/i18n/de/budget.ts | 1 + shared/src/i18n/en/budget.ts | 1 + shared/src/i18n/es/budget.ts | 1 + shared/src/i18n/et/budget.ts | 1 + shared/src/i18n/fr/budget.ts | 1 + shared/src/i18n/gr/budget.ts | 1 + shared/src/i18n/hu/budget.ts | 1 + shared/src/i18n/id/budget.ts | 1 + shared/src/i18n/it/budget.ts | 1 + shared/src/i18n/ja/budget.ts | 1 + shared/src/i18n/ko/budget.ts | 1 + shared/src/i18n/nl/budget.ts | 1 + shared/src/i18n/pl/budget.ts | 1 + shared/src/i18n/ru/budget.ts | 1 + shared/src/i18n/sk/budget.ts | 1 + shared/src/i18n/sv/budget.ts | 1 + shared/src/i18n/th/budget.ts | 1 + shared/src/i18n/tr/budget.ts | 1 + shared/src/i18n/uk/budget.ts | 1 + shared/src/i18n/vi/budget.ts | 1 + shared/src/i18n/zh-TW/budget.ts | 1 + shared/src/i18n/zh/budget.ts | 1 + 29 files changed, 116 insertions(+), 12 deletions(-) diff --git a/client/src/components/Budget/CostsPanel.test.tsx b/client/src/components/Budget/CostsPanel.test.tsx index 4eaba32ba..7f77d72a1 100644 --- a/client/src/components/Budget/CostsPanel.test.tsx +++ b/client/src/components/Budget/CostsPanel.test.tsx @@ -278,6 +278,43 @@ describe('CostsPanel — settlements in the ledger', () => { expect(within(hotelRow).queryByText(/you lent|you borrowed/)).toBeNull() }) + it('lists who an expense is split between, with each part, in the row (#1763)', async () => { + seedStore(useAuthStore, { user: buildUser({ id: 1, username: 'alice' }), isAuthenticated: true }) + seedStore(useSettingsStore, { settings: { ...useSettingsStore.getState().settings, default_currency: 'EUR' } }) + const custom = { + ...buildBudgetItem({ trip_id: 1, category: 'food', name: 'Dinner' }), + total_price: 90, + payers: [{ user_id: 1, amount: 90, username: 'alice' }], + members: [{ user_id: 1, username: 'alice', paid: 1, amount: 60 }, { user_id: 2, username: 'bob', paid: 0, amount: 30 }], + } + const crowd = { + ...buildBudgetItem({ trip_id: 1, category: 'activity', name: 'Boat' }), + total_price: 80, + payers: [{ user_id: 1, amount: 80, username: 'alice' }], + members: Array.from({ length: 8 }, (_, i) => ({ user_id: i + 1, username: `p${i + 1}`, paid: 0 })), + } + server.use( + http.get('/api/trips/1/budget', () => HttpResponse.json({ items: [custom, crowd] })), + http.get('/api/trips/1/budget/settlement', () => HttpResponse.json({ balances: [], flows: [], settlements: [] })), + ) + render() + + const dinnerRow = (await screen.findByText('Dinner')).closest('.exp-row') as HTMLElement + const chips = within(dinnerRow).getAllByTestId('split-chip') + expect(chips).toHaveLength(2) + expect(chips[0]).toHaveTextContent('60') + expect(chips[1]).toHaveTextContent('30') + // alice marked her part as paid; bob has not. + expect(within(chips[0]).getByLabelText('paid')).toBeInTheDocument() + expect(within(chips[1]).queryByLabelText('paid')).toBeNull() + + // Eight people fold into five chips and a "+3". + const boatRow = (await screen.findByText('Boat')).closest('.exp-row') as HTMLElement + expect(within(boatRow).getAllByTestId('split-chip')).toHaveLength(5) + expect(within(boatRow).getByText('+3')).toBeInTheDocument() + expect(within(boatRow).getAllByTestId('split-chip')[0]).toHaveTextContent('10') + }) + it('sums only unfinished expenses in the Outstanding amount card', async () => { // Display in the trip's own currency so FX conversion is an identity — keeps the asserted sum deterministic. seedStore(useSettingsStore, { settings: { ...useSettingsStore.getState().settings, default_currency: 'EUR' } }) @@ -2463,8 +2500,10 @@ describe('CostsPanel: a bill entered in the display currency (#2525)', () => { await screen.findByText('Aparthotel Silver') expect(screen.getByText(/\$400\.88 → 342,63\s€$/)).toBeInTheDocument() - expect(screen.getByText('$395.77')).toBeInTheDocument() - expect(screen.getByText('$30.00')).toBeInTheDocument() + // The hotel's split chips can carry the same figure, so look past them to the payment. + const outsideChips = (text: string) => screen.queryAllByText(text).filter(el => !el.closest('[data-testid="split-chip"]')) + expect(outsideChips('$395.77')).toHaveLength(1) + expect(outsideChips('$30.00')).toHaveLength(1) expect(screen.queryByText('$34.65')).toBeNull() }) diff --git a/client/src/components/Budget/CostsPanel.tsx b/client/src/components/Budget/CostsPanel.tsx index 4cdcdfe34..0c239e139 100644 --- a/client/src/components/Budget/CostsPanel.tsx +++ b/client/src/components/Budget/CostsPanel.tsx @@ -37,6 +37,9 @@ import { CountPill, EYEBROW } from '../Planner/bookings/bookingParts' import CostsToolbar, { type CostsView } from './CostsToolbar' import CostsTable, { CostsTableSummary } from './CostsTable' +/** Split chips a row shows before the rest fold into "+N" (#1763). */ +const SPLIT_CHIP_MAX = 6 + interface CostsPanelProps { tripId: number tripMembers?: TripMember[] @@ -195,19 +198,20 @@ export default function CostsPanel({ tripId, tripMembers = [] }: CostsPanelProps // total but stays out of settlements until who-paid is filled in. A negative // total (a refund, #2176) is just as unfinished until its recipient is named. const isUnfinished = (e: BudgetItem) => baseTotal(e) !== 0 && (e.payers || []).filter(p => p.amount !== 0).length === 0 - const myShareOf = (e: BudgetItem) => { - // Nobody paid, so nobody owes: the ledger skips these entirely (#2225), and - // counting them here left the tile contradicting the balances right beside it. - if (isUnfinished(e)) return 0 - const myMember = (e.members || []).find(m => m.user_id === me) - if (!myMember) return 0 - if (myMember.amount !== null && myMember.amount !== undefined) { - return booked(myMember.amount, e) + // A member's part of an expense: the custom amount when one was set, else the + // equal split the server settles with, in the display currency. + const shareOf = (e: BudgetItem, userId: number) => { + const member = (e.members || []).find(m => m.user_id === userId) + if (!member) return 0 + if (member.amount !== null && member.amount !== undefined) { + return booked(member.amount, e) } const shares = splitEqualShares(e.total_price || 0, e.members || [], e.id) - const myShare = shares[me] || 0 - return booked(myShare, e) + return booked(shares[userId] || 0, e) } + // Nobody paid, so nobody owes: the ledger skips these entirely (#2225), and + // counting them here left the tile contradicting the balances right beside it. + const myShareOf = (e: BudgetItem) => (isUnfinished(e) ? 0 : shareOf(e, me)) // `booked` carries the rates. They can land after the expenses, and without it in the // deps the cards kept the sums they were first added up with while the rows moved on. @@ -889,6 +893,7 @@ export default function CostsPanel({ tripId, tripMembers = [] }: CostsPanelProps ))} )} + {SplitChips({ e })} {/* The note, marked by a rule in the category colour instead of a box, so @@ -912,6 +917,39 @@ export default function CostsPanel({ tripId, tripMembers = [] }: CostsPanelProps ) } + // Who an expense is split between and each one's part (#1763), so checking that + // the right people are in no longer means opening every expense. Quieter than + // the payer chips above, and capped so a large group cannot crowd the row. + function SplitChips({ e }: { e: BudgetItem }) { + const members = e.members || [] + if (members.length === 0) return null + const shown = members.length > SPLIT_CHIP_MAX ? members.slice(0, SPLIT_CHIP_MAX - 1) : members + const rest = members.slice(shown.length) + const chip = 'inline-flex items-center gap-[5px] rounded-full bg-surface-tertiary text-content-secondary' + return ( +
+ {t('costs.split')} + {shown.map(m => { + const share = fmt(shareOf(e, m.user_id)) + return ( + + + + {share} + {m.paid ? : null} + + + ) + })} + {rest.length > 0 && ( + t('costs.splitChipLabel', { name: personName(m.user_id), amount: fmt(shareOf(e, m.user_id)) })).join(', ')}> + +{rest.length} + + )} +
+ ) + } + // A row's note as a pill that opens in place; an empty one keeps the column. function NoteCell({ note, noteKey, color }: { note: string; noteKey: string; color: string }) { if (!note) return diff --git a/shared/src/i18n/ar/budget.ts b/shared/src/i18n/ar/budget.ts index a0ffdf20b..fc8cf9c05 100644 --- a/shared/src/i18n/ar/budget.ts +++ b/shared/src/i18n/ar/budget.ts @@ -150,6 +150,7 @@ const budget: TranslationStrings = { 'costs.tapToInclude': 'اضغط للتضمين', 'costs.amount': 'المبلغ', 'costs.split': 'التقسيم', + 'costs.splitChipLabel': '{name}: {amount}', 'costs.splitEqually': 'بالتساوي', 'costs.splitCustom': 'مخصص', 'costs.splitTicket': 'الإيصال', diff --git a/shared/src/i18n/az/budget.ts b/shared/src/i18n/az/budget.ts index 627e494f7..c681a2b23 100644 --- a/shared/src/i18n/az/budget.ts +++ b/shared/src/i18n/az/budget.ts @@ -141,6 +141,7 @@ const budget: TranslationStrings = { 'costs.splitBetween': 'Bərabər bölüşdürüləcək şəxslər', 'costs.split': 'Bölüşdür', + 'costs.splitChipLabel': '{name}: {amount}', 'costs.splitEqually': 'Bərabər', 'costs.splitCustom': 'Fərdi', 'costs.splitTicket': 'Qəbz üzrə', diff --git a/shared/src/i18n/br/budget.ts b/shared/src/i18n/br/budget.ts index e8ed7d58b..e49e08525 100644 --- a/shared/src/i18n/br/budget.ts +++ b/shared/src/i18n/br/budget.ts @@ -150,6 +150,7 @@ const budget: TranslationStrings = { 'costs.tapToInclude': 'Toque para incluir', 'costs.amount': 'Valor', 'costs.split': 'Dividir', + 'costs.splitChipLabel': '{name}: {amount}', 'costs.splitEqually': 'Igualmente', 'costs.splitCustom': 'Personalizado', 'costs.splitTicket': 'Recibo', diff --git a/shared/src/i18n/ca/budget.ts b/shared/src/i18n/ca/budget.ts index b93468f4b..f95b85dc4 100644 --- a/shared/src/i18n/ca/budget.ts +++ b/shared/src/i18n/ca/budget.ts @@ -118,6 +118,7 @@ const budget: TranslationStrings = { 'costs.whoPaid': 'Qui ha pagat?', 'costs.splitBetween': 'Dividir a parts iguals entre', 'costs.split': 'Dividir', + 'costs.splitChipLabel': '{name}: {amount}', 'costs.splitEqually': 'A parts iguals', 'costs.splitCustom': 'Personalitzat', 'costs.splitTicket': 'Tiquet', diff --git a/shared/src/i18n/cs/budget.ts b/shared/src/i18n/cs/budget.ts index 424784b07..ab7cdd868 100644 --- a/shared/src/i18n/cs/budget.ts +++ b/shared/src/i18n/cs/budget.ts @@ -150,6 +150,7 @@ const budget: TranslationStrings = { 'costs.tapToInclude': 'Klepnutím zahrnout', 'costs.amount': 'Částka', 'costs.split': 'Rozdělení', + 'costs.splitChipLabel': '{name}: {amount}', 'costs.splitEqually': 'Rovným dílem', 'costs.splitCustom': 'Vlastní', 'costs.splitTicket': 'Účtenka', diff --git a/shared/src/i18n/de/budget.ts b/shared/src/i18n/de/budget.ts index 36a4a953b..859b14358 100644 --- a/shared/src/i18n/de/budget.ts +++ b/shared/src/i18n/de/budget.ts @@ -153,6 +153,7 @@ const budget: TranslationStrings = { 'costs.tapToInclude': 'Zum Einbeziehen tippen', 'costs.amount': 'Betrag', 'costs.split': 'Aufteilen', + 'costs.splitChipLabel': '{name}: {amount}', 'costs.splitEqually': 'Gleichmäßig', 'costs.splitCustom': 'Individuell', 'costs.splitTicket': 'Beleg', diff --git a/shared/src/i18n/en/budget.ts b/shared/src/i18n/en/budget.ts index 8e166f094..001fd9a98 100644 --- a/shared/src/i18n/en/budget.ts +++ b/shared/src/i18n/en/budget.ts @@ -126,6 +126,7 @@ const budget: TranslationStrings = { 'costs.payersUnbalanced': 'Payer amounts must add up to {amount}', 'costs.splitBetween': 'Split equally between', 'costs.split': 'Split', + 'costs.splitChipLabel': '{name}: {amount}', 'costs.splitEqually': 'Equally', 'costs.splitCustom': 'Custom', 'costs.splitTicket': 'Ticket', diff --git a/shared/src/i18n/es/budget.ts b/shared/src/i18n/es/budget.ts index ae24697e2..5ff2440db 100644 --- a/shared/src/i18n/es/budget.ts +++ b/shared/src/i18n/es/budget.ts @@ -150,6 +150,7 @@ const budget: TranslationStrings = { 'costs.tapToInclude': 'Toca para incluir', 'costs.amount': 'Importe', 'costs.split': 'Dividir', + 'costs.splitChipLabel': '{name}: {amount}', 'costs.splitEqually': 'A partes iguales', 'costs.splitCustom': 'Personalizado', 'costs.splitTicket': 'Recibo', diff --git a/shared/src/i18n/et/budget.ts b/shared/src/i18n/et/budget.ts index c25ca0c1f..3fb20e8d9 100644 --- a/shared/src/i18n/et/budget.ts +++ b/shared/src/i18n/et/budget.ts @@ -126,6 +126,7 @@ const budget: TranslationStrings = { 'costs.payersUnbalanced': 'Maksjate summad peavad kokku andma {amount}', 'costs.splitBetween': 'Jaga võrdselt järgmiste vahel', 'costs.split': 'Jaotus', + 'costs.splitChipLabel': '{name}: {amount}', 'costs.splitEqually': 'Võrdselt', 'costs.splitCustom': 'Kohandatud', 'costs.splitTicket': 'Tšekk', diff --git a/shared/src/i18n/fr/budget.ts b/shared/src/i18n/fr/budget.ts index 699920229..b7abca6e0 100644 --- a/shared/src/i18n/fr/budget.ts +++ b/shared/src/i18n/fr/budget.ts @@ -151,6 +151,7 @@ const budget: TranslationStrings = { 'costs.tapToInclude': 'Toucher pour inclure', 'costs.amount': 'Montant', 'costs.split': 'Répartition', + 'costs.splitChipLabel': '{name} : {amount}', 'costs.splitEqually': 'À parts égales', 'costs.splitCustom': 'Personnalisé', 'costs.splitTicket': 'Reçu', diff --git a/shared/src/i18n/gr/budget.ts b/shared/src/i18n/gr/budget.ts index b1ce59847..fef4b18f4 100644 --- a/shared/src/i18n/gr/budget.ts +++ b/shared/src/i18n/gr/budget.ts @@ -151,6 +151,7 @@ const budget: TranslationStrings = { 'costs.tapToInclude': 'Πατήστε για προσθήκη', 'costs.amount': 'Ποσό', 'costs.split': 'Διαμοιρασμός', + 'costs.splitChipLabel': '{name}: {amount}', 'costs.splitEqually': 'Εξίσου', 'costs.splitCustom': 'Προσαρμογή', 'costs.splitTicket': 'Απόδειξη', diff --git a/shared/src/i18n/hu/budget.ts b/shared/src/i18n/hu/budget.ts index 182a3b44d..2584386b1 100644 --- a/shared/src/i18n/hu/budget.ts +++ b/shared/src/i18n/hu/budget.ts @@ -150,6 +150,7 @@ const budget: TranslationStrings = { 'costs.tapToInclude': 'Koppintson a hozzáadáshoz', 'costs.amount': 'Összeg', 'costs.split': 'Felosztás', + 'costs.splitChipLabel': '{name}: {amount}', 'costs.splitEqually': 'Egyenlően', 'costs.splitCustom': 'Egyéni', 'costs.splitTicket': 'Blokk', diff --git a/shared/src/i18n/id/budget.ts b/shared/src/i18n/id/budget.ts index 2881de1f6..e465e0d52 100644 --- a/shared/src/i18n/id/budget.ts +++ b/shared/src/i18n/id/budget.ts @@ -150,6 +150,7 @@ const budget: TranslationStrings = { 'costs.tapToInclude': 'Ketuk untuk menyertakan', 'costs.amount': 'Jumlah', 'costs.split': 'Pembagian', + 'costs.splitChipLabel': '{name}: {amount}', 'costs.splitEqually': 'Rata', 'costs.splitCustom': 'Kustom', 'costs.splitTicket': 'Struk', diff --git a/shared/src/i18n/it/budget.ts b/shared/src/i18n/it/budget.ts index bd83689b2..2fbb9c5d8 100644 --- a/shared/src/i18n/it/budget.ts +++ b/shared/src/i18n/it/budget.ts @@ -150,6 +150,7 @@ const budget: TranslationStrings = { 'costs.tapToInclude': 'Tocca per includere', 'costs.amount': 'Importo', 'costs.split': 'Suddivisione', + 'costs.splitChipLabel': '{name}: {amount}', 'costs.splitEqually': 'In parti uguali', 'costs.splitCustom': 'Personalizzato', 'costs.splitTicket': 'Scontrino', diff --git a/shared/src/i18n/ja/budget.ts b/shared/src/i18n/ja/budget.ts index 97e2955fb..cd0f4b1c6 100644 --- a/shared/src/i18n/ja/budget.ts +++ b/shared/src/i18n/ja/budget.ts @@ -150,6 +150,7 @@ const budget: TranslationStrings = { 'costs.tapToInclude': 'タップして追加', 'costs.amount': '金額', 'costs.split': '分け方', + 'costs.splitChipLabel': '{name}:{amount}', 'costs.splitEqually': '均等', 'costs.splitCustom': 'カスタム', 'costs.splitTicket': 'レシート', diff --git a/shared/src/i18n/ko/budget.ts b/shared/src/i18n/ko/budget.ts index ec525d5c4..4b0330fea 100644 --- a/shared/src/i18n/ko/budget.ts +++ b/shared/src/i18n/ko/budget.ts @@ -150,6 +150,7 @@ const budget: TranslationStrings = { 'costs.tapToInclude': '탭하여 포함', 'costs.amount': '금액', 'costs.split': '나누기', + 'costs.splitChipLabel': '{name}: {amount}', 'costs.splitEqually': '균등하게', 'costs.splitCustom': '직접 설정', 'costs.splitTicket': '영수증', diff --git a/shared/src/i18n/nl/budget.ts b/shared/src/i18n/nl/budget.ts index a18962385..74a699694 100644 --- a/shared/src/i18n/nl/budget.ts +++ b/shared/src/i18n/nl/budget.ts @@ -150,6 +150,7 @@ const budget: TranslationStrings = { 'costs.tapToInclude': 'Tik om toe te voegen', 'costs.amount': 'Bedrag', 'costs.split': 'Verdelen', + 'costs.splitChipLabel': '{name}: {amount}', 'costs.splitEqually': 'Gelijk', 'costs.splitCustom': 'Aangepast', 'costs.splitTicket': 'Bon', diff --git a/shared/src/i18n/pl/budget.ts b/shared/src/i18n/pl/budget.ts index 06ed62e32..66693a969 100644 --- a/shared/src/i18n/pl/budget.ts +++ b/shared/src/i18n/pl/budget.ts @@ -150,6 +150,7 @@ const budget: TranslationStrings = { 'costs.tapToInclude': 'Dotknij, aby dodać', 'costs.amount': 'Kwota', 'costs.split': 'Podział', + 'costs.splitChipLabel': '{name}: {amount}', 'costs.splitEqually': 'Po równo', 'costs.splitCustom': 'Własny', 'costs.splitTicket': 'Paragon', diff --git a/shared/src/i18n/ru/budget.ts b/shared/src/i18n/ru/budget.ts index 643ac802a..606024ed8 100644 --- a/shared/src/i18n/ru/budget.ts +++ b/shared/src/i18n/ru/budget.ts @@ -150,6 +150,7 @@ const budget: TranslationStrings = { 'costs.tapToInclude': 'Нажмите, чтобы добавить', 'costs.amount': 'Сумма', 'costs.split': 'Разделение', + 'costs.splitChipLabel': '{name}: {amount}', 'costs.splitEqually': 'Поровну', 'costs.splitCustom': 'Вручную', 'costs.splitTicket': 'Чек', diff --git a/shared/src/i18n/sk/budget.ts b/shared/src/i18n/sk/budget.ts index 606d2d849..099ba0826 100644 --- a/shared/src/i18n/sk/budget.ts +++ b/shared/src/i18n/sk/budget.ts @@ -126,6 +126,7 @@ const budget: TranslationStrings = { 'costs.payersUnbalanced': 'Súčet súm platcov musí byť {amount}', 'costs.splitBetween': 'Rozdeliť rovným dielom medzi', 'costs.split': 'Rozdeliť', + 'costs.splitChipLabel': '{name}: {amount}', 'costs.splitEqually': 'Rovnakým dielom', 'costs.splitCustom': 'Vlastné', 'costs.splitTicket': 'Účtenka', diff --git a/shared/src/i18n/sv/budget.ts b/shared/src/i18n/sv/budget.ts index 0bb6ab37b..86cd5dfd8 100644 --- a/shared/src/i18n/sv/budget.ts +++ b/shared/src/i18n/sv/budget.ts @@ -150,6 +150,7 @@ const budget: TranslationStrings = { 'costs.tapToInclude': 'Tryck för att inkludera', 'costs.amount': 'Summa', 'costs.split': 'Fördelning', + 'costs.splitChipLabel': '{name}: {amount}', 'costs.splitEqually': 'Lika', 'costs.splitCustom': 'Anpassat', 'costs.splitTicket': 'Kvitto', diff --git a/shared/src/i18n/th/budget.ts b/shared/src/i18n/th/budget.ts index c460efa40..77972a19a 100644 --- a/shared/src/i18n/th/budget.ts +++ b/shared/src/i18n/th/budget.ts @@ -122,6 +122,7 @@ const budget: TranslationStrings = { 'costs.payersUnbalanced': 'จำนวนเงินของผู้ชำระจะต้องรวมกันไม่เกิน {amount}', 'costs.splitBetween': 'แบ่งเท่าๆ กันระหว่าง', 'costs.split': 'แยก', + 'costs.splitChipLabel': '{name}: {amount}', 'costs.splitEqually': 'เท่าๆ กัน', 'costs.splitCustom': 'กำหนดเอง', 'costs.splitTicket': 'ตั๋ว', diff --git a/shared/src/i18n/tr/budget.ts b/shared/src/i18n/tr/budget.ts index f33ad395a..79c133229 100644 --- a/shared/src/i18n/tr/budget.ts +++ b/shared/src/i18n/tr/budget.ts @@ -150,6 +150,7 @@ const budget: TranslationStrings = { 'costs.tapToInclude': 'Eklemek için dokun', 'costs.amount': 'Tutar', 'costs.split': 'Paylaşım', + 'costs.splitChipLabel': '{name}: {amount}', 'costs.splitEqually': 'Eşit', 'costs.splitCustom': 'Özel', 'costs.splitTicket': 'Fiş', diff --git a/shared/src/i18n/uk/budget.ts b/shared/src/i18n/uk/budget.ts index 69657f7de..3116e462b 100644 --- a/shared/src/i18n/uk/budget.ts +++ b/shared/src/i18n/uk/budget.ts @@ -150,6 +150,7 @@ const budget: TranslationStrings = { 'costs.tapToInclude': 'Натисніть, щоб додати', 'costs.amount': 'Сума', 'costs.split': 'Розподіл', + 'costs.splitChipLabel': '{name}: {amount}', 'costs.splitEqually': 'Порівну', 'costs.splitCustom': 'Вручну', 'costs.splitTicket': 'Чек', diff --git a/shared/src/i18n/vi/budget.ts b/shared/src/i18n/vi/budget.ts index 60a63c2b8..2fdd8c8af 100644 --- a/shared/src/i18n/vi/budget.ts +++ b/shared/src/i18n/vi/budget.ts @@ -150,6 +150,7 @@ const budget: TranslationStrings = { 'costs.tapToInclude': 'Nhấn để bao gồm', 'costs.amount': 'Số lượng', 'costs.split': 'Chia tiền', + 'costs.splitChipLabel': '{name}: {amount}', 'costs.splitEqually': 'Chia đều', 'costs.splitCustom': 'Tùy chỉnh', 'costs.splitTicket': 'Hóa đơn', diff --git a/shared/src/i18n/zh-TW/budget.ts b/shared/src/i18n/zh-TW/budget.ts index 03399853f..cac4c8238 100644 --- a/shared/src/i18n/zh-TW/budget.ts +++ b/shared/src/i18n/zh-TW/budget.ts @@ -149,6 +149,7 @@ const budget: TranslationStrings = { 'costs.tapToInclude': '點按以加入', 'costs.amount': '金額', 'costs.split': '分攤', + 'costs.splitChipLabel': '{name}:{amount}', 'costs.splitEqually': '平均', 'costs.splitCustom': '自訂', 'costs.splitTicket': '收據', diff --git a/shared/src/i18n/zh/budget.ts b/shared/src/i18n/zh/budget.ts index 084db3155..24f5768eb 100644 --- a/shared/src/i18n/zh/budget.ts +++ b/shared/src/i18n/zh/budget.ts @@ -149,6 +149,7 @@ const budget: TranslationStrings = { 'costs.tapToInclude': '点按以加入', 'costs.amount': '金额', 'costs.split': '分摊', + 'costs.splitChipLabel': '{name}:{amount}', 'costs.splitEqually': '平均分摊', 'costs.splitCustom': '自定义金额', 'costs.splitTicket': '按账单明细',