From eb9cc4c9eca479ca3245bb9869a8e26cc1b9d2f5 Mon Sep 17 00:00:00 2001 From: sumo_slonik Date: Fri, 19 Dec 2025 00:52:52 +0100 Subject: [PATCH 01/10] add members details page --- src/ROUTES.ts | 4 + src/SCREENS.ts | 1 + src/languages/en.ts | 1 + .../ModalStackNavigators/index.tsx | 1 + .../linkingConfig/RELATIONS/DOMAIN_TO_RHP.ts | 1 + src/libs/Navigation/linkingConfig/config.ts | 3 + src/libs/Navigation/types.ts | 8 ++ .../domain/Admins/DomainAdminDetailsPage.tsx | 99 +++---------- .../BaseDomainMemberDetailsComponent.tsx | 131 ++++++++++++++++++ .../domain/Members/DomainMembersPage.tsx | 22 ++- .../domain/Members/MembersDetailsPage.tsx | 45 ++++++ 11 files changed, 229 insertions(+), 87 deletions(-) create mode 100644 src/pages/domain/BaseDomainMemberDetailsComponent.tsx create mode 100644 src/pages/domain/Members/MembersDetailsPage.tsx diff --git a/src/ROUTES.ts b/src/ROUTES.ts index 2cc4af13b3dd3..1de75b2731821 100644 --- a/src/ROUTES.ts +++ b/src/ROUTES.ts @@ -3477,6 +3477,10 @@ const ROUTES = { route: 'domain/:domainAccountID/members', getRoute: (domainAccountID: number) => `domain/${domainAccountID}/members` as const, }, + DOMAIN_MEMBER_DETAILS: { + route: 'domain/:domainAccountID/members/:accountID', + getRoute: (domainAccountID: number, accountID: number) => `domain/${domainAccountID}/members/${accountID}` as const, + }, } as const; /** diff --git a/src/SCREENS.ts b/src/SCREENS.ts index 993cd9709d1a7..03db562f322ec 100644 --- a/src/SCREENS.ts +++ b/src/SCREENS.ts @@ -859,6 +859,7 @@ const SCREENS = { ADMINS_SETTINGS: 'Admins_Settings', ADD_PRIMARY_CONTACT: 'Add_Primary_Contact', MEMBERS: 'Domain_Members', + MEMBER_DETAILS:'Member_Details', }, } as const; diff --git a/src/languages/en.ts b/src/languages/en.ts index 493bff423e3e2..92e094d584085 100755 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -7828,6 +7828,7 @@ const translations = { members:{ title: 'Members', findMember: 'Find member', + closeAccount:'Close account', } }, }; diff --git a/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx b/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx index 85642f25d618e..12d4fc254c551 100644 --- a/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx +++ b/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx @@ -847,6 +847,7 @@ const SettingsModalStackNavigator = createModalStackNavigator require('../../../../pages/domain/Admins/DomainAdminDetailsPage').default, [SCREENS.DOMAIN.ADMINS_SETTINGS]: () => require('../../../../pages/domain/Admins/DomainAdminsSettingsPage').default, [SCREENS.DOMAIN.ADD_PRIMARY_CONTACT]: () => require('../../../../pages/domain/Admins/DomainAddPrimaryContactPage').default, + [SCREENS.DOMAIN.MEMBER_DETAILS]: () => require('../../../../pages/domain/members/MembersDetailsPage').default, }); const TwoFactorAuthenticatorStackNavigator = createModalStackNavigator({ diff --git a/src/libs/Navigation/linkingConfig/RELATIONS/DOMAIN_TO_RHP.ts b/src/libs/Navigation/linkingConfig/RELATIONS/DOMAIN_TO_RHP.ts index ea0c54caf54db..8d4333e9f93e5 100755 --- a/src/libs/Navigation/linkingConfig/RELATIONS/DOMAIN_TO_RHP.ts +++ b/src/libs/Navigation/linkingConfig/RELATIONS/DOMAIN_TO_RHP.ts @@ -6,6 +6,7 @@ const DOMAIN_TO_RHP: Partial['config'] = { [SCREENS.DOMAIN.ADD_PRIMARY_CONTACT]: { path: ROUTES.DOMAIN_ADD_PRIMARY_CONTACT.route, }, + [SCREENS.DOMAIN.MEMBER_DETAILS]: { + path: ROUTES.DOMAIN_MEMBER_DETAILS.route, + }, }, }, [SCREENS.RIGHT_MODAL.TWO_FACTOR_AUTH]: { diff --git a/src/libs/Navigation/types.ts b/src/libs/Navigation/types.ts index fa8cb7072f862..a05d23b8aff10 100644 --- a/src/libs/Navigation/types.ts +++ b/src/libs/Navigation/types.ts @@ -1342,6 +1342,10 @@ type SettingsNavigatorParamList = { [SCREENS.DOMAIN.ADD_PRIMARY_CONTACT]: { domainAccountID: number; }; + [SCREENS.DOMAIN.MEMBER_DETAILS]: { + domainAccountID: number; + accountID: number; + }; } & ReimbursementAccountNavigatorParamList; type DomainCardNavigatorParamList = { @@ -2440,6 +2444,10 @@ type DomainSplitNavigatorParamList = { [SCREENS.DOMAIN.MEMBERS]: { domainAccountID: number; }; + [SCREENS.DOMAIN.MEMBER_DETAILS]: { + domainAccountID: number; + accountID: number; + } }; type OnboardingModalNavigatorParamList = { diff --git a/src/pages/domain/Admins/DomainAdminDetailsPage.tsx b/src/pages/domain/Admins/DomainAdminDetailsPage.tsx index e7007125178ef..c6cfb53d0a2e3 100644 --- a/src/pages/domain/Admins/DomainAdminDetailsPage.tsx +++ b/src/pages/domain/Admins/DomainAdminDetailsPage.tsx @@ -1,100 +1,37 @@ -import {Str} from 'expensify-common'; -import React from 'react'; -import {View} from 'react-native'; -import type {OnyxEntry} from 'react-native-onyx'; -import Avatar from '@components/Avatar'; -import HeaderWithBackButton from '@components/HeaderWithBackButton'; -import MenuItem from '@components/MenuItem'; -import MenuItemWithTopDescription from '@components/MenuItemWithTopDescription'; -import OfflineWithFeedback from '@components/OfflineWithFeedback'; -import ScreenWrapper from '@components/ScreenWrapper'; -import ScrollView from '@components/ScrollView'; -import Text from '@components/Text'; +import React, {useMemo} from 'react'; import {useMemoizedLazyExpensifyIcons} from '@hooks/useLazyAsset'; import useLocalize from '@hooks/useLocalize'; -import useOnyx from '@hooks/useOnyx'; -import useThemeStyles from '@hooks/useThemeStyles'; -import {getDisplayNameOrDefault, getPhoneNumber} from '@libs/PersonalDetailsUtils'; import Navigation from '@navigation/Navigation'; import type {PlatformStackScreenProps} from '@navigation/PlatformStackNavigation/types'; import type {SettingsNavigatorParamList} from '@navigation/types'; -import DomainNotFoundPageWrapper from '@pages/domain/DomainNotFoundPageWrapper'; -import CONST from '@src/CONST'; -import ONYXKEYS from '@src/ONYXKEYS'; +import BaseDomainMemberDetailsComponent from '@pages/domain/BaseDomainMemberDetailsComponent'; +import type {MemberDetailsMenuItem} from '@pages/domain/BaseDomainMemberDetailsComponent'; import ROUTES from '@src/ROUTES'; import type SCREENS from '@src/SCREENS'; -import type {PersonalDetailsList} from '@src/types/onyx'; type DomainAdminDetailsPageProps = PlatformStackScreenProps; function DomainAdminDetailsPage({route}: DomainAdminDetailsPageProps) { const {domainAccountID, accountID} = route.params; - const styles = useThemeStyles(); - const {translate, formatPhoneNumber} = useLocalize(); + const {translate} = useLocalize(); const icons = useMemoizedLazyExpensifyIcons(['Info'] as const); - // eslint-disable-next-line rulesdir/no-inline-useOnyx-selector - const [adminPersonalDetails] = useOnyx(ONYXKEYS.PERSONAL_DETAILS_LIST, { - canBeMissing: true, - selector: (personalDetailsList: OnyxEntry) => personalDetailsList?.[accountID], - }); - - const displayName = formatPhoneNumber(getDisplayNameOrDefault(adminPersonalDetails)); - const memberLogin = adminPersonalDetails?.login ?? ''; - const isSMSLogin = Str.isSMSLogin(memberLogin); - const phoneNumber = getPhoneNumber(adminPersonalDetails); - const fallbackIcon = adminPersonalDetails?.fallbackIcon ?? ''; + const menuItems = useMemo((): MemberDetailsMenuItem[] => [ + { + key: 'profile', + title: translate('common.profile'), + icon: icons.Info, + onPress: () => Navigation.navigate(ROUTES.PROFILE.getRoute(accountID, Navigation.getActiveRoute())), + shouldShowRightIcon: true, + }, + ], [accountID, icons.Info, translate]); return ( - - - - - - - - - - {!!displayName && ( - - {displayName} - - )} - - - - Navigation.navigate(ROUTES.PROFILE.getRoute(accountID, Navigation.getActiveRoute()))} - shouldShowRightIcon - /> - - - - - + ); } diff --git a/src/pages/domain/BaseDomainMemberDetailsComponent.tsx b/src/pages/domain/BaseDomainMemberDetailsComponent.tsx new file mode 100644 index 0000000000000..08049ad4ce8f3 --- /dev/null +++ b/src/pages/domain/BaseDomainMemberDetailsComponent.tsx @@ -0,0 +1,131 @@ +import {Str} from 'expensify-common'; +import React from 'react'; +import {View} from 'react-native'; +import Avatar from '@components/Avatar'; +import HeaderWithBackButton from '@components/HeaderWithBackButton'; +import MenuItem from '@components/MenuItem'; +import MenuItemWithTopDescription from '@components/MenuItemWithTopDescription'; +import OfflineWithFeedback from '@components/OfflineWithFeedback'; +import ScreenWrapper from '@components/ScreenWrapper'; +import ScrollView from '@components/ScrollView'; +import Text from '@components/Text'; +import useLocalize from '@hooks/useLocalize'; +import useOnyx from '@hooks/useOnyx'; +import useThemeStyles from '@hooks/useThemeStyles'; +import {getDisplayNameOrDefault, getPhoneNumber} from '@libs/PersonalDetailsUtils'; +import CONST from '@src/CONST'; +import ONYXKEYS from '@src/ONYXKEYS'; +import type {PersonalDetails} from '@src/types/onyx'; +import type IconAsset from '@src/types/utils/IconAsset'; +import DomainNotFoundPageWrapper from '@pages/domain/DomainNotFoundPageWrapper'; + +type MemberDetailsMenuItem = { + key: string; + title: string; + icon: IconAsset; + onPress: () => void | Promise; + shouldShowRightIcon?: boolean; +}; + +type BaseDomainMemberDetailsComponentProps = { + /** ID domeny dla wrappera NotFound */ + domainAccountID: number; + + /** ID konta użytkownika */ + accountID: number; + + /** Lista pozycji menu (np. profil, uprawnienia) */ + menuItems: MemberDetailsMenuItem[]; + + /** Opcjonalny przycisk pod awatarem (np. Close Account) */ + actionButton?: React.ReactNode; + + /** Dodatkowy kontent na dole strony */ + children?: React.ReactNode; +}; + +function BaseDomainMemberDetailsComponent({ + domainAccountID, + accountID, + menuItems, + actionButton, + children, + }: BaseDomainMemberDetailsComponentProps) { + const styles = useThemeStyles(); + const {translate, formatPhoneNumber} = useLocalize(); + + const [personalDetails] = useOnyx(ONYXKEYS.PERSONAL_DETAILS_LIST, {canBeMissing: true}); + + const details = personalDetails?.[accountID] ?? ({} as PersonalDetails); + const displayName = formatPhoneNumber(getDisplayNameOrDefault(details)); + const phoneNumber = getPhoneNumber(details); + + const memberLogin = details.login ?? ''; + const isSMSLogin = Str.isSMSLogin(memberLogin); + + return ( + + + + + + + + + + + + {/* Renderujemy przycisk akcji tylko jeśli został przekazany */} + {actionButton} + + {!!displayName && ( + + {displayName} + + )} + + + + + + {menuItems.map((item) => ( + + ))} + + {children} + + + + ); +} + +BaseDomainMemberDetailsComponent.displayName = 'BaseDomainMemberDetailsComponent'; + +export type {MemberDetailsMenuItem}; +export default BaseDomainMemberDetailsComponent; diff --git a/src/pages/domain/Members/DomainMembersPage.tsx b/src/pages/domain/Members/DomainMembersPage.tsx index 01a02f2e03126..d5a2a061b6704 100644 --- a/src/pages/domain/Members/DomainMembersPage.tsx +++ b/src/pages/domain/Members/DomainMembersPage.tsx @@ -1,15 +1,19 @@ +import { selectMemberIDs } from '@selectors/Domain'; import React from 'react'; -import BaseDomainMembersPage from '@pages/domain/BaseDomainMembersPage'; +import { useMemoizedLazyIllustrations } from '@hooks/useLazyAsset'; import useLocalize from '@hooks/useLocalize'; import useOnyx from '@hooks/useOnyx'; -import type {PlatformStackScreenProps} from '@navigation/PlatformStackNavigation/types'; -import type {DomainSplitNavigatorParamList} from '@navigation/types'; +import Navigation from '@navigation/Navigation'; +import type { PlatformStackScreenProps } from '@navigation/PlatformStackNavigation/types'; +import type { DomainSplitNavigatorParamList } from '@navigation/types'; +import type { MemberOption } from '@pages/domain/BaseDomainMembersPage'; +import BaseDomainMembersPage from '@pages/domain/BaseDomainMembersPage'; import ONYXKEYS from '@src/ONYXKEYS'; +import ROUTES from '@src/ROUTES'; import type SCREENS from '@src/SCREENS'; -import {selectMemberIDs} from '@selectors/Domain'; -import {useMemoizedLazyIllustrations} from '@hooks/useLazyAsset'; import isLoadingOnyxValue from '@src/types/utils/isLoadingOnyxValue'; + type DomainMembersPageProps = PlatformStackScreenProps; function DomainMembersPage({route}: DomainMembersPageProps) { @@ -22,6 +26,12 @@ function DomainMembersPage({route}: DomainMembersPageProps) { selector: selectMemberIDs, }); + const openMemberDetails = (item: MemberOption) => { + Navigation.setNavigationActionToMicrotaskQueue(() => { + Navigation.navigate(ROUTES.DOMAIN_MEMBER_DETAILS.getRoute(domainAccountID, item.accountID)); + }); + } + return ( {}} + onSelectRow={openMemberDetails} headerIcon={illustrations.Members} /> ); diff --git a/src/pages/domain/Members/MembersDetailsPage.tsx b/src/pages/domain/Members/MembersDetailsPage.tsx new file mode 100644 index 0000000000000..df4375f53a581 --- /dev/null +++ b/src/pages/domain/Members/MembersDetailsPage.tsx @@ -0,0 +1,45 @@ +import React, {useMemo} from 'react'; +import useLocalize from '@hooks/useLocalize'; +import useThemeStyles from '@hooks/useThemeStyles'; +import type {PlatformStackScreenProps} from '@navigation/PlatformStackNavigation/types'; +import type {DomainSplitNavigatorParamList} from '@navigation/types'; +import BaseDomainMemberDetailsComponent from '@pages/domain/BaseDomainMemberDetailsComponent'; +import type {MemberDetailsMenuItem} from '@pages/domain/BaseDomainMemberDetailsComponent'; +import Button from '@components/Button'; +import * as Expensicons from '@components/Icon/Expensicons'; +import type SCREENS from '@src/SCREENS'; +import getEmptyArray from '@src/types/utils/getEmptyArray'; + +type DomainMemberDetailsPageProps = PlatformStackScreenProps; + +function DomainMemberDetailsPage({route}: DomainMemberDetailsPageProps) { + const {domainAccountID, accountID} = route.params; + const {translate} = useLocalize(); + const styles = useThemeStyles(); + + const menuItems = useMemo(() => { + return getEmptyArray(); + }, []); + + return ( + { + }} + isDisabled + icon={Expensicons.ClosedSign} + style={styles.mb5} + /> + } + /> + ); +} + +DomainMemberDetailsPage.displayName = 'DomainMemberDetailsPage'; + +export default DomainMemberDetailsPage; From f41f400da656adb59f8308b08ee05275df9ed078 Mon Sep 17 00:00:00 2001 From: war-in Date: Tue, 23 Dec 2025 18:35:11 +0100 Subject: [PATCH 02/10] refactor: remove outdated & unnecessary code --- src/SCREENS.ts | 2 +- src/languages/en.ts | 1 - .../ModalStackNavigators/index.tsx | 2 +- src/libs/Navigation/types.ts | 2 +- .../domain/Admins/DomainAdminDetailsPage.tsx | 21 ++------ .../BaseDomainMemberDetailsComponent.tsx | 51 ++++++++++--------- .../domain/Members/DomainMembersPage.tsx | 10 +--- .../domain/Members/MembersDetailsPage.tsx | 24 +-------- 8 files changed, 36 insertions(+), 77 deletions(-) diff --git a/src/SCREENS.ts b/src/SCREENS.ts index 5dc773ce5a37e..9ad47444e0535 100644 --- a/src/SCREENS.ts +++ b/src/SCREENS.ts @@ -868,7 +868,7 @@ const SCREENS = { ADD_PRIMARY_CONTACT: 'Add_Primary_Contact', ADD_ADMIN: 'Domain_Add_Admin', MEMBERS: 'Domain_Members', - MEMBER_DETAILS:'Member_Details', + MEMBER_DETAILS: 'Member_Details', }, } as const; diff --git a/src/languages/en.ts b/src/languages/en.ts index 285f549b77569..6598952f554af 100755 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -7864,7 +7864,6 @@ const translations = { members: { title: 'Members', findMember: 'Find member', - closeAccount:'Close account', }, }, }; diff --git a/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx b/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx index fc6a96dd1db48..236f9b5e5536c 100644 --- a/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx +++ b/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx @@ -797,7 +797,7 @@ const SettingsModalStackNavigator = createModalStackNavigator require('../../../../pages/domain/Admins/DomainAdminsSettingsPage').default, [SCREENS.DOMAIN.ADD_PRIMARY_CONTACT]: () => require('../../../../pages/domain/Admins/DomainAddPrimaryContactPage').default, [SCREENS.DOMAIN.ADD_ADMIN]: () => require('../../../../pages/domain/Admins/DomainAddAdminPage').default, - [SCREENS.DOMAIN.MEMBER_DETAILS]: () => require('../../../../pages/domain/members/MembersDetailsPage').default, + [SCREENS.DOMAIN.MEMBER_DETAILS]: () => require('../../../../pages/domain/Members/MembersDetailsPage').default, }); const TwoFactorAuthenticatorStackNavigator = createModalStackNavigator({ diff --git a/src/libs/Navigation/types.ts b/src/libs/Navigation/types.ts index db958b39de428..9b3daddb19c20 100644 --- a/src/libs/Navigation/types.ts +++ b/src/libs/Navigation/types.ts @@ -2498,7 +2498,7 @@ type DomainSplitNavigatorParamList = { [SCREENS.DOMAIN.MEMBER_DETAILS]: { domainAccountID: number; accountID: number; - } + }; }; type OnboardingModalNavigatorParamList = { diff --git a/src/pages/domain/Admins/DomainAdminDetailsPage.tsx b/src/pages/domain/Admins/DomainAdminDetailsPage.tsx index c6cfb53d0a2e3..9b4de12e5bc13 100644 --- a/src/pages/domain/Admins/DomainAdminDetailsPage.tsx +++ b/src/pages/domain/Admins/DomainAdminDetailsPage.tsx @@ -1,36 +1,21 @@ -import React, {useMemo} from 'react'; -import {useMemoizedLazyExpensifyIcons} from '@hooks/useLazyAsset'; -import useLocalize from '@hooks/useLocalize'; -import Navigation from '@navigation/Navigation'; +import React from 'react'; import type {PlatformStackScreenProps} from '@navigation/PlatformStackNavigation/types'; import type {SettingsNavigatorParamList} from '@navigation/types'; import BaseDomainMemberDetailsComponent from '@pages/domain/BaseDomainMemberDetailsComponent'; import type {MemberDetailsMenuItem} from '@pages/domain/BaseDomainMemberDetailsComponent'; -import ROUTES from '@src/ROUTES'; import type SCREENS from '@src/SCREENS'; +import getEmptyArray from '@src/types/utils/getEmptyArray'; type DomainAdminDetailsPageProps = PlatformStackScreenProps; function DomainAdminDetailsPage({route}: DomainAdminDetailsPageProps) { const {domainAccountID, accountID} = route.params; - const {translate} = useLocalize(); - const icons = useMemoizedLazyExpensifyIcons(['Info'] as const); - - const menuItems = useMemo((): MemberDetailsMenuItem[] => [ - { - key: 'profile', - title: translate('common.profile'), - icon: icons.Info, - onPress: () => Navigation.navigate(ROUTES.PROFILE.getRoute(accountID, Navigation.getActiveRoute())), - shouldShowRightIcon: true, - }, - ], [accountID, icons.Info, translate]); return ( ()} /> ); } diff --git a/src/pages/domain/BaseDomainMemberDetailsComponent.tsx b/src/pages/domain/BaseDomainMemberDetailsComponent.tsx index 08049ad4ce8f3..8025efbaa813b 100644 --- a/src/pages/domain/BaseDomainMemberDetailsComponent.tsx +++ b/src/pages/domain/BaseDomainMemberDetailsComponent.tsx @@ -1,6 +1,7 @@ import {Str} from 'expensify-common'; import React from 'react'; import {View} from 'react-native'; +import type {OnyxEntry} from 'react-native-onyx'; import Avatar from '@components/Avatar'; import HeaderWithBackButton from '@components/HeaderWithBackButton'; import MenuItem from '@components/MenuItem'; @@ -9,15 +10,18 @@ import OfflineWithFeedback from '@components/OfflineWithFeedback'; import ScreenWrapper from '@components/ScreenWrapper'; import ScrollView from '@components/ScrollView'; import Text from '@components/Text'; +import {useMemoizedLazyExpensifyIcons} from '@hooks/useLazyAsset'; import useLocalize from '@hooks/useLocalize'; import useOnyx from '@hooks/useOnyx'; import useThemeStyles from '@hooks/useThemeStyles'; import {getDisplayNameOrDefault, getPhoneNumber} from '@libs/PersonalDetailsUtils'; +import Navigation from '@navigation/Navigation'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; -import type {PersonalDetails} from '@src/types/onyx'; +import ROUTES from '@src/ROUTES'; +import type {PersonalDetailsList} from '@src/types/onyx'; import type IconAsset from '@src/types/utils/IconAsset'; -import DomainNotFoundPageWrapper from '@pages/domain/DomainNotFoundPageWrapper'; +import DomainNotFoundPageWrapper from './DomainNotFoundPageWrapper'; type MemberDetailsMenuItem = { key: string; @@ -37,30 +41,24 @@ type BaseDomainMemberDetailsComponentProps = { /** Lista pozycji menu (np. profil, uprawnienia) */ menuItems: MemberDetailsMenuItem[]; - /** Opcjonalny przycisk pod awatarem (np. Close Account) */ - actionButton?: React.ReactNode; - /** Dodatkowy kontent na dole strony */ children?: React.ReactNode; }; -function BaseDomainMemberDetailsComponent({ - domainAccountID, - accountID, - menuItems, - actionButton, - children, - }: BaseDomainMemberDetailsComponentProps) { +function BaseDomainMemberDetailsComponent({domainAccountID, accountID, menuItems, children}: BaseDomainMemberDetailsComponentProps) { const styles = useThemeStyles(); const {translate, formatPhoneNumber} = useLocalize(); + const icons = useMemoizedLazyExpensifyIcons(['Info'] as const); - const [personalDetails] = useOnyx(ONYXKEYS.PERSONAL_DETAILS_LIST, {canBeMissing: true}); - - const details = personalDetails?.[accountID] ?? ({} as PersonalDetails); - const displayName = formatPhoneNumber(getDisplayNameOrDefault(details)); - const phoneNumber = getPhoneNumber(details); + // eslint-disable-next-line rulesdir/no-inline-useOnyx-selector + const [personalDetails] = useOnyx(ONYXKEYS.PERSONAL_DETAILS_LIST, { + canBeMissing: true, + selector: (personalDetailsList: OnyxEntry) => personalDetailsList?.[accountID], + }); - const memberLogin = details.login ?? ''; + const displayName = formatPhoneNumber(getDisplayNameOrDefault(personalDetails)); + const phoneNumber = getPhoneNumber(personalDetails); + const memberLogin = personalDetails?.login ?? ''; const isSMSLogin = Str.isSMSLogin(memberLogin); return ( @@ -74,21 +72,18 @@ function BaseDomainMemberDetailsComponent({ - + - {/* Renderujemy przycisk akcji tylko jeśli został przekazany */} - {actionButton} - {!!displayName && ( Navigation.navigate(ROUTES.PROFILE.getRoute(accountID, Navigation.getActiveRoute()))} + shouldShowRightIcon + /> diff --git a/src/pages/domain/Members/DomainMembersPage.tsx b/src/pages/domain/Members/DomainMembersPage.tsx index 357f2ec438839..ba18072eba3f5 100644 --- a/src/pages/domain/Members/DomainMembersPage.tsx +++ b/src/pages/domain/Members/DomainMembersPage.tsx @@ -6,13 +6,11 @@ import useOnyx from '@hooks/useOnyx'; import Navigation from '@navigation/Navigation'; import type {PlatformStackScreenProps} from '@navigation/PlatformStackNavigation/types'; import type {DomainSplitNavigatorParamList} from '@navigation/types'; -import type { MemberOption } from '@pages/domain/BaseDomainMembersPage'; import BaseDomainMembersPage from '@pages/domain/BaseDomainMembersPage'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; import type SCREENS from '@src/SCREENS'; - type DomainMembersPageProps = PlatformStackScreenProps; function DomainMembersPage({route}: DomainMembersPageProps) { @@ -25,19 +23,13 @@ function DomainMembersPage({route}: DomainMembersPageProps) { selector: selectMemberIDs, }); - const openMemberDetails = (item: MemberOption) => { - Navigation.setNavigationActionToMicrotaskQueue(() => { - Navigation.navigate(ROUTES.DOMAIN_MEMBER_DETAILS.getRoute(domainAccountID, item.accountID)); - }); - } - return ( Navigation.navigate(ROUTES.DOMAIN_MEMBER_DETAILS.getRoute(domainAccountID, item.accountID))} headerIcon={illustrations.Members} /> ); diff --git a/src/pages/domain/Members/MembersDetailsPage.tsx b/src/pages/domain/Members/MembersDetailsPage.tsx index df4375f53a581..c20ff53e9456c 100644 --- a/src/pages/domain/Members/MembersDetailsPage.tsx +++ b/src/pages/domain/Members/MembersDetailsPage.tsx @@ -1,12 +1,8 @@ -import React, {useMemo} from 'react'; -import useLocalize from '@hooks/useLocalize'; -import useThemeStyles from '@hooks/useThemeStyles'; +import React from 'react'; import type {PlatformStackScreenProps} from '@navigation/PlatformStackNavigation/types'; import type {DomainSplitNavigatorParamList} from '@navigation/types'; import BaseDomainMemberDetailsComponent from '@pages/domain/BaseDomainMemberDetailsComponent'; import type {MemberDetailsMenuItem} from '@pages/domain/BaseDomainMemberDetailsComponent'; -import Button from '@components/Button'; -import * as Expensicons from '@components/Icon/Expensicons'; import type SCREENS from '@src/SCREENS'; import getEmptyArray from '@src/types/utils/getEmptyArray'; @@ -14,28 +10,12 @@ type DomainMemberDetailsPageProps = PlatformStackScreenProps { - return getEmptyArray(); - }, []); return ( { - }} - isDisabled - icon={Expensicons.ClosedSign} - style={styles.mb5} - /> - } + menuItems={getEmptyArray()} /> ); } From 3d6289ace6eac3f0c5989b4199ab125bee9eac28 Mon Sep 17 00:00:00 2001 From: war-in Date: Tue, 23 Dec 2025 18:40:43 +0100 Subject: [PATCH 03/10] chore: remove polish descriptions --- .../domain/BaseDomainMemberDetailsComponent.tsx | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/src/pages/domain/BaseDomainMemberDetailsComponent.tsx b/src/pages/domain/BaseDomainMemberDetailsComponent.tsx index 8025efbaa813b..9658032546cbe 100644 --- a/src/pages/domain/BaseDomainMemberDetailsComponent.tsx +++ b/src/pages/domain/BaseDomainMemberDetailsComponent.tsx @@ -32,20 +32,17 @@ type MemberDetailsMenuItem = { }; type BaseDomainMemberDetailsComponentProps = { - /** ID domeny dla wrappera NotFound */ + /** Domain ID */ domainAccountID: number; - /** ID konta użytkownika */ + /** User account ID */ accountID: number; - /** Lista pozycji menu (np. profil, uprawnienia) */ + /** List of additional fields (e.g., force 2FA) */ menuItems: MemberDetailsMenuItem[]; - - /** Dodatkowy kontent na dole strony */ - children?: React.ReactNode; }; -function BaseDomainMemberDetailsComponent({domainAccountID, accountID, menuItems, children}: BaseDomainMemberDetailsComponentProps) { +function BaseDomainMemberDetailsComponent({domainAccountID, accountID, menuItems}: BaseDomainMemberDetailsComponentProps) { const styles = useThemeStyles(); const {translate, formatPhoneNumber} = useLocalize(); const icons = useMemoizedLazyExpensifyIcons(['Info'] as const); @@ -113,8 +110,6 @@ function BaseDomainMemberDetailsComponent({domainAccountID, accountID, menuItems /> ))} - {children} - Date: Wed, 7 Jan 2026 14:49:33 +0100 Subject: [PATCH 04/10] refactor: apply review comments --- src/libs/Navigation/types.ts | 4 ---- src/pages/domain/BaseDomainMemberDetailsComponent.tsx | 5 +++-- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/src/libs/Navigation/types.ts b/src/libs/Navigation/types.ts index 51a80a1eec73b..b1093861e9d36 100644 --- a/src/libs/Navigation/types.ts +++ b/src/libs/Navigation/types.ts @@ -2494,10 +2494,6 @@ type DomainSplitNavigatorParamList = { [SCREENS.DOMAIN.MEMBERS]: { domainAccountID: number; }; - [SCREENS.DOMAIN.MEMBER_DETAILS]: { - domainAccountID: number; - accountID: number; - }; }; type OnboardingModalNavigatorParamList = { diff --git a/src/pages/domain/BaseDomainMemberDetailsComponent.tsx b/src/pages/domain/BaseDomainMemberDetailsComponent.tsx index 9658032546cbe..b44579786053f 100644 --- a/src/pages/domain/BaseDomainMemberDetailsComponent.tsx +++ b/src/pages/domain/BaseDomainMemberDetailsComponent.tsx @@ -57,6 +57,7 @@ function BaseDomainMemberDetailsComponent({domainAccountID, accountID, menuItems const phoneNumber = getPhoneNumber(personalDetails); const memberLogin = personalDetails?.login ?? ''; const isSMSLogin = Str.isSMSLogin(memberLogin); + const copyableName = isSMSLogin ? formatPhoneNumber(phoneNumber ?? '') : memberLogin; return ( @@ -93,8 +94,8 @@ function BaseDomainMemberDetailsComponent({domainAccountID, accountID, menuItems Date: Mon, 12 Jan 2026 12:57:24 +0100 Subject: [PATCH 05/10] fix: post-merge fixes --- .../domain/Admins/DomainAdminDetailsPage.tsx | 75 +++++++++++++------ .../BaseDomainMemberDetailsComponent.tsx | 14 +--- 2 files changed, 56 insertions(+), 33 deletions(-) diff --git a/src/pages/domain/Admins/DomainAdminDetailsPage.tsx b/src/pages/domain/Admins/DomainAdminDetailsPage.tsx index f25e8434a9e31..ec57c30d37daf 100644 --- a/src/pages/domain/Admins/DomainAdminDetailsPage.tsx +++ b/src/pages/domain/Admins/DomainAdminDetailsPage.tsx @@ -1,20 +1,55 @@ +import { adminAccountIDsSelector, domainSettingsPrimaryContactSelector } from '@selectors/Domain'; import React from 'react'; -import type {PlatformStackScreenProps} from '@navigation/PlatformStackNavigation/types'; -import type {SettingsNavigatorParamList} from '@navigation/types'; +import type { OnyxEntry } from 'react-native-onyx'; +import MenuItem from '@components/MenuItem'; +import { ModalActions } from '@components/Modal/Global/ModalContext'; +import useConfirmModal from '@hooks/useConfirmModal'; +import { useMemoizedLazyExpensifyIcons } from '@hooks/useLazyAsset'; +import useLocalize from '@hooks/useLocalize'; +import useOnyx from '@hooks/useOnyx'; +import useThemeStyles from '@hooks/useThemeStyles'; +import { getDisplayNameOrDefault } from '@libs/PersonalDetailsUtils'; +import Navigation from '@navigation/Navigation'; +import type { PlatformStackScreenProps } from '@navigation/PlatformStackNavigation/types'; +import type { SettingsNavigatorParamList } from '@navigation/types'; import BaseDomainMemberDetailsComponent from '@pages/domain/BaseDomainMemberDetailsComponent'; -import type {MemberDetailsMenuItem} from '@pages/domain/BaseDomainMemberDetailsComponent'; +import { revokeDomainAdminAccess } from '@userActions/Domain'; +import ONYXKEYS from '@src/ONYXKEYS'; import type SCREENS from '@src/SCREENS'; -import getEmptyArray from '@src/types/utils/getEmptyArray'; +import type {PersonalDetailsList} from '@src/types/onyx'; + type DomainAdminDetailsPageProps = PlatformStackScreenProps; function DomainAdminDetailsPage({route}: DomainAdminDetailsPageProps) { const {domainAccountID, accountID} = route.params; - /** + const styles = useThemeStyles(); + const {translate, formatPhoneNumber} = useLocalize(); + const icons = useMemoizedLazyExpensifyIcons(['Info', 'ClosedSign'] as const); + + const [primaryContact] = useOnyx(`${ONYXKEYS.COLLECTION.SHARED_NVP_PRIVATE_DOMAIN_MEMBER}${domainAccountID}`, { + selector: domainSettingsPrimaryContactSelector, + canBeMissing: true, + }); + + const [adminAccountIDs] = useOnyx(`${ONYXKEYS.COLLECTION.DOMAIN}${domainAccountID}`, { + canBeMissing: true, + selector: adminAccountIDsSelector, + }); + + // eslint-disable-next-line rulesdir/no-inline-useOnyx-selector + const [adminPersonalDetails] = useOnyx(ONYXKEYS.PERSONAL_DETAILS_LIST, { + canBeMissing: true, + selector: (personalDetailsList: OnyxEntry) => personalDetailsList?.[accountID], + }); + const domainHasOnlyOneAdmin = adminAccountIDs?.length === 1; - const {showConfirmModal} = useConfirmModal(); + const displayName = formatPhoneNumber(getDisplayNameOrDefault(adminPersonalDetails)); + const memberLogin = adminPersonalDetails?.login ?? ''; + const isCurrentUserPrimaryContact = primaryContact === memberLogin; + const {showConfirmModal} = useConfirmModal(); const handleRevokeAdminAccess = async () => { const confirmResult = await showConfirmModal({ title: translate('domain.admins.revokeAdminAccess'), @@ -33,26 +68,22 @@ function DomainAdminDetailsPage({route}: DomainAdminDetailsPageProps) { Navigation.dismissModal(); }; - {!domainHasOnlyOneAdmin && ( - - )} - - */ - - return ( ()} - /> + > + {!domainHasOnlyOneAdmin && ( + + )} + ); } diff --git a/src/pages/domain/BaseDomainMemberDetailsComponent.tsx b/src/pages/domain/BaseDomainMemberDetailsComponent.tsx index b44579786053f..cb031c0be615d 100644 --- a/src/pages/domain/BaseDomainMemberDetailsComponent.tsx +++ b/src/pages/domain/BaseDomainMemberDetailsComponent.tsx @@ -39,10 +39,10 @@ type BaseDomainMemberDetailsComponentProps = { accountID: number; /** List of additional fields (e.g., force 2FA) */ - menuItems: MemberDetailsMenuItem[]; + children: React.ReactNode; }; -function BaseDomainMemberDetailsComponent({domainAccountID, accountID, menuItems}: BaseDomainMemberDetailsComponentProps) { +function BaseDomainMemberDetailsComponent({domainAccountID, accountID, children}: BaseDomainMemberDetailsComponentProps) { const styles = useThemeStyles(); const {translate, formatPhoneNumber} = useLocalize(); const icons = useMemoizedLazyExpensifyIcons(['Info'] as const); @@ -101,15 +101,7 @@ function BaseDomainMemberDetailsComponent({domainAccountID, accountID, menuItems copyable /> - {menuItems.map((item) => ( - - ))} + {children} Date: Mon, 12 Jan 2026 16:23:12 +0100 Subject: [PATCH 06/10] fix: prettier & eslint --- .../domain/Admins/DomainAdminDetailsPage.tsx | 17 ++++++++--------- .../domain/BaseDomainMemberDetailsComponent.tsx | 2 +- ...ailsPage.tsx => DomainMemberDetailsPage.tsx} | 7 ++----- 3 files changed, 11 insertions(+), 15 deletions(-) rename src/pages/domain/Members/{MembersDetailsPage.tsx => DomainMemberDetailsPage.tsx} (60%) diff --git a/src/pages/domain/Admins/DomainAdminDetailsPage.tsx b/src/pages/domain/Admins/DomainAdminDetailsPage.tsx index ec57c30d37daf..a36c705c946f2 100644 --- a/src/pages/domain/Admins/DomainAdminDetailsPage.tsx +++ b/src/pages/domain/Admins/DomainAdminDetailsPage.tsx @@ -1,24 +1,23 @@ -import { adminAccountIDsSelector, domainSettingsPrimaryContactSelector } from '@selectors/Domain'; +import {adminAccountIDsSelector, domainSettingsPrimaryContactSelector} from '@selectors/Domain'; import React from 'react'; -import type { OnyxEntry } from 'react-native-onyx'; +import type {OnyxEntry} from 'react-native-onyx'; import MenuItem from '@components/MenuItem'; -import { ModalActions } from '@components/Modal/Global/ModalContext'; +import {ModalActions} from '@components/Modal/Global/ModalContext'; import useConfirmModal from '@hooks/useConfirmModal'; -import { useMemoizedLazyExpensifyIcons } from '@hooks/useLazyAsset'; +import {useMemoizedLazyExpensifyIcons} from '@hooks/useLazyAsset'; import useLocalize from '@hooks/useLocalize'; import useOnyx from '@hooks/useOnyx'; import useThemeStyles from '@hooks/useThemeStyles'; -import { getDisplayNameOrDefault } from '@libs/PersonalDetailsUtils'; +import {getDisplayNameOrDefault} from '@libs/PersonalDetailsUtils'; import Navigation from '@navigation/Navigation'; -import type { PlatformStackScreenProps } from '@navigation/PlatformStackNavigation/types'; -import type { SettingsNavigatorParamList } from '@navigation/types'; +import type {PlatformStackScreenProps} from '@navigation/PlatformStackNavigation/types'; +import type {SettingsNavigatorParamList} from '@navigation/types'; import BaseDomainMemberDetailsComponent from '@pages/domain/BaseDomainMemberDetailsComponent'; -import { revokeDomainAdminAccess } from '@userActions/Domain'; +import {revokeDomainAdminAccess} from '@userActions/Domain'; import ONYXKEYS from '@src/ONYXKEYS'; import type SCREENS from '@src/SCREENS'; import type {PersonalDetailsList} from '@src/types/onyx'; - type DomainAdminDetailsPageProps = PlatformStackScreenProps; function DomainAdminDetailsPage({route}: DomainAdminDetailsPageProps) { diff --git a/src/pages/domain/BaseDomainMemberDetailsComponent.tsx b/src/pages/domain/BaseDomainMemberDetailsComponent.tsx index cb031c0be615d..ec835f19073f2 100644 --- a/src/pages/domain/BaseDomainMemberDetailsComponent.tsx +++ b/src/pages/domain/BaseDomainMemberDetailsComponent.tsx @@ -39,7 +39,7 @@ type BaseDomainMemberDetailsComponentProps = { accountID: number; /** List of additional fields (e.g., force 2FA) */ - children: React.ReactNode; + children?: React.ReactNode; }; function BaseDomainMemberDetailsComponent({domainAccountID, accountID, children}: BaseDomainMemberDetailsComponentProps) { diff --git a/src/pages/domain/Members/MembersDetailsPage.tsx b/src/pages/domain/Members/DomainMemberDetailsPage.tsx similarity index 60% rename from src/pages/domain/Members/MembersDetailsPage.tsx rename to src/pages/domain/Members/DomainMemberDetailsPage.tsx index c20ff53e9456c..d57f13edbc699 100644 --- a/src/pages/domain/Members/MembersDetailsPage.tsx +++ b/src/pages/domain/Members/DomainMemberDetailsPage.tsx @@ -1,12 +1,10 @@ import React from 'react'; import type {PlatformStackScreenProps} from '@navigation/PlatformStackNavigation/types'; -import type {DomainSplitNavigatorParamList} from '@navigation/types'; +import type {SettingsNavigatorParamList} from '@navigation/types'; import BaseDomainMemberDetailsComponent from '@pages/domain/BaseDomainMemberDetailsComponent'; -import type {MemberDetailsMenuItem} from '@pages/domain/BaseDomainMemberDetailsComponent'; import type SCREENS from '@src/SCREENS'; -import getEmptyArray from '@src/types/utils/getEmptyArray'; -type DomainMemberDetailsPageProps = PlatformStackScreenProps; +type DomainMemberDetailsPageProps = PlatformStackScreenProps; function DomainMemberDetailsPage({route}: DomainMemberDetailsPageProps) { const {domainAccountID, accountID} = route.params; @@ -15,7 +13,6 @@ function DomainMemberDetailsPage({route}: DomainMemberDetailsPageProps) { ()} /> ); } From af4b7d1e90291d6a56e7fd892fc4e45ac645d40a Mon Sep 17 00:00:00 2001 From: war-in Date: Mon, 12 Jan 2026 16:28:25 +0100 Subject: [PATCH 07/10] fix: remove unused type --- src/pages/domain/BaseDomainMemberDetailsComponent.tsx | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/src/pages/domain/BaseDomainMemberDetailsComponent.tsx b/src/pages/domain/BaseDomainMemberDetailsComponent.tsx index ec835f19073f2..00c5a585ef91c 100644 --- a/src/pages/domain/BaseDomainMemberDetailsComponent.tsx +++ b/src/pages/domain/BaseDomainMemberDetailsComponent.tsx @@ -20,17 +20,8 @@ import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; import type {PersonalDetailsList} from '@src/types/onyx'; -import type IconAsset from '@src/types/utils/IconAsset'; import DomainNotFoundPageWrapper from './DomainNotFoundPageWrapper'; -type MemberDetailsMenuItem = { - key: string; - title: string; - icon: IconAsset; - onPress: () => void | Promise; - shouldShowRightIcon?: boolean; -}; - type BaseDomainMemberDetailsComponentProps = { /** Domain ID */ domainAccountID: number; @@ -118,5 +109,4 @@ function BaseDomainMemberDetailsComponent({domainAccountID, accountID, children} BaseDomainMemberDetailsComponent.displayName = 'BaseDomainMemberDetailsComponent'; -export type {MemberDetailsMenuItem}; export default BaseDomainMemberDetailsComponent; From 59dde9b538a00aa8822c036bdb5280ba23a016c2 Mon Sep 17 00:00:00 2001 From: war-in Date: Mon, 12 Jan 2026 16:33:45 +0100 Subject: [PATCH 08/10] chore: add comment --- src/pages/domain/BaseDomainMemberDetailsComponent.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/pages/domain/BaseDomainMemberDetailsComponent.tsx b/src/pages/domain/BaseDomainMemberDetailsComponent.tsx index 00c5a585ef91c..2c685d7f8d980 100644 --- a/src/pages/domain/BaseDomainMemberDetailsComponent.tsx +++ b/src/pages/domain/BaseDomainMemberDetailsComponent.tsx @@ -36,8 +36,10 @@ type BaseDomainMemberDetailsComponentProps = { function BaseDomainMemberDetailsComponent({domainAccountID, accountID, children}: BaseDomainMemberDetailsComponentProps) { const styles = useThemeStyles(); const {translate, formatPhoneNumber} = useLocalize(); - const icons = useMemoizedLazyExpensifyIcons(['Info'] as const); + const icons = useMemoizedLazyExpensifyIcons(['Info']); + // The selector depends on the dynamic `accountID`, so it cannot be extracted + // to a static function outside the component. // eslint-disable-next-line rulesdir/no-inline-useOnyx-selector const [personalDetails] = useOnyx(ONYXKEYS.PERSONAL_DETAILS_LIST, { canBeMissing: true, From 951678c14ec913e7ac862c69e9a11ff3032e16ab Mon Sep 17 00:00:00 2001 From: war-in Date: Mon, 12 Jan 2026 18:47:54 +0100 Subject: [PATCH 09/10] fix: eslint --- src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx b/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx index 072e02e652b6d..863f7b3df7461 100644 --- a/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx +++ b/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx @@ -807,7 +807,7 @@ const SettingsModalStackNavigator = createModalStackNavigator require('../../../../pages/domain/Admins/DomainAdminsSettingsPage').default, [SCREENS.DOMAIN.ADD_PRIMARY_CONTACT]: () => require('../../../../pages/domain/Admins/DomainAddPrimaryContactPage').default, [SCREENS.DOMAIN.ADD_ADMIN]: () => require('../../../../pages/domain/Admins/DomainAddAdminPage').default, - [SCREENS.DOMAIN.MEMBER_DETAILS]: () => require('../../../../pages/domain/Members/MembersDetailsPage').default, + [SCREENS.DOMAIN.MEMBER_DETAILS]: () => require('../../../../pages/domain/Members/DomainMemberDetailsPage').default, }); const TwoFactorAuthenticatorStackNavigator = createModalStackNavigator({ From 7dd21ae12cb1fc1c3d869095e5a5330fea44e816 Mon Sep 17 00:00:00 2001 From: war-in Date: Tue, 13 Jan 2026 11:38:13 +0100 Subject: [PATCH 10/10] fix: correct layout wrapping --- .../BaseDomainMemberDetailsComponent.tsx | 35 +++++++++---------- 1 file changed, 17 insertions(+), 18 deletions(-) diff --git a/src/pages/domain/BaseDomainMemberDetailsComponent.tsx b/src/pages/domain/BaseDomainMemberDetailsComponent.tsx index 2c685d7f8d980..2ebd592482636 100644 --- a/src/pages/domain/BaseDomainMemberDetailsComponent.tsx +++ b/src/pages/domain/BaseDomainMemberDetailsComponent.tsx @@ -84,25 +84,24 @@ function BaseDomainMemberDetailsComponent({domainAccountID, accountID, children} )} + + + {children} + Navigation.navigate(ROUTES.PROFILE.getRoute(accountID, Navigation.getActiveRoute()))} + shouldShowRightIcon + /> + - - - - {children} - - Navigation.navigate(ROUTES.PROFILE.getRoute(accountID, Navigation.getActiveRoute()))} - shouldShowRightIcon - />