diff --git a/src/CONST/index.ts b/src/CONST/index.ts index 77258ca14675..545ee09c1ff6 100755 --- a/src/CONST/index.ts +++ b/src/CONST/index.ts @@ -3182,6 +3182,7 @@ const CONST = { }, LIMIT_VALUE: 21474836, STEP_NAMES: ['1', '2', '3', '4', '5', '6'], + ASSIGNEE_EXCLUDED_STEP_NAMES: ['1', '2', '3', '4', '5'], STEP: { ASSIGNEE: 'Assignee', CARD_TYPE: 'CardType', diff --git a/src/libs/actions/Card.ts b/src/libs/actions/Card.ts index d147406c6490..a70180185d20 100644 --- a/src/libs/actions/Card.ts +++ b/src/libs/actions/Card.ts @@ -40,6 +40,9 @@ type IssueNewCardFlowData = { /** ID of the policy */ policyID: string | undefined; + + /** Whether the changing assignee is disabled. E.g., The assignee is auto selected from workspace members page */ + isChangeAssigneeDisabled?: boolean; }; function reportVirtualExpensifyCardFraud(card: Card, validateCode: string) { @@ -363,12 +366,13 @@ function getCardDefaultName(userName?: string) { return `${userName}'s Card`; } -function setIssueNewCardStepAndData({data, isEditing, step, policyID}: IssueNewCardFlowData) { +function setIssueNewCardStepAndData({data, isEditing, step, policyID, isChangeAssigneeDisabled}: IssueNewCardFlowData) { Onyx.merge(`${ONYXKEYS.COLLECTION.ISSUE_NEW_EXPENSIFY_CARD}${policyID}`, { data, isEditing, currentStep: step, errors: null, + isChangeAssigneeDisabled, }); } diff --git a/src/pages/workspace/expensifyCard/WorkspaceExpensifyCardBankAccounts.tsx b/src/pages/workspace/expensifyCard/WorkspaceExpensifyCardBankAccounts.tsx index 6e912362cd4a..d41b03a4b2ed 100644 --- a/src/pages/workspace/expensifyCard/WorkspaceExpensifyCardBankAccounts.tsx +++ b/src/pages/workspace/expensifyCard/WorkspaceExpensifyCardBankAccounts.tsx @@ -25,7 +25,7 @@ import Navigation from '@navigation/Navigation'; import type {SettingsNavigatorParamList} from '@navigation/types'; import AccessOrNotFoundWrapper from '@pages/workspace/AccessOrNotFoundWrapper'; import variables from '@styles/variables'; -import {configureExpensifyCardsForPolicy} from '@userActions/Card'; +import {configureExpensifyCardsForPolicy, setIssueNewCardStepAndData} from '@userActions/Card'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; @@ -160,6 +160,7 @@ function WorkspaceExpensifyCardBankAccounts({route}: WorkspaceExpensifyCardBankA style={[styles.m5, bottomSafeAreaPaddingStyle]} pressOnEnter onPress={() => { + setIssueNewCardStepAndData({policyID, isChangeAssigneeDisabled: false}); Navigation.dismissModal(); Navigation.navigate(ROUTES.WORKSPACE_EXPENSIFY_CARD_ISSUE_NEW.getRoute(policyID)); }} diff --git a/src/pages/workspace/expensifyCard/WorkspaceExpensifyCardListPage.tsx b/src/pages/workspace/expensifyCard/WorkspaceExpensifyCardListPage.tsx index 531e77cbf6c1..003ed2c91bc2 100644 --- a/src/pages/workspace/expensifyCard/WorkspaceExpensifyCardListPage.tsx +++ b/src/pages/workspace/expensifyCard/WorkspaceExpensifyCardListPage.tsx @@ -28,6 +28,7 @@ import useResponsiveLayout from '@hooks/useResponsiveLayout'; import useSearchResults from '@hooks/useSearchResults'; import useThemeStyles from '@hooks/useThemeStyles'; import useWindowDimensions from '@hooks/useWindowDimensions'; +import {setIssueNewCardStepAndData} from '@libs/actions/Card'; import {clearDeletePaymentMethodError} from '@libs/actions/PaymentMethods'; import {filterCardsByPersonalDetails, getCardsByCardholderName, sortCardsByCardholderName} from '@libs/CardUtils'; import type {PlatformStackRouteProp} from '@libs/Navigation/PlatformStackNavigation/types'; @@ -102,6 +103,7 @@ function WorkspaceExpensifyCardListPage({route, cardsList, fundID}: WorkspaceExp return; } const activeRoute = Navigation.getActiveRoute(); + setIssueNewCardStepAndData({policyID, isChangeAssigneeDisabled: false}); Navigation.navigate(ROUTES.WORKSPACE_EXPENSIFY_CARD_ISSUE_NEW.getRoute(policyID, activeRoute)); }; diff --git a/src/pages/workspace/expensifyCard/issueNew/AssigneeStep.tsx b/src/pages/workspace/expensifyCard/issueNew/AssigneeStep.tsx index 23f1f62e89ba..8e8a9874b14d 100644 --- a/src/pages/workspace/expensifyCard/issueNew/AssigneeStep.tsx +++ b/src/pages/workspace/expensifyCard/issueNew/AssigneeStep.tsx @@ -27,9 +27,15 @@ const MINIMUM_MEMBER_TO_SHOW_SEARCH = 8; type AssigneeStepProps = { // The policy that the card will be issued under policy: OnyxEntry; + + /** Array of step names */ + stepNames: readonly string[]; + + /** Start from step index */ + startStepIndex: number; }; -function AssigneeStep({policy}: AssigneeStepProps) { +function AssigneeStep({policy, stepNames, startStepIndex}: AssigneeStepProps) { const {translate, formatPhoneNumber, localeCompare} = useLocalize(); const styles = useThemeStyles(); const {isOffline} = useNetwork(); @@ -139,8 +145,8 @@ function AssigneeStep({policy}: AssigneeStepProps) { shouldEnableMaxHeight headerTitle={translate('workspace.card.issueCard')} handleBackButtonPress={handleBackButtonPress} - startStepIndex={0} - stepNames={CONST.EXPENSIFY_CARD.STEP_NAMES} + startStepIndex={startStepIndex} + stepNames={stepNames} enableEdgeToEdgeBottomSafeAreaPadding > {translate('workspace.card.issueNewCard.whoNeedsCard')} diff --git a/src/pages/workspace/expensifyCard/issueNew/CardNameStep.tsx b/src/pages/workspace/expensifyCard/issueNew/CardNameStep.tsx index 3cc992b129ab..73dc495cee3c 100644 --- a/src/pages/workspace/expensifyCard/issueNew/CardNameStep.tsx +++ b/src/pages/workspace/expensifyCard/issueNew/CardNameStep.tsx @@ -21,13 +21,19 @@ import INPUT_IDS from '@src/types/form/IssueNewExpensifyCardForm'; type CardNameStepProps = { /** ID of the policy */ policyID: string | undefined; + + /** Array of step names */ + stepNames: readonly string[]; + + /** Start from step index */ + startStepIndex: number; }; -function CardNameStep({policyID}: CardNameStepProps) { +function CardNameStep({policyID, stepNames, startStepIndex}: CardNameStepProps) { const {translate} = useLocalize(); const styles = useThemeStyles(); const {inputCallbackRef} = useAutoFocusInput(); - const [issueNewCard] = useOnyx(`${ONYXKEYS.COLLECTION.ISSUE_NEW_EXPENSIFY_CARD}${policyID}`); + const [issueNewCard] = useOnyx(`${ONYXKEYS.COLLECTION.ISSUE_NEW_EXPENSIFY_CARD}${policyID}`, {canBeMissing: true}); const isEditing = issueNewCard?.isEditing; const data = issueNewCard?.data; @@ -73,8 +79,8 @@ function CardNameStep({policyID}: CardNameStepProps) { shouldEnableMaxHeight headerTitle={translate('workspace.card.issueCard')} handleBackButtonPress={handleBackButtonPress} - startStepIndex={4} - stepNames={CONST.EXPENSIFY_CARD.STEP_NAMES} + startStepIndex={startStepIndex} + stepNames={stepNames} enableEdgeToEdgeBottomSafeAreaPadding > {translate('workspace.card.issueNewCard.giveItName')} diff --git a/src/pages/workspace/expensifyCard/issueNew/CardTypeStep.tsx b/src/pages/workspace/expensifyCard/issueNew/CardTypeStep.tsx index 8d53ddb21846..18060016de44 100644 --- a/src/pages/workspace/expensifyCard/issueNew/CardTypeStep.tsx +++ b/src/pages/workspace/expensifyCard/issueNew/CardTypeStep.tsx @@ -8,7 +8,8 @@ import Text from '@components/Text'; import useLocalize from '@hooks/useLocalize'; import useOnyx from '@hooks/useOnyx'; import useThemeStyles from '@hooks/useThemeStyles'; -import {setIssueNewCardStepAndData} from '@libs/actions/Card'; +import {clearIssueNewCardFlow, setIssueNewCardStepAndData} from '@libs/actions/Card'; +import Navigation from '@libs/Navigation/Navigation'; import variables from '@styles/variables'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; @@ -16,12 +17,18 @@ import ONYXKEYS from '@src/ONYXKEYS'; type CardTypeStepProps = { /** ID of the policy */ policyID: string | undefined; + + /** Array of step names */ + stepNames: readonly string[]; + + /** Start from step index */ + startStepIndex: number; }; -function CardTypeStep({policyID}: CardTypeStepProps) { +function CardTypeStep({policyID, stepNames, startStepIndex}: CardTypeStepProps) { const {translate} = useLocalize(); const styles = useThemeStyles(); - const [issueNewCard] = useOnyx(`${ONYXKEYS.COLLECTION.ISSUE_NEW_EXPENSIFY_CARD}${policyID}`); + const [issueNewCard] = useOnyx(`${ONYXKEYS.COLLECTION.ISSUE_NEW_EXPENSIFY_CARD}${policyID}`, {canBeMissing: true}); const isEditing = issueNewCard?.isEditing; @@ -41,6 +48,11 @@ function CardTypeStep({policyID}: CardTypeStepProps) { setIssueNewCardStepAndData({step: CONST.EXPENSIFY_CARD.STEP.CONFIRMATION, isEditing: false, policyID}); return; } + if (issueNewCard?.isChangeAssigneeDisabled) { + Navigation.goBack(); + clearIssueNewCardFlow(policyID); + return; + } setIssueNewCardStepAndData({step: CONST.EXPENSIFY_CARD.STEP.ASSIGNEE, policyID}); }; @@ -52,8 +64,8 @@ function CardTypeStep({policyID}: CardTypeStepProps) { offlineIndicatorStyle={styles.mtAuto} headerTitle={translate('workspace.card.issueCard')} handleBackButtonPress={handleBackButtonPress} - startStepIndex={1} - stepNames={CONST.EXPENSIFY_CARD.STEP_NAMES} + startStepIndex={startStepIndex} + stepNames={stepNames} enableEdgeToEdgeBottomSafeAreaPadding > {translate('workspace.card.issueNewCard.chooseCardType')} diff --git a/src/pages/workspace/expensifyCard/issueNew/ConfirmationStep.tsx b/src/pages/workspace/expensifyCard/issueNew/ConfirmationStep.tsx index 37768790ccde..50542a23c9a1 100644 --- a/src/pages/workspace/expensifyCard/issueNew/ConfirmationStep.tsx +++ b/src/pages/workspace/expensifyCard/issueNew/ConfirmationStep.tsx @@ -31,9 +31,15 @@ type ConfirmationStepProps = { /** Route to navigate to */ backTo?: Route; + + /** Array of step names */ + stepNames: readonly string[]; + + /** Start from step index */ + startStepIndex: number; }; -function ConfirmationStep({policyID, backTo}: ConfirmationStepProps) { +function ConfirmationStep({policyID, backTo, stepNames, startStepIndex}: ConfirmationStepProps) { const {translate} = useLocalize(); const styles = useThemeStyles(); const {isOffline} = useNetwork(); @@ -90,8 +96,8 @@ function ConfirmationStep({policyID, backTo}: ConfirmationStepProps) { shouldEnableMaxHeight headerTitle={translate('workspace.card.issueCard')} handleBackButtonPress={handleBackButtonPress} - startStepIndex={5} - stepNames={CONST.EXPENSIFY_CARD.STEP_NAMES} + startStepIndex={startStepIndex} + stepNames={stepNames} enableEdgeToEdgeBottomSafeAreaPadding > editStep(CONST.EXPENSIFY_CARD.STEP.ASSIGNEE)} /> ; +function getStartStepIndex(issueNewCard: OnyxEntry): number { + if (!issueNewCard) { + return 0; + } + + const STEP_INDEXES: Record = { + [CONST.EXPENSIFY_CARD.STEP.ASSIGNEE]: 0, + [CONST.EXPENSIFY_CARD.STEP.CARD_TYPE]: 1, + [CONST.EXPENSIFY_CARD.STEP.LIMIT_TYPE]: 2, + [CONST.EXPENSIFY_CARD.STEP.LIMIT]: 3, + [CONST.EXPENSIFY_CARD.STEP.CARD_NAME]: 4, + [CONST.EXPENSIFY_CARD.STEP.CONFIRMATION]: 5, + }; + + const stepIndex = STEP_INDEXES[issueNewCard.currentStep]; + return issueNewCard.isChangeAssigneeDisabled ? stepIndex - 1 : stepIndex; +} + function IssueNewCardPage({policy, route}: IssueNewCardPageProps) { const policyID = policy?.id; const [issueNewCard] = useOnyx(`${ONYXKEYS.COLLECTION.ISSUE_NEW_EXPENSIFY_CARD}${policyID}`, {canBeMissing: true}); @@ -30,6 +50,8 @@ function IssueNewCardPage({policy, route}: IssueNewCardPageProps) { const firstAssigneeEmail = useInitial(issueNewCard?.data?.assigneeEmail); const shouldUseBackToParam = !firstAssigneeEmail || firstAssigneeEmail === issueNewCard?.data?.assigneeEmail; const [isActingAsDelegate] = useOnyx(ONYXKEYS.ACCOUNT, {selector: (account) => !!account?.delegatedAccess?.delegate, canBeMissing: true}); + const stepNames = issueNewCard?.isChangeAssigneeDisabled ? CONST.EXPENSIFY_CARD.ASSIGNEE_EXCLUDED_STEP_NAMES : CONST.EXPENSIFY_CARD.STEP_NAMES; + const startStepIndex = useMemo(() => getStartStepIndex(issueNewCard), [issueNewCard]); useEffect(() => { startIssueNewCardFlow(policyID); @@ -38,24 +60,62 @@ function IssueNewCardPage({policy, route}: IssueNewCardPageProps) { const getCurrentStep = () => { switch (currentStep) { case CONST.EXPENSIFY_CARD.STEP.ASSIGNEE: - return ; + return ( + + ); case CONST.EXPENSIFY_CARD.STEP.CARD_TYPE: - return ; + return ( + + ); case CONST.EXPENSIFY_CARD.STEP.LIMIT_TYPE: - return ; + return ( + + ); case CONST.EXPENSIFY_CARD.STEP.LIMIT: - return ; + return ( + + ); case CONST.EXPENSIFY_CARD.STEP.CARD_NAME: - return ; + return ( + + ); case CONST.EXPENSIFY_CARD.STEP.CONFIRMATION: return ( ); default: - return ; + return ( + + ); } }; diff --git a/src/pages/workspace/expensifyCard/issueNew/LimitStep.tsx b/src/pages/workspace/expensifyCard/issueNew/LimitStep.tsx index 59d1386fa3a5..abf002f35125 100644 --- a/src/pages/workspace/expensifyCard/issueNew/LimitStep.tsx +++ b/src/pages/workspace/expensifyCard/issueNew/LimitStep.tsx @@ -19,13 +19,19 @@ import INPUT_IDS from '@src/types/form/IssueNewExpensifyCardForm'; type LimitStepProps = { /** ID of the policy */ policyID: string | undefined; + + /** Array of step names */ + stepNames: readonly string[]; + + /** Start from step index */ + startStepIndex: number; }; -function LimitStep({policyID}: LimitStepProps) { +function LimitStep({policyID, stepNames, startStepIndex}: LimitStepProps) { const {translate} = useLocalize(); const {inputCallbackRef} = useAutoFocusInput(); const styles = useThemeStyles(); - const [issueNewCard] = useOnyx(`${ONYXKEYS.COLLECTION.ISSUE_NEW_EXPENSIFY_CARD}${policyID}`); + const [issueNewCard] = useOnyx(`${ONYXKEYS.COLLECTION.ISSUE_NEW_EXPENSIFY_CARD}${policyID}`, {canBeMissing: true}); const isEditing = issueNewCard?.isEditing; const submit = useCallback( @@ -75,8 +81,8 @@ function LimitStep({policyID}: LimitStepProps) { shouldEnableMaxHeight headerTitle={translate('workspace.card.issueCard')} handleBackButtonPress={handleBackButtonPress} - startStepIndex={3} - stepNames={CONST.EXPENSIFY_CARD.STEP_NAMES} + startStepIndex={startStepIndex} + stepNames={stepNames} enableEdgeToEdgeBottomSafeAreaPadding > {translate('workspace.card.issueNewCard.setLimit')} diff --git a/src/pages/workspace/expensifyCard/issueNew/LimitTypeStep.tsx b/src/pages/workspace/expensifyCard/issueNew/LimitTypeStep.tsx index 67e13e0090ab..da8b2f6011a5 100644 --- a/src/pages/workspace/expensifyCard/issueNew/LimitTypeStep.tsx +++ b/src/pages/workspace/expensifyCard/issueNew/LimitTypeStep.tsx @@ -17,13 +17,19 @@ import type * as OnyxTypes from '@src/types/onyx'; type LimitTypeStepProps = { // The policy that the card will be issued under policy: OnyxEntry; + + /** Start from step index */ + startStepIndex: number; + + /** Array of step names */ + stepNames: readonly string[]; }; -function LimitTypeStep({policy}: LimitTypeStepProps) { +function LimitTypeStep({policy, stepNames, startStepIndex}: LimitTypeStepProps) { const {translate} = useLocalize(); const styles = useThemeStyles(); const policyID = policy?.id; - const [issueNewCard] = useOnyx(`${ONYXKEYS.COLLECTION.ISSUE_NEW_EXPENSIFY_CARD}${policyID}`); + const [issueNewCard] = useOnyx(`${ONYXKEYS.COLLECTION.ISSUE_NEW_EXPENSIFY_CARD}${policyID}`, {canBeMissing: true}); const areApprovalsConfigured = getApprovalWorkflow(policy) !== CONST.POLICY.APPROVAL_MODE.OPTIONAL; const defaultType = areApprovalsConfigured ? CONST.EXPENSIFY_CARD.LIMIT_TYPES.SMART : CONST.EXPENSIFY_CARD.LIMIT_TYPES.MONTHLY; @@ -89,8 +95,8 @@ function LimitTypeStep({policy}: LimitTypeStepProps) { shouldEnableMaxHeight headerTitle={translate('workspace.card.issueCard')} handleBackButtonPress={handleBackButtonPress} - startStepIndex={2} - stepNames={CONST.EXPENSIFY_CARD.STEP_NAMES} + startStepIndex={startStepIndex} + stepNames={stepNames} enableEdgeToEdgeBottomSafeAreaPadding > {translate('workspace.card.issueNewCard.chooseLimitType')} diff --git a/src/pages/workspace/members/WorkspaceMemberDetailsPage.tsx b/src/pages/workspace/members/WorkspaceMemberDetailsPage.tsx index 6bb122e5a70e..2b6416ebaee9 100644 --- a/src/pages/workspace/members/WorkspaceMemberDetailsPage.tsx +++ b/src/pages/workspace/members/WorkspaceMemberDetailsPage.tsx @@ -252,6 +252,7 @@ function WorkspaceMemberDetailsPage({personalDetails, policy, route}: WorkspaceM assigneeEmail: memberLogin, }, isEditing: false, + isChangeAssigneeDisabled: true, policyID, }); Navigation.navigate(ROUTES.WORKSPACE_EXPENSIFY_CARD_ISSUE_NEW.getRoute(policyID, activeRoute)); diff --git a/src/pages/workspace/members/WorkspaceMemberNewCardPage.tsx b/src/pages/workspace/members/WorkspaceMemberNewCardPage.tsx index ffff569fd4f7..c2d4418975a1 100644 --- a/src/pages/workspace/members/WorkspaceMemberNewCardPage.tsx +++ b/src/pages/workspace/members/WorkspaceMemberNewCardPage.tsx @@ -90,6 +90,7 @@ function WorkspaceMemberNewCardPage({route, personalDetails}: WorkspaceMemberNew assigneeEmail: memberLogin, }, isEditing: false, + isChangeAssigneeDisabled: true, policyID, }); Navigation.navigate(ROUTES.WORKSPACE_EXPENSIFY_CARD_ISSUE_NEW.getRoute(policyID, ROUTES.WORKSPACE_MEMBER_DETAILS.getRoute(policyID, accountID))); diff --git a/src/types/onyx/Card.ts b/src/types/onyx/Card.ts index 1ddcd51a9f68..55308ad81fb9 100644 --- a/src/types/onyx/Card.ts +++ b/src/types/onyx/Card.ts @@ -236,6 +236,9 @@ type IssueNewCard = { /** Whether the user is editing step */ isEditing: boolean; + /** Whether the changing assignee is disabled. E.g., The assignee is auto selected from workspace members page */ + isChangeAssigneeDisabled: boolean; + /** Whether the request is being processed */ isLoading?: boolean;