Skip to content

Commit 6b9184a

Browse files
authored
Merge branch 'Expensify:main' into fixresetNonUSDBankAccount
2 parents 6c1d0ee + 8f4897f commit 6b9184a

36 files changed

Lines changed: 1368 additions & 36 deletions

src/ONYXKEYS.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -466,6 +466,9 @@ const ONYXKEYS = {
466466

467467
NVP_PRIVATE_CANCELLATION_DETAILS: 'nvp_private_cancellationDetails',
468468

469+
/** Stores the information about duplicated workspace */
470+
DUPLICATE_WORKSPACE: 'duplicateWorkspace',
471+
469472
/** Stores the information about currently edited advanced approval workflow */
470473
APPROVAL_WORKFLOW: 'approvalWorkflow',
471474

@@ -683,6 +686,8 @@ const ONYXKEYS = {
683686
WORKSPACE_CATEGORY_FORM: 'workspaceCategoryForm',
684687
WORKSPACE_CONFIRMATION_FORM: 'workspaceConfirmationForm',
685688
WORKSPACE_CONFIRMATION_FORM_DRAFT: 'workspaceConfirmationFormDraft',
689+
WORKSPACE_DUPLICATE_FORM: 'workspaceDuplicateForm',
690+
WORKSPACE_DUPLICATE_FORM_DRAFT: 'workspaceDuplicateFormDraft',
686691
WORKSPACE_CATEGORY_FORM_DRAFT: 'workspaceCategoryFormDraft',
687692
WORKSPACE_CATEGORY_DESCRIPTION_HINT_FORM: 'workspaceCategoryDescriptionHintForm',
688693
WORKSPACE_CATEGORY_DESCRIPTION_HINT_FORM_DRAFT: 'workspaceCategoryDescriptionHintFormDraft',
@@ -889,6 +894,7 @@ type OnyxFormValuesMapping = {
889894
[ONYXKEYS.FORMS.WORKSPACE_SETTINGS_FORM]: FormTypes.WorkspaceSettingsForm;
890895
[ONYXKEYS.FORMS.WORKSPACE_CATEGORY_FORM]: FormTypes.WorkspaceCategoryForm;
891896
[ONYXKEYS.FORMS.WORKSPACE_CONFIRMATION_FORM]: FormTypes.WorkspaceConfirmationForm;
897+
[ONYXKEYS.FORMS.WORKSPACE_DUPLICATE_FORM]: FormTypes.WorkspaceDuplicateForm;
892898
[ONYXKEYS.FORMS.ONBOARDING_WORKSPACE_DETAILS_FORM]: FormTypes.WorkspaceConfirmationForm;
893899
[ONYXKEYS.FORMS.WORKSPACE_TAG_FORM]: FormTypes.WorkspaceTagForm;
894900
[ONYXKEYS.FORMS.WORKSPACE_TAX_CUSTOM_NAME]: FormTypes.WorkspaceTaxCustomName;
@@ -1190,6 +1196,7 @@ type OnyxValuesMapping = {
11901196
[ONYXKEYS.ADD_NEW_COMPANY_CARD]: OnyxTypes.AddNewCompanyCardFeed;
11911197
[ONYXKEYS.ASSIGN_CARD]: OnyxTypes.AssignCard;
11921198
[ONYXKEYS.MOBILE_SELECTION_MODE]: boolean;
1199+
[ONYXKEYS.DUPLICATE_WORKSPACE]: OnyxTypes.DuplicateWorkspace;
11931200
[ONYXKEYS.NVP_FIRST_DAY_FREE_TRIAL]: string;
11941201
[ONYXKEYS.NVP_LAST_DAY_FREE_TRIAL]: string;
11951202
[ONYXKEYS.NVP_BILLING_FUND_ID]: number;

src/ROUTES.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1872,6 +1872,14 @@ const ROUTES = {
18721872
return getUrlWithBackToParam(`workspaces/${policyID}/per-diem`, backTo);
18731873
},
18741874
},
1875+
WORKSPACE_DUPLICATE: {
1876+
route: 'workspace/:policyID/duplicate',
1877+
getRoute: (policyID: string, backTo?: string) => getUrlWithBackToParam(`workspace/${policyID}/duplicate`, backTo),
1878+
},
1879+
WORKSPACE_DUPLICATE_SELECT_FEATURES: {
1880+
route: 'workspace/:policyID/duplicate/select-features',
1881+
getRoute: (policyID: string, backTo?: string) => getUrlWithBackToParam(`workspace/${policyID}/duplicate/select-features`, backTo),
1882+
},
18751883
WORKSPACE_RECEIPT_PARTNERS: {
18761884
route: 'workspaces/:policyID/receipt-partners',
18771885
getRoute: (policyID: string | undefined, backTo?: string) => {

src/SCREENS.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,7 @@ const SCREENS = {
192192
REPORT_DETAILS: 'Report_Details',
193193
REPORT_CHANGE_WORKSPACE: 'ReportChangeWorkspace',
194194
WORKSPACE_CONFIRMATION: 'Workspace_Confirmation',
195+
WORKSPACE_DUPLICATE: 'Workspace_Duplicate',
195196
REPORT_SETTINGS: 'Report_Settings',
196197
REPORT_DESCRIPTION: 'Report_Description',
197198
PARTICIPANTS: 'Participants',
@@ -387,6 +388,7 @@ const SCREENS = {
387388
},
388389

389390
WORKSPACE_CONFIRMATION: {ROOT: 'Workspace_Confirmation_Root'},
391+
WORKSPACE_DUPLICATE: {ROOT: 'Workspace_Duplicate_Root', SELECT_FEATURES: 'Workspace_Duplicate_Select_Features'},
390392

391393
WORKSPACES_LIST: 'Workspaces_List',
392394

src/components/WorkspaceConfirmationForm.tsx

Lines changed: 8 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -4,17 +4,18 @@ import useAutoFocusInput from '@hooks/useAutoFocusInput';
44
import useLocalize from '@hooks/useLocalize';
55
import useOnyx from '@hooks/useOnyx';
66
import useThemeStyles from '@hooks/useThemeStyles';
7+
import useWorkspaceConfirmationAvatar from '@hooks/useWorkspaceConfirmationAvatar';
78
import {generateDefaultWorkspaceName, generatePolicyID} from '@libs/actions/Policy/Policy';
89
import type {CustomRNImageManipulatorResult} from '@libs/cropOrRotateImage/types';
910
import {addErrorMessage} from '@libs/ErrorUtils';
11+
import getFirstAlphaNumericCharacter from '@libs/getFirstAlphaNumericCharacter';
1012
import Navigation from '@libs/Navigation/Navigation';
1113
import {getDefaultWorkspaceAvatar} from '@libs/ReportUtils';
1214
import {isRequiredFulfilled} from '@libs/ValidationUtils';
1315
import CONST from '@src/CONST';
1416
import ONYXKEYS from '@src/ONYXKEYS';
1517
import INPUT_IDS from '@src/types/form/WorkspaceConfirmationForm';
1618
import isLoadingOnyxValue from '@src/types/utils/isLoadingOnyxValue';
17-
import Avatar from './Avatar';
1819
import AvatarWithImagePicker from './AvatarWithImagePicker';
1920
import CurrencyPicker from './CurrencyPicker';
2021
import FormProvider from './Form/FormProvider';
@@ -26,13 +27,6 @@ import ScrollView from './ScrollView';
2627
import Text from './Text';
2728
import TextInput from './TextInput';
2829

29-
function getFirstAlphaNumericCharacter(str = '') {
30-
return str
31-
.normalize('NFD')
32-
.replace(/[^0-9a-z]/gi, '')
33-
.toUpperCase()[0];
34-
}
35-
3630
type WorkspaceConfirmationSubmitFunctionParams = {
3731
name: string;
3832
currency: string;
@@ -100,22 +94,12 @@ function WorkspaceConfirmationForm({onSubmit, policyOwnerEmail = '', onBackButto
10094

10195
const stashedLocalAvatarImage = workspaceAvatar?.avatarUri ?? undefined;
10296

103-
const DefaultAvatar = useCallback(
104-
() => (
105-
<Avatar
106-
containerStyles={styles.avatarXLarge}
107-
imageStyles={[styles.avatarXLarge, styles.alignSelfCenter]}
108-
// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing -- nullish coalescing cannot be used if left side can be empty string
109-
source={workspaceAvatar?.avatarUri || getDefaultWorkspaceAvatar(workspaceNameFirstCharacter)}
110-
fallbackIcon={Expensicons.FallbackWorkspaceAvatar}
111-
size={CONST.AVATAR_SIZE.X_LARGE}
112-
name={workspaceNameFirstCharacter}
113-
avatarID={policyID}
114-
type={CONST.ICON_TYPE_WORKSPACE}
115-
/>
116-
),
117-
[workspaceAvatar?.avatarUri, workspaceNameFirstCharacter, styles.alignSelfCenter, styles.avatarXLarge, policyID],
118-
);
97+
const DefaultAvatar = useWorkspaceConfirmationAvatar({
98+
policyID,
99+
// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing -- nullish coalescing cannot be used if left side can be empty string
100+
source: stashedLocalAvatarImage || getDefaultWorkspaceAvatar(workspaceNameFirstCharacter),
101+
name: workspaceNameFirstCharacter,
102+
});
119103

120104
return (
121105
<>
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import React, {useCallback} from 'react';
2+
import Avatar from '@components/Avatar';
3+
import * as Expensicons from '@components/Icon/Expensicons';
4+
import type {AvatarSource} from '@libs/UserUtils';
5+
import CONST from '@src/CONST';
6+
import useThemeStyles from './useThemeStyles';
7+
8+
function useWorkspaceConfirmationAvatar({policyID, source, name}: {policyID: string | undefined; source: AvatarSource; name: string}) {
9+
const styles = useThemeStyles();
10+
11+
return useCallback(
12+
() => (
13+
<Avatar
14+
containerStyles={styles.avatarXLarge}
15+
imageStyles={[styles.avatarXLarge, styles.alignSelfCenter]}
16+
source={source}
17+
fallbackIcon={Expensicons.FallbackWorkspaceAvatar}
18+
size={CONST.AVATAR_SIZE.X_LARGE}
19+
name={name}
20+
avatarID={policyID}
21+
type={CONST.ICON_TYPE_WORKSPACE}
22+
/>
23+
),
24+
[name, policyID, source, styles.alignSelfCenter, styles.avatarXLarge],
25+
);
26+
}
27+
28+
export default useWorkspaceConfirmationAvatar;

src/languages/de.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -318,6 +318,7 @@ const translations = {
318318
count: 'Zählen',
319319
cancel: 'Abbrechen',
320320
dismiss: 'Verwerfen',
321+
proceed: 'Fortfahren',
321322
yes: 'Ja',
322323
no: 'No',
323324
ok: 'OK',
@@ -3442,12 +3443,14 @@ const translations = {
34423443
customField1: 'Benutzerdefiniertes Feld 1',
34433444
customField2: 'Benutzerdefiniertes Feld 2',
34443445
customFieldHint: 'Fügen Sie benutzerdefinierten Code hinzu, der für alle Ausgaben dieses Mitglieds gilt.',
3446+
reports: 'Berichte',
34453447
reportFields: 'Berichtsfelder',
34463448
reportTitle: 'Berichtstitel',
34473449
reportField: 'Berichtsfeld',
34483450
taxes: 'Steuern',
34493451
bills: 'Rechnungen',
34503452
invoices: 'Rechnungen',
3453+
perDiem: 'Per diem',
34513454
travel: 'Reisen',
34523455
members: 'Mitglieder',
34533456
accounting: 'Buchhaltung',
@@ -3460,6 +3463,7 @@ const translations = {
34603463
testTransactions: 'Transaktionen testen',
34613464
issueAndManageCards: 'Karten ausstellen und verwalten',
34623465
reconcileCards: 'Karten abstimmen',
3466+
selectAll: 'Alle auswählen',
34633467
selected: () => ({
34643468
one: '1 ausgewählt',
34653469
other: (count: number) => `${count} ausgewählt`,
@@ -3473,6 +3477,8 @@ const translations = {
34733477
memberNotFound: 'Mitglied nicht gefunden. Um ein neues Mitglied zum Arbeitsbereich einzuladen, verwenden Sie bitte die Einladungsschaltfläche oben.',
34743478
notAuthorized: `Sie haben keinen Zugriff auf diese Seite. Wenn Sie versuchen, diesem Arbeitsbereich beizutreten, bitten Sie einfach den Besitzer des Arbeitsbereichs, Sie als Mitglied hinzuzufügen. Etwas anderes? Kontaktieren Sie ${CONST.EMAIL.CONCIERGE}.`,
34753479
goToWorkspace: 'Zum Arbeitsbereich gehen',
3480+
duplicateWorkspace: 'Arbeitsbereich duplizieren',
3481+
duplicateWorkspacePrefix: 'Duplizieren',
34763482
goToWorkspaces: 'Zu Arbeitsbereichen gehen',
34773483
clearFilter: 'Filter löschen',
34783484
workspaceName: 'Arbeitsbereichsname',
@@ -4881,6 +4887,18 @@ const translations = {
48814887
taxCode: 'Steuercode',
48824888
updateTaxCodeFailureMessage: 'Beim Aktualisieren des Steuercodes ist ein Fehler aufgetreten, bitte versuchen Sie es erneut.',
48834889
},
4890+
duplicateWorkspace: {
4891+
title: 'Benennen Sie Ihren neuen Arbeitsbereich',
4892+
selectFeatures: 'Auswählen der zu kopierenden Features',
4893+
whichFeatures: 'Welche Funktionen möchten Sie in Ihren neuen Arbeitsbereich kopieren?',
4894+
confirmDuplicate: '\n\nMöchten Sie fortfahren?',
4895+
categories: 'Kategorien und Ihre Auto-Kategorisierungsregeln',
4896+
reimbursementAccount: 'Erstattungskonto',
4897+
delayedSubmission: 'verspätete Einreichung',
4898+
welcomeNote: 'Bitte beginnen Sie mit der Nutzung meines neuen Arbeitsbereichs',
4899+
confirmTitle: ({newWorkspaceName, totalMembers}: {newWorkspaceName?: string; totalMembers?: number}) =>
4900+
`Sie sind dabei, ${newWorkspaceName ?? ''} zu erstellen und mit ${totalMembers ?? 0} Mitgliedern aus dem ursprünglichen Arbeitsbereich zu teilen.`,
4901+
},
48844902
emptyWorkspace: {
48854903
title: 'Sie haben keine Arbeitsbereiche',
48864904
subtitle: 'Verfolgen Sie Belege, erstatten Sie Ausgaben, verwalten Sie Reisen, senden Sie Rechnungen und mehr.',

src/languages/en.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -310,6 +310,7 @@ const translations = {
310310
count: 'Count',
311311
cancel: 'Cancel',
312312
dismiss: 'Dismiss',
313+
proceed: 'Proceed',
313314
yes: 'Yes',
314315
no: 'No',
315316
ok: 'OK',
@@ -3439,12 +3440,14 @@ const translations = {
34393440
customField1: 'Custom field 1',
34403441
customField2: 'Custom field 2',
34413442
customFieldHint: 'Add custom coding that applies to all spend from this member.',
3443+
reports: 'Reports',
34423444
reportFields: 'Report fields',
34433445
reportTitle: 'Report title',
34443446
reportField: 'Report field',
34453447
taxes: 'Taxes',
34463448
bills: 'Bills',
34473449
invoices: 'Invoices',
3450+
perDiem: 'Per diem',
34483451
travel: 'Travel',
34493452
members: 'Members',
34503453
accounting: 'Accounting',
@@ -3457,6 +3460,7 @@ const translations = {
34573460
testTransactions: 'Test transactions',
34583461
issueAndManageCards: 'Issue and manage cards',
34593462
reconcileCards: 'Reconcile cards',
3463+
selectAll: 'Select all',
34603464
selected: () => ({
34613465
one: '1 selected',
34623466
other: (count: number) => `${count} selected`,
@@ -3470,6 +3474,8 @@ const translations = {
34703474
memberNotFound: 'Member not found. To invite a new member to the workspace, please use the invite button above.',
34713475
notAuthorized: `You don't have access to this page. If you're trying to join this workspace, just ask the workspace owner to add you as a member. Something else? Reach out to ${CONST.EMAIL.CONCIERGE}.`,
34723476
goToWorkspace: 'Go to workspace',
3477+
duplicateWorkspace: 'Duplicate Workspace',
3478+
duplicateWorkspacePrefix: 'Duplicate',
34733479
goToWorkspaces: 'Go to workspaces',
34743480
clearFilter: 'Clear filter',
34753481
workspaceName: 'Workspace name',
@@ -4864,6 +4870,18 @@ const translations = {
48644870
taxCode: 'Tax code',
48654871
updateTaxCodeFailureMessage: 'An error occurred while updating the tax code, please try again',
48664872
},
4873+
duplicateWorkspace: {
4874+
title: 'Name your new workspace',
4875+
selectFeatures: 'Select features to copy',
4876+
whichFeatures: 'Which features do you want to copy over to your new workspace?',
4877+
confirmDuplicate: '\n\nDo you want to continue?',
4878+
categories: 'categories and your auto-categorization rules',
4879+
reimbursementAccount: 'reimbursement account',
4880+
welcomeNote: 'Please start using my new workspace',
4881+
delayedSubmission: 'delayed submission',
4882+
confirmTitle: ({newWorkspaceName, totalMembers}: {newWorkspaceName?: string; totalMembers?: number}) =>
4883+
`You’re about to create and share ${newWorkspaceName ?? ''} with ${totalMembers ?? 0} members from the original workspace.`,
4884+
},
48674885
emptyWorkspace: {
48684886
title: 'You have no workspaces',
48694887
subtitle: 'Track receipts, reimburse expenses, manage travel, send invoices, and more.',

src/languages/es.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -300,6 +300,7 @@ const translations = {
300300
count: 'Contar',
301301
cancel: 'Cancelar',
302302
dismiss: 'Descartar',
303+
proceed: 'Proceed',
303304
yes: 'Sí',
304305
no: 'No',
305306
ok: 'OK',
@@ -3427,11 +3428,13 @@ const translations = {
34273428
customField1: 'Campo personalizado 1',
34283429
customField2: 'Campo personalizado 2',
34293430
customFieldHint: 'Añade una codificación personalizada que se aplique a todos los gastos de este miembro.',
3431+
reports: 'Informes',
34303432
reportFields: 'Campos de informe',
34313433
reportTitle: 'El título del informe.',
34323434
taxes: 'Impuestos',
34333435
bills: 'Pagar facturas',
34343436
invoices: 'Facturas',
3437+
perDiem: 'Per diem',
34353438
travel: 'Viajes',
34363439
members: 'Miembros',
34373440
accounting: 'Contabilidad',
@@ -3444,6 +3447,7 @@ const translations = {
34443447
testTransactions: 'Transacciones de prueba',
34453448
issueAndManageCards: 'Emitir y gestionar tarjetas',
34463449
reconcileCards: 'Reconciliar tarjetas',
3450+
selectAll: 'Seleccionar todo',
34473451
selected: () => ({
34483452
one: '1 seleccionado',
34493453
other: (count: number) => `${count} seleccionados`,
@@ -3457,6 +3461,8 @@ const translations = {
34573461
memberNotFound: 'Miembro no encontrado. Para invitar a un nuevo miembro al espacio de trabajo, por favor, utiliza el botón invitar que está arriba.',
34583462
notAuthorized: `No tienes acceso a esta página. Si estás intentando unirte a este espacio de trabajo, pide al dueño del espacio de trabajo que te añada como miembro. ¿Necesitas algo más? Comunícate con ${CONST.EMAIL.CONCIERGE}`,
34593463
goToWorkspace: 'Ir al espacio de trabajo',
3464+
duplicateWorkspace: 'Duplicar espacio de trabajo',
3465+
duplicateWorkspacePrefix: 'Duplicar',
34603466
goToWorkspaces: 'Ir a espacios de trabajo',
34613467
clearFilter: 'Borrar filtro',
34623468
workspaceName: 'Nombre del espacio de trabajo',
@@ -4874,6 +4880,18 @@ const translations = {
48744880
taxCode: 'Código de impuesto',
48754881
updateTaxCodeFailureMessage: 'Se produjo un error al actualizar el código tributario, inténtelo nuevamente',
48764882
},
4883+
duplicateWorkspace: {
4884+
title: 'Nombra tu nuevo espacio de trabajo',
4885+
selectFeatures: 'Selecciona las funciones a copiar',
4886+
whichFeatures: '¿Qué funciones deseas copiar a tu nuevo espacio de trabajo?',
4887+
confirmDuplicate: '\n\n¿Quieres continuar?',
4888+
categories: 'categorías y tus reglas de auto-categorización',
4889+
reimbursementAccount: 'cuenta de reembolso',
4890+
delayedSubmission: 'presentación retrasada',
4891+
welcomeNote: 'Por favor, comience a utilizar mi nuevo espacio de trabajo.',
4892+
confirmTitle: ({newWorkspaceName, totalMembers}: {newWorkspaceName?: string; totalMembers?: number}) =>
4893+
`Estás a punto de crear y compartir ${newWorkspaceName ?? ''} con ${totalMembers ?? 0} miembros del espacio de trabajo original.`,
4894+
},
48774895
emptyWorkspace: {
48784896
title: 'No tienes espacios de trabajo',
48794897
subtitle: 'Organiza recibos, reembolsa gastos, gestiona viajes, envía facturas y mucho más.',

0 commit comments

Comments
 (0)