From 159a9aa88b380940f17700504d79331f296c53a4 Mon Sep 17 00:00:00 2001 From: VickyStash Date: Wed, 17 Jul 2024 14:32:10 +0200 Subject: [PATCH 1/9] Implement EditCardLimitType page --- src/ROUTES.ts | 4 + src/SCREENS.ts | 1 + .../SelectionList/BaseSelectionList.tsx | 2 + .../SelectionList/RadioListItem.tsx | 4 +- src/components/SelectionList/types.ts | 9 ++ .../ModalStackNavigators/index.tsx | 1 + .../FULL_SCREEN_TO_RHP_MAPPING.ts | 7 +- src/libs/Navigation/linkingConfig/config.ts | 3 + src/libs/Navigation/types.ts | 4 + .../WorkspaceEditCardLimitTypePage.tsx | 114 ++++++++++++++++++ 10 files changed, 147 insertions(+), 2 deletions(-) create mode 100644 src/pages/workspace/expensifyCard/WorkspaceEditCardLimitTypePage.tsx diff --git a/src/ROUTES.ts b/src/ROUTES.ts index 2f3372144610..65c53b15b3b6 100644 --- a/src/ROUTES.ts +++ b/src/ROUTES.ts @@ -851,6 +851,10 @@ const ROUTES = { route: 'settings/workspaces/:policyID/expensify-card/:cardID', getRoute: (policyID: string, cardID: string, backTo?: string) => getUrlWithBackToParam(`settings/workspaces/${policyID}/expensify-card/${cardID}`, backTo), }, + WORKSPACE_EXPENSIFY_CARD_LIMIT_TYPE: { + route: 'settings/workspaces/:policyID/expensify-card/:cardID/edit/limit-type', + getRoute: (policyID: string, cardID: string) => `settings/workspaces/${policyID}/expensify-card/${cardID}/edit/limit-type` as const, + }, WORKSPACE_EXPENSIFY_CARD_ISSUE_NEW: { route: 'settings/workspaces/:policyID/expensify-card/issue-new', getRoute: (policyID: string) => `settings/workspaces/${policyID}/expensify-card/issue-new` as const, diff --git a/src/SCREENS.ts b/src/SCREENS.ts index b2f1ee002669..4a7cb574b51f 100644 --- a/src/SCREENS.ts +++ b/src/SCREENS.ts @@ -344,6 +344,7 @@ const SCREENS = { EXPENSIFY_CARD: 'Workspace_ExpensifyCard', EXPENSIFY_CARD_DETAILS: 'Workspace_ExpensifyCard_Details', EXPENSIFY_CARD_ISSUE_NEW: 'Workspace_ExpensifyCard_New', + EXPENSIFY_CARD_LIMIT_TYPE: 'Workspace_ExpensifyCard_LimitType', EXPENSIFY_CARD_BANK_ACCOUNT: 'Workspace_ExpensifyCard_BankAccount', BILLS: 'Workspace_Bills', INVOICES: 'Workspace_Invoices', diff --git a/src/components/SelectionList/BaseSelectionList.tsx b/src/components/SelectionList/BaseSelectionList.tsx index 6cc6b403dc7a..ebb5c2e939b7 100644 --- a/src/components/SelectionList/BaseSelectionList.tsx +++ b/src/components/SelectionList/BaseSelectionList.tsx @@ -80,6 +80,7 @@ function BaseSelectionList( customListHeaderHeight = 0, listHeaderWrapperStyle, isRowMultilineSupported = false, + isAlternateTextMultilineSupported = false, textInputRef, headerMessageStyle, shouldHideListOnInitialRender = true, @@ -461,6 +462,7 @@ function BaseSelectionList( rightHandSideComponent={rightHandSideComponent} keyForList={item.keyForList ?? ''} isMultilineSupported={isRowMultilineSupported} + isAlternateTextMultilineSupported={isAlternateTextMultilineSupported} onFocus={() => { if (isDisabled) { return; diff --git a/src/components/SelectionList/RadioListItem.tsx b/src/components/SelectionList/RadioListItem.tsx index 48ca474f6c60..52f0d33b7774 100644 --- a/src/components/SelectionList/RadioListItem.tsx +++ b/src/components/SelectionList/RadioListItem.tsx @@ -16,6 +16,7 @@ function RadioListItem({ shouldPreventEnterKeySubmit, rightHandSideComponent, isMultilineSupported = false, + isAlternateTextMultilineSupported = false, onFocus, shouldSyncFocus, }: RadioListItemProps) { @@ -61,7 +62,8 @@ function RadioListItem({ )} diff --git a/src/components/SelectionList/types.ts b/src/components/SelectionList/types.ts index 723e48699b3c..3c8ab75906db 100644 --- a/src/components/SelectionList/types.ts +++ b/src/components/SelectionList/types.ts @@ -58,6 +58,9 @@ type CommonListItemProps = { /** Whether to wrap long text up to 2 lines */ isMultilineSupported?: boolean; + /** Whether to wrap the alternate text up to 2 lines */ + isAlternateTextMultilineSupported?: boolean; + /** Handles what to do when the item is focused */ onFocus?: () => void; @@ -128,6 +131,9 @@ type ListItem = { /** Whether to wrap long text up to 2 lines */ isMultilineSupported?: boolean; + /** Whether to wrap the alternate text up to 2 lines */ + isAlternateTextMultilineSupported?: boolean; + /** The search value from the selection list */ searchText?: string | null; @@ -437,6 +443,9 @@ type BaseSelectionListProps = Partial & { /** Whether to wrap long text up to 2 lines */ isRowMultilineSupported?: boolean; + /** Whether to wrap the alternate text up to 2 lines */ + isAlternateTextMultilineSupported?: boolean; + /** Ref for textInput */ textInputRef?: MutableRefObject | ((ref: TextInput | null) => void); diff --git a/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx b/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx index 6c8873b1f0bf..13f44b48e39e 100644 --- a/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx +++ b/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx @@ -411,6 +411,7 @@ const SettingsModalStackNavigator = createModalStackNavigator require('../../../../pages/workspace/card/issueNew/IssueNewCardPage').default, [SCREENS.WORKSPACE.EXPENSIFY_CARD_BANK_ACCOUNT]: () => require('../../../../pages/workspace/expensifyCard/WorkspaceExpensifyCardBankAccounts').default, [SCREENS.WORKSPACE.EXPENSIFY_CARD_DETAILS]: () => require('../../../../pages/workspace/expensifyCard/WorkspaceExpensifyCardDetailsPage').default, + [SCREENS.WORKSPACE.EXPENSIFY_CARD_LIMIT_TYPE]: () => require('../../../../pages/workspace/expensifyCard/WorkspaceEditCardLimitTypePage').default, [SCREENS.SETTINGS.SAVE_THE_WORLD]: () => require('../../../../pages/TeachersUnite/SaveTheWorldPage').default, [SCREENS.SETTINGS.SUBSCRIPTION.CHANGE_PAYMENT_CURRENCY]: () => require('../../../../pages/settings/PaymentCard/ChangeCurrency').default, [SCREENS.SETTINGS.SUBSCRIPTION.CHANGE_BILLING_CURRENCY]: () => require('../../../../pages/settings/Subscription/PaymentCard/ChangeBillingCurrency').default, diff --git a/src/libs/Navigation/linkingConfig/FULL_SCREEN_TO_RHP_MAPPING.ts b/src/libs/Navigation/linkingConfig/FULL_SCREEN_TO_RHP_MAPPING.ts index f03b837cb9fc..63852d61ba9d 100755 --- a/src/libs/Navigation/linkingConfig/FULL_SCREEN_TO_RHP_MAPPING.ts +++ b/src/libs/Navigation/linkingConfig/FULL_SCREEN_TO_RHP_MAPPING.ts @@ -161,7 +161,12 @@ const FULL_SCREEN_TO_RHP_MAPPING: Partial> = { SCREENS.WORKSPACE.REPORT_FIELDS_EDIT_VALUE, SCREENS.WORKSPACE.REPORT_FIELDS_EDIT_INITIAL_VALUE, ], - [SCREENS.WORKSPACE.EXPENSIFY_CARD]: [SCREENS.WORKSPACE.EXPENSIFY_CARD_ISSUE_NEW, SCREENS.WORKSPACE.EXPENSIFY_CARD_BANK_ACCOUNT, SCREENS.WORKSPACE.EXPENSIFY_CARD_DETAILS], + [SCREENS.WORKSPACE.EXPENSIFY_CARD]: [ + SCREENS.WORKSPACE.EXPENSIFY_CARD_ISSUE_NEW, + SCREENS.WORKSPACE.EXPENSIFY_CARD_BANK_ACCOUNT, + SCREENS.WORKSPACE.EXPENSIFY_CARD_DETAILS, + SCREENS.WORKSPACE.EXPENSIFY_CARD_LIMIT_TYPE, + ], }; export default FULL_SCREEN_TO_RHP_MAPPING; diff --git a/src/libs/Navigation/linkingConfig/config.ts b/src/libs/Navigation/linkingConfig/config.ts index e5b4683a617b..f9713a20eac1 100644 --- a/src/libs/Navigation/linkingConfig/config.ts +++ b/src/libs/Navigation/linkingConfig/config.ts @@ -465,6 +465,9 @@ const config: LinkingOptions['config'] = { [SCREENS.WORKSPACE.EXPENSIFY_CARD_ISSUE_NEW]: { path: ROUTES.WORKSPACE_EXPENSIFY_CARD_ISSUE_NEW.route, }, + [SCREENS.WORKSPACE.EXPENSIFY_CARD_LIMIT_TYPE]: { + path: ROUTES.WORKSPACE_EXPENSIFY_CARD_LIMIT_TYPE.route, + }, [SCREENS.WORKSPACE.EXPENSIFY_CARD_BANK_ACCOUNT]: { path: ROUTES.WORKSPACE_EXPENSIFY_CARD_BANK_ACCOUNT.route, }, diff --git a/src/libs/Navigation/types.ts b/src/libs/Navigation/types.ts index b66e74e384b3..f43af45390a5 100644 --- a/src/libs/Navigation/types.ts +++ b/src/libs/Navigation/types.ts @@ -661,6 +661,10 @@ type SettingsNavigatorParamList = { cardID: string; backTo?: Routes; }; + [SCREENS.WORKSPACE.EXPENSIFY_CARD_LIMIT_TYPE]: { + policyID: string; + cardID: string; + }; } & ReimbursementAccountNavigatorParamList; type NewChatNavigatorParamList = { diff --git a/src/pages/workspace/expensifyCard/WorkspaceEditCardLimitTypePage.tsx b/src/pages/workspace/expensifyCard/WorkspaceEditCardLimitTypePage.tsx new file mode 100644 index 000000000000..1a2092525145 --- /dev/null +++ b/src/pages/workspace/expensifyCard/WorkspaceEditCardLimitTypePage.tsx @@ -0,0 +1,114 @@ +import type {StackScreenProps} from '@react-navigation/stack'; +import React, {useMemo, useState} from 'react'; +import {useOnyx} from 'react-native-onyx'; +import Button from '@components/Button'; +import HeaderWithBackButton from '@components/HeaderWithBackButton'; +import ScreenWrapper from '@components/ScreenWrapper'; +import SelectionList from '@components/SelectionList'; +import RadioListItem from '@components/SelectionList/RadioListItem'; +import useLocalize from '@hooks/useLocalize'; +import useThemeStyles from '@hooks/useThemeStyles'; +import type {SettingsNavigatorParamList} from '@navigation/types'; +import CONST from '@src/CONST'; +import ONYXKEYS from '@src/ONYXKEYS'; +import type SCREENS from '@src/SCREENS'; +import {isEmptyObject} from '@src/types/utils/EmptyObject'; + +// TODO: remove when Onyx data is available +const mockedCard = { + accountID: 885646, + availableSpend: 1000, + nameValuePairs: { + cardTitle: 'Test 1', + isVirtual: true, + limit: 2000, + limitType: CONST.EXPENSIFY_CARD.LIMIT_TYPES.SMART, + }, + lastFourPAN: '1234', +}; + +type WorkspaceEditCardLimitTypePageProps = StackScreenProps; + +function WorkspaceEditCardLimitTypePage({route}: WorkspaceEditCardLimitTypePageProps) { + const {policyID, cardID} = route.params; + + const {translate} = useLocalize(); + const styles = useThemeStyles(); + + const [cardsList] = useOnyx(`${ONYXKEYS.COLLECTION.WORKSPACE_CARDS_LIST}${policyID}_${CONST.EXPENSIFY_CARD.BANK}`); + const [policy] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY}${policyID}`); + + const card = cardsList?.[cardID] ?? mockedCard; + const areApprovalsConfigured = !isEmptyObject(policy?.approver) && policy?.approvalMode !== CONST.POLICY.APPROVAL_MODE.OPTIONAL; + const defaultType = areApprovalsConfigured ? CONST.EXPENSIFY_CARD.LIMIT_TYPES.SMART : CONST.EXPENSIFY_CARD.LIMIT_TYPES.MONTHLY; + + const [typeSelected, setTypeSelected] = useState(card.nameValuePairs?.limitType ?? defaultType); + + const submit = () => { + // TODO: add API call when it's supported https://github.com/Expensify/Expensify/issues/407833 + }; + + const data = useMemo(() => { + const options = []; + + options.push( + { + value: CONST.EXPENSIFY_CARD.LIMIT_TYPES.SMART, + text: translate('workspace.card.issueNewCard.smartLimit'), + alternateText: translate('workspace.card.issueNewCard.smartLimitDescription'), + keyForList: CONST.EXPENSIFY_CARD.LIMIT_TYPES.SMART, + isMultilineSupported: true, + isSelected: typeSelected === CONST.EXPENSIFY_CARD.LIMIT_TYPES.SMART, + }, + { + value: CONST.EXPENSIFY_CARD.LIMIT_TYPES.MONTHLY, + text: translate('workspace.card.issueNewCard.monthly'), + alternateText: translate('workspace.card.issueNewCard.monthlyDescription'), + keyForList: CONST.EXPENSIFY_CARD.LIMIT_TYPES.MONTHLY, + isMultilineSupported: true, + isSelected: typeSelected === CONST.EXPENSIFY_CARD.LIMIT_TYPES.MONTHLY, + }, + { + value: CONST.EXPENSIFY_CARD.LIMIT_TYPES.FIXED, + text: translate('workspace.card.issueNewCard.fixedAmount'), + alternateText: translate('workspace.card.issueNewCard.fixedAmountDescription'), + keyForList: CONST.EXPENSIFY_CARD.LIMIT_TYPES.FIXED, + isMultilineSupported: true, + isSelected: typeSelected === CONST.EXPENSIFY_CARD.LIMIT_TYPES.FIXED, + }, + ); + + return options; + }, [translate, typeSelected]); + + return ( + + + setTypeSelected(value)} + sections={[{data}]} + shouldDebounceRowSelect + isAlternateTextMultilineSupported + initiallyFocusedOptionKey={typeSelected} + /> +