Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
48 commits
Select commit Hold shift + click to select a range
94c6a8d
lots of placeholder code worth saving
Gonals Jan 25, 2026
b145fc0
Add draft
Gonals Jan 27, 2026
90a7ac5
new pages
Gonals Jan 27, 2026
1d68335
bunch of date handling
Gonals Jan 27, 2026
7857b51
Let's add release 1 code for testing
Gonals Jan 27, 2026
ae937c7
better IDs
Gonals Jan 27, 2026
17c936f
Add file
Gonals Jan 28, 2026
4757bab
better cardIDs
Gonals Jan 28, 2026
9528a6c
better translations
Gonals Jan 28, 2026
6d0f85e
prettier
Gonals Jan 28, 2026
9571ccf
properly format button
Gonals Jan 28, 2026
e7ca91c
conflicts
Gonals Jan 29, 2026
30d13fc
comment
Gonals Jan 29, 2026
e9cae47
remove uneeded file
Gonals Jan 29, 2026
b54f090
fix errors
Gonals Jan 29, 2026
f7cb785
fix a bunch of errors
Gonals Jan 29, 2026
e45637c
cell format
Gonals Jan 29, 2026
551b336
typescript fix
Gonals Jan 29, 2026
337eaf8
one more
Gonals Jan 29, 2026
87c8139
Merge branch 'main' into alberto-release3
Gonals Jan 29, 2026
3129d58
a few more
Gonals Jan 29, 2026
6880b25
translations
Gonals Jan 29, 2026
097225f
better styling
Gonals Jan 29, 2026
9d33861
and more
Gonals Jan 29, 2026
73730da
conflicts
Gonals Jan 29, 2026
058f86f
prettier
Gonals Jan 29, 2026
e308138
linter
Gonals Jan 29, 2026
b88269d
lint
Gonals Jan 29, 2026
a87a9c4
lint
Gonals Jan 29, 2026
e0470e2
another try
Gonals Jan 29, 2026
e94679e
another
Gonals Jan 29, 2026
87a5f5e
navigate back
Gonals Jan 29, 2026
12de07b
hide behind beta
Gonals Jan 29, 2026
4199f66
prettier
Gonals Jan 29, 2026
93c4643
fix cursors
Gonals Jan 29, 2026
81cbcba
Card name required
Gonals Jan 29, 2026
186c96d
use numbers
Gonals Jan 29, 2026
47faaaf
better transition
Gonals Jan 29, 2026
5ec797d
conflicts
Gonals Jan 30, 2026
1943b27
remove deprecated and displaynames
Gonals Jan 30, 2026
b705e49
Merge branch 'main' into alberto-release3
Gonals Jan 30, 2026
ce6558a
prettier
Gonals Jan 30, 2026
38618b0
actual hook
Gonals Jan 30, 2026
feed797
conflicts
Gonals Jan 30, 2026
97e96b1
lint
Gonals Jan 30, 2026
b7a6508
key bug
Gonals Jan 30, 2026
cc115e9
transition
Gonals Jan 30, 2026
cbcb187
unrelated changes
Gonals Jan 30, 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
2 changes: 1 addition & 1 deletion Mobile-Expensify
3 changes: 3 additions & 0 deletions src/ONYXKEYS.ts
Original file line number Diff line number Diff line change
Expand Up @@ -800,6 +800,8 @@ const ONYXKEYS = {
WORKSPACE_COMPANY_CARD_FEED_NAME_DRAFT: 'workspaceCompanyCardFeedNameDraft',
EDIT_WORKSPACE_COMPANY_CARD_NAME_FORM: 'editCompanyCardName',
EDIT_WORKSPACE_COMPANY_CARD_NAME_DRAFT_FORM: 'editCompanyCardNameDraft',
IMPORT_TRANSACTIONS_FORM: 'importTransactionsForm',
IMPORT_TRANSACTIONS_FORM_DRAFT: 'importTransactionsFormDraft',
EDIT_PERSONAL_CARD_NAME_FORM: 'editPersonalCardName',
EDIT_PERSONAL_CARD_NAME_FORM_DRAFT: 'editPersonalCardNameDraft',
WORKSPACE_REPORT_FIELDS_FORM: 'workspaceReportFieldForm',
Expand Down Expand Up @@ -1018,6 +1020,7 @@ type OnyxFormValuesMapping = {
[ONYXKEYS.FORMS.WORKSPACE_TAX_CUSTOM_NAME]: FormTypes.WorkspaceTaxCustomName;
[ONYXKEYS.FORMS.WORKSPACE_COMPANY_CARD_FEED_NAME]: FormTypes.WorkspaceCompanyCardFeedName;
[ONYXKEYS.FORMS.EDIT_WORKSPACE_COMPANY_CARD_NAME_FORM]: FormTypes.WorkspaceCompanyCardEditName;
[ONYXKEYS.FORMS.IMPORT_TRANSACTIONS_FORM]: FormTypes.ImportTransactionsForm;
[ONYXKEYS.FORMS.EDIT_PERSONAL_CARD_NAME_FORM]: FormTypes.EditPersonalCardNameForm;
[ONYXKEYS.FORMS.WORKSPACE_REPORT_FIELDS_FORM]: FormTypes.WorkspaceReportFieldForm;
[ONYXKEYS.FORMS.WORKSPACE_CATEGORY_DESCRIPTION_HINT_FORM]: FormTypes.WorkspaceCategoryDescriptionHintForm;
Expand Down
21 changes: 21 additions & 0 deletions src/ROUTES.ts
Original file line number Diff line number Diff line change
Expand Up @@ -393,6 +393,27 @@ const ROUTES = {
},
SETTINGS_WALLET_TRANSFER_BALANCE: 'settings/wallet/transfer-balance',
SETTINGS_WALLET_CHOOSE_TRANSFER_ACCOUNT: 'settings/wallet/choose-transfer-account',
SETTINGS_WALLET_IMPORT_TRANSACTIONS: 'settings/wallet/import-transactions',
SETTINGS_WALLET_IMPORT_TRANSACTIONS_CARD_NAME: 'settings/wallet/import-transactions/card-name',
SETTINGS_WALLET_IMPORT_TRANSACTIONS_CURRENCY: 'settings/wallet/import-transactions/currency',
SETTINGS_WALLET_IMPORT_TRANSACTIONS_SPREADSHEET: {
route: 'settings/wallet/import-transactions-spreadsheet/:cardID?',
getRoute: (cardID?: number) => {
if (cardID) {
return `settings/wallet/import-transactions-spreadsheet/${cardID}` as const;
}
return 'settings/wallet/import-transactions-spreadsheet' as const;
},
},
SETTINGS_WALLET_TRANSACTIONS_IMPORTED: {
route: 'settings/wallet/transactions-imported/:cardID?',
getRoute: (cardID?: number) => {
if (cardID) {
return `settings/wallet/transactions-imported/${cardID}` as const;
}
return 'settings/wallet/transactions-imported' as const;
},
},
SETTINGS_WALLET_REPORT_CARD_LOST_OR_DAMAGED: {
route: 'settings/wallet/card/:cardID/report-card-lost-or-damaged',
getRoute: (cardID: string) => `settings/wallet/card/${cardID}/report-card-lost-or-damaged` as const,
Expand Down
5 changes: 5 additions & 0 deletions src/SCREENS.ts
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,11 @@ const SCREENS = {
CARD_MISSING_DETAILS_CONFIRM_MAGIC_CODE: 'Settings_Wallet_Card_MissingDetails_ConfirmMagicCode',
TRANSFER_BALANCE: 'Settings_Wallet_Transfer_Balance',
CHOOSE_TRANSFER_ACCOUNT: 'Settings_Wallet_Choose_Transfer_Account',
IMPORT_TRANSACTIONS: 'Settings_Wallet_Import_Transactions',
IMPORT_TRANSACTIONS_CARD_NAME: 'Settings_Wallet_Import_Transactions_Card_Name',
IMPORT_TRANSACTIONS_CURRENCY: 'Settings_Wallet_Import_Transactions_Currency',
IMPORT_TRANSACTIONS_SPREADSHEET: 'Settings_Wallet_Import_Transactions_Spreadsheet',
TRANSACTIONS_IMPORTED: 'Settings_Wallet_Transactions_Imported',
ENABLE_PAYMENTS: 'Settings_Wallet_EnablePayments',
CARD_ACTIVATE: 'Settings_Wallet_Card_Activate',
REPORT_VIRTUAL_CARD_FRAUD: 'Settings_Wallet_ReportVirtualCardFraud',
Expand Down
26 changes: 21 additions & 5 deletions src/components/ImportSpreadsheet.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import RNFetchBlob from 'react-native-blob-util';
import type {TupleToUnion} from 'type-fest';
import {useMemoizedLazyExpensifyIcons} from '@hooks/useLazyAsset';
import useLocalize from '@hooks/useLocalize';
import useOnyx from '@hooks/useOnyx';
import useResponsiveLayout from '@hooks/useResponsiveLayout';
import useThemeStyles from '@hooks/useThemeStyles';
import {setSpreadsheetData} from '@libs/actions/ImportSpreadsheet';
Expand All @@ -13,6 +14,7 @@ import {splitExtensionFromFileName} from '@libs/fileDownload/FileUtils';
import Navigation from '@libs/Navigation/Navigation';
import CONST from '@src/CONST';
import type {TranslationPaths} from '@src/languages/types';
import ONYXKEYS from '@src/ONYXKEYS';
import type {Route as Routes} from '@src/ROUTES';
import type {FileObject} from '@src/types/utils/Attachment';
import Button from './Button';
Expand All @@ -38,6 +40,7 @@ type ImportSpreadsheetProps = {
};

function ImportSpreadsheet({backTo, goTo, isImportingMultiLevelTags}: ImportSpreadsheetProps) {
const [importedSpreadsheet] = useOnyx(ONYXKEYS.IMPORTED_SPREADSHEET, {canBeMissing: true});
const icons = useMemoizedLazyExpensifyIcons(['SpreadsheetComputer']);
const styles = useThemeStyles();
const {translate} = useLocalize();
Expand Down Expand Up @@ -104,20 +107,33 @@ function ImportSpreadsheet({backTo, goTo, isImportingMultiLevelTags}: ImportSpre
.then((data) => {
return data.text();
})
.then((text) => XLSX.read(text, {type: 'string'}));
.then((text) => XLSX.read(text, {type: 'string', raw: true}));
}
return fetch(fileURI)
.then((data) => {
return data.arrayBuffer();
})
.then((arrayBuffer) => XLSX.read(new Uint8Array(arrayBuffer), {type: 'buffer'}));
.then((arrayBuffer) => XLSX.read(new Uint8Array(arrayBuffer), {type: 'buffer', raw: true}));
};
readWorkbook()
.then((workbook) => {
const worksheet = workbook.Sheets[workbook.SheetNames[0]];
const data = XLSX.utils.sheet_to_json(worksheet, {header: 1, blankrows: false}) as string[][] | unknown[][];
const formattedSpreadsheetData = data.map((row) => row.map((cell) => String(cell)));
setSpreadsheetData(formattedSpreadsheetData, fileURI, file.type, file.name, isImportingMultiLevelTags ?? false)
// Use raw: true to preserve original string values from CSV (especially dates)
const data = XLSX.utils.sheet_to_json(worksheet, {header: 1, blankrows: false, raw: true}) as string[][] | unknown[][];
Comment on lines +121 to +122

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.

raw: true

Is this safe change? ImportSpreadSheet is common component and we should make sure that this doesn't cause regressions on other pages like categories, tags, per diem etc.

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.

I did some testing around and it seems fine. It is mainly for dates

const formattedSpreadsheetData = data.map((row) =>
row.map((cell) => {
if (cell == null) {
return '';
}
// Handle primitives (string, number, boolean) directly
if (typeof cell === 'string' || typeof cell === 'number' || typeof cell === 'boolean') {
return String(cell);
}
// For objects (Date, arrays, etc.), serialize to JSON
return JSON.stringify(cell);
}),
);
setSpreadsheetData(formattedSpreadsheetData, fileURI, file.type, file.name, isImportingMultiLevelTags ?? false, importedSpreadsheet?.importTransactionSettings)
.then(() => {
Navigation.navigate(goTo);
})
Expand Down
9 changes: 1 addition & 8 deletions src/components/PopoverMenu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -430,14 +430,7 @@ function BasePopoverMenu({
if (!headerText || enteredSubMenuIndexes.length !== 0) {
return;
}
return (
<Text
key="header-text"
style={[styles.createMenuHeaderText, styles.ph5, styles.pv3, headerStyles]}
>
{headerText}
</Text>
);
return <Text style={[styles.createMenuHeaderText, styles.ph5, styles.pv3, headerStyles]}>{headerText}</Text>;

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.

@Gonals is there any reason you removed the key prop from here? It's causing this issue.

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.

It was a weird merge conflict. Fixing it in the other PR

};

useKeyboardShortcut(
Expand Down
11 changes: 11 additions & 0 deletions src/languages/de.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1057,6 +1057,8 @@ const translations: TranslationDeepObject<typeof en> = {
other: (count: number) =>
`Bitte bestätige die folgenden Details für die ${count} neuen Workspace-Mitglieder, die im Rahmen dieses Uploads hinzugefügt werden. Bestehende Mitglieder erhalten keine Rollenaktualisierungen oder Einladung Nachrichten.`,
}),
importTransactionsSuccessfulDescription: ({transactions}: {transactions: number}) =>
transactions > 1 ? `${transactions} Transaktionen wurden importiert.` : '1 Transaktion wurde importiert.',
},
receipt: {
upload: 'Beleg hochladen',
Expand Down Expand Up @@ -4963,6 +4965,15 @@ _Für ausführlichere Anweisungen [besuchen Sie unsere Hilfeseite](${CONST.NETSU
editStartDateDescription:
'Wählen Sie ein neues Startdatum für Transaktionen. Wir synchronisieren alle Transaktionen ab diesem Datum, ausgenommen diejenigen, die wir bereits importiert haben.',
unassignCardFailedError: 'Aufhebung der Kartenzuweisung fehlgeschlagen.',
importTransactions: {
title: 'Transaktionen aus Datei importieren',
description: 'Bitte passen Sie die Einstellungen für Ihre Datei an, die beim Import angewendet werden.',
cardDisplayName: 'Name der Karte',
currency: 'Währung',
transactionsAreReimbursable: 'Transaktionen sind erstattungsfähig',
flipAmountSign: 'Vorzeichen des Betrags umkehren',
importButton: 'Transaktionen importieren',
},
error: {
workspaceFeedsCouldNotBeLoadedTitle: 'Kartenfeeds konnten nicht geladen werden',
workspaceFeedsCouldNotBeLoadedMessage:
Expand Down
11 changes: 11 additions & 0 deletions src/languages/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1073,6 +1073,8 @@ const translations = {
importTagsSuccessfulDescription: ({tags}: {tags: number}) => (tags > 1 ? `${tags} tags have been added.` : '1 tag has been added.'),
importMultiLevelTagsSuccessfulDescription: 'Multi-level tags have been added.',
importPerDiemRatesSuccessfulDescription: ({rates}: {rates: number}) => (rates > 1 ? `${rates} per diem rates have been added.` : '1 per diem rate has been added.'),
importTransactionsSuccessfulDescription: ({transactions}: {transactions: number}) =>
transactions > 1 ? `${transactions} transactions have been imported.` : '1 transaction has been imported.',
importFailedTitle: 'Import failed',
importFailedDescription: 'Please ensure all fields are filled out correctly and try again. If the problem persists, please reach out to Concierge.',
importDescription: 'Choose which fields to map from your spreadsheet by clicking the dropdown next to each imported column below.',
Expand Down Expand Up @@ -4921,6 +4923,15 @@ const translations = {
assignCardFailedError: 'Card assignment failed.',
unassignCardFailedError: 'Card unassignment failed.',
cardAlreadyAssignedError: 'This card is already assigned to a user in another workspace.',
importTransactions: {
title: 'Import transactions from file',
description: 'Please adjust the settings for your file that will be applied on import.',
cardDisplayName: 'Card display name',
currency: 'Currency',
transactionsAreReimbursable: 'Transactions are reimbursable',
flipAmountSign: 'Flip amount sign',
importButton: 'Import transactions',
},
},
expensifyCard: {
issueAndManageCards: 'Issue and manage your Expensify Cards',
Expand Down
10 changes: 10 additions & 0 deletions src/languages/es.ts
Original file line number Diff line number Diff line change
Expand Up @@ -820,6 +820,7 @@ const translations: TranslationDeepObject<typeof en> = {
importTagsSuccessfulDescription: ({tags}) => (tags > 1 ? `Se han agregado ${tags} etiquetas.` : 'Se ha agregado 1 etiqueta.'),
importMultiLevelTagsSuccessfulDescription: 'Etiquetas de nivel múltiple han sido agregadas.',
importPerDiemRatesSuccessfulDescription: ({rates}) => (rates > 1 ? `Se han añadido ${rates} tasas de per diem.` : 'Se ha añadido 1 tasa de per diem.'),
importTransactionsSuccessfulDescription: ({transactions}) => (transactions > 1 ? `Se han importado ${transactions} transacciones.` : 'Se ha importado 1 transacción.'),
importSuccessfulTitle: 'Importar categorías',
importDescription: 'Elige qué campos mapear desde tu hoja de cálculo haciendo clic en el menú desplegable junto a cada columna importada a continuación.',
sizeNotMet: 'El archivo adjunto debe ser más grande que 0 bytes.',
Expand Down Expand Up @@ -4672,6 +4673,15 @@ ${amount} para ${merchant} - ${date}`,
assignCardFailedError: 'Error al asignar la tarjeta.',
unassignCardFailedError: 'Error al desasignar la tarjeta.',
cardAlreadyAssignedError: 'Esta tarjeta ya está asignada a un usuario en otro espacio de trabajo.',
importTransactions: {
title: 'Importar transacciones desde archivo',
description: 'Por favor, ajusta la configuración de archivo que se aplicará al importar.',
cardDisplayName: 'Nombre de la tarjeta',
currency: 'Moneda',
transactionsAreReimbursable: 'Las transacciones son reembolsables',
flipAmountSign: 'Invertir el signo del monto',
importButton: 'Importar transacciones',
},
},
expensifyCard: {
issueAndManageCards: 'Emitir y gestionar Tarjetas Expensify',
Expand Down
11 changes: 11 additions & 0 deletions src/languages/fr.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1059,6 +1059,8 @@ const translations: TranslationDeepObject<typeof en> = {
other: (count: number) =>
`Veuillez confirmer les détails ci-dessous pour les ${count} nouveaux membres d’espace de travail qui seront ajoutés dans le cadre de cet import. Les membres existants ne recevront aucune mise à jour de rôle ni message d’invitation.`,
}),
importTransactionsSuccessfulDescription: ({transactions}: {transactions: number}) =>
transactions > 1 ? `${transactions} transactions ont été importées.` : '1 transaction a été importée.',
},
receipt: {
upload: 'Téléverser un reçu',
Expand Down Expand Up @@ -4970,6 +4972,15 @@ _Pour des instructions plus détaillées, [visitez notre site d’aide](${CONST.
editStartDateDescription:
'Choisissez une nouvelle date de début des transactions. Nous synchroniserons toutes les transactions à partir de cette date, en excluant celles que nous avons déjà importées.',
unassignCardFailedError: 'Échec de la désaffectation de la carte.',
importTransactions: {
title: 'Importer des transactions depuis un fichier',
description: 'Veuillez ajuster les paramètres de votre fichier qui seront appliqués lors de l’importation.',
cardDisplayName: 'Nom d’affichage de la carte',
currency: 'Devise',
transactionsAreReimbursable: 'Les transactions sont remboursables',
flipAmountSign: 'Inverser le signe du montant',
importButton: 'Importer des transactions',
},
error: {
workspaceFeedsCouldNotBeLoadedTitle: 'Impossible de charger les flux de carte',
workspaceFeedsCouldNotBeLoadedMessage:
Expand Down
11 changes: 11 additions & 0 deletions src/languages/it.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1055,6 +1055,8 @@ const translations: TranslationDeepObject<typeof en> = {
other: (count: number) =>
`Conferma i dettagli qui sotto per i ${count} nuovi membri dello spazio di lavoro che verranno aggiunti come parte di questo caricamento. I membri esistenti non riceveranno aggiornamenti del ruolo né messaggi di invito.`,
}),
importTransactionsSuccessfulDescription: ({transactions}: {transactions: number}) =>
transactions > 1 ? `${transactions} transazioni sono state importate.` : '1 transazione è stata importata.',
},
receipt: {
upload: 'Carica ricevuta',
Expand Down Expand Up @@ -4948,6 +4950,15 @@ _Per istruzioni più dettagliate, [visita il nostro sito di assistenza](${CONST.
cardAlreadyAssignedError: 'This card is already assigned to a user in another workspace.',
editStartDateDescription: 'Scegli una nuova data di inizio per le transazioni. Sincronizzeremo tutte le transazioni da quella data in poi, escludendo quelle già importate.',
unassignCardFailedError: 'Rimozione della carta non riuscita.',
importTransactions: {
title: 'Importa transazioni da file',
description: 'Regola le impostazioni per il tuo file che verranno applicate all’importazione.',
cardDisplayName: 'Nome visualizzato carta',
currency: 'Valuta',
transactionsAreReimbursable: 'Le transazioni sono rimborsabili',
flipAmountSign: 'Inverti segno importo',
importButton: 'Importa transazioni',
},
error: {
workspaceFeedsCouldNotBeLoadedTitle: 'Impossibile caricare i feed delle carte',
workspaceFeedsCouldNotBeLoadedMessage:
Expand Down
11 changes: 11 additions & 0 deletions src/languages/ja.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1053,6 +1053,8 @@ const translations: TranslationDeepObject<typeof en> = {
other: (count: number) =>
`このアップロードで追加される新しいワークスペースメンバー${count}人分の詳細を、以下で確認してください。既存のメンバーには、ロールの更新や招待メッセージは送信されません。`,
}),
importTransactionsSuccessfulDescription: ({transactions}: {transactions: number}) =>
transactions > 1 ? `${transactions}件の取引がインポートされました。` : '1件の取引がインポートされました。',
},
receipt: {
upload: '領収書をアップロード',
Expand Down Expand Up @@ -4919,6 +4921,15 @@ _より詳しい手順については、[ヘルプサイトをご覧ください
cardAlreadyAssignedError: 'This card is already assigned to a user in another workspace.',
editStartDateDescription: '新しい取引の開始日を選択してください。その日以降のすべての取引を、すでに取り込んだものを除いて同期します。',
unassignCardFailedError: 'カードの割り当て解除に失敗しました。',
importTransactions: {
title: 'ファイルから取引をインポート',
description: 'インポート時に適用されるファイルの設定を調整してください。',
cardDisplayName: 'カード表示名',
currency: '通貨',
transactionsAreReimbursable: '取扱明細は精算対象です',
flipAmountSign: '金額の符号を反転',
importButton: '取引をインポート',
},
error: {
workspaceFeedsCouldNotBeLoadedTitle: 'カードフィードを読み込めませんでした',
workspaceFeedsCouldNotBeLoadedMessage: 'ワークスペースのカードフィードを読み込む際にエラーが発生しました。もう一度お試しいただくか、管理者に連絡してください。',
Expand Down
Loading
Loading