diff --git a/src/ONYXKEYS.ts b/src/ONYXKEYS.ts index c737341e066f..195c8b7eaea9 100755 --- a/src/ONYXKEYS.ts +++ b/src/ONYXKEYS.ts @@ -588,6 +588,9 @@ const ONYXKEYS = { /** Stores the information about the members imported from the spreadsheet */ IMPORTED_SPREADSHEET_MEMBER_DATA: 'importedSpreadsheetMemberData', + /** Stores the role selected for members being imported from a spreadsheet */ + IMPORTED_SPREADSHEET_MEMBER_ROLE: 'importedSpreadsheetMemberRole', + /** Stores the route to open after changing app permission from settings */ LAST_ROUTE: 'lastRoute', @@ -1547,6 +1550,7 @@ type OnyxValuesMapping = { [ONYXKEYS.APPROVAL_WORKFLOW]: OnyxTypes.ApprovalWorkflowOnyx; [ONYXKEYS.IMPORTED_SPREADSHEET]: OnyxTypes.ImportedSpreadsheet; [ONYXKEYS.IMPORTED_SPREADSHEET_MEMBER_DATA]: OnyxTypes.ImportedSpreadsheetMemberData[]; + [ONYXKEYS.IMPORTED_SPREADSHEET_MEMBER_ROLE]: ValueOf; [ONYXKEYS.LAST_ROUTE]: string; [ONYXKEYS.IS_USING_IMPORTED_STATE]: boolean; [ONYXKEYS.NVP_EXPENSIFY_COMPANY_CARDS_CUSTOM_NAMES]: Record; diff --git a/src/ROUTES.ts b/src/ROUTES.ts index 62a1b10a4f5f..b96be5c7294d 100644 --- a/src/ROUTES.ts +++ b/src/ROUTES.ts @@ -103,6 +103,10 @@ const DYNAMIC_ROUTES = { path: 'expense-limit-type', entryScreens: [SCREENS.WORKSPACE.DYNAMIC_CATEGORY_FLAG_AMOUNTS_OVER], }, + IMPORTED_MEMBERS_ROLE: { + path: 'imported-members-role', + entryScreens: [SCREENS.WORKSPACE.MEMBERS_IMPORTED_CONFIRMATION], + }, REPORT_SETTINGS_NAME: { path: 'settings/name', entryScreens: [SCREENS.REPORT_DETAILS.ROOT], diff --git a/src/SCREENS.ts b/src/SCREENS.ts index 6ecbcfabb809..ebc40c2b9d02 100644 --- a/src/SCREENS.ts +++ b/src/SCREENS.ts @@ -801,6 +801,7 @@ const SCREENS = { DYNAMIC_CATEGORY_DEFAULT_TAX_RATE: 'Dynamic_Category_Default_Tax_Rate', DYNAMIC_CATEGORY_FLAG_AMOUNTS_OVER: 'Dynamic_Category_Flag_Amounts_Over', DYNAMIC_EXPENSE_LIMIT_TYPE_SELECTOR: 'Dynamic_Expense_Limit_Type_Selector', + DYNAMIC_IMPORTED_MEMBERS_ROLE: 'Dynamic_Imported_Members_Role', DYNAMIC_CATEGORY_DESCRIPTION_HINT: 'Dynamic_Category_Description_Hint', DYNAMIC_CATEGORY_APPROVER: 'Dynamic_Category_Approver', DYNAMIC_CATEGORY_REQUIRE_RECEIPTS_OVER: 'Dynamic_Category_Require_Receipts_Over', diff --git a/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx b/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx index 14f4865e5025..11b8e828cc23 100644 --- a/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx +++ b/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx @@ -559,6 +559,7 @@ const SettingsModalStackNavigator = createModalStackNavigator require('../../../../pages/workspace/categories/DynamicCategoryDefaultTaxRatePage').default, [SCREENS.WORKSPACE.DYNAMIC_CATEGORY_FLAG_AMOUNTS_OVER]: () => require('../../../../pages/workspace/categories/DynamicCategoryFlagAmountsOverPage').default, [SCREENS.WORKSPACE.DYNAMIC_EXPENSE_LIMIT_TYPE_SELECTOR]: () => require('../../../../pages/workspace/categories/DynamicExpenseLimitTypeSelectorPage').default, + [SCREENS.WORKSPACE.DYNAMIC_IMPORTED_MEMBERS_ROLE]: () => require('../../../../pages/workspace/members/DynamicImportedMembersRoleSelectionPage').default, [SCREENS.WORKSPACE.DYNAMIC_CATEGORY_DESCRIPTION_HINT]: () => require('../../../../pages/workspace/categories/DynamicCategoryDescriptionHintPage').default, [SCREENS.WORKSPACE.DYNAMIC_CATEGORY_REQUIRE_RECEIPTS_OVER]: () => require('../../../../pages/workspace/categories/DynamicCategoryRequireReceiptsOverPage').default, [SCREENS.WORKSPACE.DYNAMIC_CATEGORY_REQUIRE_ITEMIZED_RECEIPTS_OVER]: () => diff --git a/src/libs/Navigation/linkingConfig/RELATIONS/WORKSPACE_TO_RHP.ts b/src/libs/Navigation/linkingConfig/RELATIONS/WORKSPACE_TO_RHP.ts index 1061dc839f61..83bb987cf2dd 100755 --- a/src/libs/Navigation/linkingConfig/RELATIONS/WORKSPACE_TO_RHP.ts +++ b/src/libs/Navigation/linkingConfig/RELATIONS/WORKSPACE_TO_RHP.ts @@ -24,6 +24,7 @@ const WORKSPACE_TO_RHP: Partial['config'] = { [SCREENS.WORKSPACE.DYNAMIC_CATEGORY_DEFAULT_TAX_RATE]: DYNAMIC_ROUTES.WORKSPACE_CATEGORY_DEFAULT_TAX_RATE.path, [SCREENS.WORKSPACE.DYNAMIC_CATEGORY_FLAG_AMOUNTS_OVER]: DYNAMIC_ROUTES.WORKSPACE_CATEGORY_FLAG_AMOUNTS_OVER.path, [SCREENS.WORKSPACE.DYNAMIC_EXPENSE_LIMIT_TYPE_SELECTOR]: DYNAMIC_ROUTES.EXPENSE_LIMIT_TYPE_SELECTOR.path, + [SCREENS.WORKSPACE.DYNAMIC_IMPORTED_MEMBERS_ROLE]: DYNAMIC_ROUTES.IMPORTED_MEMBERS_ROLE.path, [SCREENS.WORKSPACE.DYNAMIC_CATEGORY_DESCRIPTION_HINT]: DYNAMIC_ROUTES.WORKSPACE_CATEGORY_DESCRIPTION_HINT.path, [SCREENS.WORKSPACE.DYNAMIC_CATEGORY_APPROVER]: DYNAMIC_ROUTES.WORKSPACE_CATEGORY_APPROVER.path, [SCREENS.WORKSPACE.DYNAMIC_CATEGORY_REQUIRE_RECEIPTS_OVER]: DYNAMIC_ROUTES.WORKSPACE_CATEGORY_REQUIRE_RECEIPTS_OVER.path, diff --git a/src/libs/Navigation/types.ts b/src/libs/Navigation/types.ts index 7ae846bd8db7..4c82fc96db55 100644 --- a/src/libs/Navigation/types.ts +++ b/src/libs/Navigation/types.ts @@ -373,6 +373,9 @@ type SettingsNavigatorParamList = { policyID: string; categoryName: string; }; + [SCREENS.WORKSPACE.DYNAMIC_IMPORTED_MEMBERS_ROLE]: { + policyID: string; + }; [SCREENS.WORKSPACE.DYNAMIC_CATEGORY_DESCRIPTION_HINT]: { policyID: string; categoryName: string; diff --git a/src/libs/actions/Policy/Member.ts b/src/libs/actions/Policy/Member.ts index a59721afb0ca..2d664a33a23c 100644 --- a/src/libs/actions/Policy/Member.ts +++ b/src/libs/actions/Policy/Member.ts @@ -1398,8 +1398,13 @@ function setImportedSpreadsheetMemberData(memberData: ImportedSpreadsheetMemberD Onyx.set(ONYXKEYS.IMPORTED_SPREADSHEET_MEMBER_DATA, memberData); } +function setImportedSpreadsheetMemberRole(role: ValueOf) { + Onyx.set(ONYXKEYS.IMPORTED_SPREADSHEET_MEMBER_ROLE, role); +} + function clearImportedSpreadsheetMemberData() { Onyx.set(ONYXKEYS.IMPORTED_SPREADSHEET_MEMBER_DATA, null); + Onyx.set(ONYXKEYS.IMPORTED_SPREADSHEET_MEMBER_ROLE, null); } export { @@ -1430,5 +1435,6 @@ export { setWorkspaceInviteApproverDraft, clearWorkspaceInviteApproverDraft, setImportedSpreadsheetMemberData, + setImportedSpreadsheetMemberRole, clearImportedSpreadsheetMemberData, }; diff --git a/src/pages/workspace/WorkspaceMemberRoleSelectionModal.tsx b/src/pages/workspace/WorkspaceMemberRoleSelectionModal.tsx deleted file mode 100644 index da2bad85375e..000000000000 --- a/src/pages/workspace/WorkspaceMemberRoleSelectionModal.tsx +++ /dev/null @@ -1,73 +0,0 @@ -import React from 'react'; -import {View} from 'react-native'; -import type {ValueOf} from 'type-fest'; -import HeaderWithBackButton from '@components/HeaderWithBackButton'; -import Modal from '@components/Modal'; -import ScreenWrapper from '@components/ScreenWrapper'; -import SelectionList from '@components/SelectionList'; -import SingleSelectListItem from '@components/SelectionList/ListItem/SingleSelectListItem'; -import type {ListItem} from '@components/SelectionList/types'; -import useLocalize from '@hooks/useLocalize'; -import useThemeStyles from '@hooks/useThemeStyles'; -import CONST from '@src/CONST'; - -type ListItemType = ListItem> & { - value: ValueOf; - text: string; - alternateText: string; - isSelected: boolean; -}; - -type WorkspaceMemberDetailsPageProps = { - /** Whether the modal is visible */ - isVisible: boolean; - - /** The list of items to render */ - items: ListItemType[]; - - /** Function to call when the user selects a role */ - onRoleChange: ({value}: ListItemType) => void; - - /** Function to call when the user closes the role selection modal */ - onClose: () => void; -}; - -function WorkspaceMemberDetailsRoleSelectionModal({isVisible, items, onRoleChange, onClose}: WorkspaceMemberDetailsPageProps) { - const {translate} = useLocalize(); - const styles = useThemeStyles(); - - return ( - onClose?.()} - onModalHide={onClose} - enableEdgeToEdgeBottomSafeAreaPadding - > - - - - item.isSelected)?.keyForList} - addBottomSafeAreaPadding - /> - - - - ); -} - -export type {ListItemType}; - -export default WorkspaceMemberDetailsRoleSelectionModal; diff --git a/src/pages/workspace/members/DynamicImportedMembersRoleSelectionPage.tsx b/src/pages/workspace/members/DynamicImportedMembersRoleSelectionPage.tsx new file mode 100644 index 000000000000..e7fb3915a8b8 --- /dev/null +++ b/src/pages/workspace/members/DynamicImportedMembersRoleSelectionPage.tsx @@ -0,0 +1,51 @@ +import React from 'react'; +import ScreenWrapper from '@components/ScreenWrapper'; +import WorkspaceMemberRoleList from '@components/WorkspaceMemberRoleList'; +import useDynamicBackPath from '@hooks/useDynamicBackPath'; +import useOnyx from '@hooks/useOnyx'; +import usePolicy from '@hooks/usePolicy'; +import {setImportedSpreadsheetMemberRole} from '@libs/actions/Policy/Member'; +import Navigation from '@libs/Navigation/Navigation'; +import type {PlatformStackScreenProps} from '@libs/Navigation/PlatformStackNavigation/types'; +import type {SettingsNavigatorParamList} from '@libs/Navigation/types'; +import AccessOrNotFoundWrapper from '@pages/workspace/AccessOrNotFoundWrapper'; +import CONST from '@src/CONST'; +import ONYXKEYS from '@src/ONYXKEYS'; +import {DYNAMIC_ROUTES} from '@src/ROUTES'; +import type SCREENS from '@src/SCREENS'; + +type DynamicImportedMembersRoleSelectionPageProps = PlatformStackScreenProps; + +function DynamicImportedMembersRoleSelectionPage({route}: DynamicImportedMembersRoleSelectionPageProps) { + const {policyID} = route.params; + const policy = usePolicy(policyID); + const [role = CONST.POLICY.ROLE.USER] = useOnyx(ONYXKEYS.IMPORTED_SPREADSHEET_MEMBER_ROLE); + const backPath = useDynamicBackPath(DYNAMIC_ROUTES.IMPORTED_MEMBERS_ROLE.path); + + return ( + + + { + setImportedSpreadsheetMemberRole(value); + Navigation.setNavigationActionToMicrotaskQueue(() => { + Navigation.goBack(backPath); + }); + }} + navigateBackTo={backPath} + /> + + + ); +} + +export default DynamicImportedMembersRoleSelectionPage; diff --git a/src/pages/workspace/members/ImportedMembersConfirmationPage.tsx b/src/pages/workspace/members/ImportedMembersConfirmationPage.tsx index 7bcd00b74b0c..aff9d2608eda 100644 --- a/src/pages/workspace/members/ImportedMembersConfirmationPage.tsx +++ b/src/pages/workspace/members/ImportedMembersConfirmationPage.tsx @@ -2,7 +2,6 @@ import {useIsFocused} from '@react-navigation/native'; import React, {useCallback, useEffect, useMemo, useRef, useState} from 'react'; import {View} from 'react-native'; import type {GestureResponderEvent} from 'react-native/Libraries/Types/CoreEventTypes'; -import type {ValueOf} from 'type-fest'; import Button from '@components/Button'; import FixedFooter from '@components/FixedFooter'; import HeaderWithBackButton from '@components/HeaderWithBackButton'; @@ -22,17 +21,16 @@ import useThemeStyles from '@hooks/useThemeStyles'; import {closeImportPage} from '@libs/actions/ImportSpreadsheet'; import {openExternalLink} from '@libs/actions/Link'; import {clearImportedSpreadsheetMemberData, importPolicyMembers} from '@libs/actions/Policy/Member'; +import createDynamicRoute from '@libs/Navigation/helpers/dynamicRoutesUtils/createDynamicRoute'; import Navigation from '@libs/Navigation/Navigation'; import type {PlatformStackScreenProps} from '@libs/Navigation/PlatformStackNavigation/types'; import type {SettingsNavigatorParamList} from '@libs/Navigation/types'; import {getAccountIDsByLogins} from '@libs/PersonalDetailsUtils'; -import {isControlPolicy, isPolicyMemberWithoutPendingDelete} from '@libs/PolicyUtils'; +import {isPolicyMemberWithoutPendingDelete} from '@libs/PolicyUtils'; import NotFoundPage from '@pages/ErrorPage/NotFoundPage'; -import WorkspaceMemberDetailsRoleSelectionModal from '@pages/workspace/WorkspaceMemberRoleSelectionModal'; -import type {ListItemType} from '@pages/workspace/WorkspaceMemberRoleSelectionModal'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; -import ROUTES from '@src/ROUTES'; +import ROUTES, {DYNAMIC_ROUTES} from '@src/ROUTES'; import type SCREENS from '@src/SCREENS'; type ImportedMembersConfirmationPageProps = PlatformStackScreenProps; @@ -41,8 +39,7 @@ function ImportedMembersConfirmationPage({route}: ImportedMembersConfirmationPag const styles = useThemeStyles(); const {translate} = useLocalize(); const [spreadsheet] = useOnyx(ONYXKEYS.IMPORTED_SPREADSHEET); - const [role, setRole] = useState>(CONST.POLICY.ROLE.USER); - const [isRoleSelectionModalVisible, setIsRoleSelectionModalVisible] = useState(false); + const [role = CONST.POLICY.ROLE.USER] = useOnyx(ONYXKEYS.IMPORTED_SPREADSHEET_MEMBER_ROLE); const policyID = route.params.policyID; const policy = usePolicy(policyID); @@ -108,42 +105,6 @@ function ImportedMembersConfirmationPage({route}: ImportedMembersConfirmationPag Navigation.goBack(ROUTES.WORKSPACE_MEMBERS.getRoute(policyID)); }; - const onRoleChange = (item: ListItemType) => { - setRole(item.value); - setIsRoleSelectionModalVisible(false); - }; - - const roleItems: ListItemType[] = useMemo(() => { - const items: ListItemType[] = [ - { - value: CONST.POLICY.ROLE.ADMIN, - text: translate('common.admin'), - alternateText: translate('workspace.common.adminAlternateText'), - isSelected: role === CONST.POLICY.ROLE.ADMIN, - keyForList: CONST.POLICY.ROLE.ADMIN, - }, - { - value: CONST.POLICY.ROLE.AUDITOR, - text: translate('common.auditor'), - alternateText: translate('workspace.common.auditorAlternateText'), - isSelected: role === CONST.POLICY.ROLE.AUDITOR, - keyForList: CONST.POLICY.ROLE.AUDITOR, - }, - { - value: CONST.POLICY.ROLE.USER, - text: translate('common.member'), - alternateText: translate('workspace.common.memberAlternateText'), - isSelected: role === CONST.POLICY.ROLE.USER, - keyForList: CONST.POLICY.ROLE.USER, - }, - ]; - - if (!isControlPolicy(policy)) { - return items.filter((item) => item.value !== CONST.POLICY.ROLE.AUDITOR); - } - return items; - }, [role, translate, policy]); - if (!spreadsheet || !importedSpreadsheetMemberData) { return ; } @@ -184,9 +145,7 @@ function ImportedMembersConfirmationPage({route}: ImportedMembersConfirmationPag title={translate(`workspace.common.roleName`, role)} description={translate('common.role')} shouldShowRightIcon - onPress={() => { - setIsRoleSelectionModalVisible(true); - }} + onPress={() => Navigation.navigate(createDynamicRoute(DYNAMIC_ROUTES.IMPORTED_MEMBERS_ROLE.path))} /> @@ -220,12 +179,6 @@ function ImportedMembersConfirmationPage({route}: ImportedMembersConfirmationPag closeImportPageAndModal={closeImportPageAndModal} shouldHandleNavigationBack={false} /> - setIsRoleSelectionModalVisible(false)} - /> ); }