Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
d64c058
feat: Display Per-Person Amount and Attendees in Expense Table
nkdengineer Mar 13, 2026
d6de2ee
Merge branch 'main' of https://github.com/nkdengineer/App into fix/83223
nkdengineer Mar 16, 2026
c7c8457
implement hook to get the attendees
nkdengineer Mar 16, 2026
c2ef12a
use getAttendees util
nkdengineer Mar 16, 2026
c7ac28c
use avatar from attendee data to display the avatar
nkdengineer Mar 16, 2026
21c7142
change condition to display total per attendee
nkdengineer Mar 16, 2026
3aff07e
Merge branch 'main' into fix/83223
nkdengineer Mar 17, 2026
8c93a95
update border color and text style
nkdengineer Mar 17, 2026
b2a3aa4
Merge branch 'main' into fix/83223
nkdengineer Mar 18, 2026
b2a4de5
Merge branch 'main' into fix/83223
nkdengineer Mar 18, 2026
2e65591
update overlay style and translation
nkdengineer Mar 18, 2026
6b8f45c
update fixed width for attendee column
nkdengineer Mar 18, 2026
bb9ba44
Merge branch 'main' into fix/83223
nkdengineer Mar 20, 2026
ff0857d
hide attendee column when the feature is not enalbed
nkdengineer Mar 20, 2026
3a4438d
fix attendee avatar
nkdengineer Mar 20, 2026
384ae0c
Merge branch 'main' into fix/83223
nkdengineer Mar 23, 2026
4e9cea8
update avatar size
nkdengineer Mar 23, 2026
c960ff0
update shouldShowAttendees conditon for each transaction
nkdengineer Mar 23, 2026
57aa4f0
implement AttendeesCell component
nkdengineer Mar 24, 2026
de668d5
resolve conflict
nkdengineer Mar 24, 2026
83c9223
fix lint
nkdengineer Mar 24, 2026
3df12e0
fix key for attendee cell
nkdengineer Mar 24, 2026
af7ca04
update translation
nkdengineer Mar 24, 2026
d8bf593
Merge branch 'main' into fix/83223
nkdengineer Mar 26, 2026
8966c13
fix crash bug
nkdengineer Mar 26, 2026
d8db554
Merge branch 'main' into fix/83223
nkdengineer Mar 27, 2026
e3a8f7e
fix ts
nkdengineer Mar 27, 2026
e01e871
resolve conflict
nkdengineer Mar 30, 2026
3321898
Merge branch 'main' into fix/83223
nkdengineer Mar 31, 2026
7ad2906
fix attendee avatar
nkdengineer Mar 31, 2026
b364061
Merge branch 'main' into fix/83223
nkdengineer Apr 1, 2026
f3ae724
remove useMemo
nkdengineer Apr 1, 2026
3c00f7e
show scanning per attendee
nkdengineer Apr 1, 2026
17b20bd
Merge branch 'main' into fix/83223
nkdengineer Apr 1, 2026
98d6923
Merge branch 'main' of https://github.com/nkdengineer/App into fix/83223
nkdengineer Apr 1, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions src/CONST/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7455,6 +7455,8 @@ const CONST = {
POLICY_NAME: this.TABLE_COLUMNS.POLICY_NAME,
CARD: this.TABLE_COLUMNS.CARD,
CATEGORY: this.TABLE_COLUMNS.CATEGORY,
ATTENDEES: this.TABLE_COLUMNS.ATTENDEES,
TOTAL_PER_ATTENDEE: this.TABLE_COLUMNS.TOTAL_PER_ATTENDEE,
TAG: this.TABLE_COLUMNS.TAG,
EXCHANGE_RATE: this.TABLE_COLUMNS.EXCHANGE_RATE,
ORIGINAL_AMOUNT: this.TABLE_COLUMNS.ORIGINAL_AMOUNT,
Expand Down Expand Up @@ -7578,6 +7580,8 @@ const CONST = {
this.TABLE_COLUMNS.MERCHANT,
this.TABLE_COLUMNS.FROM,
this.TABLE_COLUMNS.CATEGORY,
this.TABLE_COLUMNS.ATTENDEES,
this.TABLE_COLUMNS.TOTAL_PER_ATTENDEE,
this.TABLE_COLUMNS.TAG,
this.TABLE_COLUMNS.TOTAL_AMOUNT,
],
Expand Down Expand Up @@ -7679,6 +7683,8 @@ const CONST = {
BILLABLE: 'billable',
TAX_RATE: 'taxrate',
TOTAL_AMOUNT: 'amount',
ATTENDEES: 'attendees',
TOTAL_PER_ATTENDEE: 'totalPerAttendee',
TOTAL: 'total',
TYPE: 'type',
ACTION: 'action',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -266,8 +266,9 @@ function MoneyRequestReportTransactionList({
shouldShowBillableColumn,
shouldShowReimbursableColumn: hasNonReimbursableTransactions(transactions),
reportCurrency: report?.currency,
policy,
});
}, [transactions, currentUserDetails?.accountID, isExpenseReportViewFromIOUReport, shouldShowBillableColumn, reportDetailsColumns, report?.currency]);
}, [currentUserDetails?.accountID, transactions, isExpenseReportViewFromIOUReport, reportDetailsColumns, shouldShowBillableColumn, report?.currency, policy]);

const {windowWidth, windowHeight} = useWindowDimensions();
const minTableWidth = getTableMinWidth(columnsToShow);
Expand Down
136 changes: 136 additions & 0 deletions src/components/Search/SearchList/ListItem/AttendeesCell.tsx
Copy link
Copy Markdown
Contributor

@hungvu193 hungvu193 Mar 31, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NAB: Let's remove useMemo, inline style, so react-compiler can optimize the component itself

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We have a few feedbacks so let's fix this @nkdengineer

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This one as well @nkdengineer

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@justinpersaud I updated.

Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
import React from 'react';
import {View} from 'react-native';
import Avatar from '@components/Avatar';
import Text from '@components/Text';
import Tooltip from '@components/Tooltip';
import UserDetailsTooltip from '@components/UserDetailsTooltip';
import useDefaultAvatars from '@hooks/useDefaultAvatars';
import useLocalize from '@hooks/useLocalize';
import useOnyx from '@hooks/useOnyx';
import useStyleUtils from '@hooks/useStyleUtils';
import useTheme from '@hooks/useTheme';
import useThemeStyles from '@hooks/useThemeStyles';
import {getUserDetailTooltipText, sortIconsByName} from '@libs/ReportUtils';
import {getDefaultAvatar} from '@libs/UserAvatarUtils';
import colors from '@styles/theme/colors';
import variables from '@styles/variables';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
import type {Attendee} from '@src/types/onyx/IOU';
import type {Icon as IconType} from '@src/types/onyx/OnyxCommon';

type AttendeesCellProps = {
attendees: Attendee[];
isHovered: boolean;
isPressed: boolean;
};

function AttendeesCell({attendees, isHovered, isPressed}: AttendeesCellProps) {
const defaultAvatars = useDefaultAvatars();
const attendeeIcons: IconType[] = attendees.map((attendee) => ({
id: attendee.accountID ?? CONST.DEFAULT_NUMBER_ID,
name: attendee.displayName ?? attendee.email,
source: (attendee.avatarUrl || getDefaultAvatar({accountID: attendee.accountID, accountEmail: attendee.email, defaultAvatars})) ?? '',
type: CONST.ICON_TYPE_AVATAR,
}));

const theme = useTheme();
const styles = useThemeStyles();
const StyleUtils = useStyleUtils();
const {localeCompare, formatPhoneNumber} = useLocalize();

const [personalDetails] = useOnyx(ONYXKEYS.PERSONAL_DETAILS_LIST);

const size = CONST.AVATAR_SIZE.SMALLER;
const maxAvatarsInRow = CONST.AVATAR_ROW_SIZE.DEFAULT;
const oneAvatarSize = StyleUtils.getAvatarStyle(size);
const oneAvatarBorderWidth = StyleUtils.getAvatarBorderWidth(size).borderWidth ?? 0;
const overlapSize = oneAvatarSize.width / 3 + 2 * oneAvatarBorderWidth;
const height = oneAvatarSize.height;
const avatarContainerStyles = StyleUtils.combineStyles([styles.alignItemsCenter, styles.flexRow, StyleUtils.getHeight(height), styles.overflowHidden]);

const icons = sortIconsByName(attendeeIcons, personalDetails, localeCompare);
const tooltipTexts = icons.map((icon) => getUserDetailTooltipText(Number(icon.id), formatPhoneNumber, icon.name));

return (
<View
style={avatarContainerStyles}
testID="AttendeesCell-Row"
>
{[...icons].splice(0, maxAvatarsInRow).map((icon, index) => (
<UserDetailsTooltip
// eslint-disable-next-line react/no-array-index-key
key={`stackedAvatars-${icon.id}-${index}`}
accountID={Number(icon.id)}
icon={icon}
fallbackUserDetails={{
// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
displayName: icon.name,
}}
shouldRender
>
<View style={[StyleUtils.getHorizontalStackedAvatarStyle(index, overlapSize, -oneAvatarBorderWidth), StyleUtils.getAvatarBorderRadius(size, icon.type)]}>
<Avatar
iconAdditionalStyles={[
StyleUtils.getHorizontalStackedAvatarBorderStyle({
theme,
isHovered,
isPressed,
isInReportAction: true,
shouldUseCardBackground: true,
isActive: false,
customPressedBorderColor: theme.activeComponentBG,
}),
StyleUtils.getAvatarBorderWidth(size),
]}
source={icon.source}
size={size}
name={icon.name}
avatarID={icon.id}
type={icon.type}
fallbackIcon={icon.fallbackIcon}
testID="AttendeesCell-Avatar"
/>
</View>
</UserDetailsTooltip>
))}
{icons.length > maxAvatarsInRow && (
<Tooltip
// We only want to cap tooltips to only 10 users or so since some reports have hundreds of users, causing performance to degrade.
text={tooltipTexts.slice(maxAvatarsInRow - 1, maxAvatarsInRow + 9).join(', ')}
Comment thread
justinpersaud marked this conversation as resolved.
shouldRender
>
<View
testID="AttendeesCell-LimitReached"
style={[
styles.alignItemsCenter,
styles.justifyContentCenter,
StyleUtils.getHorizontalStackedAvatarBorderStyle({
theme,
isHovered,
isPressed,
isInReportAction: true,
shouldUseCardBackground: true,
customPressedBorderColor: theme.activeComponentBG,
}),

// Set overlay background color with RGBA value so that the text will not inherit opacity
StyleUtils.getHorizontalStackedOverlayAvatarStyle(oneAvatarSize, oneAvatarBorderWidth),
icons.at(3)?.type === CONST.ICON_TYPE_WORKSPACE && StyleUtils.getAvatarBorderRadius(size, icons.at(3)?.type),
Comment thread
justinpersaud marked this conversation as resolved.
StyleUtils.getBackgroundColorWithOpacityStyle(colors.productDark400, variables.overlayOpacity),
]}
>
<View style={[styles.justifyContentCenter, styles.alignItemsCenter, StyleUtils.getHeight(oneAvatarSize.height), StyleUtils.getWidthStyle(oneAvatarSize.width)]}>
<Text
style={[styles.avatarInnerTextSmall, StyleUtils.getAvatarExtraFontSizeStyle(size), styles.userSelectNone, styles.textMicroBold, styles.buttonSuccessText]}
dataSet={{[CONST.SELECTION_SCRAPER_HIDDEN_ELEMENT]: true}}
>{`+${icons.length - maxAvatarsInRow}`}</Text>
</View>
</View>
</Tooltip>
)}
</View>
);
}

export default AttendeesCell;
8 changes: 8 additions & 0 deletions src/components/Search/SearchTableHeader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,14 @@ const getExpenseHeaders = (groupBy?: SearchGroupBy): SearchColumnConfig[] => [
columnName: CONST.SEARCH.TABLE_COLUMNS.CATEGORY,
translationKey: 'common.category',
},
{
columnName: CONST.SEARCH.TABLE_COLUMNS.ATTENDEES,
translationKey: 'iou.attendees',
},
{
columnName: CONST.SEARCH.TABLE_COLUMNS.TOTAL_PER_ATTENDEE,
translationKey: 'iou.totalPerAttendee',
},
{
columnName: CONST.SEARCH.TABLE_COLUMNS.TAG,
translationKey: 'common.tag',
Expand Down
52 changes: 52 additions & 0 deletions src/components/TransactionItemRow/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import type {TransactionWithOptionalHighlight} from '@components/MoneyRequestRep
import {PressableWithFeedback} from '@components/Pressable';
import RadioButton from '@components/RadioButton';
import ActionCell from '@components/Search/SearchList/ListItem/ActionCell';
import AttendeesCell from '@components/Search/SearchList/ListItem/AttendeesCell';
import DateCell from '@components/Search/SearchList/ListItem/DateCell';
import ExportedIconCell from '@components/Search/SearchList/ListItem/ExportedIconCell';
import StatusCell from '@components/Search/SearchList/ListItem/StatusCell';
Expand All @@ -16,6 +17,7 @@ import UserInfoCell from '@components/Search/SearchList/ListItem/UserInfoCell';
import WorkspaceCell from '@components/Search/SearchList/ListItem/WorkspaceCell';
import type {SearchColumnType, TableColumnSize} from '@components/Search/types';
import Text from '@components/Text';
import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails';
import {useMemoizedLazyExpensifyIcons} from '@hooks/useLazyAsset';
import useLocalize from '@hooks/useLocalize';
import useResponsiveLayout from '@hooks/useResponsiveLayout';
Expand All @@ -29,6 +31,9 @@ import {getReportName} from '@libs/ReportNameUtils';
import {isExpenseReport, isIOUReport, isSettled} from '@libs/ReportUtils';
import StringUtils from '@libs/StringUtils';
import {
getAmount,
getAttendees,
getCurrency,
getDescription,
getExchangeRate,
getMerchant,
Expand All @@ -43,6 +48,7 @@ import {
isScanning,
isTimeRequest,
isUnreportedAndHasInvalidDistanceRateTransaction,
shouldShowAttendees as shouldShowAttendeesUtils,
} from '@libs/TransactionUtils';
import variables from '@styles/variables';
import CONST from '@src/CONST';
Expand Down Expand Up @@ -202,6 +208,7 @@ function TransactionItemRow({
const hasCategoryOrTag = !isCategoryMissing(transactionItem?.category) || !!transactionItem.tag;
const createdAt = getTransactionCreated(transactionItem);
const expensicons = useMemoizedLazyExpensifyIcons(['ArrowRight']);
const currentUserPersonalDetails = useCurrentUserPersonalDetails();
const transactionThreadReportID = reportActions ? getIOUActionForTransactionID(reportActions, transactionItem.transactionID)?.childReportID : undefined;

const isDateColumnWide = dateColumnSize === CONST.SEARCH.TABLE_COLUMN_SIZES.WIDE;
Expand Down Expand Up @@ -267,6 +274,21 @@ function TransactionItemRow({
return transactionItem.cardName;
}, [transactionItem.cardID, transactionItem.cardName, transactionItem.isCardFeedDeleted, customCardNames, translate]);

const transactionAttendees = useMemo(() => getAttendees(transactionItem, currentUserPersonalDetails), [transactionItem, currentUserPersonalDetails]);

const shouldShowAttendees = shouldShowAttendeesUtils(CONST.IOU.TYPE.SUBMIT, policy) && transactionAttendees.length > 0;

const totalPerAttendee = useMemo(() => {
const attendeesCount = transactionAttendees.length ?? 0;
const totalAmount = getAmount(transactionItem);

if (!attendeesCount || totalAmount === undefined) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we also need to check isScanning(transactionItem) here in case the receipt hasn't been filled in yet

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@nkdengineer can you look into this one?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@justinpersaud What we should show in the case isScanning? 0 or Scanning.. or empty string.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@justinpersaud Updated.

return undefined;
}

return totalAmount / attendeesCount;
}, [transactionAttendees.length, transactionItem]);

const renderColumn = (column: SearchColumnType): React.ReactNode => {
switch (column) {
case CONST.SEARCH.TABLE_COLUMNS.TYPE:
Expand Down Expand Up @@ -495,6 +517,21 @@ function TransactionItemRow({
<TextCell text={cardName} />
</View>
);
case CONST.SEARCH.TABLE_COLUMNS.ATTENDEES:
return (
<View
key={column}
style={[StyleUtils.getReportTableColumnStyles(CONST.SEARCH.TABLE_COLUMNS.ATTENDEES)]}
>
{shouldShowAttendees && (
<AttendeesCell
attendees={transactionAttendees}
isHovered={isHover}
isPressed={isSelected}
/>
)}
</View>
);
case CONST.SEARCH.TABLE_COLUMNS.COMMENTS:
return (
<View
Expand Down Expand Up @@ -529,6 +566,21 @@ function TransactionItemRow({
/>
</View>
);
case CONST.SEARCH.TABLE_COLUMNS.TOTAL_PER_ATTENDEE:
return (
<View
key={column}
style={[StyleUtils.getReportTableColumnStyles(CONST.SEARCH.TABLE_COLUMNS.TOTAL_PER_ATTENDEE, undefined, isAmountColumnWide)]}
>
{shouldShowAttendees && (
<AmountCell
total={totalPerAttendee ?? 0}
currency={getCurrency(transactionItem)}
isScanning={isScanning(transactionItem)}
/>
)}
</View>
);
case CONST.SEARCH.TABLE_COLUMNS.ORIGINAL_AMOUNT:
return (
<View
Expand Down
1 change: 1 addition & 0 deletions src/languages/de.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1541,6 +1541,7 @@ const translations: TranslationDeepObject<typeof en> = {
bookingArchived: 'Diese Buchung ist archiviert',
bookingArchivedDescription: 'Diese Buchung ist archiviert, weil das Reisedatum verstrichen ist. Füge bei Bedarf eine Ausgabe für den endgültigen Betrag hinzu.',
attendees: 'Teilnehmende',
totalPerAttendee: 'Pro Teilnehmendem',
whoIsYourAccountant: 'Wer ist Ihre Steuerberaterin bzw. Ihr Steuerberater?',
paymentComplete: 'Zahlung abgeschlossen',
time: 'Zeit',
Expand Down
1 change: 1 addition & 0 deletions src/languages/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1594,6 +1594,7 @@ const translations = {
bookingArchived: 'This booking is archived',
bookingArchivedDescription: 'This booking is archived because the trip date has passed. Add an expense for the final amount if needed.',
attendees: 'Attendees',
totalPerAttendee: 'Per attendee',
whoIsYourAccountant: 'Who is your accountant?',
paymentComplete: 'Payment complete',
time: 'Time',
Expand Down
1 change: 1 addition & 0 deletions src/languages/es.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1462,6 +1462,7 @@ const translations: TranslationDeepObject<typeof en> = {
bookingArchived: 'Esta reserva está archivada',
bookingArchivedDescription: 'Esta reserva está archivada porque la fecha del viaje ha pasado. Agregue un gasto por el monto final si es necesario.',
attendees: 'Asistentes',
totalPerAttendee: 'Por asistente',
whoIsYourAccountant: '¿Quién es tu contador?',
paymentComplete: 'Pago completo',
time: 'Tiempo',
Expand Down
1 change: 1 addition & 0 deletions src/languages/fr.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1545,6 +1545,7 @@ const translations: TranslationDeepObject<typeof en> = {
bookingArchived: 'Cette réservation est archivée',
bookingArchivedDescription: 'Cette réservation est archivée car la date du voyage est passée. Ajoutez une dépense pour le montant final si nécessaire.',
attendees: 'Participants',
totalPerAttendee: 'Par participant',
whoIsYourAccountant: 'Qui est votre comptable ?',
paymentComplete: 'Paiement terminé',
time: 'Heure',
Expand Down
1 change: 1 addition & 0 deletions src/languages/it.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1543,6 +1543,7 @@ const translations: TranslationDeepObject<typeof en> = {
bookingArchived: 'Questa prenotazione è archiviata',
bookingArchivedDescription: 'Questa prenotazione è archiviata perché la data del viaggio è passata. Aggiungi una spesa per l’importo finale, se necessario.',
attendees: 'Partecipanti',
totalPerAttendee: 'Per partecipante',
whoIsYourAccountant: 'Chi è il tuo commercialista?',
paymentComplete: 'Pagamento completato',
time: 'Ora',
Expand Down
1 change: 1 addition & 0 deletions src/languages/ja.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1524,6 +1524,7 @@ const translations: TranslationDeepObject<typeof en> = {
bookingArchived: 'この予約はアーカイブされています',
bookingArchivedDescription: 'この予約は旅行日が過ぎたためアーカイブされています。必要に応じて、最終金額の経費を追加してください。',
attendees: '参加者',
totalPerAttendee: '参加者ごと',
whoIsYourAccountant: 'あなたの会計士は誰ですか?',
paymentComplete: '支払いが完了しました',
time: '時間',
Expand Down
1 change: 1 addition & 0 deletions src/languages/nl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1540,6 +1540,7 @@ const translations: TranslationDeepObject<typeof en> = {
bookingArchived: 'Deze boeking is gearchiveerd',
bookingArchivedDescription: 'Deze boeking is gearchiveerd omdat de reisdatum is verstreken. Voeg indien nodig een uitgave toe voor het eindbedrag.',
attendees: 'Deelnemers',
totalPerAttendee: 'Per deelnemer',
whoIsYourAccountant: 'Wie is jouw accountant?',
paymentComplete: 'Betaling voltooid',
time: 'Tijd',
Expand Down
1 change: 1 addition & 0 deletions src/languages/pl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1538,6 +1538,7 @@ const translations: TranslationDeepObject<typeof en> = {
bookingArchived: 'Ta rezerwacja jest zarchiwizowana',
bookingArchivedDescription: 'Ta rezerwacja została zarchiwizowana, ponieważ data podróży już minęła. W razie potrzeby dodaj wydatek na ostateczną kwotę.',
attendees: 'Uczestnicy',
totalPerAttendee: 'Na uczestnika',
whoIsYourAccountant: 'Kim jest twój księgowy?',
paymentComplete: 'Płatność zakończona',
time: 'Czas',
Expand Down
1 change: 1 addition & 0 deletions src/languages/pt-BR.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1537,6 +1537,7 @@ const translations: TranslationDeepObject<typeof en> = {
bookingArchived: 'Esta reserva está arquivada',
bookingArchivedDescription: 'Esta reserva está arquivada porque a data da viagem já passou. Adicione uma despesa com o valor final, se necessário.',
attendees: 'Participantes',
totalPerAttendee: 'Por participante',
whoIsYourAccountant: 'Quem é seu contador?',
paymentComplete: 'Pagamento concluído',
time: 'Hora',
Expand Down
1 change: 1 addition & 0 deletions src/languages/zh-hans.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1490,6 +1490,7 @@ const translations: TranslationDeepObject<typeof en> = {
bookingArchived: '此预订已归档',
bookingArchivedDescription: '此预订已归档,因为行程日期已过。如有需要,请为最终金额添加一笔报销。',
attendees: '参与者',
totalPerAttendee: '每位参与者',
whoIsYourAccountant: '你的会计是谁?',
paymentComplete: '付款完成',
time: '时间',
Expand Down
Loading
Loading