From b7a9a4fb56e464a943fc2b82fda9d2b56a3c4422 Mon Sep 17 00:00:00 2001 From: Scott Deeter Date: Thu, 24 Oct 2024 14:10:45 -0700 Subject: [PATCH 01/75] Conditionally add nvp_onboarding when not invited --- src/libs/actions/IOU.ts | 2 +- src/libs/actions/Report.ts | 9 ++++++--- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index 497f43f93317..531f8176021a 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -7604,7 +7604,7 @@ function completePaymentOnboarding(paymentSelected: ValueOf, full = true) { diff --git a/src/libs/actions/Report.ts b/src/libs/actions/Report.ts index dce8f2d19559..45fd68df1be7 100644 --- a/src/libs/actions/Report.ts +++ b/src/libs/actions/Report.ts @@ -3329,6 +3329,7 @@ function completeOnboarding( paymentSelected?: string, companySize?: OnboardingCompanySizeType, userReportedIntegration?: OnboardingAccountingType, + wasInvited = false, ) { const actorAccountID = CONST.ACCOUNT_ID.CONCIERGE; const targetChatReport = ReportUtils.getChatByParticipants([actorAccountID, currentUserAccountID]); @@ -3574,12 +3575,14 @@ function completeOnboarding( key: ONYXKEYS.NVP_INTRO_SELECTED, value: {choice: engagementChoice}, }, - { + ); + if (!wasInvited) { + optimisticData.push({ onyxMethod: Onyx.METHOD.MERGE, key: ONYXKEYS.NVP_ONBOARDING, value: {hasCompletedGuidedSetupFlow: true}, - }, - ); + }); + } const successData: OnyxUpdate[] = [...tasksForSuccessData]; successData.push({ From 45e8f104b299128b7a565da3fbb6bff70a81fb9c Mon Sep 17 00:00:00 2001 From: Scott Deeter Date: Thu, 24 Oct 2024 16:15:57 -0700 Subject: [PATCH 02/75] Add undefined params to get pamentSeleted in right place; wasinvited too --- src/libs/actions/IOU.ts | 13 ++++++++++++- src/libs/actions/Report.ts | 2 +- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index 531f8176021a..7d0281a43b21 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -7604,7 +7604,18 @@ function completePaymentOnboarding(paymentSelected: ValueOf, full = true) { diff --git a/src/libs/actions/Report.ts b/src/libs/actions/Report.ts index 45fd68df1be7..6e5ecf7f53a6 100644 --- a/src/libs/actions/Report.ts +++ b/src/libs/actions/Report.ts @@ -3329,7 +3329,7 @@ function completeOnboarding( paymentSelected?: string, companySize?: OnboardingCompanySizeType, userReportedIntegration?: OnboardingAccountingType, - wasInvited = false, + wasInvited?: boolean, ) { const actorAccountID = CONST.ACCOUNT_ID.CONCIERGE; const targetChatReport = ReportUtils.getChatByParticipants([actorAccountID, currentUserAccountID]); From 9935cc3d8449f8a71c80522eff09a0defceedcd7 Mon Sep 17 00:00:00 2001 From: Scott Deeter Date: Thu, 24 Oct 2024 16:27:47 -0700 Subject: [PATCH 03/75] Apply same reasoning to failure data --- src/libs/actions/Report.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/libs/actions/Report.ts b/src/libs/actions/Report.ts index 6e5ecf7f53a6..84b438d4a2fa 100644 --- a/src/libs/actions/Report.ts +++ b/src/libs/actions/Report.ts @@ -3637,6 +3637,9 @@ function completeOnboarding( key: ONYXKEYS.NVP_INTRO_SELECTED, value: {choice: null}, }, + ); + + if (!wasInvited) failureData.push( { onyxMethod: Onyx.METHOD.MERGE, key: ONYXKEYS.NVP_ONBOARDING, From 38d75c4255ff953f1188061704251bf18fe69749 Mon Sep 17 00:00:00 2001 From: Scott Deeter Date: Thu, 24 Oct 2024 17:17:21 -0700 Subject: [PATCH 04/75] Fix bad syntax --- src/libs/actions/Report.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/libs/actions/Report.ts b/src/libs/actions/Report.ts index 84b438d4a2fa..91eec9df1d77 100644 --- a/src/libs/actions/Report.ts +++ b/src/libs/actions/Report.ts @@ -3639,13 +3639,13 @@ function completeOnboarding( }, ); - if (!wasInvited) failureData.push( - { + if (!wasInvited) { + failureData.push({ onyxMethod: Onyx.METHOD.MERGE, key: ONYXKEYS.NVP_ONBOARDING, value: {hasCompletedGuidedSetupFlow: false}, - }, - ); + }); + } if (userReportedIntegration) { optimisticData.push({ From 0abb380ce52148fba4eea4d2a64f2a3b0e6e0c61 Mon Sep 17 00:00:00 2001 From: nkdengineer Date: Tue, 29 Oct 2024 10:51:25 +0700 Subject: [PATCH 05/75] fix: Deleted room is displayed in the 'Assign task' --- .../SidebarScreen/FloatingActionButtonAndPopover.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/pages/home/sidebar/SidebarScreen/FloatingActionButtonAndPopover.tsx b/src/pages/home/sidebar/SidebarScreen/FloatingActionButtonAndPopover.tsx index a49b474b185e..329066b00a99 100644 --- a/src/pages/home/sidebar/SidebarScreen/FloatingActionButtonAndPopover.tsx +++ b/src/pages/home/sidebar/SidebarScreen/FloatingActionButtonAndPopover.tsx @@ -189,9 +189,10 @@ function FloatingActionButtonAndPopover( const {canUseSpotnanaTravel, canUseCombinedTrackSubmit} = usePermissions(); const canSendInvoice = useMemo(() => PolicyUtils.canSendInvoice(allPolicies as OnyxCollection, session?.email), [allPolicies, session?.email]); + const isValidReport = !(isEmptyObject(quickActionReport) || ReportUtils.isArchivedRoom(quickActionReport, reportNameValuePairs)); const quickActionAvatars = useMemo(() => { - if (quickActionReport) { + if (isValidReport) { const avatars = ReportUtils.getIcons(quickActionReport, personalDetails); return avatars.length <= 1 || ReportUtils.isPolicyExpenseChat(quickActionReport) ? avatars : avatars.filter((avatar) => avatar.id !== session?.accountID); } @@ -223,7 +224,7 @@ function FloatingActionButtonAndPopover( }, [quickAction, translate, quickActionAvatars, quickActionReport]); const hideQABSubtitle = useMemo(() => { - if (isEmptyObject(quickActionReport)) { + if (isValidReport) { return true; } if (quickActionAvatars.length === 0) { @@ -231,7 +232,7 @@ function FloatingActionButtonAndPopover( } const displayName = personalDetails?.[quickActionAvatars.at(0)?.id ?? -1]?.firstName ?? ''; return quickAction?.action === CONST.QUICK_ACTIONS.SEND_MONEY && displayName.length === 0; - }, [personalDetails, quickActionReport, quickAction?.action, quickActionAvatars]); + }, [isValidReport, quickActionAvatars, personalDetails, quickAction?.action]); const navigateToQuickAction = () => { const selectOption = (onSelected: () => void, shouldRestrictAction: boolean) => { @@ -243,7 +244,6 @@ function FloatingActionButtonAndPopover( onSelected(); }; - const isValidReport = !(isEmptyObject(quickActionReport) || ReportUtils.isArchivedRoom(quickActionReport, reportNameValuePairs)); const quickActionReportID = isValidReport ? quickActionReport?.reportID ?? '-1' : ReportUtils.generateReportID(); switch (quickAction?.action) { From 5889c08cc90ef34eb83e6d42b7922db32e4785e4 Mon Sep 17 00:00:00 2001 From: nkdengineer Date: Tue, 29 Oct 2024 17:29:05 +0700 Subject: [PATCH 06/75] fix eslint --- .../FloatingActionButtonAndPopover.tsx | 86 ++++--------------- 1 file changed, 15 insertions(+), 71 deletions(-) diff --git a/src/pages/home/sidebar/SidebarScreen/FloatingActionButtonAndPopover.tsx b/src/pages/home/sidebar/SidebarScreen/FloatingActionButtonAndPopover.tsx index 329066b00a99..b8b76e5a00eb 100644 --- a/src/pages/home/sidebar/SidebarScreen/FloatingActionButtonAndPopover.tsx +++ b/src/pages/home/sidebar/SidebarScreen/FloatingActionButtonAndPopover.tsx @@ -1,10 +1,10 @@ import {useIsFocused as useIsFocusedOriginal, useNavigationState} from '@react-navigation/native'; import type {ImageContentFit} from 'expo-image'; -import type {ForwardedRef, RefAttributes} from 'react'; +import type {ForwardedRef} from 'react'; import React, {forwardRef, useCallback, useEffect, useImperativeHandle, useMemo, useRef, useState} from 'react'; import {View} from 'react-native'; import type {OnyxCollection, OnyxEntry} from 'react-native-onyx'; -import {useOnyx, withOnyx} from 'react-native-onyx'; +import {useOnyx} from 'react-native-onyx'; import type {SvgProps} from 'react-native-svg'; import FloatingActionButton from '@components/FloatingActionButton'; import * as Expensicons from '@components/Icon/Expensicons'; @@ -39,6 +39,7 @@ import SCREENS from '@src/SCREENS'; import type * as OnyxTypes from '@src/types/onyx'; import type {QuickActionName} from '@src/types/onyx/QuickAction'; import {isEmptyObject} from '@src/types/utils/EmptyObject'; +import mapOnyxCollectionItems from '@src/utils/mapOnyxCollectionItems'; // On small screen we hide the search page from central pane to show the search bottom tab page with bottom tab bar. // We need to take this in consideration when checking if the screen is focused. @@ -51,33 +52,7 @@ const useIsFocused = () => { type PolicySelector = Pick; -type FloatingActionButtonAndPopoverOnyxProps = { - /** The list of policies the user has access to. */ - allPolicies: OnyxCollection; - - /** Whether app is in loading state */ - isLoading: OnyxEntry; - - /** Information on the last taken action to display as Quick Action */ - quickAction: OnyxEntry; - - /** The report data of the quick action */ - quickActionReport: OnyxEntry; - - /** The policy data of the quick action */ - quickActionPolicy: OnyxEntry; - - /** The current session */ - session: OnyxEntry; - - /** Personal details of all the users */ - personalDetails: OnyxEntry; - - /** Has user seen track expense training interstitial */ - hasSeenTrackTraining: OnyxEntry; -}; - -type FloatingActionButtonAndPopoverProps = FloatingActionButtonAndPopoverOnyxProps & { +type FloatingActionButtonAndPopoverProps = { /* Callback function when the menu is shown */ onShowCreateMenu?: () => void; @@ -161,23 +136,18 @@ const getQuickActionTitle = (action: QuickActionName): TranslationPaths => { * Responsible for rendering the {@link PopoverMenu}, and the accompanying * FAB that can open or close the menu. */ -function FloatingActionButtonAndPopover( - { - onHideCreateMenu, - onShowCreateMenu, - isLoading = false, - allPolicies, - quickAction, - quickActionReport, - quickActionPolicy, - session, - personalDetails, - hasSeenTrackTraining, - }: FloatingActionButtonAndPopoverProps, - ref: ForwardedRef, -) { +function FloatingActionButtonAndPopover({onHideCreateMenu, onShowCreateMenu}: FloatingActionButtonAndPopoverProps, ref: ForwardedRef) { const styles = useThemeStyles(); const {translate} = useLocalize(); + const [allPolicies] = useOnyx(ONYXKEYS.COLLECTION.POLICY, {selector: (c) => mapOnyxCollectionItems(c, policySelector)}); + const [isLoading] = useOnyx(ONYXKEYS.IS_LOADING_APP); + const [quickAction] = useOnyx(ONYXKEYS.NVP_QUICK_ACTION_GLOBAL_CREATE); + const [quickActionReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${quickAction?.chatReportID}`); + const [quickActionPolicy] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY}${quickActionReport?.policyID}`); + const [personalDetails] = useOnyx(ONYXKEYS.PERSONAL_DETAILS_LIST); + const [session] = useOnyx(ONYXKEYS.SESSION); + const [hasSeenTrackTraining] = useOnyx(ONYXKEYS.NVP_HAS_SEEN_TRACK_TRAINING); + const [reportNameValuePairs] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}${quickActionReport?.reportID ?? -1}`); const [isCreateMenuActive, setIsCreateMenuActive] = useState(false); const fabRef = useRef(null); @@ -510,32 +480,6 @@ function FloatingActionButtonAndPopover( FloatingActionButtonAndPopover.displayName = 'FloatingActionButtonAndPopover'; -export default withOnyx, FloatingActionButtonAndPopoverOnyxProps>({ - allPolicies: { - key: ONYXKEYS.COLLECTION.POLICY, - selector: policySelector, - }, - isLoading: { - key: ONYXKEYS.IS_LOADING_APP, - }, - quickAction: { - key: ONYXKEYS.NVP_QUICK_ACTION_GLOBAL_CREATE, - }, - quickActionReport: { - key: ({quickAction}) => `${ONYXKEYS.COLLECTION.REPORT}${quickAction?.chatReportID}`, - }, - quickActionPolicy: { - key: ({quickActionReport}) => `${ONYXKEYS.COLLECTION.POLICY}${quickActionReport?.policyID}`, - }, - personalDetails: { - key: ONYXKEYS.PERSONAL_DETAILS_LIST, - }, - session: { - key: ONYXKEYS.SESSION, - }, - hasSeenTrackTraining: { - key: ONYXKEYS.NVP_HAS_SEEN_TRACK_TRAINING, - }, -})(forwardRef(FloatingActionButtonAndPopover)); +export default forwardRef(FloatingActionButtonAndPopover); export type {PolicySelector}; From 8206ad0092a999dfd633f5874ec073d042059380 Mon Sep 17 00:00:00 2001 From: mkzie2 Date: Wed, 30 Oct 2024 17:35:14 +0700 Subject: [PATCH 07/75] fix: sound plays before paying held expense report --- src/components/ProcessMoneyReportHoldMenu.tsx | 2 ++ src/components/SettlementButton/index.tsx | 4 +++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/components/ProcessMoneyReportHoldMenu.tsx b/src/components/ProcessMoneyReportHoldMenu.tsx index 3d6ad9006dc5..f1a72cc7fb8e 100644 --- a/src/components/ProcessMoneyReportHoldMenu.tsx +++ b/src/components/ProcessMoneyReportHoldMenu.tsx @@ -4,6 +4,7 @@ import useLocalize from '@hooks/useLocalize'; import useResponsiveLayout from '@hooks/useResponsiveLayout'; import Navigation from '@libs/Navigation/Navigation'; import {isLinkedTransactionHeld} from '@libs/ReportActionsUtils'; +import playSound, {SOUNDS} from '@libs/Sound'; import * as IOU from '@userActions/IOU'; import CONST from '@src/CONST'; import ROUTES from '@src/ROUTES'; @@ -74,6 +75,7 @@ function ProcessMoneyReportHoldMenu({ if (startAnimation) { startAnimation(); } + playSound(SOUNDS.SUCCESS); IOU.payMoneyRequest(paymentType, chatReport, moneyRequestReport, full); } onClose(); diff --git a/src/components/SettlementButton/index.tsx b/src/components/SettlementButton/index.tsx index 53b09bfcbf31..f98415a5f11a 100644 --- a/src/components/SettlementButton/index.tsx +++ b/src/components/SettlementButton/index.tsx @@ -208,7 +208,9 @@ function SettlementButton({ return; } - playSound(SOUNDS.SUCCESS); + if (!ReportUtils.hasHeldExpenses(iouReport?.reportID)) { + playSound(SOUNDS.SUCCESS); + } onPress(iouPaymentType); }; From 83a70f9ab7c2d40e003be64f86118a92e1302cc5 Mon Sep 17 00:00:00 2001 From: nkdengineer Date: Fri, 1 Nov 2024 15:07:55 +0700 Subject: [PATCH 08/75] fix show QAB subtitle --- .../sidebar/SidebarScreen/FloatingActionButtonAndPopover.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/home/sidebar/SidebarScreen/FloatingActionButtonAndPopover.tsx b/src/pages/home/sidebar/SidebarScreen/FloatingActionButtonAndPopover.tsx index b8b76e5a00eb..c58b725a6b49 100644 --- a/src/pages/home/sidebar/SidebarScreen/FloatingActionButtonAndPopover.tsx +++ b/src/pages/home/sidebar/SidebarScreen/FloatingActionButtonAndPopover.tsx @@ -194,7 +194,7 @@ function FloatingActionButtonAndPopover({onHideCreateMenu, onShowCreateMenu}: Fl }, [quickAction, translate, quickActionAvatars, quickActionReport]); const hideQABSubtitle = useMemo(() => { - if (isValidReport) { + if (!isValidReport) { return true; } if (quickActionAvatars.length === 0) { From 936b5bd45d821e34eecbf852f9627c1cef0f7d45 Mon Sep 17 00:00:00 2001 From: jaydamani Date: Sat, 2 Nov 2024 13:23:30 +0100 Subject: [PATCH 09/75] feature(onboarding): combine category and tag setup task for connections --- src/CONST.ts | 9 +++++++++ src/libs/actions/Report.ts | 6 +++++- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/src/CONST.ts b/src/CONST.ts index 437ee4e7fd42..b79c831264eb 100755 --- a/src/CONST.ts +++ b/src/CONST.ts @@ -4891,6 +4891,15 @@ const CONST = { '\n' + `Chat with the specialist in your [#admins room](${adminsRoomLink}).`, }, + { + type: 'addAccountingIntegration', + autoCompleted: false, + title: 'Set up categories and tags', + description: ({workspaceCategoriesLink, workspaceAccountingLink}) => + '*Set up categories and tags* so your team can code expenses for easy reporting.\n' + + '\n' + + `Import them automatically by [connecting your accounting software](${workspaceAccountingLink}), or set them up manually in your [workspace settings](${workspaceCategoriesLink}).`, + }, { type: 'setupCategories', autoCompleted: false, diff --git a/src/libs/actions/Report.ts b/src/libs/actions/Report.ts index 3eac21cd1b18..0333577fff8d 100644 --- a/src/libs/actions/Report.ts +++ b/src/libs/actions/Report.ts @@ -3439,7 +3439,11 @@ function completeOnboarding( const tasksData = data.tasks .filter((task) => { - if (task.type === 'addAccountingIntegration' && !userReportedIntegration) { + if (['setupCategories', 'setupTags'].includes(task.type) && userReportedIntegration) { + return false; + } + + if (['addAccountingIntegration', 'setupCategoriesAndTags'].includes(task.type) && !userReportedIntegration) { return false; } return true; From 3c8de6dbc3ff0c764b6d4973d664ea7c386c0f16 Mon Sep 17 00:00:00 2001 From: jaydamani Date: Sat, 2 Nov 2024 13:32:05 +0100 Subject: [PATCH 10/75] update task type --- src/CONST.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/CONST.ts b/src/CONST.ts index b79c831264eb..1ce3b14acc53 100755 --- a/src/CONST.ts +++ b/src/CONST.ts @@ -4892,7 +4892,7 @@ const CONST = { `Chat with the specialist in your [#admins room](${adminsRoomLink}).`, }, { - type: 'addAccountingIntegration', + type: 'setupCategoriesAndTags', autoCompleted: false, title: 'Set up categories and tags', description: ({workspaceCategoriesLink, workspaceAccountingLink}) => From 766e05bca88f1fa039404b0e0d6b92cce7452560 Mon Sep 17 00:00:00 2001 From: jaydamani Date: Tue, 5 Nov 2024 19:55:41 +0100 Subject: [PATCH 11/75] update link in setupCategoriesAndTags --- src/CONST.ts | 5 +++-- src/libs/actions/Report.ts | 1 + 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/CONST.ts b/src/CONST.ts index 1ce3b14acc53..97b8ac45acc7 100755 --- a/src/CONST.ts +++ b/src/CONST.ts @@ -264,6 +264,7 @@ type OnboardingTaskType = { workspaceMembersLink: string; integrationName: string; workspaceAccountingLink: string; + workspaceSettingsLink: string; }>, ) => string); }; @@ -4895,10 +4896,10 @@ const CONST = { type: 'setupCategoriesAndTags', autoCompleted: false, title: 'Set up categories and tags', - description: ({workspaceCategoriesLink, workspaceAccountingLink}) => + description: ({workspaceSettingsLink, workspaceAccountingLink}) => '*Set up categories and tags* so your team can code expenses for easy reporting.\n' + '\n' + - `Import them automatically by [connecting your accounting software](${workspaceAccountingLink}), or set them up manually in your [workspace settings](${workspaceCategoriesLink}).`, + `Import them automatically by [connecting your accounting software](${workspaceAccountingLink}), or set them up manually in your [workspace settings](${workspaceSettingsLink}).`, }, { type: 'setupCategories', diff --git a/src/libs/actions/Report.ts b/src/libs/actions/Report.ts index 0333577fff8d..b560dc244d89 100644 --- a/src/libs/actions/Report.ts +++ b/src/libs/actions/Report.ts @@ -3458,6 +3458,7 @@ function completeOnboarding( workspaceMoreFeaturesLink: `${environmentURL}/${ROUTES.WORKSPACE_MORE_FEATURES.getRoute(onboardingPolicyID ?? '-1')}`, integrationName, workspaceAccountingLink: `${environmentURL}/${ROUTES.POLICY_ACCOUNTING.getRoute(onboardingPolicyID ?? '-1')}`, + workspaceSettingsLink: `${environmentURL}/${ROUTES.WORKSPACE_INITIAL.getRoute(onboardingPolicyID ?? '01')}`, }) : task.description; const taskTitle = From 597791ac2162aa03430c6cb895dc414a5aa53a6a Mon Sep 17 00:00:00 2001 From: burczu Date: Tue, 29 Oct 2024 13:18:10 +0100 Subject: [PATCH 12/75] enter email, hang tight and director check components added --- .../simple-illustration__pillow.svg | 28 +++++ src/components/Icon/Illustrations.ts | 2 + src/languages/en.ts | 21 ++++ src/languages/es.ts | 23 +++- src/languages/params.ts | 5 + .../NonUSD/SignerInfo/DirectorCheck.tsx | 65 ++++++++++ .../NonUSD/SignerInfo/EnterEmail.tsx | 83 +++++++++++++ .../NonUSD/SignerInfo/HangTight.tsx | 58 +++++++++ src/types/form/ReimbursementAccountForm.ts | 117 ++++++++++++++++++ 9 files changed, 401 insertions(+), 1 deletion(-) create mode 100644 assets/images/simple-illustrations/simple-illustration__pillow.svg create mode 100644 src/pages/ReimbursementAccount/NonUSD/SignerInfo/DirectorCheck.tsx create mode 100644 src/pages/ReimbursementAccount/NonUSD/SignerInfo/EnterEmail.tsx create mode 100644 src/pages/ReimbursementAccount/NonUSD/SignerInfo/HangTight.tsx diff --git a/assets/images/simple-illustrations/simple-illustration__pillow.svg b/assets/images/simple-illustrations/simple-illustration__pillow.svg new file mode 100644 index 000000000000..97a0811266ae --- /dev/null +++ b/assets/images/simple-illustrations/simple-illustration__pillow.svg @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/components/Icon/Illustrations.ts b/src/components/Icon/Illustrations.ts index 0efb65ed7a61..98142ec02def 100644 --- a/src/components/Icon/Illustrations.ts +++ b/src/components/Icon/Illustrations.ts @@ -102,6 +102,7 @@ import OpenSafe from '@assets/images/simple-illustrations/simple-illustration__o import PalmTree from '@assets/images/simple-illustrations/simple-illustration__palmtree.svg'; import Pencil from '@assets/images/simple-illustrations/simple-illustration__pencil.svg'; import PiggyBank from '@assets/images/simple-illustrations/simple-illustration__piggybank.svg'; +import Pillow from '@assets/images/simple-illustrations/simple-illustration__pillow.svg'; import Profile from '@assets/images/simple-illustrations/simple-illustration__profile.svg'; import QRCode from '@assets/images/simple-illustrations/simple-illustration__qr-code.svg'; import ReceiptEnvelope from '@assets/images/simple-illustrations/simple-illustration__receipt-envelope.svg'; @@ -222,6 +223,7 @@ export { ExpensifyCardIllustration, SplitBill, PiggyBank, + Pillow, Accounting, Car, Coins, diff --git a/src/languages/en.ts b/src/languages/en.ts index f1339ed88373..0ac1767687d7 100755 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -41,6 +41,7 @@ import type { CharacterLimitParams, CompanyCardBankName, CompanyCardFeedNameParams, + CompanyNameParams, ConfirmThatParams, ConnectionNameParams, ConnectionParams, @@ -2257,6 +2258,26 @@ const translations = { }, signerInfoStep: { signerInfo: 'Signer info', + areYouDirector: ({companyName}: CompanyNameParams) => `Are you a director or senior officer at ${companyName}?`, + regulationRequiresUs: 'Regulation requires us to verify if the signer has the authority to take this action on behalf of the business.', + whatsYourName: "What's your legal name", + fullName: 'Legal full name', + whatsYourJobTitle: "What's your job title?", + jobTitle: 'Job title', + whatsYourDOB: "What's your date of birth?", + uploadID: 'Upload ID and proof of address', + id: "ID (driver's license or passport)", + personalAddress: 'Proof of personal address (e.g. utility bill)', + letsDoubleCheck: 'Let’s double check that everything looks right.', + legalName: 'Legal name', + proofOf: 'Proof of personal address', + enterOneEmail: 'Enter the email of director or senior officer at', + regulationRequiresOneMoreDirector: 'Regulation requires one more director or senior officer as a signer.', + hangTight: 'Hang tight...', + enterTwoEmails: 'Enter the emails of two directors or senior officers at', + sendReminder: 'Send a reminder', + chooseFile: 'Choose file', + weAreWaiting: "We're waiting for others to verify their identities as directors or senior officers of the business.", }, agreementsStep: { agreements: 'Agreements', diff --git a/src/languages/es.ts b/src/languages/es.ts index a9ebfedf1cc3..12e1de851c67 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -39,6 +39,7 @@ import type { CharacterLimitParams, CompanyCardBankName, CompanyCardFeedNameParams, + CompanyNameParams, ConfirmThatParams, ConnectionNameParams, ConnectionParams, @@ -2279,7 +2280,27 @@ const translations = { selectCountry: 'Seleccione su país', }, signerInfoStep: { - signerInfo: 'Información del firmante', + signerInfo: 'Signer info', + areYouDirector: ({companyName}: CompanyNameParams) => `Are you a director or senior officer at ${companyName}?`, + regulationRequiresUs: 'Regulation requires us to verify if the signer has the authority to take this action on behalf of the business.', + whatsYourName: "What's your legal name", + fullName: 'Legal full name', + whatsYourJobTitle: "What's your job title?", + jobTitle: 'Job title', + whatsYourDOB: "What's your date of birth?", + uploadID: 'Upload ID and proof of address', + id: "ID (driver's license or passport)", + personalAddress: 'Proof of personal address (e.g. utility bill)', + letsDoubleCheck: 'Let’s double check that everything looks right.', + legalName: 'Legal name', + proofOf: 'Proof of personal address', + enterOneEmail: 'Enter the email of director or senior officer at', + regulationRequiresOneMoreDirector: 'Regulation requires one more director or senior officer as a signer.', + hangTight: 'Hang tight...', + enterTwoEmails: 'Enter the emails of two directors or senior officers at', + sendReminder: 'Send a reminder', + chooseFile: 'Choose file', + weAreWaiting: "We're waiting for others to verify their identities as directors or senior officers of the business.", }, agreementsStep: { agreements: 'Acuerdos', diff --git a/src/languages/params.ts b/src/languages/params.ts index e9f0c4370357..79eae25ae9a6 100644 --- a/src/languages/params.ts +++ b/src/languages/params.ts @@ -547,6 +547,10 @@ type CompanyCardBankName = { bankName: string; }; +type CompanyNameParams = { + companyName: string; +}; + export type { AuthenticationErrorParams, ImportMembersSuccessfullDescriptionParams, @@ -746,4 +750,5 @@ export type { OptionalParam, AssignCardParams, ImportedTypesParams, + CompanyNameParams, }; diff --git a/src/pages/ReimbursementAccount/NonUSD/SignerInfo/DirectorCheck.tsx b/src/pages/ReimbursementAccount/NonUSD/SignerInfo/DirectorCheck.tsx new file mode 100644 index 000000000000..648fd37e543b --- /dev/null +++ b/src/pages/ReimbursementAccount/NonUSD/SignerInfo/DirectorCheck.tsx @@ -0,0 +1,65 @@ +import React, {useMemo, useState} from 'react'; +import FormProvider from '@components/Form/FormProvider'; +import type {Choice} from '@components/RadioButtons'; +import RadioButtons from '@components/RadioButtons'; +import Text from '@components/Text'; +import useLocalize from '@hooks/useLocalize'; +import useThemeStyles from '@hooks/useThemeStyles'; +import ONYXKEYS from '@src/ONYXKEYS'; + +type DirectorCheckProps = { + /** The title of the question */ + title: string; + + /** The default value of the radio button */ + defaultValue: boolean; + + /** Callback when the value is selected */ + onSelectedValue: (value: boolean) => void; +}; + +function DirectorCheck({title, onSelectedValue, defaultValue}: DirectorCheckProps) { + const {translate} = useLocalize(); + const styles = useThemeStyles(); + const [value, setValue] = useState(defaultValue); + + const handleSubmit = () => { + onSelectedValue(value); + }; + const handleSelectValue = (newValue: string) => setValue(newValue === 'true'); + const options = useMemo( + () => [ + { + label: translate('common.yes'), + value: 'true', + }, + { + label: translate('common.no'), + value: 'false', + }, + ], + [translate], + ); + + return ( + + {title} + {translate('signerInfoStep.regulationRequiresUs')} + + + ); +} + +DirectorCheck.displayName = 'DirectorCheck'; + +export default DirectorCheck; diff --git a/src/pages/ReimbursementAccount/NonUSD/SignerInfo/EnterEmail.tsx b/src/pages/ReimbursementAccount/NonUSD/SignerInfo/EnterEmail.tsx new file mode 100644 index 000000000000..338820c78cf1 --- /dev/null +++ b/src/pages/ReimbursementAccount/NonUSD/SignerInfo/EnterEmail.tsx @@ -0,0 +1,83 @@ +import {Str} from 'expensify-common'; +import React, {useCallback} from 'react'; +import {useOnyx} from 'react-native-onyx'; +import FormProvider from '@components/Form/FormProvider'; +import InputWrapper from '@components/Form/InputWrapper'; +import type {FormInputErrors, FormOnyxValues} from '@components/Form/types'; +import Text from '@components/Text'; +import TextInput from '@components/TextInput'; +import useLocalize from '@hooks/useLocalize'; +import useThemeStyles from '@hooks/useThemeStyles'; +import * as ValidationUtils from '@libs/ValidationUtils'; +import CONST from '@src/CONST'; +import ONYXKEYS from '@src/ONYXKEYS'; +import INPUT_IDS from '@src/types/form/ReimbursementAccountForm'; + +type EnterEmailProps = { + onSubmit: () => void; + + isUserDirector: boolean; +}; + +const {SIGNER_EMAIL, SECOND_SIGNER_EMAIL} = INPUT_IDS.ADDITIONAL_DATA.CORPAY; + +function EnterEmail({onSubmit, isUserDirector}: EnterEmailProps) { + const {translate} = useLocalize(); + const styles = useThemeStyles(); + + const [reimbursementAccount] = useOnyx(ONYXKEYS.REIMBURSEMENT_ACCOUNT); + const policyID = reimbursementAccount?.achData?.policyID ?? '-1'; + const [policy] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY}${policyID}`); + const currency = policy?.outputCurrency ?? ''; + const shouldGatherBothEmails = currency === CONST.CURRENCY.AUD && !isUserDirector; + + const validate = useCallback( + (values: FormOnyxValues): FormInputErrors => { + const errors = ValidationUtils.getFieldRequiredErrors(values, shouldGatherBothEmails ? [SIGNER_EMAIL, SECOND_SIGNER_EMAIL] : [SIGNER_EMAIL]); + if (values[SIGNER_EMAIL] && !Str.isValidEmail(values[SIGNER_EMAIL])) { + errors[SIGNER_EMAIL] = translate('bankAccount.error.firstName'); + } + + if (shouldGatherBothEmails && values[SECOND_SIGNER_EMAIL] && !Str.isValidEmail(values[SECOND_SIGNER_EMAIL])) { + errors[SECOND_SIGNER_EMAIL] = translate('bankAccount.error.lastName'); + } + + return errors; + }, + [shouldGatherBothEmails, translate], + ); + + return ( + + {translate(shouldGatherBothEmails ? 'signerInfoStep.enterTwoEmails' : 'signerInfoStep.enterOneEmail')} + + {shouldGatherBothEmails && ( + + )} + + ); +} + +EnterEmail.displayName = 'EnterEmail'; + +export default EnterEmail; diff --git a/src/pages/ReimbursementAccount/NonUSD/SignerInfo/HangTight.tsx b/src/pages/ReimbursementAccount/NonUSD/SignerInfo/HangTight.tsx new file mode 100644 index 000000000000..15fea5e46691 --- /dev/null +++ b/src/pages/ReimbursementAccount/NonUSD/SignerInfo/HangTight.tsx @@ -0,0 +1,58 @@ +import React from 'react'; +import {View} from 'react-native'; +import Button from '@components/Button'; +import Icon from '@components/Icon'; +import * as Expensicons from '@components/Icon/Expensicons'; +import * as Illustrations from '@components/Icon/Illustrations'; +import SafeAreaConsumer from '@components/SafeAreaConsumer'; +import ScrollView from '@components/ScrollView'; +import Text from '@components/Text'; +import useLocalize from '@hooks/useLocalize'; +import useThemeStyles from '@hooks/useThemeStyles'; + +function HangTight({tempSubmit}: {tempSubmit: () => void}) { + const {translate} = useLocalize(); + const styles = useThemeStyles(); + + const handleSendReminder = () => { + // TODO remove that + tempSubmit(); + }; + + return ( + + {({safeAreaPaddingBottomStyle}) => ( + + + + + + {translate('signerInfoStep.hangTight')} + {translate('signerInfoStep.weAreWaiting')} + + +