From 105f463865f7c38cf40cf1bd398c113760a274f0 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jan=20Musia=C5=82?= <jan.musial@swmansion.com>
Date: Thu, 18 Dec 2025 17:19:18 +0100
Subject: [PATCH 01/50] Decision modal for on close account

---
 package-lock.json                             |   2 +-
 src/ROUTES.ts                                 |   4 +
 src/SCREENS.ts                                |   1 +
 src/components/ConfirmContent.tsx             |   6 +
 src/components/DecisionModal.tsx              |  44 ++++-
 src/languages/en.ts                           |   4 +
 .../ModalStackNavigators/index.tsx            |   1 +
 .../linkingConfig/RELATIONS/DOMAIN_TO_RHP.ts  |   1 +
 src/libs/Navigation/linkingConfig/config.ts   |   3 +
 src/libs/Navigation/types.ts                  |   4 +
 src/pages/domain/BaseDomainMembersPage.tsx    |  96 +++++------
 .../Members/DomainMemberDetailsPage.tsx       | 156 ++++++++++++++++++
 .../domain/Members/DomainMembersPage.tsx      |   9 +-
 13 files changed, 269 insertions(+), 62 deletions(-)
 create mode 100644 src/pages/domain/Members/DomainMemberDetailsPage.tsx

diff --git a/package-lock.json b/package-lock.json
index 05a096708934..b345ebfc9b45 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -244,7 +244,7 @@
         "electron-builder": "26.0.19",
         "eslint": "^9.36.0",
         "eslint-config-airbnb-typescript": "^18.0.0",
-        "eslint-config-expensify": "^2.0.101",
+        "eslint-config-expensify": "2.0.101",
         "eslint-config-prettier": "^9.1.0",
         "eslint-plugin-jest": "^29.0.1",
         "eslint-plugin-jsdoc": "^60.7.0",
diff --git a/src/ROUTES.ts b/src/ROUTES.ts
index dbabd1274e82..02fcbe07a471 100644
--- a/src/ROUTES.ts
+++ b/src/ROUTES.ts
@@ -3460,6 +3460,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 00bbafaaccc2..f8368b38b52b 100644
--- a/src/SCREENS.ts
+++ b/src/SCREENS.ts
@@ -855,6 +855,7 @@ const SCREENS = {
         ADMINS: 'Domain_Admins',
         ADMIN_DETAILS: 'Domain_Admin_Details',
         MEMBERS: 'Domain_Members',
+        MEMBER_DETAILS: 'Domain_Member_Details',
     },
 } as const;
 
diff --git a/src/components/ConfirmContent.tsx b/src/components/ConfirmContent.tsx
index 3d5af3240302..0b772d974b5a 100644
--- a/src/components/ConfirmContent.tsx
+++ b/src/components/ConfirmContent.tsx
@@ -40,6 +40,9 @@ type ConfirmContentProps = {
     /** Whether we should use the success button color */
     success?: boolean;
 
+    /** Whether we should use the success button color for secondary action */
+    secondarySuccess?: boolean;
+
     /** Whether we should use the danger button color. Use if the action is destructive */
     danger?: boolean;
 
@@ -112,6 +115,7 @@ function ConfirmContent({
     cancelText = '',
     prompt = '',
     success = true,
+    secondarySuccess = false,
     danger = false,
     shouldDisableConfirmButtonWhenOffline = false,
     shouldShowCancelButton = false,
@@ -201,6 +205,7 @@ function ConfirmContent({
                                 onPress={onCancel}
                                 large
                                 text={cancelText || translate('common.no')}
+                                success={secondarySuccess}
                             />
                         )}
                         <Button
@@ -232,6 +237,7 @@ function ConfirmContent({
                                 style={[styles.noSelect, styles.flex1]}
                                 onPress={onCancel}
                                 text={cancelText || translate('common.no')}
+                                success={secondarySuccess}
                             />
                         )}
                         <Button
diff --git a/src/components/DecisionModal.tsx b/src/components/DecisionModal.tsx
index 77a291c1c2ef..fe82b62ff75f 100644
--- a/src/components/DecisionModal.tsx
+++ b/src/components/DecisionModal.tsx
@@ -5,6 +5,7 @@ import CONST from '@src/CONST';
 import Button from './Button';
 import Header from './Header';
 import Modal from './Modal';
+import RenderHTML from './RenderHTML';
 import Text from './Text';
 
 type DecisionModalProps = {
@@ -20,6 +21,18 @@ type DecisionModalProps = {
     /** Text content used in second button */
     secondOptionText: string;
 
+    /** TODO */
+    firstOptionSuccess?: boolean;
+
+    /** TODO */
+    secondOptionSuccess?: boolean;
+
+    /** TODO */
+    firstOptionDanger?: boolean;
+
+    /** TODO */
+    secondOptionDanger?: boolean;
+
     /** onSubmit callback fired after clicking on first button */
     onFirstOptionSubmit?: () => void;
 
@@ -36,7 +49,21 @@ type DecisionModalProps = {
     isVisible: boolean;
 };
 
-function DecisionModal({title, prompt = '', firstOptionText, secondOptionText, onFirstOptionSubmit, onSecondOptionSubmit, isSmallScreenWidth, onClose, isVisible}: DecisionModalProps) {
+function DecisionModal({
+    title,
+    prompt = '',
+    firstOptionText,
+    secondOptionText,
+    onFirstOptionSubmit,
+    onSecondOptionSubmit,
+    isSmallScreenWidth,
+    onClose,
+    isVisible,
+    firstOptionDanger = false,
+    firstOptionSuccess = true,
+    secondOptionSuccess = false,
+    secondOptionDanger = false,
+}: DecisionModalProps) {
     const styles = useThemeStyles();
 
     return (
@@ -46,20 +73,23 @@ function DecisionModal({title, prompt = '', firstOptionText, secondOptionText, o
             type={isSmallScreenWidth ? CONST.MODAL.MODAL_TYPE.BOTTOM_DOCKED : CONST.MODAL.MODAL_TYPE.CONFIRM}
             innerContainerStyle={styles.pv0}
         >
-            <View style={[styles.m5]}>
+            <View style={styles.m5}>
                 <View>
                     <View style={[styles.flexRow, styles.mb5]}>
                         <Header
                             title={title}
-                            containerStyles={[styles.alignItemsCenter]}
+                            containerStyles={styles.alignItemsCenter}
                         />
                     </View>
-                    <Text>{prompt}</Text>
+                    <Text>
+                        <RenderHTML html={prompt} />
+                    </Text>
                 </View>
                 {!!firstOptionText && (
                     <Button
-                        success
-                        style={[styles.mt5]}
+                        success={firstOptionSuccess}
+                        danger={firstOptionDanger}
+                        style={styles.mt5}
                         onPress={onFirstOptionSubmit}
                         pressOnEnter
                         text={firstOptionText}
@@ -70,6 +100,8 @@ function DecisionModal({title, prompt = '', firstOptionText, secondOptionText, o
                     style={[firstOptionText ? styles.mt3 : styles.mt5, styles.noSelect]}
                     onPress={onSecondOptionSubmit}
                     text={secondOptionText}
+                    success={secondOptionSuccess}
+                    danger={secondOptionDanger}
                     large
                 />
             </View>
diff --git a/src/languages/en.ts b/src/languages/en.ts
index 42b9e2ea55e8..27548f1600f4 100755
--- a/src/languages/en.ts
+++ b/src/languages/en.ts
@@ -7822,6 +7822,10 @@ const translations = {
         members:{
             title: 'Members',
             findMember: 'Find member',
+            closeAccount: 'Close account',
+            forceCloseAccount: 'Force close account',
+            safeCloseAccount: 'Close account safely',
+            closeAccountInfo: 'We recommend closing the account safely to skip closing it in case there are: <ul><li>Pending approvals</li><li>Active reimbursements</li><li>No alternative login methods</li></ul><br/> Otherwise, you can ignore the safety precautions above and force close the selected account.'
         }
     },
 };
diff --git a/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx b/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx
index e43e06fcfa88..cab0c4e13f08 100644
--- a/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx
+++ b/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx
@@ -848,6 +848,7 @@ const SettingsModalStackNavigator = createModalStackNavigator<SettingsNavigatorP
     [SCREENS.DOMAIN.VERIFY]: () => require<ReactComponentModule>('../../../../pages/domain/SamlVerifyDomainPage').default,
     [SCREENS.DOMAIN.VERIFIED]: () => require<ReactComponentModule>('../../../../pages/domain/SamlDomainVerifiedPage').default,
     [SCREENS.DOMAIN.ADMIN_DETAILS]: () => require<ReactComponentModule>('../../../../pages/domain/Admins/DomainAdminDetailsPage').default,
+    [SCREENS.DOMAIN.MEMBER_DETAILS]: () => require<ReactComponentModule>('../../../../pages/domain/Members/DomainMemberDetailsPage').default,
 });
 
 const TwoFactorAuthenticatorStackNavigator = createModalStackNavigator<EnablePaymentsNavigatorParamList>({
diff --git a/src/libs/Navigation/linkingConfig/RELATIONS/DOMAIN_TO_RHP.ts b/src/libs/Navigation/linkingConfig/RELATIONS/DOMAIN_TO_RHP.ts
index c7769a21a367..de609baf2da2 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<Record<keyof DomainSplitNavigatorParamList, string[
     [SCREENS.DOMAIN.INITIAL]: [],
     [SCREENS.DOMAIN.SAML]: [SCREENS.DOMAIN.VERIFY, SCREENS.DOMAIN.VERIFIED],
     [SCREENS.DOMAIN.ADMINS]: [SCREENS.DOMAIN.ADMIN_DETAILS],
+    [SCREENS.DOMAIN.MEMBERS]: [SCREENS.DOMAIN.MEMBER_DETAILS],
 };
 
 export default DOMAIN_TO_RHP;
diff --git a/src/libs/Navigation/linkingConfig/config.ts b/src/libs/Navigation/linkingConfig/config.ts
index 5e371d02c802..2938693cf398 100644
--- a/src/libs/Navigation/linkingConfig/config.ts
+++ b/src/libs/Navigation/linkingConfig/config.ts
@@ -1111,6 +1111,9 @@ const config: LinkingOptions<RootNavigatorParamList>['config'] = {
                         [SCREENS.DOMAIN.ADMIN_DETAILS]: {
                             path: ROUTES.DOMAIN_ADMIN_DETAILS.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 c7e3d5f5d8f0..bf9e81e7060b 100644
--- a/src/libs/Navigation/types.ts
+++ b/src/libs/Navigation/types.ts
@@ -1332,6 +1332,10 @@ type SettingsNavigatorParamList = {
         domainAccountID: number;
         accountID: number;
     };
+    [SCREENS.DOMAIN.MEMBER_DETAILS]: {
+        domainAccountID: number;
+        accountID: number;
+    };
 } & ReimbursementAccountNavigatorParamList;
 
 type DomainCardNavigatorParamList = {
diff --git a/src/pages/domain/BaseDomainMembersPage.tsx b/src/pages/domain/BaseDomainMembersPage.tsx
index 8e1fbcef5711..80c9cd493add 100644
--- a/src/pages/domain/BaseDomainMembersPage.tsx
+++ b/src/pages/domain/BaseDomainMembersPage.tsx
@@ -44,17 +44,10 @@ type BaseDomainMembersPageProps = {
     /** Callback fired when a row is selected */
     onSelectRow: (item: MemberOption) => void;
 
-    hederIcon:  IconAsset;
+    hederIcon: IconAsset;
 };
 
-function BaseDomainMembersPage({
-                                   accountIDs,
-                                   headerTitle,
-                                   searchPlaceholder,
-                                   headerContent,
-                                   onSelectRow,
-                                   hederIcon
-                               }: BaseDomainMembersPageProps) {
+function BaseDomainMembersPage({accountIDs, headerTitle, searchPlaceholder, headerContent, onSelectRow, hederIcon}: BaseDomainMembersPageProps) {
     const {formatPhoneNumber, localeCompare} = useLocalize();
     const styles = useThemeStyles();
     const {shouldUseNarrowLayout} = useResponsiveLayout();
@@ -88,10 +81,7 @@ function BaseDomainMembersPage({
         return results.length > 0;
     }, []);
 
-    const sortMembers = useCallback(
-        (options: MemberOption[]) => sortAlphabetically(options, 'text', localeCompare),
-        [localeCompare],
-    );
+    const sortMembers = useCallback((options: MemberOption[]) => sortAlphabetically(options, 'text', localeCompare), [localeCompare]);
 
     const [inputValue, setInputValue, filteredData] = useSearchResults(data, filterMember, sortMembers);
 
@@ -99,17 +89,23 @@ function BaseDomainMembersPage({
         if (filteredData.length === 0) {
             return null;
         }
-        return <CustomListHeader canSelectMultiple={false} leftHeaderText={headerTitle} />;
+        return (
+            <CustomListHeader
+                canSelectMultiple={false}
+                leftHeaderText={headerTitle}
+            />
+        );
     };
 
-    const listHeaderContent = data.length > CONST.SEARCH_ITEM_LIMIT ? (
-        <SearchBar
-            inputValue={inputValue}
-            onChangeText={setInputValue}
-            label={searchPlaceholder}
-            shouldShowEmptyState={!filteredData.length}
-        />
-    ) : null;
+    const listHeaderContent =
+        data.length > CONST.SEARCH_ITEM_LIMIT ? (
+            <SearchBar
+                inputValue={inputValue}
+                onChangeText={setInputValue}
+                label={searchPlaceholder}
+                shouldShowEmptyState={!filteredData.length}
+            />
+        ) : null;
 
     return (
         <ScreenWrapper
@@ -118,36 +114,32 @@ function BaseDomainMembersPage({
             shouldShowOfflineIndicatorInWideScreen
             testID={BaseDomainMembersPage.displayName}
         >
-                <HeaderWithBackButton
-                    title={headerTitle}
-                    onBackButtonPress={Navigation.popToSidebar}
-                    icon={hederIcon}
-                    shouldShowBackButton={shouldUseNarrowLayout}
-                >
-                    {!shouldUseNarrowLayout && !!headerContent && (
-                        <View style={[styles.flexRow, styles.gap2]}>{headerContent}</View>
-                    )}
-                </HeaderWithBackButton>
-
-                {shouldUseNarrowLayout && !!headerContent && (
-                    <View style={[styles.pl5, styles.pr5, styles.flexRow, styles.gap2]}>{headerContent}</View>
-                )}
-
-                <SelectionList
-                    sections={[{data: filteredData}]}
-                    shouldShowRightCaret
-                    canSelectMultiple={false}
-                    listHeaderContent={listHeaderContent}
-                    listHeaderWrapperStyle={[styles.ph9, styles.pv3, styles.pb5]}
-                    ListItem={TableListItem}
-                    onSelectRow={onSelectRow}
-                    shouldShowListEmptyContent={false}
-                    listItemTitleContainerStyles={shouldUseNarrowLayout ? undefined : [styles.pr3]}
-                    showScrollIndicator={false}
-                    addBottomSafeAreaPadding
-                    customListHeader={getCustomListHeader()}
-                    containerStyle={[styles.flex1]}
-                />
+            <HeaderWithBackButton
+                title={headerTitle}
+                onBackButtonPress={Navigation.popToSidebar}
+                icon={hederIcon}
+                shouldShowBackButton={shouldUseNarrowLayout}
+            >
+                {!shouldUseNarrowLayout && !!headerContent && <View style={[styles.flexRow, styles.gap2]}>{headerContent}</View>}
+            </HeaderWithBackButton>
+
+            {shouldUseNarrowLayout && !!headerContent && <View style={[styles.pl5, styles.pr5, styles.flexRow, styles.gap2]}>{headerContent}</View>}
+
+            <SelectionList
+                sections={[{data: filteredData}]}
+                shouldShowRightCaret
+                canSelectMultiple={false}
+                listHeaderContent={listHeaderContent}
+                listHeaderWrapperStyle={[styles.ph9, styles.pv3, styles.pb5]}
+                ListItem={TableListItem}
+                onSelectRow={onSelectRow}
+                shouldShowListEmptyContent={false}
+                listItemTitleContainerStyles={shouldUseNarrowLayout ? undefined : [styles.pr3]}
+                showScrollIndicator={false}
+                addBottomSafeAreaPadding
+                customListHeader={getCustomListHeader()}
+                containerStyle={styles.flex1}
+            />
         </ScreenWrapper>
     );
 }
diff --git a/src/pages/domain/Members/DomainMemberDetailsPage.tsx b/src/pages/domain/Members/DomainMemberDetailsPage.tsx
new file mode 100644
index 000000000000..c8fdcc39ca69
--- /dev/null
+++ b/src/pages/domain/Members/DomainMemberDetailsPage.tsx
@@ -0,0 +1,156 @@
+import {adminAccountIDsSelector} from '@selectors/Domain';
+import {Str} from 'expensify-common';
+import React, {useState} from 'react';
+import {View} from 'react-native';
+import type {OnyxEntry} from 'react-native-onyx';
+import Avatar from '@components/Avatar';
+import FullPageNotFoundView from '@components/BlockingViews/FullPageNotFoundView';
+import Button from '@components/Button';
+import DecisionModal from '@components/DecisionModal';
+import FullScreenLoadingIndicator from '@components/FullscreenLoadingIndicator';
+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 {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 {getCurrentUserAccountID} from '@userActions/Report';
+import CONST from '@src/CONST';
+import ONYXKEYS from '@src/ONYXKEYS';
+import ROUTES from '@src/ROUTES';
+import type SCREENS from '@src/SCREENS';
+import type {PersonalDetailsList} from '@src/types/onyx';
+import isLoadingOnyxValue from '@src/types/utils/isLoadingOnyxValue';
+
+type DomainMemberDetailsPageProps = PlatformStackScreenProps<SettingsNavigatorParamList, typeof SCREENS.DOMAIN.MEMBER_DETAILS>;
+
+function DomainMemberDetailsPage({route}: DomainMemberDetailsPageProps) {
+    const {domainAccountID, accountID} = route.params;
+    const styles = useThemeStyles();
+    const {translate, formatPhoneNumber} = useLocalize();
+    const icons = useMemoizedLazyExpensifyIcons(['Info', 'RemoveMembers'] as const);
+    const [isModalVisible, setIsModalVisible] = useState(false);
+
+    const [adminAccountIDs, domainMetadata] = useOnyx(`${ONYXKEYS.COLLECTION.DOMAIN}${domainAccountID}`, {
+        canBeMissing: true,
+        selector: adminAccountIDsSelector,
+    });
+
+    // eslint-disable-next-line rulesdir/no-inline-useOnyx-selector
+    const [personalDetails] = useOnyx(ONYXKEYS.PERSONAL_DETAILS_LIST, {
+        canBeMissing: true,
+        selector: (personalDetailsList: OnyxEntry<PersonalDetailsList>) => personalDetailsList?.[accountID],
+    });
+
+    const displayName = formatPhoneNumber(getDisplayNameOrDefault(personalDetails));
+    const memberLogin = personalDetails?.login ?? '';
+    const isSMSLogin = Str.isSMSLogin(memberLogin);
+    const phoneNumber = getPhoneNumber(personalDetails);
+    const fallbackIcon = personalDetails?.fallbackIcon ?? '';
+
+    const currentUserAccountID = getCurrentUserAccountID();
+    const isAdmin = adminAccountIDs?.includes(currentUserAccountID);
+
+    if (isLoadingOnyxValue(domainMetadata)) {
+        return <FullScreenLoadingIndicator shouldUseGoBackButton />;
+    }
+
+    const handleForceCloseAccount = () => {
+        console.log('force closing');
+        setIsModalVisible(false);
+        Navigation.dismissModal();
+    };
+
+    const handleSafeCloseAccount = () => {
+        console.log('safe closing');
+        setIsModalVisible(false);
+        Navigation.dismissModal();
+    };
+
+    return (
+        <ScreenWrapper
+            enableEdgeToEdgeBottomSafeAreaPadding
+            testID={DomainMemberDetailsPage.displayName}
+        >
+            <FullPageNotFoundView
+                onBackButtonPress={() => Navigation.goBack(ROUTES.WORKSPACES_LIST.route)}
+                shouldForceFullScreen
+            >
+                <HeaderWithBackButton title={displayName} />
+                <ScrollView addBottomSafeAreaPadding>
+                    <View style={[styles.containerWithSpaceBetween, styles.pointerEventsBoxNone, styles.justifyContentStart]}>
+                        <View style={[styles.avatarSectionWrapper, styles.pb0]}>
+                            <OfflineWithFeedback pendingAction={personalDetails?.pendingFields?.avatar}>
+                                <Avatar
+                                    containerStyles={[styles.avatarXLarge, styles.mb4, styles.noOutline]}
+                                    imageStyles={styles.avatarXLarge}
+                                    source={personalDetails?.avatar}
+                                    avatarID={accountID}
+                                    type={CONST.ICON_TYPE_AVATAR}
+                                    size={CONST.AVATAR_SIZE.X_LARGE}
+                                    fallbackIcon={fallbackIcon}
+                                />
+                            </OfflineWithFeedback>
+                            {!!displayName && (
+                                <Text
+                                    style={[styles.textHeadline, styles.pre, styles.mb8, styles.w100, styles.textAlignCenter]}
+                                    numberOfLines={1}
+                                >
+                                    {displayName}
+                                </Text>
+                            )}
+                            <Button
+                                text={translate('domain.members.closeAccount')}
+                                onPress={() => setIsModalVisible(true)}
+                                // isDisabled={isAdmin}
+                                icon={icons.RemoveMembers}
+                                style={styles.mb5}
+                            />
+                        </View>
+                        <View style={styles.w100}>
+                            <MenuItemWithTopDescription
+                                title={isSMSLogin ? formatPhoneNumber(phoneNumber ?? '') : memberLogin}
+                                copyValue={isSMSLogin ? formatPhoneNumber(phoneNumber ?? '') : memberLogin}
+                                description={translate(isSMSLogin ? 'common.phoneNumber' : 'common.email')}
+                                interactive={false}
+                                copyable
+                            />
+                            <MenuItem
+                                style={styles.mb5}
+                                title={translate('common.profile')}
+                                icon={icons.Info}
+                                onPress={() => Navigation.navigate(ROUTES.PROFILE.getRoute(accountID, Navigation.getActiveRoute()))}
+                                shouldShowRightIcon
+                            />
+                        </View>
+                    </View>
+                </ScrollView>
+            </FullPageNotFoundView>
+            <DecisionModal
+                title={translate('domain.members.closeAccount')}
+                prompt={translate('domain.members.closeAccountInfo')}
+                isSmallScreenWidth={false}
+                onSecondOptionSubmit={handleSafeCloseAccount}
+                secondOptionText={translate('domain.members.safeCloseAccount')}
+                firstOptionText={translate('domain.members.forceCloseAccount')}
+                isVisible={isModalVisible}
+                onClose={handleForceCloseAccount}
+                firstOptionDanger
+                secondOptionSuccess
+            />
+        </ScreenWrapper>
+    );
+}
+
+DomainMemberDetailsPage.displayName = 'DomainAdminDetailsPage';
+
+export default DomainMemberDetailsPage;
diff --git a/src/pages/domain/Members/DomainMembersPage.tsx b/src/pages/domain/Members/DomainMembersPage.tsx
index 2b2c65046ba5..88a67622acae 100644
--- a/src/pages/domain/Members/DomainMembersPage.tsx
+++ b/src/pages/domain/Members/DomainMembersPage.tsx
@@ -1,12 +1,15 @@
+import {selectMemberIDs} from '@selectors/Domain';
 import React from 'react';
-import BaseDomainMembersPage from '@pages/domain/BaseDomainMembersPage';
 import useLocalize from '@hooks/useLocalize';
 import useOnyx from '@hooks/useOnyx';
+import Navigation from '@libs/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';
 
 type DomainMembersPageProps = PlatformStackScreenProps<DomainSplitNavigatorParamList, typeof SCREENS.DOMAIN.MEMBERS>;
 
@@ -29,7 +32,7 @@ function DomainMembersPage({route}: DomainMembersPageProps) {
             accountIDs={memberIDs ?? []}
             headerTitle={translate('domain.members.title')}
             searchPlaceholder={translate('domain.members.findMember')}
-            onSelectRow={()=>{}}
+            onSelectRow={(item: MemberOption) => Navigation.navigate(ROUTES.DOMAIN_MEMBER_DETAILS.getRoute(domainAccountID, item.accountID))}
             shouldShowNotFoundView={shouldShowNotFoundView}
         />
     );

From 29a1b2ae6b6ac7adbff9d621bfa46ef4dea94e18 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jan=20Musia=C5=82?= <jan.musial@swmansion.com>
Date: Fri, 19 Dec 2025 16:10:23 +0100
Subject: [PATCH 02/50] add dev buttons update action handlers

---
 src/languages/en.ts                             |  2 +-
 src/pages/domain/DomainInitialPage.tsx          | 17 ++++++++++++++++-
 .../domain/Members/DomainMemberDetailsPage.tsx  | 14 ++++++--------
 3 files changed, 23 insertions(+), 10 deletions(-)

diff --git a/src/languages/en.ts b/src/languages/en.ts
index 0a8ad6152124..b72498f2a56a 100755
--- a/src/languages/en.ts
+++ b/src/languages/en.ts
@@ -7845,7 +7845,7 @@ const translations = {
             closeAccount: 'Close account',
             forceCloseAccount: 'Force close account',
             safeCloseAccount: 'Close account safely',
-            closeAccountInfo: 'We recommend closing the account safely to skip closing it in case there are: <ul><li>Pending approvals</li><li>Active reimbursements</li><li>No alternative login methods</li></ul><br/> Otherwise, you can ignore the safety precautions above and force close the selected account.'
+            closeAccountInfo: 'We recommend closing the account safely to skip closing it in case there are: <ul><li>Pending approvals</li><li>Active reimbursements</li><li>No alternative login methods</li></ul> Otherwise, you can ignore the safety precautions above and force close the selected account.'
         }
     },
 };
diff --git a/src/pages/domain/DomainInitialPage.tsx b/src/pages/domain/DomainInitialPage.tsx
index ef2cb6080cc0..17334432611d 100644
--- a/src/pages/domain/DomainInitialPage.tsx
+++ b/src/pages/domain/DomainInitialPage.tsx
@@ -67,6 +67,21 @@ function DomainInitialPage({route}: DomainInitialPageProps) {
                 action: singleExecution(waitForNavigate(() => Navigation.navigate(ROUTES.DOMAIN_SAML.getRoute(domainAccountID)))),
                 screenName: SCREENS.DOMAIN.SAML,
             },
+            // --- DEV only below this line 
+            {
+                translationKey: 'domain.admins.title',
+                icon: icons.UserLock,
+                action: singleExecution(waitForNavigate(() => Navigation.navigate(ROUTES.DOMAIN_ADMINS.getRoute(domainAccountID)))),
+                screenName: SCREENS.DOMAIN.ADMINS,
+            },
+            {
+                translationKey: 'domain.members.title',
+                icon: icons.UserLock,
+                action: singleExecution(waitForNavigate(() => Navigation.navigate(ROUTES.DOMAIN_MEMBERS.getRoute(domainAccountID)))),
+                screenName: SCREENS.DOMAIN.MEMBERS,
+            },
+            // --- DEV only above this line 
+
         ];
 
         return menuItems;
@@ -111,7 +126,7 @@ function DomainInitialPage({route}: DomainInitialPageProps) {
                 />
 
                 <ScrollView contentContainerStyle={[styles.flexColumn]}>
-                    <View style={[styles.pb4, styles.mh3, styles.mt3]}>
+                    <View style={[styles.pb4, styles.mh3, styles.mt3, styles.gap2]}>
                         {/*
                             Ideally we should use MenuList component for MenuItems with singleExecution/Navigation actions.
                             In this case where user can click on menu items, we need to have a check for `isExecuting`. So, we are directly mapping menuItems.
diff --git a/src/pages/domain/Members/DomainMemberDetailsPage.tsx b/src/pages/domain/Members/DomainMemberDetailsPage.tsx
index c8fdcc39ca69..233e4d50df74 100644
--- a/src/pages/domain/Members/DomainMemberDetailsPage.tsx
+++ b/src/pages/domain/Members/DomainMemberDetailsPage.tsx
@@ -18,12 +18,12 @@ import Text from '@components/Text';
 import {useMemoizedLazyExpensifyIcons} from '@hooks/useLazyAsset';
 import useLocalize from '@hooks/useLocalize';
 import useOnyx from '@hooks/useOnyx';
+import useResponsiveLayout from '@hooks/useResponsiveLayout';
 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 {getCurrentUserAccountID} from '@userActions/Report';
 import CONST from '@src/CONST';
 import ONYXKEYS from '@src/ONYXKEYS';
 import ROUTES from '@src/ROUTES';
@@ -39,6 +39,8 @@ function DomainMemberDetailsPage({route}: DomainMemberDetailsPageProps) {
     const {translate, formatPhoneNumber} = useLocalize();
     const icons = useMemoizedLazyExpensifyIcons(['Info', 'RemoveMembers'] as const);
     const [isModalVisible, setIsModalVisible] = useState(false);
+    // eslint-disable-next-line rulesdir/prefer-shouldUseNarrowLayout-instead-of-isSmallScreenWidth
+    const {isSmallScreenWidth} = useResponsiveLayout();
 
     const [adminAccountIDs, domainMetadata] = useOnyx(`${ONYXKEYS.COLLECTION.DOMAIN}${domainAccountID}`, {
         canBeMissing: true,
@@ -57,21 +59,16 @@ function DomainMemberDetailsPage({route}: DomainMemberDetailsPageProps) {
     const phoneNumber = getPhoneNumber(personalDetails);
     const fallbackIcon = personalDetails?.fallbackIcon ?? '';
 
-    const currentUserAccountID = getCurrentUserAccountID();
-    const isAdmin = adminAccountIDs?.includes(currentUserAccountID);
-
     if (isLoadingOnyxValue(domainMetadata)) {
         return <FullScreenLoadingIndicator shouldUseGoBackButton />;
     }
 
     const handleForceCloseAccount = () => {
-        console.log('force closing');
         setIsModalVisible(false);
         Navigation.dismissModal();
     };
 
     const handleSafeCloseAccount = () => {
-        console.log('safe closing');
         setIsModalVisible(false);
         Navigation.dismissModal();
     };
@@ -138,12 +135,13 @@ function DomainMemberDetailsPage({route}: DomainMemberDetailsPageProps) {
             <DecisionModal
                 title={translate('domain.members.closeAccount')}
                 prompt={translate('domain.members.closeAccountInfo')}
-                isSmallScreenWidth={false}
+                isSmallScreenWidth={isSmallScreenWidth}
                 onSecondOptionSubmit={handleSafeCloseAccount}
+                onFirstOptionSubmit={handleForceCloseAccount}
                 secondOptionText={translate('domain.members.safeCloseAccount')}
                 firstOptionText={translate('domain.members.forceCloseAccount')}
                 isVisible={isModalVisible}
-                onClose={handleForceCloseAccount}
+                onClose={() => setIsModalVisible(false)}
                 firstOptionDanger
                 secondOptionSuccess
             />

From 20065758c8318e2ec7b59a71f96c070a8fc64b88 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jan=20Musia=C5=82?= <jan.musial@swmansion.com>
Date: Mon, 22 Dec 2025 13:40:40 +0100
Subject: [PATCH 03/50] code cleanup

---
 src/components/ConfirmContent.tsx                   |  6 ------
 src/components/DecisionModal.tsx                    |  8 ++++----
 .../Navigators/DomainSplitNavigator.tsx             |  3 +--
 src/pages/domain/BaseDomainMembersPage.tsx          | 13 +++++++------
 src/pages/domain/DomainInitialPage.tsx              |  5 ++---
 5 files changed, 14 insertions(+), 21 deletions(-)

diff --git a/src/components/ConfirmContent.tsx b/src/components/ConfirmContent.tsx
index 0b772d974b5a..3d5af3240302 100644
--- a/src/components/ConfirmContent.tsx
+++ b/src/components/ConfirmContent.tsx
@@ -40,9 +40,6 @@ type ConfirmContentProps = {
     /** Whether we should use the success button color */
     success?: boolean;
 
-    /** Whether we should use the success button color for secondary action */
-    secondarySuccess?: boolean;
-
     /** Whether we should use the danger button color. Use if the action is destructive */
     danger?: boolean;
 
@@ -115,7 +112,6 @@ function ConfirmContent({
     cancelText = '',
     prompt = '',
     success = true,
-    secondarySuccess = false,
     danger = false,
     shouldDisableConfirmButtonWhenOffline = false,
     shouldShowCancelButton = false,
@@ -205,7 +201,6 @@ function ConfirmContent({
                                 onPress={onCancel}
                                 large
                                 text={cancelText || translate('common.no')}
-                                success={secondarySuccess}
                             />
                         )}
                         <Button
@@ -237,7 +232,6 @@ function ConfirmContent({
                                 style={[styles.noSelect, styles.flex1]}
                                 onPress={onCancel}
                                 text={cancelText || translate('common.no')}
-                                success={secondarySuccess}
                             />
                         )}
                         <Button
diff --git a/src/components/DecisionModal.tsx b/src/components/DecisionModal.tsx
index fe82b62ff75f..df30e689077c 100644
--- a/src/components/DecisionModal.tsx
+++ b/src/components/DecisionModal.tsx
@@ -21,16 +21,16 @@ type DecisionModalProps = {
     /** Text content used in second button */
     secondOptionText: string;
 
-    /** TODO */
+    /** Weather first option should use a success themed button */
     firstOptionSuccess?: boolean;
 
-    /** TODO */
+    /** Weather second option should use a success themed button  */
     secondOptionSuccess?: boolean;
 
-    /** TODO */
+    /** Weather first option should use a danger themed button  */
     firstOptionDanger?: boolean;
 
-    /** TODO */
+    /** Weather second option should use a danger themed button  */
     secondOptionDanger?: boolean;
 
     /** onSubmit callback fired after clicking on first button */
diff --git a/src/libs/Navigation/AppNavigator/Navigators/DomainSplitNavigator.tsx b/src/libs/Navigation/AppNavigator/Navigators/DomainSplitNavigator.tsx
index 115b30098624..649f911492e0 100644
--- a/src/libs/Navigation/AppNavigator/Navigators/DomainSplitNavigator.tsx
+++ b/src/libs/Navigation/AppNavigator/Navigators/DomainSplitNavigator.tsx
@@ -14,8 +14,7 @@ import type ReactComponentModule from '@src/types/utils/ReactComponentModule';
 const loadDomainInitialPage = () => require<ReactComponentModule>('../../../../pages/domain/DomainInitialPage').default;
 const loadDomainSamlPage = () => require<ReactComponentModule>('../../../../pages/domain/DomainSamlPage').default;
 const loadDomainAdminsPage = () => require<ReactComponentModule>('../../../../pages/domain/Admins/DomainAdminsPage').default;
-const loadDomainMembersPage = () => require<ReactComponentModule>('../../../../pages/domain/members/DomainMembersPage').default;
-
+const loadDomainMembersPage = () => require<ReactComponentModule>('../../../../pages/domain/Members/DomainMembersPage').default;
 
 const Split = createSplitNavigator<DomainSplitNavigatorParamList>();
 
diff --git a/src/pages/domain/BaseDomainMembersPage.tsx b/src/pages/domain/BaseDomainMembersPage.tsx
index 80c9cd493add..a8a3f83e7c79 100644
--- a/src/pages/domain/BaseDomainMembersPage.tsx
+++ b/src/pages/domain/BaseDomainMembersPage.tsx
@@ -1,14 +1,14 @@
 import React, {useCallback, useMemo} from 'react';
 import {View} from 'react-native';
-import FullPageNotFoundView from '@components/BlockingViews/FullPageNotFoundView';
 import HeaderWithBackButton from '@components/HeaderWithBackButton';
-import {FallbackAvatar} from '@components/Icon/Expensicons';
 import ScreenWrapper from '@components/ScreenWrapper';
 import SearchBar from '@components/SearchBar';
 import CustomListHeader from '@components/SelectionListWithModal/CustomListHeader';
+// eslint-disable-next-line no-restricted-imports
 import SelectionList from '@components/SelectionListWithSections';
 import TableListItem from '@components/SelectionListWithSections/TableListItem';
 import type {ListItem} from '@components/SelectionListWithSections/types';
+import {useMemoizedLazyExpensifyIcons} from '@hooks/useLazyAsset';
 import useLocalize from '@hooks/useLocalize';
 import useOnyx from '@hooks/useOnyx';
 import useResponsiveLayout from '@hooks/useResponsiveLayout';
@@ -20,8 +20,7 @@ import tokenizedSearch from '@libs/tokenizedSearch';
 import Navigation from '@navigation/Navigation';
 import CONST from '@src/CONST';
 import ONYXKEYS from '@src/ONYXKEYS';
-import ROUTES from '@src/ROUTES';
-import IconAsset from '@src/types/utils/IconAsset';
+import type IconAsset from '@src/types/utils/IconAsset';
 
 type MemberOption = Omit<ListItem, 'accountID' | 'login'> & {
     accountID: number;
@@ -50,6 +49,8 @@ type BaseDomainMembersPageProps = {
 function BaseDomainMembersPage({accountIDs, headerTitle, searchPlaceholder, headerContent, onSelectRow, hederIcon}: BaseDomainMembersPageProps) {
     const {formatPhoneNumber, localeCompare} = useLocalize();
     const styles = useThemeStyles();
+    const icons = useMemoizedLazyExpensifyIcons(['FallbackAvatar'] as const);
+
     const {shouldUseNarrowLayout} = useResponsiveLayout();
     const [personalDetails] = useOnyx(ONYXKEYS.PERSONAL_DETAILS_LIST, {canBeMissing: true});
 
@@ -65,7 +66,7 @@ function BaseDomainMembersPage({accountIDs, headerTitle, searchPlaceholder, head
                 alternateText: formatPhoneNumber(details?.login ?? ''),
                 icons: [
                     {
-                        source: details?.avatar ?? FallbackAvatar,
+                        source: details?.avatar ?? icons.FallbackAvatar,
                         name: formatPhoneNumber(details?.login ?? ''),
                         type: CONST.ICON_TYPE_AVATAR,
                         id: accountID,
@@ -74,7 +75,7 @@ function BaseDomainMembersPage({accountIDs, headerTitle, searchPlaceholder, head
             });
         }
         return options;
-    }, [accountIDs, personalDetails, formatPhoneNumber]);
+    }, [accountIDs, personalDetails, formatPhoneNumber, icons.FallbackAvatar]);
 
     const filterMember = useCallback((option: MemberOption, searchQuery: string) => {
         const results = tokenizedSearch([option], searchQuery, (item) => [item.text ?? '', item.alternateText ?? '']);
diff --git a/src/pages/domain/DomainInitialPage.tsx b/src/pages/domain/DomainInitialPage.tsx
index 17334432611d..060d929a3767 100644
--- a/src/pages/domain/DomainInitialPage.tsx
+++ b/src/pages/domain/DomainInitialPage.tsx
@@ -67,7 +67,7 @@ function DomainInitialPage({route}: DomainInitialPageProps) {
                 action: singleExecution(waitForNavigate(() => Navigation.navigate(ROUTES.DOMAIN_SAML.getRoute(domainAccountID)))),
                 screenName: SCREENS.DOMAIN.SAML,
             },
-            // --- DEV only below this line 
+            // --- DEV only below this line
             {
                 translationKey: 'domain.admins.title',
                 icon: icons.UserLock,
@@ -80,8 +80,7 @@ function DomainInitialPage({route}: DomainInitialPageProps) {
                 action: singleExecution(waitForNavigate(() => Navigation.navigate(ROUTES.DOMAIN_MEMBERS.getRoute(domainAccountID)))),
                 screenName: SCREENS.DOMAIN.MEMBERS,
             },
-            // --- DEV only above this line 
-
+            // --- DEV only above this line
         ];
 
         return menuItems;

From 0a9420963ab4bf4e332857126325c678abbe3253 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jan=20Musia=C5=82?= <jan.musial@swmansion.com>
Date: Mon, 22 Dec 2025 16:41:21 +0100
Subject: [PATCH 04/50] update with RBR and pending

---
 src/ONYXKEYS.ts                               |  2 +-
 src/languages/en.ts                           |  7 +-
 src/libs/actions/Domain.ts                    | 81 ++++++++++++++++++-
 src/pages/domain/BaseDomainMembersPage.tsx    | 22 ++++-
 .../Members/DomainMemberDetailsPage.tsx       | 13 ++-
 .../domain/Members/DomainMembersPage.tsx      |  1 +
 src/selectors/Domain.ts                       | 44 +++++++++-
 src/types/onyx/DomainErrors.ts                | 13 +++
 src/types/onyx/DomainPendingActions.ts        |  5 ++
 src/types/onyx/index.ts                       |  4 +-
 10 files changed, 179 insertions(+), 13 deletions(-)

diff --git a/src/ONYXKEYS.ts b/src/ONYXKEYS.ts
index c4da6c337214..df1cfb08fa44 100755
--- a/src/ONYXKEYS.ts
+++ b/src/ONYXKEYS.ts
@@ -1148,7 +1148,7 @@ type OnyxCollectionValuesMapping = {
     [ONYXKEYS.COLLECTION.EXPENSIFY_ADMIN_ACCESS_PREFIX]: number;
     [ONYXKEYS.COLLECTION.DOMAIN_PENDING_ACTIONS]: OnyxTypes.DomainPendingActions;
     [ONYXKEYS.COLLECTION.DOMAIN_ERRORS]: OnyxTypes.DomainErrors;
-    [ONYXKEYS.COLLECTION.DOMAIN_SECURITY_GROUP] : OnyxTypes.DomainSecurityGroup;
+    [ONYXKEYS.COLLECTION.DOMAIN_SECURITY_GROUP]: OnyxTypes.DomainSecurityGroup;
 };
 
 type OnyxValuesMapping = {
diff --git a/src/languages/en.ts b/src/languages/en.ts
index 8ae877909f68..15a56ba8f807 100755
--- a/src/languages/en.ts
+++ b/src/languages/en.ts
@@ -7844,14 +7844,15 @@ const translations = {
             addPrimaryContact: 'Add primary contact',
             settings: 'Settings',
         },
-        members:{
+        members: {
             title: 'Members',
             findMember: 'Find member',
             closeAccount: 'Close account',
             forceCloseAccount: 'Force close account',
             safeCloseAccount: 'Close account safely',
-            closeAccountInfo: 'We recommend closing the account safely to skip closing it in case there are: <ul><li>Pending approvals</li><li>Active reimbursements</li><li>No alternative login methods</li></ul> Otherwise, you can ignore the safety precautions above and force close the selected account.'
-        }
+            closeAccountInfo:
+                'We recommend closing the account safely to skip closing it in case there are: <ul><li>Pending approvals</li><li>Active reimbursements</li><li>No alternative login methods</li></ul> Otherwise, you can ignore the safety precautions above and force close the selected account.',
+        },
     },
 };
 
diff --git a/src/libs/actions/Domain.ts b/src/libs/actions/Domain.ts
index 51d553f1d841..581690470187 100644
--- a/src/libs/actions/Domain.ts
+++ b/src/libs/actions/Domain.ts
@@ -1,5 +1,5 @@
 import Onyx from 'react-native-onyx';
-import type {OnyxUpdate} from 'react-native-onyx';
+import type {OnyxMergeInput, OnyxUpdate} from 'react-native-onyx';
 import * as API from '@libs/API';
 import type {SetTechnicalContactEmailParams} from '@libs/API/parameters';
 import {READ_COMMANDS, SIDE_EFFECT_REQUEST_COMMANDS, WRITE_COMMANDS} from '@libs/API/types';
@@ -416,6 +416,83 @@ function clearSetPrimaryContactError(domainAccountID: number) {
     });
 }
 
+/** Sends a request to remove user from a domain and close their account */
+function closeUserAccount(domainAccountID: number, securityGroupIDs: number[], accountID: number, force = false) {
+    const optimisticValue: OnyxMergeInput<`domain_${string}`> = {};
+    for (const groupID of securityGroupIDs) {
+        // @ts-expect-error
+        optimisticValue[`${ONYXKEYS.COLLECTION.DOMAIN_SECURITY_GROUP}_${groupID}`] = {
+            shared: {
+                [accountID]: null,
+            },
+        };
+    }
+
+    const optimisticData: OnyxUpdate[] = [
+        {
+            onyxMethod: Onyx.METHOD.MERGE,
+            key: `${ONYXKEYS.COLLECTION.DOMAIN}${domainAccountID}`,
+            value: optimisticValue,
+        },
+        {
+            onyxMethod: Onyx.METHOD.MERGE,
+            key: `${ONYXKEYS.COLLECTION.DOMAIN_PENDING_ACTIONS}${domainAccountID}`,
+            value: {
+                members: {[accountID]: CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE},
+            },
+        },
+    ];
+
+    const successData: OnyxUpdate[] = [
+        {
+            onyxMethod: Onyx.METHOD.MERGE,
+            key: `${ONYXKEYS.COLLECTION.DOMAIN_PENDING_ACTIONS}${domainAccountID}`,
+            value: {
+                members: {[accountID]: null},
+            },
+        },
+    ];
+    const failureData = [
+        {
+            onyxMethod: Onyx.METHOD.MERGE,
+            key: `${ONYXKEYS.COLLECTION.DOMAIN_PENDING_ACTIONS}${domainAccountID}`,
+            value: {
+                members: {[accountID]: null},
+            },
+        },
+        // DEV
+        {
+            onyxMethod: Onyx.METHOD.MERGE,
+            key: `${ONYXKEYS.COLLECTION.DOMAIN_ERRORS}${domainAccountID}`,
+            value: {
+                memberErrors: {
+                    [accountID]: {errors: {[Date.now()]: 'Unable to remove this user. Please try again.'}},
+                },
+            },
+        },
+        {
+            onyxMethod: Onyx.METHOD.MERGE,
+            key: `${ONYXKEYS.COLLECTION.DOMAIN}${domainAccountID}`,
+            value: {
+                expensify_securityGroup_1: {
+                    shared: {
+                        [accountID]: 'read',
+                    },
+                },
+            },
+        },
+    ];
+    API.write('', {force}, {optimisticData, successData, failureData});
+}
+
+function clearDomainMemberError(domainAccountID: number, accountID: number) {
+    Onyx.merge(`${ONYXKEYS.COLLECTION.DOMAIN_ERRORS}${domainAccountID}`, {
+        memberErrors: {
+            [accountID]: null,
+        },
+    });
+}
+
 export {
     getDomainValidationCode,
     validateDomain,
@@ -432,4 +509,6 @@ export {
     resetCreateDomainForm,
     setPrimaryContact,
     clearSetPrimaryContactError,
+    closeUserAccount,
+    clearDomainMemberError,
 };
diff --git a/src/pages/domain/BaseDomainMembersPage.tsx b/src/pages/domain/BaseDomainMembersPage.tsx
index a8a3f83e7c79..948d6653cbfb 100644
--- a/src/pages/domain/BaseDomainMembersPage.tsx
+++ b/src/pages/domain/BaseDomainMembersPage.tsx
@@ -14,12 +14,15 @@ import useOnyx from '@hooks/useOnyx';
 import useResponsiveLayout from '@hooks/useResponsiveLayout';
 import useSearchResults from '@hooks/useSearchResults';
 import useThemeStyles from '@hooks/useThemeStyles';
+import {clearDomainMemberError} from '@libs/actions/Domain';
+import {getLatestError} from '@libs/ErrorUtils';
 import {sortAlphabetically} from '@libs/OptionsListUtils';
 import {getDisplayNameOrDefault} from '@libs/PersonalDetailsUtils';
 import tokenizedSearch from '@libs/tokenizedSearch';
 import Navigation from '@navigation/Navigation';
 import CONST from '@src/CONST';
 import ONYXKEYS from '@src/ONYXKEYS';
+import {memberPendingActionSelector} from '@src/selectors/Domain';
 import type IconAsset from '@src/types/utils/IconAsset';
 
 type MemberOption = Omit<ListItem, 'accountID' | 'login'> & {
@@ -28,6 +31,9 @@ type MemberOption = Omit<ListItem, 'accountID' | 'login'> & {
 };
 
 type BaseDomainMembersPageProps = {
+    /** The accountID for the domain */
+    domainAccountID: number;
+
     /** The list of accountIDs to display */
     accountIDs: number[];
 
@@ -46,7 +52,7 @@ type BaseDomainMembersPageProps = {
     hederIcon: IconAsset;
 };
 
-function BaseDomainMembersPage({accountIDs, headerTitle, searchPlaceholder, headerContent, onSelectRow, hederIcon}: BaseDomainMembersPageProps) {
+function BaseDomainMembersPage({domainAccountID, accountIDs, headerTitle, searchPlaceholder, headerContent, onSelectRow, hederIcon}: BaseDomainMembersPageProps) {
     const {formatPhoneNumber, localeCompare} = useLocalize();
     const styles = useThemeStyles();
     const icons = useMemoizedLazyExpensifyIcons(['FallbackAvatar'] as const);
@@ -54,6 +60,15 @@ function BaseDomainMembersPage({accountIDs, headerTitle, searchPlaceholder, head
     const {shouldUseNarrowLayout} = useResponsiveLayout();
     const [personalDetails] = useOnyx(ONYXKEYS.PERSONAL_DETAILS_LIST, {canBeMissing: true});
 
+    const [domainErrors] = useOnyx(`${ONYXKEYS.COLLECTION.DOMAIN_ERRORS}${domainAccountID}`, {
+        canBeMissing: true,
+    });
+
+    const [domainPendingAction] = useOnyx(`${ONYXKEYS.COLLECTION.DOMAIN_PENDING_ACTIONS}${domainAccountID}`, {
+        canBeMissing: true,
+        selector: memberPendingActionSelector,
+    });
+
     const data: MemberOption[] = useMemo(() => {
         const options: MemberOption[] = [];
         for (const accountID of accountIDs) {
@@ -72,10 +87,12 @@ function BaseDomainMembersPage({accountIDs, headerTitle, searchPlaceholder, head
                         id: accountID,
                     },
                 ],
+                errors: getLatestError(domainErrors?.memberErrors?.[accountID]?.errors),
+                pendingAction: domainPendingAction?.[accountID],
             });
         }
         return options;
-    }, [accountIDs, personalDetails, formatPhoneNumber, icons.FallbackAvatar]);
+    }, [accountIDs, personalDetails, formatPhoneNumber, icons.FallbackAvatar, domainErrors?.memberErrors, domainPendingAction]);
 
     const filterMember = useCallback((option: MemberOption, searchQuery: string) => {
         const results = tokenizedSearch([option], searchQuery, (item) => [item.text ?? '', item.alternateText ?? '']);
@@ -140,6 +157,7 @@ function BaseDomainMembersPage({accountIDs, headerTitle, searchPlaceholder, head
                 addBottomSafeAreaPadding
                 customListHeader={getCustomListHeader()}
                 containerStyle={styles.flex1}
+                onDismissError={(item: MemberOption) => clearDomainMemberError(domainAccountID, item.accountID)}
             />
         </ScreenWrapper>
     );
diff --git a/src/pages/domain/Members/DomainMemberDetailsPage.tsx b/src/pages/domain/Members/DomainMemberDetailsPage.tsx
index 233e4d50df74..075025501d50 100644
--- a/src/pages/domain/Members/DomainMemberDetailsPage.tsx
+++ b/src/pages/domain/Members/DomainMemberDetailsPage.tsx
@@ -1,4 +1,4 @@
-import {adminAccountIDsSelector} from '@selectors/Domain';
+import {adminAccountIDsSelector, selectSecurityGroupIDsForAccount} from '@selectors/Domain';
 import {Str} from 'expensify-common';
 import React, {useState} from 'react';
 import {View} from 'react-native';
@@ -20,6 +20,7 @@ import useLocalize from '@hooks/useLocalize';
 import useOnyx from '@hooks/useOnyx';
 import useResponsiveLayout from '@hooks/useResponsiveLayout';
 import useThemeStyles from '@hooks/useThemeStyles';
+import {closeUserAccount} from '@libs/actions/Domain';
 import {getDisplayNameOrDefault, getPhoneNumber} from '@libs/PersonalDetailsUtils';
 import Navigation from '@navigation/Navigation';
 import type {PlatformStackScreenProps} from '@navigation/PlatformStackNavigation/types';
@@ -28,7 +29,7 @@ import CONST from '@src/CONST';
 import ONYXKEYS from '@src/ONYXKEYS';
 import ROUTES from '@src/ROUTES';
 import type SCREENS from '@src/SCREENS';
-import type {PersonalDetailsList} from '@src/types/onyx';
+import type {Domain, PersonalDetailsList} from '@src/types/onyx';
 import isLoadingOnyxValue from '@src/types/utils/isLoadingOnyxValue';
 
 type DomainMemberDetailsPageProps = PlatformStackScreenProps<SettingsNavigatorParamList, typeof SCREENS.DOMAIN.MEMBER_DETAILS>;
@@ -47,6 +48,12 @@ function DomainMemberDetailsPage({route}: DomainMemberDetailsPageProps) {
         selector: adminAccountIDsSelector,
     });
 
+    // eslint-disable-next-line rulesdir/no-inline-useOnyx-selector
+    const [securityGroupIDs] = useOnyx(`${ONYXKEYS.COLLECTION.DOMAIN}${domainAccountID}`, {
+        canBeMissing: true,
+        selector: (domain: OnyxEntry<Domain>) => selectSecurityGroupIDsForAccount(domain, accountID),
+    });
+
     // eslint-disable-next-line rulesdir/no-inline-useOnyx-selector
     const [personalDetails] = useOnyx(ONYXKEYS.PERSONAL_DETAILS_LIST, {
         canBeMissing: true,
@@ -64,11 +71,13 @@ function DomainMemberDetailsPage({route}: DomainMemberDetailsPageProps) {
     }
 
     const handleForceCloseAccount = () => {
+        closeUserAccount(domainAccountID, securityGroupIDs ?? [], accountID, true);
         setIsModalVisible(false);
         Navigation.dismissModal();
     };
 
     const handleSafeCloseAccount = () => {
+        closeUserAccount(domainAccountID, securityGroupIDs ?? [], accountID);
         setIsModalVisible(false);
         Navigation.dismissModal();
     };
diff --git a/src/pages/domain/Members/DomainMembersPage.tsx b/src/pages/domain/Members/DomainMembersPage.tsx
index 88a67622acae..3d80468c1f33 100644
--- a/src/pages/domain/Members/DomainMembersPage.tsx
+++ b/src/pages/domain/Members/DomainMembersPage.tsx
@@ -29,6 +29,7 @@ function DomainMembersPage({route}: DomainMembersPageProps) {
 
     return (
         <BaseDomainMembersPage
+            domainAccountID={domainAccountID}
             accountIDs={memberIDs ?? []}
             headerTitle={translate('domain.members.title')}
             searchPlaceholder={translate('domain.members.findMember')}
diff --git a/src/selectors/Domain.ts b/src/selectors/Domain.ts
index c67a0920e158..c48e80df4eda 100644
--- a/src/selectors/Domain.ts
+++ b/src/selectors/Domain.ts
@@ -1,7 +1,7 @@
 import {Str} from 'expensify-common';
 import type {OnyxEntry} from 'react-native-onyx';
 import ONYXKEYS from '@src/ONYXKEYS';
-import type {CardFeeds, Domain, SamlMetadata} from '@src/types/onyx';
+import type {CardFeeds, Domain, DomainPendingActions, SamlMetadata} from '@src/types/onyx';
 import getEmptyArray from '@src/types/utils/getEmptyArray';
 
 const domainMemberSamlSettingsSelector = (domainSettings: OnyxEntry<CardFeeds>) => domainSettings?.settings;
@@ -75,4 +75,44 @@ function selectMemberIDs(domain: Domain | undefined): number[] {
     return [...new Set(memberIDs)];
 }
 
-export {domainMemberSamlSettingsSelector, domainSamlSettingsStateSelector, domainNameSelector, metaIdentitySelector, adminAccountIDsSelector, technicalContactEmailSelector, selectMemberIDs};
+/**
+ * Gets all security group IDs for a given account ID.
+ * It searches through all security groups in the domain and returns the group IDs
+ * where the account ID appears in the 'shared' property.
+ *
+ * @param domain - The domain object from Onyx
+ * @param accountID - The account ID to search for
+ * @returns An array of security group IDs (as strings) that the account belongs to
+ */
+function selectSecurityGroupIDsForAccount(domain: Domain | undefined, accountID: number): number[] {
+    if (!domain) {
+        return [];
+    }
+
+    const accountIDStr = String(accountID);
+
+    return Object.entries(domain)
+        .filter(([key]) => key.startsWith(ONYXKEYS.COLLECTION.DOMAIN_SECURITY_GROUP))
+        .filter(([, value]) => {
+            const groupData = value as {shared?: Record<string, string>};
+            return groupData?.shared && accountIDStr in groupData.shared;
+        })
+        .map(([key]) => {
+            // Extract the group ID from the key: "expensify_securityGroup_<groupID>" -> "<groupID>"
+            return parseInt(key.replace(`${ONYXKEYS.COLLECTION.DOMAIN_SECURITY_GROUP}_`, ''), 10);
+        });
+}
+
+const memberPendingActionSelector = (pendingAction: OnyxEntry<DomainPendingActions>) => pendingAction?.members ?? {};
+
+export {
+    domainMemberSamlSettingsSelector,
+    domainSamlSettingsStateSelector,
+    domainNameSelector,
+    metaIdentitySelector,
+    adminAccountIDsSelector,
+    technicalContactEmailSelector,
+    selectMemberIDs,
+    selectSecurityGroupIDsForAccount,
+    memberPendingActionSelector,
+};
diff --git a/src/types/onyx/DomainErrors.ts b/src/types/onyx/DomainErrors.ts
index f4d4f59f7f79..da3d02b76ddf 100644
--- a/src/types/onyx/DomainErrors.ts
+++ b/src/types/onyx/DomainErrors.ts
@@ -8,6 +8,19 @@ type DomainErrors = {
      * Errors related to the technical contact email
      */
     technicalContactEmailErrors?: OnyxCommon.Errors;
+
+    /**
+     * Errors for members
+     */
+    memberErrors?: Record<
+        number,
+        {
+            /**
+             * General member errors
+             */
+            errors: OnyxCommon.Errors;
+        }
+    >;
 };
 
 export default DomainErrors;
diff --git a/src/types/onyx/DomainPendingActions.ts b/src/types/onyx/DomainPendingActions.ts
index fd5edbab9f0a..769dd97db9de 100644
--- a/src/types/onyx/DomainPendingActions.ts
+++ b/src/types/onyx/DomainPendingActions.ts
@@ -8,6 +8,11 @@ type DomainPendingAction = {
      * Pending action for the technical contact email
      */
     technicalContactEmail?: OnyxCommon.PendingAction;
+
+    /**
+     * Pending action for members
+     */
+    members?: Record<number, OnyxCommon.PendingAction>;
 };
 
 export default DomainPendingAction;
diff --git a/src/types/onyx/index.ts b/src/types/onyx/index.ts
index 73bcb0886ddd..d76177dc06df 100644
--- a/src/types/onyx/index.ts
+++ b/src/types/onyx/index.ts
@@ -31,7 +31,7 @@ import type {OutstandingReportsByPolicyIDDerivedValue, ReportAttributesDerivedVa
 import type DismissedProductTraining from './DismissedProductTraining';
 import type DismissedReferralBanners from './DismissedReferralBanners';
 import type Domain from './Domain';
-import type {SamlMetadata, SecurityGroup as DomainSecurityGroup} from './Domain';
+import type {SecurityGroup as DomainSecurityGroup, SamlMetadata} from './Domain';
 import type DomainErrors from './DomainErrors';
 import type DomainPendingActions from './DomainPendingActions';
 import type Download from './Download';
@@ -305,5 +305,5 @@ export type {
     SamlMetadata,
     DomainErrors,
     DomainPendingActions,
-    DomainSecurityGroup
+    DomainSecurityGroup,
 };

From 23a4ee905ef33118bd8c97b861b117fc49ec95f2 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jan=20Musia=C5=82?= <jan.musial@swmansion.com>
Date: Tue, 13 Jan 2026 15:15:28 +0100
Subject: [PATCH 05/50] Cleanup after merge

---
 src/ONYXKEYS.ts                               |   3 -
 src/components/DecisionModal.tsx              |   5 +-
 src/libs/actions/Domain.ts                    |  10 +-
 .../domain/Admins/DomainAdminDetailsPage.tsx  |   4 +-
 src/pages/domain/BaseDomainMembersPage.tsx    |  87 +++++++--------
 .../domain/Members/DomainMembersPage.tsx      |   6 +-
 src/selectors/Domain.ts                       |   2 -
 src/types/onyx/Domain.ts                      |   2 +-
 src/types/onyx/index.ts                       |   2 +-
 tests/unit/DomainSelectorsTest.ts             | 101 ------------------
 10 files changed, 53 insertions(+), 169 deletions(-)

diff --git a/src/ONYXKEYS.ts b/src/ONYXKEYS.ts
index e5f2dccafa5f..c152ad1f3160 100755
--- a/src/ONYXKEYS.ts
+++ b/src/ONYXKEYS.ts
@@ -761,9 +761,6 @@ const ONYXKEYS = {
         /** SAML login metadata for a domain */
         SAML_METADATA: 'saml_metadata_',
 
-        /** Stores domain admin account ID */
-        EXPENSIFY_ADMIN_ACCESS_PREFIX: 'expensify_adminPermissions_',
-
         /** Stores domain security group */
         DOMAIN_SECURITY_GROUP: 'expensify_securityGroup',
 
diff --git a/src/components/DecisionModal.tsx b/src/components/DecisionModal.tsx
index df30e689077c..c249a4451070 100644
--- a/src/components/DecisionModal.tsx
+++ b/src/components/DecisionModal.tsx
@@ -6,7 +6,6 @@ import Button from './Button';
 import Header from './Header';
 import Modal from './Modal';
 import RenderHTML from './RenderHTML';
-import Text from './Text';
 
 type DecisionModalProps = {
     /** Title describing purpose of modal */
@@ -81,9 +80,7 @@ function DecisionModal({
                             containerStyles={styles.alignItemsCenter}
                         />
                     </View>
-                    <Text>
-                        <RenderHTML html={prompt} />
-                    </Text>
+                    <RenderHTML html={prompt} />
                 </View>
                 {!!firstOptionText && (
                     <Button
diff --git a/src/libs/actions/Domain.ts b/src/libs/actions/Domain.ts
index b6abde6a2fbd..0d96a650ac8b 100644
--- a/src/libs/actions/Domain.ts
+++ b/src/libs/actions/Domain.ts
@@ -7,6 +7,7 @@ import {getMicroSecondOnyxErrorWithTranslationKey} from '@libs/ErrorUtils';
 import {getAuthToken} from '@libs/Network/NetworkStore';
 import CONST from '@src/CONST';
 import ONYXKEYS from '@src/ONYXKEYS';
+import {DomainSecurityGroup} from '@src/types/onyx';
 import type PrefixedRecord from '@src/types/utils/PrefixedRecord';
 import type {ScimTokenWithState} from './ScimToken/ScimTokenUtils';
 import {ScimTokenState} from './ScimToken/ScimTokenUtils';
@@ -700,10 +701,9 @@ function revokeDomainAdminAccess(domainAccountID: number, accountID: number) {
 
 /** Sends a request to remove user from a domain and close their account */
 function closeUserAccount(domainAccountID: number, securityGroupIDs: number[], accountID: number, force = false) {
-    const optimisticValue: OnyxMergeInput<`domain_${string}`> = {};
+    const optimisticValue: PrefixedRecord<typeof CONST.DOMAIN.DOMAIN_SECURITY_GROUP_PREFIX, Partial<DomainSecurityGroup>> = {};
     for (const groupID of securityGroupIDs) {
-        // @ts-expect-error
-        optimisticValue[`${ONYXKEYS.COLLECTION.DOMAIN_SECURITY_GROUP}_${groupID}`] = {
+        optimisticValue[`${CONST.DOMAIN.DOMAIN_SECURITY_GROUP_PREFIX}${groupID}`] = {
             shared: {
                 [accountID]: null,
             },
@@ -773,6 +773,10 @@ function clearDomainMemberError(domainAccountID: number, accountID: number) {
             [accountID]: null,
         },
     });
+
+    Onyx.merge(`${ONYXKEYS.COLLECTION.DOMAIN_PENDING_ACTIONS}${domainAccountID}`, {
+        members: {[accountID]: null},
+    });
 }
 
 export {
diff --git a/src/pages/domain/Admins/DomainAdminDetailsPage.tsx b/src/pages/domain/Admins/DomainAdminDetailsPage.tsx
index 0d9eb0b582a4..91df4a2580ac 100644
--- a/src/pages/domain/Admins/DomainAdminDetailsPage.tsx
+++ b/src/pages/domain/Admins/DomainAdminDetailsPage.tsx
@@ -85,7 +85,7 @@ function DomainAdminDetailsPage({route}: DomainAdminDetailsPageProps) {
         <DomainNotFoundPageWrapper domainAccountID={domainAccountID}>
             <ScreenWrapper
                 enableEdgeToEdgeBottomSafeAreaPadding
-                testID={DomainAdminDetailsPage.displayName}
+                testID="DomainAdminDetailsPage"
             >
                 <HeaderWithBackButton title={displayName} />
                 <ScrollView addBottomSafeAreaPadding>
@@ -144,6 +144,4 @@ function DomainAdminDetailsPage({route}: DomainAdminDetailsPageProps) {
     );
 }
 
-DomainAdminDetailsPage.displayName = 'DomainAdminDetailsPage';
-
 export default DomainAdminDetailsPage;
diff --git a/src/pages/domain/BaseDomainMembersPage.tsx b/src/pages/domain/BaseDomainMembersPage.tsx
index b8e7a4fae725..8feb4bf27b88 100644
--- a/src/pages/domain/BaseDomainMembersPage.tsx
+++ b/src/pages/domain/BaseDomainMembersPage.tsx
@@ -14,7 +14,6 @@ import useOnyx from '@hooks/useOnyx';
 import useResponsiveLayout from '@hooks/useResponsiveLayout';
 import useSearchResults from '@hooks/useSearchResults';
 import useThemeStyles from '@hooks/useThemeStyles';
-import {clearDomainMemberError} from '@libs/actions/Domain';
 import {getLatestError} from '@libs/ErrorUtils';
 import {sortAlphabetically} from '@libs/OptionsListUtils';
 import {getDisplayNameOrDefault} from '@libs/PersonalDetailsUtils';
@@ -22,7 +21,6 @@ import tokenizedSearch from '@libs/tokenizedSearch';
 import Navigation from '@navigation/Navigation';
 import CONST from '@src/CONST';
 import ONYXKEYS from '@src/ONYXKEYS';
-import {memberPendingActionSelector} from '@src/selectors/Domain';
 import type {Errors, PendingAction} from '@src/types/onyx/OnyxCommon';
 import type IconAsset from '@src/types/utils/IconAsset';
 import DomainNotFoundPageWrapper from './DomainNotFoundPageWrapper';
@@ -80,19 +78,9 @@ function BaseDomainMembersPage({
 }: BaseDomainMembersPageProps) {
     const {formatPhoneNumber, localeCompare} = useLocalize();
     const styles = useThemeStyles();
-    const icons = useMemoizedLazyExpensifyIcons(['FallbackAvatar'] as const);
-
     const {shouldUseNarrowLayout} = useResponsiveLayout();
     const [personalDetails] = useOnyx(ONYXKEYS.PERSONAL_DETAILS_LIST, {canBeMissing: true});
-
-    const [domainErrors] = useOnyx(`${ONYXKEYS.COLLECTION.DOMAIN_ERRORS}${domainAccountID}`, {
-        canBeMissing: true,
-    });
-
-    const [domainPendingAction] = useOnyx(`${ONYXKEYS.COLLECTION.DOMAIN_PENDING_ACTIONS}${domainAccountID}`, {
-        canBeMissing: true,
-        selector: memberPendingActionSelector,
-    });
+    const icons = useMemoizedLazyExpensifyIcons(['FallbackAvatar']);
 
     const data: MemberOption[] = accountIDs.map((accountID) => {
         const details = personalDetails?.[accountID];
@@ -115,8 +103,8 @@ function BaseDomainMembersPage({
                 },
             ],
             rightElement: getCustomRightElement?.(accountID),
-            errors: getLatestError(customProps?.errors) ?? getLatestError(domainErrors?.memberErrors?.[accountID]?.errors),
-            pendingAction: customProps?.pendingAction ?? domainPendingAction?.[accountID],
+            errors: getLatestError(customProps?.errors),
+            pendingAction: customProps?.pendingAction,
             isInteractive: !isPendingActionDelete,
             isDisabled: isPendingActionDelete,
         };
@@ -127,7 +115,7 @@ function BaseDomainMembersPage({
         return results.length > 0;
     };
 
-    const sortMembers = useCallback((options: MemberOption[]) => sortAlphabetically(options, 'text', localeCompare), [localeCompare]);
+    const sortMembers = (options: MemberOption[]) => sortAlphabetically(options, 'text', localeCompare);
 
     const [inputValue, setInputValue, filteredData] = useSearchResults(data, filterMember, sortMembers);
 
@@ -154,39 +142,42 @@ function BaseDomainMembersPage({
         ) : null;
 
     return (
-        <ScreenWrapper
-            enableEdgeToEdgeBottomSafeAreaPadding
-            shouldEnableMaxHeight
-            shouldShowOfflineIndicatorInWideScreen
-            testID={BaseDomainMembersPage.displayName}
-        >
-            <HeaderWithBackButton
-                title={headerTitle}
-                onBackButtonPress={Navigation.popToSidebar}
-                icon={hederIcon}
-                shouldShowBackButton={shouldUseNarrowLayout}
+        <DomainNotFoundPageWrapper domainAccountID={domainAccountID}>
+            <ScreenWrapper
+                enableEdgeToEdgeBottomSafeAreaPadding
+                shouldEnableMaxHeight
+                shouldShowOfflineIndicatorInWideScreen
+                testID={BaseDomainMembersPage.displayName}
             >
-                {!shouldUseNarrowLayout && !!headerContent && <View style={[styles.flexRow, styles.gap2]}>{headerContent}</View>}
-            </HeaderWithBackButton>
-
-            {shouldUseNarrowLayout && !!headerContent && <View style={[styles.pl5, styles.pr5, styles.flexRow, styles.gap2]}>{headerContent}</View>}
-
-            <SelectionList
-                sections={[{data: filteredData}]}
-                shouldShowRightCaret
-                canSelectMultiple={false}
-                listHeaderContent={listHeaderContent}
-                listHeaderWrapperStyle={[styles.ph9, styles.pv3, styles.pb5]}
-                ListItem={TableListItem}
-                onSelectRow={onSelectRow}
-                shouldShowListEmptyContent={false}
-                listItemTitleContainerStyles={shouldUseNarrowLayout ? undefined : [styles.pr3]}
-                showScrollIndicator={false}
-                addBottomSafeAreaPadding
-                customListHeader={getCustomListHeader()}
-                containerStyle={[styles.flex1]}
-            />
-        </ScreenWrapper>
+                <HeaderWithBackButton
+                    title={headerTitle}
+                    onBackButtonPress={Navigation.popToSidebar}
+                    icon={headerIcon}
+                    shouldShowBackButton={shouldUseNarrowLayout}
+                >
+                    {!shouldUseNarrowLayout && !!headerContent && <View style={[styles.flexRow, styles.gap2]}>{headerContent}</View>}
+                </HeaderWithBackButton>
+
+                {shouldUseNarrowLayout && !!headerContent && <View style={[styles.pl5, styles.pr5, styles.flexRow, styles.gap2]}>{headerContent}</View>}
+
+                <SelectionList
+                    sections={[{data: filteredData}]}
+                    shouldShowRightCaret
+                    canSelectMultiple={false}
+                    listHeaderContent={listHeaderContent}
+                    listHeaderWrapperStyle={[styles.ph9, styles.pv3, styles.pb5]}
+                    ListItem={TableListItem}
+                    onSelectRow={onSelectRow}
+                    onDismissError={onDismissError}
+                    shouldShowListEmptyContent={false}
+                    listItemTitleContainerStyles={shouldUseNarrowLayout ? undefined : styles.pr3}
+                    showScrollIndicator={false}
+                    addBottomSafeAreaPadding
+                    customListHeader={getCustomListHeader()}
+                    containerStyle={styles.flex1}
+                />
+            </ScreenWrapper>
+        </DomainNotFoundPageWrapper>
     );
 }
 
diff --git a/src/pages/domain/Members/DomainMembersPage.tsx b/src/pages/domain/Members/DomainMembersPage.tsx
index 5aa1a238ebc3..01ecedbe03d1 100644
--- a/src/pages/domain/Members/DomainMembersPage.tsx
+++ b/src/pages/domain/Members/DomainMembersPage.tsx
@@ -1,6 +1,6 @@
 import {memberAccountIDsSelector} from '@selectors/Domain';
 import React from 'react';
-import {useMemoizedLazyIllustrations} from '@hooks/useLazyAsset';
+import {useMemoizedLazyExpensifyIcons} from '@hooks/useLazyAsset';
 import useLocalize from '@hooks/useLocalize';
 import useOnyx from '@hooks/useOnyx';
 import Navigation from '@libs/Navigation/Navigation';
@@ -17,7 +17,7 @@ type DomainMembersPageProps = PlatformStackScreenProps<DomainSplitNavigatorParam
 function DomainMembersPage({route}: DomainMembersPageProps) {
     const {domainAccountID} = route.params;
     const {translate} = useLocalize();
-    const illustrations = useMemoizedLazyIllustrations(['Profile']);
+    const icons = useMemoizedLazyExpensifyIcons(['Profile']);
 
     const [memberIDs] = useOnyx(`${ONYXKEYS.COLLECTION.DOMAIN}${domainAccountID}`, {
         canBeMissing: true,
@@ -28,10 +28,10 @@ function DomainMembersPage({route}: DomainMembersPageProps) {
         <BaseDomainMembersPage
             domainAccountID={domainAccountID}
             accountIDs={memberIDs ?? []}
+            headerIcon={icons.Profile}
             headerTitle={translate('domain.members.title')}
             searchPlaceholder={translate('domain.members.findMember')}
             onSelectRow={(item: MemberOption) => Navigation.navigate(ROUTES.DOMAIN_MEMBER_DETAILS.getRoute(domainAccountID, item.accountID))}
-            shouldShowNotFoundView={shouldShowNotFoundView}
         />
     );
 }
diff --git a/src/selectors/Domain.ts b/src/selectors/Domain.ts
index 96624bc55f8d..592b8d2b8347 100644
--- a/src/selectors/Domain.ts
+++ b/src/selectors/Domain.ts
@@ -106,7 +106,5 @@ export {
     domainEmailSelector,
     adminPendingActionSelector,
     technicalContactSettingsSelector,
-    selectMemberIDs,
-    selectSecurityGroupIDsForAccount,
     memberPendingActionSelector,
 };
diff --git a/src/types/onyx/Domain.ts b/src/types/onyx/Domain.ts
index 163b070e814c..b89c6efe56cc 100644
--- a/src/types/onyx/Domain.ts
+++ b/src/types/onyx/Domain.ts
@@ -98,7 +98,7 @@ type DomainSecurityGroup = SecurityGroup & {
      * A map of member account IDs
      * Key: The accountID of the member
      */
-    shared: Record<string, 'read'>;
+    shared: Record<string, 'read' | null>;
 };
 
 export {type SamlMetadata, type DomainSecurityGroup};
diff --git a/src/types/onyx/index.ts b/src/types/onyx/index.ts
index 9b056b114bfc..a6fd3fbfcdae 100644
--- a/src/types/onyx/index.ts
+++ b/src/types/onyx/index.ts
@@ -32,7 +32,7 @@ import type {OutstandingReportsByPolicyIDDerivedValue, ReportAttributesDerivedVa
 import type DismissedProductTraining from './DismissedProductTraining';
 import type DismissedReferralBanners from './DismissedReferralBanners';
 import type Domain from './Domain';
-import type {SecurityGroup as DomainSecurityGroup, SamlMetadata} from './Domain';
+import type {DomainSecurityGroup, SamlMetadata} from './Domain';
 import type DomainErrors from './DomainErrors';
 import type DomainPendingActions from './DomainPendingActions';
 import type Download from './Download';
diff --git a/tests/unit/DomainSelectorsTest.ts b/tests/unit/DomainSelectorsTest.ts
index 2d9d6e0eecb6..a56fadeeaa91 100644
--- a/tests/unit/DomainSelectorsTest.ts
+++ b/tests/unit/DomainSelectorsTest.ts
@@ -170,105 +170,4 @@ describe('domainSelectors', () => {
             });
         });
     });
-    describe('selectMemberIDs', () => {
-        it('Should return an empty array if the domain object is undefined', () => {
-            expect(memberAccountIDsSelector(undefined)).toEqual([]);
-        });
-
-        it('Should return an empty array if the domain object is empty', () => {
-            const domain = {} as OnyxEntry<Domain>;
-            expect(memberAccountIDsSelector(domain)).toEqual([]);
-        });
-
-        it('Should return member IDs when keys start with the security group prefix', () => {
-            const domain = {
-                [`${CONST.DOMAIN.DOMAIN_SECURITY_GROUP_PREFIX}1`]: {
-                    shared: {
-                        // eslint-disable-next-line @typescript-eslint/naming-convention
-                        '100': 'value',
-                        // eslint-disable-next-line @typescript-eslint/naming-convention
-                        '200': 'value',
-                    },
-                },
-                [`${CONST.DOMAIN.DOMAIN_SECURITY_GROUP_PREFIX}2`]: {
-                    shared: {
-                        // eslint-disable-next-line @typescript-eslint/naming-convention
-                        '300': 'value',
-                    },
-                },
-            } as unknown as OnyxEntry<Domain>;
-
-            expect(memberAccountIDsSelector(domain).sort()).toEqual([100, 200, 300]);
-        });
-
-        it('Should return unique member IDs if they appear in multiple security groups', () => {
-            const domain = {
-                [`${CONST.DOMAIN.DOMAIN_SECURITY_GROUP_PREFIX}1`]: {
-                    shared: {
-                        // eslint-disable-next-line @typescript-eslint/naming-convention
-                        '123': 'value',
-                    },
-                },
-                [`${CONST.DOMAIN.DOMAIN_SECURITY_GROUP_PREFIX}2`]: {
-                    shared: {
-                        // eslint-disable-next-line @typescript-eslint/naming-convention
-                        '123': 'value',
-                    },
-                },
-            } as unknown as OnyxEntry<Domain>;
-
-            expect(memberAccountIDsSelector(domain)).toEqual([123]);
-        });
-
-        it('Should ignore keys that do not start with the security group prefix', () => {
-            const domain = {
-                [`${CONST.DOMAIN.DOMAIN_SECURITY_GROUP_PREFIX}1`]: {
-                    shared: {
-                        // eslint-disable-next-line @typescript-eslint/naming-convention
-                        '456': 'value',
-                    },
-                },
-                someOtherKey: {
-                    shared: {
-                        // eslint-disable-next-line @typescript-eslint/naming-convention
-                        '789': 'value',
-                    },
-                },
-            } as unknown as OnyxEntry<Domain>;
-
-            expect(memberAccountIDsSelector(domain)).toEqual([456]);
-        });
-
-        it('Should ignore groups that do not have a shared property', () => {
-            const domain = {
-                [`${CONST.DOMAIN.DOMAIN_SECURITY_GROUP_PREFIX}1`]: {},
-                [`${CONST.DOMAIN.DOMAIN_SECURITY_GROUP_PREFIX}2`]: {shared: null},
-                [`${CONST.DOMAIN.DOMAIN_SECURITY_GROUP_PREFIX}3`]: {
-                    shared: {
-                        // eslint-disable-next-line @typescript-eslint/naming-convention
-                        '111': 'value',
-                    },
-                },
-            } as unknown as OnyxEntry<Domain>;
-
-            expect(memberAccountIDsSelector(domain)).toEqual([111]);
-        });
-
-        it('Should filter out non-numeric shared keys', () => {
-            const domain = {
-                [`${CONST.DOMAIN.DOMAIN_SECURITY_GROUP_PREFIX}1`]: {
-                    shared: {
-                        // eslint-disable-next-line @typescript-eslint/naming-convention
-                        '123': 'value',
-                        // eslint-disable-next-line @typescript-eslint/naming-convention
-                        'not-a-number': 'value',
-                        // eslint-disable-next-line @typescript-eslint/naming-convention
-                        '456': 'value',
-                    },
-                },
-            } as unknown as OnyxEntry<Domain>;
-
-            expect(memberAccountIDsSelector(domain).sort()).toEqual([123, 456]);
-        });
-    });
 });

From 58b92f7d4f8a56bd0dcc3988223ec94e113520b7 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jan=20Musia=C5=82?= <jan.musial@swmansion.com>
Date: Fri, 16 Jan 2026 12:19:58 +0100
Subject: [PATCH 06/50] add a second modal confirm modal

---
 src/languages/en.ts                           |  1 +
 .../parameters/DeleteDomainMemberParams.ts    |  7 ++
 src/libs/API/parameters/index.ts              |  1 +
 src/libs/API/types.ts                         |  2 +
 src/libs/actions/Domain.ts                    | 72 +++++++++----------
 .../Members/DomainMemberDetailsPage.tsx       | 46 ++++++++----
 .../domain/Members/DomainMembersPage.tsx      |  2 -
 src/selectors/Domain.ts                       | 29 ++++++++
 8 files changed, 105 insertions(+), 55 deletions(-)
 create mode 100644 src/libs/API/parameters/DeleteDomainMemberParams.ts

diff --git a/src/languages/en.ts b/src/languages/en.ts
index 221e104da2e4..ab8d835c9295 100755
--- a/src/languages/en.ts
+++ b/src/languages/en.ts
@@ -7924,6 +7924,7 @@ const translations = {
             title: 'Members',
             findMember: 'Find member',
             closeAccount: 'Close account',
+            closeAccountPrompt: 'Are you sure? This action is permanent.',
             forceCloseAccount: 'Force close account',
             safeCloseAccount: 'Close account safely',
             closeAccountInfo:
diff --git a/src/libs/API/parameters/DeleteDomainMemberParams.ts b/src/libs/API/parameters/DeleteDomainMemberParams.ts
new file mode 100644
index 000000000000..d33c94769bd7
--- /dev/null
+++ b/src/libs/API/parameters/DeleteDomainMemberParams.ts
@@ -0,0 +1,7 @@
+type DeleteDomainMemberParams = {
+    targetEmail: string;
+    domain: string;
+    overrideProcessingReports: boolean;
+};
+
+export default DeleteDomainMemberParams;
diff --git a/src/libs/API/parameters/index.ts b/src/libs/API/parameters/index.ts
index 3bbb70cda9ed..2fc3f86c9f2b 100644
--- a/src/libs/API/parameters/index.ts
+++ b/src/libs/API/parameters/index.ts
@@ -459,4 +459,5 @@ export type {default as SetPolicyRequireCompanyCardsEnabledParams} from './SetPo
 export type {default as SetTechnicalContactEmailParams} from './SetTechnicalContactEmailParams';
 export type {default as ToggleConsolidatedDomainBillingParams} from './ToggleConsolidatedDomainBillingParams';
 export type {default as RemoveDomainAdminParams} from './RemoveDomainAdminParams';
+export type {default as DeleteDomainMemberParams} from './DeleteDomainMemberParams';
 export type {default as DeleteDomainParams} from './DeleteDomainParams';
diff --git a/src/libs/API/types.ts b/src/libs/API/types.ts
index 0a6e0ac8853c..e0c852c23621 100644
--- a/src/libs/API/types.ts
+++ b/src/libs/API/types.ts
@@ -536,6 +536,7 @@ const WRITE_COMMANDS = {
     ADD_DOMAIN_ADMIN: 'AddDomainAdmin',
     REMOVE_DOMAIN_ADMIN: 'RemoveDomainAdmin',
     DELETE_DOMAIN: 'DeleteDomain',
+    DELETE_DOMAIN_MEMBER: 'DeleteDomainMember',
 } as const;
 
 type WriteCommand = ValueOf<typeof WRITE_COMMANDS>;
@@ -1091,6 +1092,7 @@ type WriteCommandParameters = {
     [WRITE_COMMANDS.SET_TECHNICAL_CONTACT_EMAIL]: Parameters.SetTechnicalContactEmailParams;
     [WRITE_COMMANDS.TOGGLE_CONSOLIDATED_DOMAIN_BILLING]: Parameters.ToggleConsolidatedDomainBillingParams;
     [WRITE_COMMANDS.REMOVE_DOMAIN_ADMIN]: Parameters.RemoveDomainAdminParams;
+    [WRITE_COMMANDS.DELETE_DOMAIN_MEMBER]: Parameters.DeleteDomainMemberParams;
     [WRITE_COMMANDS.DELETE_DOMAIN]: Parameters.DeleteDomainParams;
     [WRITE_COMMANDS.ADD_DOMAIN_ADMIN]: Parameters.AddAdminToDomainParams;
 };
diff --git a/src/libs/actions/Domain.ts b/src/libs/actions/Domain.ts
index 06a28e1cc344..5692b09513d0 100644
--- a/src/libs/actions/Domain.ts
+++ b/src/libs/actions/Domain.ts
@@ -1,12 +1,19 @@
 import Onyx from 'react-native-onyx';
-import type {OnyxMergeInput, OnyxUpdate} from 'react-native-onyx';
+import type {OnyxUpdate} from 'react-native-onyx';
 import * as API from '@libs/API';
-import type {AddAdminToDomainParams, DeleteDomainParams, RemoveDomainAdminParams, SetTechnicalContactEmailParams, ToggleConsolidatedDomainBillingParams} from '@libs/API/parameters';
+import type {
+    AddAdminToDomainParams,
+    DeleteDomainParams,
+    RemoveDomainAdminParams,
+    DeleteDomainMemberParams,
+    SetTechnicalContactEmailParams,
+    ToggleConsolidatedDomainBillingParams,
+} from '@libs/API/parameters';
 import {READ_COMMANDS, SIDE_EFFECT_REQUEST_COMMANDS, WRITE_COMMANDS} from '@libs/API/types';
 import {getMicroSecondOnyxErrorWithTranslationKey} from '@libs/ErrorUtils';
 import CONST from '@src/CONST';
 import ONYXKEYS from '@src/ONYXKEYS';
-import type {Domain, DomainSecurityGroup} from '@src/types/onyx';
+import type {Domain} from '@src/types/onyx';
 import type PrefixedRecord from '@src/types/utils/PrefixedRecord';
 import type {ScimTokenWithState} from './ScimToken/ScimTokenUtils';
 import {ScimTokenState} from './ScimToken/ScimTokenUtils';
@@ -779,22 +786,22 @@ function clearDomainErrors(domainAccountID: number) {
 }
 
 /** Sends a request to remove user from a domain and close their account */
-function closeUserAccount(domainAccountID: number, securityGroupIDs: number[], accountID: number, force = false) {
-    const optimisticValue: PrefixedRecord<typeof CONST.DOMAIN.DOMAIN_SECURITY_GROUP_PREFIX, Partial<DomainSecurityGroup>> = {};
-    for (const groupID of securityGroupIDs) {
-        optimisticValue[`${CONST.DOMAIN.DOMAIN_SECURITY_GROUP_PREFIX}${groupID}`] = {
-            shared: {
-                [accountID]: null,
-            },
-        };
-    }
+function closeUserAccount(domainAccountID: number, domain: string, accountID: number, targetEmail: string, overrideProcessingReports = false) {
+    // const optimisticValue: PrefixedRecord<typeof CONST.DOMAIN.DOMAIN_SECURITY_GROUP_PREFIX, Partial<DomainSecurityGroup>> = {};
+    // for (const groupID of securityGroupIDs) {
+    //     optimisticValue[`${CONST.DOMAIN.DOMAIN_SECURITY_GROUP_PREFIX}${groupID}`] = {
+    //         shared: {
+    //             [accountID]: null,
+    //         },
+    //     };
+    // }
 
     const optimisticData: OnyxUpdate[] = [
-        {
-            onyxMethod: Onyx.METHOD.MERGE,
-            key: `${ONYXKEYS.COLLECTION.DOMAIN}${domainAccountID}`,
-            value: optimisticValue,
-        },
+        // {
+        //     onyxMethod: Onyx.METHOD.MERGE,
+        //     key: `${ONYXKEYS.COLLECTION.DOMAIN}${domainAccountID}`,
+        //     value: optimisticValue,
+        // },
         {
             onyxMethod: Onyx.METHOD.MERGE,
             key: `${ONYXKEYS.COLLECTION.DOMAIN_PENDING_ACTIONS}${domainAccountID}`,
@@ -813,7 +820,7 @@ function closeUserAccount(domainAccountID: number, securityGroupIDs: number[], a
             },
         },
     ];
-    const failureData = [
+    const failureData: OnyxUpdate[] = [
         {
             onyxMethod: Onyx.METHOD.MERGE,
             key: `${ONYXKEYS.COLLECTION.DOMAIN_PENDING_ACTIONS}${domainAccountID}`,
@@ -821,29 +828,14 @@ function closeUserAccount(domainAccountID: number, securityGroupIDs: number[], a
                 members: {[accountID]: null},
             },
         },
-        // DEV
-        {
-            onyxMethod: Onyx.METHOD.MERGE,
-            key: `${ONYXKEYS.COLLECTION.DOMAIN_ERRORS}${domainAccountID}`,
-            value: {
-                memberErrors: {
-                    [accountID]: {errors: {[Date.now()]: 'Unable to remove this user. Please try again.'}},
-                },
-            },
-        },
-        {
-            onyxMethod: Onyx.METHOD.MERGE,
-            key: `${ONYXKEYS.COLLECTION.DOMAIN}${domainAccountID}`,
-            value: {
-                expensify_securityGroup_1: {
-                    shared: {
-                        [accountID]: 'read',
-                    },
-                },
-            },
-        },
     ];
-    API.write('', {force}, {optimisticData, successData, failureData});
+    const parameters: DeleteDomainMemberParams = {
+        domain,
+        targetEmail,
+        overrideProcessingReports,
+    };
+
+    API.write(WRITE_COMMANDS.DELETE_DOMAIN_MEMBER, parameters, {optimisticData, successData, failureData});
 }
 
 function clearDomainMemberError(domainAccountID: number, accountID: number) {
diff --git a/src/pages/domain/Members/DomainMemberDetailsPage.tsx b/src/pages/domain/Members/DomainMemberDetailsPage.tsx
index 075025501d50..dc7b49224d9f 100644
--- a/src/pages/domain/Members/DomainMemberDetailsPage.tsx
+++ b/src/pages/domain/Members/DomainMemberDetailsPage.tsx
@@ -1,4 +1,4 @@
-import {adminAccountIDsSelector, selectSecurityGroupIDsForAccount} from '@selectors/Domain';
+import {adminAccountIDsSelector, domainNameSelector} from '@selectors/Domain';
 import {Str} from 'expensify-common';
 import React, {useState} from 'react';
 import {View} from 'react-native';
@@ -11,10 +11,12 @@ import FullScreenLoadingIndicator from '@components/FullscreenLoadingIndicator';
 import HeaderWithBackButton from '@components/HeaderWithBackButton';
 import MenuItem from '@components/MenuItem';
 import MenuItemWithTopDescription from '@components/MenuItemWithTopDescription';
+import {ModalActions} from '@components/Modal/Global/ModalContext';
 import OfflineWithFeedback from '@components/OfflineWithFeedback';
 import ScreenWrapper from '@components/ScreenWrapper';
 import ScrollView from '@components/ScrollView';
 import Text from '@components/Text';
+import useConfirmModal from '@hooks/useConfirmModal';
 import {useMemoizedLazyExpensifyIcons} from '@hooks/useLazyAsset';
 import useLocalize from '@hooks/useLocalize';
 import useOnyx from '@hooks/useOnyx';
@@ -29,7 +31,7 @@ import CONST from '@src/CONST';
 import ONYXKEYS from '@src/ONYXKEYS';
 import ROUTES from '@src/ROUTES';
 import type SCREENS from '@src/SCREENS';
-import type {Domain, PersonalDetailsList} from '@src/types/onyx';
+import type {PersonalDetailsList} from '@src/types/onyx';
 import isLoadingOnyxValue from '@src/types/utils/isLoadingOnyxValue';
 
 type DomainMemberDetailsPageProps = PlatformStackScreenProps<SettingsNavigatorParamList, typeof SCREENS.DOMAIN.MEMBER_DETAILS>;
@@ -42,17 +44,19 @@ function DomainMemberDetailsPage({route}: DomainMemberDetailsPageProps) {
     const [isModalVisible, setIsModalVisible] = useState(false);
     // eslint-disable-next-line rulesdir/prefer-shouldUseNarrowLayout-instead-of-isSmallScreenWidth
     const {isSmallScreenWidth} = useResponsiveLayout();
+    const {showConfirmModal} = useConfirmModal();
 
-    const [adminAccountIDs, domainMetadata] = useOnyx(`${ONYXKEYS.COLLECTION.DOMAIN}${domainAccountID}`, {
+    const [_, domainMetadata] = useOnyx(`${ONYXKEYS.COLLECTION.DOMAIN}${domainAccountID}`, {
         canBeMissing: true,
         selector: adminAccountIDsSelector,
     });
 
-    // eslint-disable-next-line rulesdir/no-inline-useOnyx-selector
-    const [securityGroupIDs] = useOnyx(`${ONYXKEYS.COLLECTION.DOMAIN}${domainAccountID}`, {
-        canBeMissing: true,
-        selector: (domain: OnyxEntry<Domain>) => selectSecurityGroupIDsForAccount(domain, accountID),
-    });
+    // Commenting out data needed for optimistic updates for now
+    // // eslint-disable-next-line rulesdir/no-inline-useOnyx-selector
+    // const [securityGroupIDs] = useOnyx(`${ONYXKEYS.COLLECTION.DOMAIN}${domainAccountID}`, {
+    //     canBeMissing: true,
+    //     selector: (domain: OnyxEntry<Domain>) => selectSecurityGroupIDsForAccount(domain, accountID),
+    // });
 
     // eslint-disable-next-line rulesdir/no-inline-useOnyx-selector
     const [personalDetails] = useOnyx(ONYXKEYS.PERSONAL_DETAILS_LIST, {
@@ -60,6 +64,8 @@ function DomainMemberDetailsPage({route}: DomainMemberDetailsPageProps) {
         selector: (personalDetailsList: OnyxEntry<PersonalDetailsList>) => personalDetailsList?.[accountID],
     });
 
+    const [domainName] = useOnyx(`${ONYXKEYS.COLLECTION.DOMAIN}${domainAccountID}`, {canBeMissing: false, selector: domainNameSelector});
+
     const displayName = formatPhoneNumber(getDisplayNameOrDefault(personalDetails));
     const memberLogin = personalDetails?.login ?? '';
     const isSMSLogin = Str.isSMSLogin(memberLogin);
@@ -70,16 +76,30 @@ function DomainMemberDetailsPage({route}: DomainMemberDetailsPageProps) {
         return <FullScreenLoadingIndicator shouldUseGoBackButton />;
     }
 
-    const handleForceCloseAccount = () => {
-        closeUserAccount(domainAccountID, securityGroupIDs ?? [], accountID, true);
+    const handleCloseAccount = async (force: boolean) => {
         setIsModalVisible(false);
+        const result = await showConfirmModal({
+            title: translate('domain.members.closeAccount'),
+            prompt: translate('domain.members.closeAccountPrompt'),
+            confirmText: translate('domain.members.closeAccount'),
+            cancelText: translate('common.cancel'),
+            danger: true,
+            shouldShowCancelButton: true,
+        });
+        if (result.action !== ModalActions.CONFIRM) {
+            setIsModalVisible(true);
+            return;
+        }
+        closeUserAccount(domainAccountID, domainName ?? '', accountID, memberLogin, force);
         Navigation.dismissModal();
     };
 
+    const handleForceCloseAccount = () => {
+        handleCloseAccount(true);
+    };
+
     const handleSafeCloseAccount = () => {
-        closeUserAccount(domainAccountID, securityGroupIDs ?? [], accountID);
-        setIsModalVisible(false);
-        Navigation.dismissModal();
+        handleCloseAccount(false);
     };
 
     return (
diff --git a/src/pages/domain/Members/DomainMembersPage.tsx b/src/pages/domain/Members/DomainMembersPage.tsx
index 8717f164177c..6b411762542d 100644
--- a/src/pages/domain/Members/DomainMembersPage.tsx
+++ b/src/pages/domain/Members/DomainMembersPage.tsx
@@ -7,7 +7,6 @@ import Navigation from '@navigation/Navigation';
 import type {PlatformStackScreenProps} from '@navigation/PlatformStackNavigation/types';
 import type {DomainSplitNavigatorParamList} from '@navigation/types';
 import BaseDomainMembersPage from '@pages/domain/BaseDomainMembersPage';
-import type {MemberOption} from '@pages/domain/BaseDomainMembersPage';
 import ONYXKEYS from '@src/ONYXKEYS';
 import ROUTES from '@src/ROUTES';
 import type SCREENS from '@src/SCREENS';
@@ -32,7 +31,6 @@ function DomainMembersPage({route}: DomainMembersPageProps) {
             headerTitle={translate('domain.members.title')}
             searchPlaceholder={translate('domain.members.findMember')}
             onSelectRow={(item) => Navigation.navigate(ROUTES.DOMAIN_MEMBER_DETAILS.getRoute(domainAccountID, item.accountID))}
-            headerIcon={illustrations.Profile}
         />
     );
 }
diff --git a/src/selectors/Domain.ts b/src/selectors/Domain.ts
index 592b8d2b8347..9f5f50078627 100644
--- a/src/selectors/Domain.ts
+++ b/src/selectors/Domain.ts
@@ -91,6 +91,34 @@ function memberAccountIDsSelector(domain: OnyxEntry<Domain>): number[] {
     return uniqueIDs.length > 0 ? uniqueIDs : getEmptyArray<number>();
 }
 
+/**
+ * Gets all security group IDs for a given account ID.
+ * It searches through all security groups in the domain and returns the group IDs
+ * where the account ID appears in the 'shared' property.
+ *
+ * @param domain - The domain object from Onyx
+ * @param accountID - The account ID to search for
+ * @returns An array of security group IDs that the account belongs to
+ */
+function selectSecurityGroupIDsForAccount(domain: Domain | undefined, accountID: number): number[] {
+    if (!domain) {
+        return [];
+    }
+
+    const accountIDStr = String(accountID);
+
+    return Object.entries(domain)
+        .filter(([key]) => key.startsWith(CONST.DOMAIN.DOMAIN_SECURITY_GROUP_PREFIX))
+        .filter(([, value]) => {
+            const groupData = value as {shared?: Record<string, string>};
+            return groupData?.shared && accountIDStr in groupData.shared;
+        })
+        .map(([key]) => {
+            // Extract the group ID from the key: "domain_securityGroup_<groupID>" -> "<groupID>"
+            return parseInt(key.replace(`${CONST.DOMAIN.DOMAIN_SECURITY_GROUP_PREFIX}`, ''), 10);
+        });
+}
+
 const memberPendingActionSelector = (pendingAction: OnyxEntry<DomainPendingActions>) => pendingAction?.members ?? {};
 
 const adminPendingActionSelector = (pendingAction: OnyxEntry<DomainPendingActions>) => pendingAction?.admin ?? {};
@@ -106,5 +134,6 @@ export {
     domainEmailSelector,
     adminPendingActionSelector,
     technicalContactSettingsSelector,
+    selectSecurityGroupIDsForAccount,
     memberPendingActionSelector,
 };

From c538487e1166e4f77f5e507dd40f69dd508cd368 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jan=20Musia=C5=82?= <jan.musial@swmansion.com>
Date: Fri, 16 Jan 2026 12:29:17 +0100
Subject: [PATCH 07/50] cleanup

---
 src/ONYXKEYS.ts                               |   4 -
 .../domain/Members/DomainMembersPage.tsx      |   6 +-
 tests/unit/DomainSelectorsTest.ts             | 101 ++++++++++++++++++
 3 files changed, 104 insertions(+), 7 deletions(-)

diff --git a/src/ONYXKEYS.ts b/src/ONYXKEYS.ts
index 1e8fce451bf2..37cf5fb7cac2 100755
--- a/src/ONYXKEYS.ts
+++ b/src/ONYXKEYS.ts
@@ -764,9 +764,6 @@ const ONYXKEYS = {
         /** SAML login metadata for a domain */
         SAML_METADATA: 'saml_metadata_',
 
-        /** Stores domain security group */
-        DOMAIN_SECURITY_GROUP: 'expensify_securityGroup',
-
         /** Pending actions for a domain */
         DOMAIN_PENDING_ACTIONS: 'domainPendingActions_',
 
@@ -1168,7 +1165,6 @@ type OnyxCollectionValuesMapping = {
     [ONYXKEYS.COLLECTION.SAML_METADATA]: OnyxTypes.SamlMetadata;
     [ONYXKEYS.COLLECTION.DOMAIN_PENDING_ACTIONS]: OnyxTypes.DomainPendingActions;
     [ONYXKEYS.COLLECTION.DOMAIN_ERRORS]: OnyxTypes.DomainErrors;
-    [ONYXKEYS.COLLECTION.DOMAIN_SECURITY_GROUP]: OnyxTypes.DomainSecurityGroup;
 };
 
 type OnyxValuesMapping = {
diff --git a/src/pages/domain/Members/DomainMembersPage.tsx b/src/pages/domain/Members/DomainMembersPage.tsx
index 6b411762542d..1d75e1cbff47 100644
--- a/src/pages/domain/Members/DomainMembersPage.tsx
+++ b/src/pages/domain/Members/DomainMembersPage.tsx
@@ -1,6 +1,6 @@
 import {memberAccountIDsSelector} from '@selectors/Domain';
 import React from 'react';
-import {useMemoizedLazyExpensifyIcons} from '@hooks/useLazyAsset';
+import {useMemoizedLazyIllustrations} from '@hooks/useLazyAsset';
 import useLocalize from '@hooks/useLocalize';
 import useOnyx from '@hooks/useOnyx';
 import Navigation from '@navigation/Navigation';
@@ -16,7 +16,7 @@ type DomainMembersPageProps = PlatformStackScreenProps<DomainSplitNavigatorParam
 function DomainMembersPage({route}: DomainMembersPageProps) {
     const {domainAccountID} = route.params;
     const {translate} = useLocalize();
-    const icons = useMemoizedLazyExpensifyIcons(['Profile']);
+    const illustrations = useMemoizedLazyIllustrations(['Profile']);
 
     const [memberIDs] = useOnyx(`${ONYXKEYS.COLLECTION.DOMAIN}${domainAccountID}`, {
         canBeMissing: true,
@@ -27,10 +27,10 @@ function DomainMembersPage({route}: DomainMembersPageProps) {
         <BaseDomainMembersPage
             domainAccountID={domainAccountID}
             accountIDs={memberIDs ?? []}
-            headerIcon={icons.Profile}
             headerTitle={translate('domain.members.title')}
             searchPlaceholder={translate('domain.members.findMember')}
             onSelectRow={(item) => Navigation.navigate(ROUTES.DOMAIN_MEMBER_DETAILS.getRoute(domainAccountID, item.accountID))}
+            headerIcon={illustrations.Profile}
         />
     );
 }
diff --git a/tests/unit/DomainSelectorsTest.ts b/tests/unit/DomainSelectorsTest.ts
index a56fadeeaa91..2d9d6e0eecb6 100644
--- a/tests/unit/DomainSelectorsTest.ts
+++ b/tests/unit/DomainSelectorsTest.ts
@@ -170,4 +170,105 @@ describe('domainSelectors', () => {
             });
         });
     });
+    describe('selectMemberIDs', () => {
+        it('Should return an empty array if the domain object is undefined', () => {
+            expect(memberAccountIDsSelector(undefined)).toEqual([]);
+        });
+
+        it('Should return an empty array if the domain object is empty', () => {
+            const domain = {} as OnyxEntry<Domain>;
+            expect(memberAccountIDsSelector(domain)).toEqual([]);
+        });
+
+        it('Should return member IDs when keys start with the security group prefix', () => {
+            const domain = {
+                [`${CONST.DOMAIN.DOMAIN_SECURITY_GROUP_PREFIX}1`]: {
+                    shared: {
+                        // eslint-disable-next-line @typescript-eslint/naming-convention
+                        '100': 'value',
+                        // eslint-disable-next-line @typescript-eslint/naming-convention
+                        '200': 'value',
+                    },
+                },
+                [`${CONST.DOMAIN.DOMAIN_SECURITY_GROUP_PREFIX}2`]: {
+                    shared: {
+                        // eslint-disable-next-line @typescript-eslint/naming-convention
+                        '300': 'value',
+                    },
+                },
+            } as unknown as OnyxEntry<Domain>;
+
+            expect(memberAccountIDsSelector(domain).sort()).toEqual([100, 200, 300]);
+        });
+
+        it('Should return unique member IDs if they appear in multiple security groups', () => {
+            const domain = {
+                [`${CONST.DOMAIN.DOMAIN_SECURITY_GROUP_PREFIX}1`]: {
+                    shared: {
+                        // eslint-disable-next-line @typescript-eslint/naming-convention
+                        '123': 'value',
+                    },
+                },
+                [`${CONST.DOMAIN.DOMAIN_SECURITY_GROUP_PREFIX}2`]: {
+                    shared: {
+                        // eslint-disable-next-line @typescript-eslint/naming-convention
+                        '123': 'value',
+                    },
+                },
+            } as unknown as OnyxEntry<Domain>;
+
+            expect(memberAccountIDsSelector(domain)).toEqual([123]);
+        });
+
+        it('Should ignore keys that do not start with the security group prefix', () => {
+            const domain = {
+                [`${CONST.DOMAIN.DOMAIN_SECURITY_GROUP_PREFIX}1`]: {
+                    shared: {
+                        // eslint-disable-next-line @typescript-eslint/naming-convention
+                        '456': 'value',
+                    },
+                },
+                someOtherKey: {
+                    shared: {
+                        // eslint-disable-next-line @typescript-eslint/naming-convention
+                        '789': 'value',
+                    },
+                },
+            } as unknown as OnyxEntry<Domain>;
+
+            expect(memberAccountIDsSelector(domain)).toEqual([456]);
+        });
+
+        it('Should ignore groups that do not have a shared property', () => {
+            const domain = {
+                [`${CONST.DOMAIN.DOMAIN_SECURITY_GROUP_PREFIX}1`]: {},
+                [`${CONST.DOMAIN.DOMAIN_SECURITY_GROUP_PREFIX}2`]: {shared: null},
+                [`${CONST.DOMAIN.DOMAIN_SECURITY_GROUP_PREFIX}3`]: {
+                    shared: {
+                        // eslint-disable-next-line @typescript-eslint/naming-convention
+                        '111': 'value',
+                    },
+                },
+            } as unknown as OnyxEntry<Domain>;
+
+            expect(memberAccountIDsSelector(domain)).toEqual([111]);
+        });
+
+        it('Should filter out non-numeric shared keys', () => {
+            const domain = {
+                [`${CONST.DOMAIN.DOMAIN_SECURITY_GROUP_PREFIX}1`]: {
+                    shared: {
+                        // eslint-disable-next-line @typescript-eslint/naming-convention
+                        '123': 'value',
+                        // eslint-disable-next-line @typescript-eslint/naming-convention
+                        'not-a-number': 'value',
+                        // eslint-disable-next-line @typescript-eslint/naming-convention
+                        '456': 'value',
+                    },
+                },
+            } as unknown as OnyxEntry<Domain>;
+
+            expect(memberAccountIDsSelector(domain).sort()).toEqual([123, 456]);
+        });
+    });
 });

From 0f41e23b52a41f4f49bba510ef6acca8e90d9f7a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jan=20Musia=C5=82?= <jan.musial@swmansion.com>
Date: Fri, 16 Jan 2026 12:37:38 +0100
Subject: [PATCH 08/50] prettier fix

---
 src/libs/actions/Domain.ts | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/libs/actions/Domain.ts b/src/libs/actions/Domain.ts
index 5692b09513d0..251c06c82a32 100644
--- a/src/libs/actions/Domain.ts
+++ b/src/libs/actions/Domain.ts
@@ -3,9 +3,9 @@ import type {OnyxUpdate} from 'react-native-onyx';
 import * as API from '@libs/API';
 import type {
     AddAdminToDomainParams,
+    DeleteDomainMemberParams,
     DeleteDomainParams,
     RemoveDomainAdminParams,
-    DeleteDomainMemberParams,
     SetTechnicalContactEmailParams,
     ToggleConsolidatedDomainBillingParams,
 } from '@libs/API/parameters';

From 44549c13fcc9b9aa8ea031ad87b7e1c796d13b09 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jan=20Musia=C5=82?= <jan.musial@swmansion.com>
Date: Mon, 19 Jan 2026 13:04:55 +0100
Subject: [PATCH 09/50] use BaseDomainMemberPage

---
 .../BaseDomainMemberDetailsComponent.tsx      |   6 +-
 .../Members/DomainMemberDetailsPage.tsx       | 107 +++++-------------
 2 files changed, 32 insertions(+), 81 deletions(-)

diff --git a/src/pages/domain/BaseDomainMemberDetailsComponent.tsx b/src/pages/domain/BaseDomainMemberDetailsComponent.tsx
index 2ebd59248263..18a541253f99 100644
--- a/src/pages/domain/BaseDomainMemberDetailsComponent.tsx
+++ b/src/pages/domain/BaseDomainMemberDetailsComponent.tsx
@@ -31,9 +31,12 @@ type BaseDomainMemberDetailsComponentProps = {
 
     /** List of additional fields (e.g., force 2FA) */
     children?: React.ReactNode;
+
+    /** Button to display below avatar picture */
+    avatarButton?: React.ReactNode;
 };
 
-function BaseDomainMemberDetailsComponent({domainAccountID, accountID, children}: BaseDomainMemberDetailsComponentProps) {
+function BaseDomainMemberDetailsComponent({domainAccountID, accountID, children, avatarButton}: BaseDomainMemberDetailsComponentProps) {
     const styles = useThemeStyles();
     const {translate, formatPhoneNumber} = useLocalize();
     const icons = useMemoizedLazyExpensifyIcons(['Info']);
@@ -83,6 +86,7 @@ function BaseDomainMemberDetailsComponent({domainAccountID, accountID, children}
                                     {displayName}
                                 </Text>
                             )}
+                            {avatarButton}
                         </View>
                         <View style={styles.w100}>
                             <MenuItemWithTopDescription
diff --git a/src/pages/domain/Members/DomainMemberDetailsPage.tsx b/src/pages/domain/Members/DomainMemberDetailsPage.tsx
index dc7b49224d9f..e69d62e046c6 100644
--- a/src/pages/domain/Members/DomainMemberDetailsPage.tsx
+++ b/src/pages/domain/Members/DomainMemberDetailsPage.tsx
@@ -1,35 +1,23 @@
 import {adminAccountIDsSelector, domainNameSelector} from '@selectors/Domain';
-import {Str} from 'expensify-common';
 import React, {useState} from 'react';
-import {View} from 'react-native';
 import type {OnyxEntry} from 'react-native-onyx';
-import Avatar from '@components/Avatar';
-import FullPageNotFoundView from '@components/BlockingViews/FullPageNotFoundView';
 import Button from '@components/Button';
 import DecisionModal from '@components/DecisionModal';
 import FullScreenLoadingIndicator from '@components/FullscreenLoadingIndicator';
-import HeaderWithBackButton from '@components/HeaderWithBackButton';
-import MenuItem from '@components/MenuItem';
-import MenuItemWithTopDescription from '@components/MenuItemWithTopDescription';
 import {ModalActions} from '@components/Modal/Global/ModalContext';
-import OfflineWithFeedback from '@components/OfflineWithFeedback';
-import ScreenWrapper from '@components/ScreenWrapper';
-import ScrollView from '@components/ScrollView';
-import Text from '@components/Text';
 import useConfirmModal from '@hooks/useConfirmModal';
+import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails';
 import {useMemoizedLazyExpensifyIcons} from '@hooks/useLazyAsset';
 import useLocalize from '@hooks/useLocalize';
 import useOnyx from '@hooks/useOnyx';
 import useResponsiveLayout from '@hooks/useResponsiveLayout';
 import useThemeStyles from '@hooks/useThemeStyles';
 import {closeUserAccount} from '@libs/actions/Domain';
-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 CONST from '@src/CONST';
+import BaseDomainMemberDetailsComponent from '@pages/domain/BaseDomainMemberDetailsComponent';
 import ONYXKEYS from '@src/ONYXKEYS';
-import ROUTES from '@src/ROUTES';
 import type SCREENS from '@src/SCREENS';
 import type {PersonalDetailsList} from '@src/types/onyx';
 import isLoadingOnyxValue from '@src/types/utils/isLoadingOnyxValue';
@@ -39,13 +27,18 @@ type DomainMemberDetailsPageProps = PlatformStackScreenProps<SettingsNavigatorPa
 function DomainMemberDetailsPage({route}: DomainMemberDetailsPageProps) {
     const {domainAccountID, accountID} = route.params;
     const styles = useThemeStyles();
-    const {translate, formatPhoneNumber} = useLocalize();
+    const {translate} = useLocalize();
     const icons = useMemoizedLazyExpensifyIcons(['Info', 'RemoveMembers'] as const);
     const [isModalVisible, setIsModalVisible] = useState(false);
     // eslint-disable-next-line rulesdir/prefer-shouldUseNarrowLayout-instead-of-isSmallScreenWidth
     const {isSmallScreenWidth} = useResponsiveLayout();
     const {showConfirmModal} = useConfirmModal();
 
+    const [adminAccountIDs] = useOnyx(`${ONYXKEYS.COLLECTION.DOMAIN}${domainAccountID}`, {
+        canBeMissing: true,
+        selector: adminAccountIDsSelector,
+    });
+
     const [_, domainMetadata] = useOnyx(`${ONYXKEYS.COLLECTION.DOMAIN}${domainAccountID}`, {
         canBeMissing: true,
         selector: adminAccountIDsSelector,
@@ -66,11 +59,10 @@ function DomainMemberDetailsPage({route}: DomainMemberDetailsPageProps) {
 
     const [domainName] = useOnyx(`${ONYXKEYS.COLLECTION.DOMAIN}${domainAccountID}`, {canBeMissing: false, selector: domainNameSelector});
 
-    const displayName = formatPhoneNumber(getDisplayNameOrDefault(personalDetails));
     const memberLogin = personalDetails?.login ?? '';
-    const isSMSLogin = Str.isSMSLogin(memberLogin);
-    const phoneNumber = getPhoneNumber(personalDetails);
-    const fallbackIcon = personalDetails?.fallbackIcon ?? '';
+
+    const {accountID: currentUserAccountID} = useCurrentUserPersonalDetails();
+    const isAdmin = adminAccountIDs?.includes(currentUserAccountID);
 
     if (isLoadingOnyxValue(domainMetadata)) {
         return <FullScreenLoadingIndicator shouldUseGoBackButton />;
@@ -102,65 +94,22 @@ function DomainMemberDetailsPage({route}: DomainMemberDetailsPageProps) {
         handleCloseAccount(false);
     };
 
+    const avatarButton = (
+        <Button
+            text={translate('domain.members.closeAccount')}
+            onPress={() => setIsModalVisible(true)}
+            isDisabled={!isAdmin}
+            icon={icons.RemoveMembers}
+            style={styles.mb5}
+        />
+    );
     return (
-        <ScreenWrapper
-            enableEdgeToEdgeBottomSafeAreaPadding
-            testID={DomainMemberDetailsPage.displayName}
-        >
-            <FullPageNotFoundView
-                onBackButtonPress={() => Navigation.goBack(ROUTES.WORKSPACES_LIST.route)}
-                shouldForceFullScreen
-            >
-                <HeaderWithBackButton title={displayName} />
-                <ScrollView addBottomSafeAreaPadding>
-                    <View style={[styles.containerWithSpaceBetween, styles.pointerEventsBoxNone, styles.justifyContentStart]}>
-                        <View style={[styles.avatarSectionWrapper, styles.pb0]}>
-                            <OfflineWithFeedback pendingAction={personalDetails?.pendingFields?.avatar}>
-                                <Avatar
-                                    containerStyles={[styles.avatarXLarge, styles.mb4, styles.noOutline]}
-                                    imageStyles={styles.avatarXLarge}
-                                    source={personalDetails?.avatar}
-                                    avatarID={accountID}
-                                    type={CONST.ICON_TYPE_AVATAR}
-                                    size={CONST.AVATAR_SIZE.X_LARGE}
-                                    fallbackIcon={fallbackIcon}
-                                />
-                            </OfflineWithFeedback>
-                            {!!displayName && (
-                                <Text
-                                    style={[styles.textHeadline, styles.pre, styles.mb8, styles.w100, styles.textAlignCenter]}
-                                    numberOfLines={1}
-                                >
-                                    {displayName}
-                                </Text>
-                            )}
-                            <Button
-                                text={translate('domain.members.closeAccount')}
-                                onPress={() => setIsModalVisible(true)}
-                                // isDisabled={isAdmin}
-                                icon={icons.RemoveMembers}
-                                style={styles.mb5}
-                            />
-                        </View>
-                        <View style={styles.w100}>
-                            <MenuItemWithTopDescription
-                                title={isSMSLogin ? formatPhoneNumber(phoneNumber ?? '') : memberLogin}
-                                copyValue={isSMSLogin ? formatPhoneNumber(phoneNumber ?? '') : memberLogin}
-                                description={translate(isSMSLogin ? 'common.phoneNumber' : 'common.email')}
-                                interactive={false}
-                                copyable
-                            />
-                            <MenuItem
-                                style={styles.mb5}
-                                title={translate('common.profile')}
-                                icon={icons.Info}
-                                onPress={() => Navigation.navigate(ROUTES.PROFILE.getRoute(accountID, Navigation.getActiveRoute()))}
-                                shouldShowRightIcon
-                            />
-                        </View>
-                    </View>
-                </ScrollView>
-            </FullPageNotFoundView>
+        <>
+            <BaseDomainMemberDetailsComponent
+                domainAccountID={domainAccountID}
+                accountID={accountID}
+                avatarButton={avatarButton}
+            />
             <DecisionModal
                 title={translate('domain.members.closeAccount')}
                 prompt={translate('domain.members.closeAccountInfo')}
@@ -174,10 +123,8 @@ function DomainMemberDetailsPage({route}: DomainMemberDetailsPageProps) {
                 firstOptionDanger
                 secondOptionSuccess
             />
-        </ScreenWrapper>
+        </>
     );
 }
 
-DomainMemberDetailsPage.displayName = 'DomainAdminDetailsPage';
-
 export default DomainMemberDetailsPage;

From c41472de2e32305fa7f77df7c7f4d9a13e4d7ba2 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jan=20Musia=C5=82?= <jan.musial@swmansion.com>
Date: Mon, 19 Jan 2026 18:37:07 +0100
Subject: [PATCH 10/50] add test

---
 src/languages/en.ts                           |   3 +
 src/libs/actions/Domain.ts                    |  71 ++++++---
 src/pages/domain/BaseDomainMembersPage.tsx    |   4 +-
 .../Members/DomainMemberDetailsPage.tsx       |  20 +--
 .../domain/Members/DomainMembersPage.tsx      |  19 ++-
 src/selectors/Domain.ts                       |  56 +++++--
 src/types/onyx/DomainErrors.ts                |   2 +-
 src/types/onyx/DomainPendingActions.ts        |   2 +-
 tests/actions/DomainTest.ts                   | 150 +++++++++++++++++-
 tests/unit/DomainSelectorsTest.ts             |  62 ++++++++
 10 files changed, 333 insertions(+), 56 deletions(-)

diff --git a/src/languages/en.ts b/src/languages/en.ts
index f9e6fc8d1c21..e0e74cedf049 100755
--- a/src/languages/en.ts
+++ b/src/languages/en.ts
@@ -7935,6 +7935,9 @@ const translations = {
             safeCloseAccount: 'Close account safely',
             closeAccountInfo:
                 'We recommend closing the account safely to skip closing it in case there are: <ul><li>Pending approvals</li><li>Active reimbursements</li><li>No alternative login methods</li></ul> Otherwise, you can ignore the safety precautions above and force close the selected account.',
+            error: {
+                removeMember: 'Unable to remove this user. Please try again.',
+            },
         },
     },
 };
diff --git a/src/libs/actions/Domain.ts b/src/libs/actions/Domain.ts
index 251c06c82a32..a229eba0c503 100644
--- a/src/libs/actions/Domain.ts
+++ b/src/libs/actions/Domain.ts
@@ -13,7 +13,7 @@ import {READ_COMMANDS, SIDE_EFFECT_REQUEST_COMMANDS, WRITE_COMMANDS} from '@libs
 import {getMicroSecondOnyxErrorWithTranslationKey} from '@libs/ErrorUtils';
 import CONST from '@src/CONST';
 import ONYXKEYS from '@src/ONYXKEYS';
-import type {Domain} from '@src/types/onyx';
+import type {Domain, DomainSecurityGroup} from '@src/types/onyx';
 import type PrefixedRecord from '@src/types/utils/PrefixedRecord';
 import type {ScimTokenWithState} from './ScimToken/ScimTokenUtils';
 import {ScimTokenState} from './ScimToken/ScimTokenUtils';
@@ -786,27 +786,41 @@ function clearDomainErrors(domainAccountID: number) {
 }
 
 /** Sends a request to remove user from a domain and close their account */
-function closeUserAccount(domainAccountID: number, domain: string, accountID: number, targetEmail: string, overrideProcessingReports = false) {
-    // const optimisticValue: PrefixedRecord<typeof CONST.DOMAIN.DOMAIN_SECURITY_GROUP_PREFIX, Partial<DomainSecurityGroup>> = {};
-    // for (const groupID of securityGroupIDs) {
-    //     optimisticValue[`${CONST.DOMAIN.DOMAIN_SECURITY_GROUP_PREFIX}${groupID}`] = {
-    //         shared: {
-    //             [accountID]: null,
-    //         },
-    //     };
-    // }
+function closeUserAccount(
+    domainAccountID: number,
+    domain: string,
+    accountID: number,
+    targetEmail: string,
+    securityGroupIDs: Array<`${typeof CONST.DOMAIN.DOMAIN_SECURITY_GROUP_PREFIX}${string}`> = [],
+    securityGroups: PrefixedRecord<typeof CONST.DOMAIN.DOMAIN_SECURITY_GROUP_PREFIX, DomainSecurityGroup> = {},
+    overrideProcessingReports = false,
+) {
+    const optimisticValue: PrefixedRecord<typeof CONST.DOMAIN.DOMAIN_SECURITY_GROUP_PREFIX, Partial<DomainSecurityGroup>> = {};
+    const failureValue: PrefixedRecord<typeof CONST.DOMAIN.DOMAIN_SECURITY_GROUP_PREFIX, Partial<DomainSecurityGroup>> = {};
+    for (const groupID of securityGroupIDs) {
+        optimisticValue[groupID] = {
+            shared: {
+                [accountID]: null,
+            },
+        };
+        failureValue[groupID] = {
+            shared: {
+                [accountID]: securityGroups?.[groupID]?.shared?.[accountID],
+            },
+        };
+    }
 
     const optimisticData: OnyxUpdate[] = [
-        // {
-        //     onyxMethod: Onyx.METHOD.MERGE,
-        //     key: `${ONYXKEYS.COLLECTION.DOMAIN}${domainAccountID}`,
-        //     value: optimisticValue,
-        // },
+        {
+            onyxMethod: Onyx.METHOD.MERGE,
+            key: `${ONYXKEYS.COLLECTION.DOMAIN}${domainAccountID}`,
+            value: optimisticValue,
+        },
         {
             onyxMethod: Onyx.METHOD.MERGE,
             key: `${ONYXKEYS.COLLECTION.DOMAIN_PENDING_ACTIONS}${domainAccountID}`,
             value: {
-                members: {[accountID]: CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE},
+                members: {[targetEmail]: CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE},
             },
         },
     ];
@@ -816,19 +830,35 @@ function closeUserAccount(domainAccountID: number, domain: string, accountID: nu
             onyxMethod: Onyx.METHOD.MERGE,
             key: `${ONYXKEYS.COLLECTION.DOMAIN_PENDING_ACTIONS}${domainAccountID}`,
             value: {
-                members: {[accountID]: null},
+                members: {[targetEmail]: null},
             },
         },
     ];
+
     const failureData: OnyxUpdate[] = [
+        {
+            onyxMethod: Onyx.METHOD.MERGE,
+            key: `${ONYXKEYS.COLLECTION.DOMAIN_ERRORS}${domainAccountID}`,
+            value: {
+                memberErrors: {
+                    [targetEmail]: {errors: getMicroSecondOnyxErrorWithTranslationKey('domain.members.error.removeMember')},
+                },
+            },
+        },
+        {
+            onyxMethod: Onyx.METHOD.MERGE,
+            key: `${ONYXKEYS.COLLECTION.DOMAIN}${domainAccountID}`,
+            value: failureValue,
+        },
         {
             onyxMethod: Onyx.METHOD.MERGE,
             key: `${ONYXKEYS.COLLECTION.DOMAIN_PENDING_ACTIONS}${domainAccountID}`,
             value: {
-                members: {[accountID]: null},
+                members: {[targetEmail]: null},
             },
         },
     ];
+
     const parameters: DeleteDomainMemberParams = {
         domain,
         targetEmail,
@@ -838,15 +868,16 @@ function closeUserAccount(domainAccountID: number, domain: string, accountID: nu
     API.write(WRITE_COMMANDS.DELETE_DOMAIN_MEMBER, parameters, {optimisticData, successData, failureData});
 }
 
-function clearDomainMemberError(domainAccountID: number, accountID: number) {
+function clearDomainMemberError(domainAccountID: number, accountID: number, email: string) {
     Onyx.merge(`${ONYXKEYS.COLLECTION.DOMAIN_ERRORS}${domainAccountID}`, {
         memberErrors: {
             [accountID]: null,
+            [email]: null,
         },
     });
 
     Onyx.merge(`${ONYXKEYS.COLLECTION.DOMAIN_PENDING_ACTIONS}${domainAccountID}`, {
-        members: {[accountID]: null},
+        members: {[email]: null},
     });
 }
 
diff --git a/src/pages/domain/BaseDomainMembersPage.tsx b/src/pages/domain/BaseDomainMembersPage.tsx
index e49510c14bcb..71a3302a032c 100644
--- a/src/pages/domain/BaseDomainMembersPage.tsx
+++ b/src/pages/domain/BaseDomainMembersPage.tsx
@@ -58,7 +58,7 @@ type BaseDomainMembersPageProps = {
     getCustomRightElement?: (accountID: number) => React.ReactNode;
 
     /** Function to return additional row-specific properties like errors or pending actions */
-    getCustomRowProps?: (accountID: number) => {errors?: Errors; pendingAction?: PendingAction};
+    getCustomRowProps?: (accountID: number, accountEmail?: string) => {errors?: Errors; pendingAction?: PendingAction};
 
     /** Callback fired when the user dismisses an error message for a specific row */
     onDismissError?: (item: MemberOption) => void;
@@ -85,7 +85,7 @@ function BaseDomainMembersPage({
     const data: MemberOption[] = accountIDs.map((accountID) => {
         const details = personalDetails?.[accountID];
         const login = details?.login ?? '';
-        const customProps = getCustomRowProps?.(accountID);
+        const customProps = getCustomRowProps?.(accountID, login);
         const isPendingActionDelete = customProps?.pendingAction === CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE;
 
         return {
diff --git a/src/pages/domain/Members/DomainMemberDetailsPage.tsx b/src/pages/domain/Members/DomainMemberDetailsPage.tsx
index e69d62e046c6..d97ece099fa5 100644
--- a/src/pages/domain/Members/DomainMemberDetailsPage.tsx
+++ b/src/pages/domain/Members/DomainMemberDetailsPage.tsx
@@ -1,4 +1,4 @@
-import {adminAccountIDsSelector, domainNameSelector} from '@selectors/Domain';
+import {adminAccountIDsSelector, domainNameSelector, selectSecurityGroupsForAccount} from '@selectors/Domain';
 import React, {useState} from 'react';
 import type {OnyxEntry} from 'react-native-onyx';
 import Button from '@components/Button';
@@ -19,7 +19,7 @@ import type {SettingsNavigatorParamList} from '@navigation/types';
 import BaseDomainMemberDetailsComponent from '@pages/domain/BaseDomainMemberDetailsComponent';
 import ONYXKEYS from '@src/ONYXKEYS';
 import type SCREENS from '@src/SCREENS';
-import type {PersonalDetailsList} from '@src/types/onyx';
+import type {Domain, PersonalDetailsList} from '@src/types/onyx';
 import isLoadingOnyxValue from '@src/types/utils/isLoadingOnyxValue';
 
 type DomainMemberDetailsPageProps = PlatformStackScreenProps<SettingsNavigatorParamList, typeof SCREENS.DOMAIN.MEMBER_DETAILS>;
@@ -34,23 +34,17 @@ function DomainMemberDetailsPage({route}: DomainMemberDetailsPageProps) {
     const {isSmallScreenWidth} = useResponsiveLayout();
     const {showConfirmModal} = useConfirmModal();
 
-    const [adminAccountIDs] = useOnyx(`${ONYXKEYS.COLLECTION.DOMAIN}${domainAccountID}`, {
+    const [adminAccountIDs, domainMetadata] = useOnyx(`${ONYXKEYS.COLLECTION.DOMAIN}${domainAccountID}`, {
         canBeMissing: true,
         selector: adminAccountIDsSelector,
     });
 
-    const [_, domainMetadata] = useOnyx(`${ONYXKEYS.COLLECTION.DOMAIN}${domainAccountID}`, {
+    // eslint-disable-next-line rulesdir/no-inline-useOnyx-selector
+    const [securityGroupsData] = useOnyx(`${ONYXKEYS.COLLECTION.DOMAIN}${domainAccountID}`, {
         canBeMissing: true,
-        selector: adminAccountIDsSelector,
+        selector: (d: OnyxEntry<Domain>) => selectSecurityGroupsForAccount(d, accountID),
     });
 
-    // Commenting out data needed for optimistic updates for now
-    // // eslint-disable-next-line rulesdir/no-inline-useOnyx-selector
-    // const [securityGroupIDs] = useOnyx(`${ONYXKEYS.COLLECTION.DOMAIN}${domainAccountID}`, {
-    //     canBeMissing: true,
-    //     selector: (domain: OnyxEntry<Domain>) => selectSecurityGroupIDsForAccount(domain, accountID),
-    // });
-
     // eslint-disable-next-line rulesdir/no-inline-useOnyx-selector
     const [personalDetails] = useOnyx(ONYXKEYS.PERSONAL_DETAILS_LIST, {
         canBeMissing: true,
@@ -82,7 +76,7 @@ function DomainMemberDetailsPage({route}: DomainMemberDetailsPageProps) {
             setIsModalVisible(true);
             return;
         }
-        closeUserAccount(domainAccountID, domainName ?? '', accountID, memberLogin, force);
+        closeUserAccount(domainAccountID, domainName ?? '', accountID, memberLogin, securityGroupsData?.keys ?? [], securityGroupsData?.securityGroups, force);
         Navigation.dismissModal();
     };
 
diff --git a/src/pages/domain/Members/DomainMembersPage.tsx b/src/pages/domain/Members/DomainMembersPage.tsx
index 1d75e1cbff47..4a253d908ff2 100644
--- a/src/pages/domain/Members/DomainMembersPage.tsx
+++ b/src/pages/domain/Members/DomainMembersPage.tsx
@@ -1,8 +1,9 @@
-import {memberAccountIDsSelector} from '@selectors/Domain';
+import {memberAccountIDsSelector, memberPendingActionSelector} from '@selectors/Domain';
 import React from 'react';
 import {useMemoizedLazyIllustrations} from '@hooks/useLazyAsset';
 import useLocalize from '@hooks/useLocalize';
 import useOnyx from '@hooks/useOnyx';
+import {clearDomainMemberError} from '@libs/actions/Domain';
 import Navigation from '@navigation/Navigation';
 import type {PlatformStackScreenProps} from '@navigation/PlatformStackNavigation/types';
 import type {DomainSplitNavigatorParamList} from '@navigation/types';
@@ -23,6 +24,20 @@ function DomainMembersPage({route}: DomainMembersPageProps) {
         selector: memberAccountIDsSelector,
     });
 
+    const [domainErrors] = useOnyx(`${ONYXKEYS.COLLECTION.DOMAIN_ERRORS}${domainAccountID}`, {
+        canBeMissing: true,
+    });
+
+    const [domainPendingAction] = useOnyx(`${ONYXKEYS.COLLECTION.DOMAIN_PENDING_ACTIONS}${domainAccountID}`, {
+        canBeMissing: true,
+        selector: memberPendingActionSelector,
+    });
+
+    const getCustomRowProps = (accountID: number, accountEmail?: string) => ({
+        errors: accountEmail && domainErrors?.memberErrors?.[accountEmail]?.errors ? domainErrors?.memberErrors?.[accountEmail]?.errors : domainErrors?.memberErrors?.[accountID]?.errors,
+        pendingAction: accountEmail ? domainPendingAction?.[accountEmail] : undefined,
+    });
+
     return (
         <BaseDomainMembersPage
             domainAccountID={domainAccountID}
@@ -31,6 +46,8 @@ function DomainMembersPage({route}: DomainMembersPageProps) {
             searchPlaceholder={translate('domain.members.findMember')}
             onSelectRow={(item) => Navigation.navigate(ROUTES.DOMAIN_MEMBER_DETAILS.getRoute(domainAccountID, item.accountID))}
             headerIcon={illustrations.Profile}
+            getCustomRowProps={getCustomRowProps}
+            onDismissError={(item) => clearDomainMemberError(domainAccountID, item.accountID, item.login)}
         />
     );
 }
diff --git a/src/selectors/Domain.ts b/src/selectors/Domain.ts
index 9f5f50078627..b1f8cda75f09 100644
--- a/src/selectors/Domain.ts
+++ b/src/selectors/Domain.ts
@@ -3,6 +3,7 @@ import type {OnyxEntry} from 'react-native-onyx';
 import CONST from '@src/CONST';
 import type {CardFeeds, Domain, DomainPendingActions, DomainSecurityGroup, DomainSettings, SamlMetadata} from '@src/types/onyx';
 import getEmptyArray from '@src/types/utils/getEmptyArray';
+import type PrefixedRecord from '@src/types/utils/PrefixedRecord';
 
 const domainMemberSamlSettingsSelector = (domainSettings: OnyxEntry<CardFeeds>) => domainSettings?.settings;
 
@@ -91,32 +92,55 @@ function memberAccountIDsSelector(domain: OnyxEntry<Domain>): number[] {
     return uniqueIDs.length > 0 ? uniqueIDs : getEmptyArray<number>();
 }
 
+type SecurityGroupKey = `${typeof CONST.DOMAIN.DOMAIN_SECURITY_GROUP_PREFIX}${string}`;
+
+/**
+ * Type guard to check if a domain entry is a security group entry.
+ */
+function isSecurityGroupEntry(entry: [string, unknown]): entry is [SecurityGroupKey, DomainSecurityGroup] {
+    const [key, value] = entry;
+    return key.startsWith(CONST.DOMAIN.DOMAIN_SECURITY_GROUP_PREFIX) && typeof value === 'object' && value !== null && 'shared' in value;
+}
+
 /**
- * Gets all security group IDs for a given account ID.
- * It searches through all security groups in the domain and returns the group IDs
+ * Gets all security groups for a given account ID.
+ * Searches through all security groups in the domain and returns those
  * where the account ID appears in the 'shared' property.
  *
  * @param domain - The domain object from Onyx
  * @param accountID - The account ID to search for
- * @returns An array of security group IDs that the account belongs to
+ * @returns Object containing:
+ *   - keys: Array of security group keys the account belongs to
+ *   - securityGroups: Record of security group data keyed by their prefixed IDs
  */
-function selectSecurityGroupIDsForAccount(domain: Domain | undefined, accountID: number): number[] {
+function selectSecurityGroupsForAccount(
+    domain: Domain | undefined,
+    accountID: number,
+): {
+    keys: SecurityGroupKey[];
+    securityGroups: PrefixedRecord<typeof CONST.DOMAIN.DOMAIN_SECURITY_GROUP_PREFIX, Partial<DomainSecurityGroup>>;
+} {
     if (!domain) {
-        return [];
+        return {keys: [], securityGroups: {}};
     }
 
     const accountIDStr = String(accountID);
+    const keys: SecurityGroupKey[] = [];
+    const securityGroups: PrefixedRecord<typeof CONST.DOMAIN.DOMAIN_SECURITY_GROUP_PREFIX, Partial<DomainSecurityGroup>> = {};
+
+    for (const entry of Object.entries(domain)) {
+        if (!isSecurityGroupEntry(entry)) {
+            continue;
+        }
+
+        const [key, group] = entry;
+        if (accountIDStr in group.shared) {
+            keys.push(key);
+            securityGroups[key] = group;
+        }
+    }
 
-    return Object.entries(domain)
-        .filter(([key]) => key.startsWith(CONST.DOMAIN.DOMAIN_SECURITY_GROUP_PREFIX))
-        .filter(([, value]) => {
-            const groupData = value as {shared?: Record<string, string>};
-            return groupData?.shared && accountIDStr in groupData.shared;
-        })
-        .map(([key]) => {
-            // Extract the group ID from the key: "domain_securityGroup_<groupID>" -> "<groupID>"
-            return parseInt(key.replace(`${CONST.DOMAIN.DOMAIN_SECURITY_GROUP_PREFIX}`, ''), 10);
-        });
+    return {keys, securityGroups};
 }
 
 const memberPendingActionSelector = (pendingAction: OnyxEntry<DomainPendingActions>) => pendingAction?.members ?? {};
@@ -134,6 +158,6 @@ export {
     domainEmailSelector,
     adminPendingActionSelector,
     technicalContactSettingsSelector,
-    selectSecurityGroupIDsForAccount,
+    selectSecurityGroupsForAccount,
     memberPendingActionSelector,
 };
diff --git a/src/types/onyx/DomainErrors.ts b/src/types/onyx/DomainErrors.ts
index 43d21f7a46e5..af040e3336f1 100644
--- a/src/types/onyx/DomainErrors.ts
+++ b/src/types/onyx/DomainErrors.ts
@@ -33,7 +33,7 @@ type DomainErrors = {
      * Errors for members
      */
     memberErrors?: Record<
-        number,
+        number | string,
         {
             /**
              * General member errors
diff --git a/src/types/onyx/DomainPendingActions.ts b/src/types/onyx/DomainPendingActions.ts
index cb4e4acf0154..32cc28459aa1 100644
--- a/src/types/onyx/DomainPendingActions.ts
+++ b/src/types/onyx/DomainPendingActions.ts
@@ -38,7 +38,7 @@ type DomainPendingAction = {
     /**
      * Pending action for members
      */
-    members?: Record<number, OnyxCommon.PendingAction>;
+    members?: Record<string, OnyxCommon.PendingAction>;
 };
 
 export default DomainPendingAction;
diff --git a/tests/actions/DomainTest.ts b/tests/actions/DomainTest.ts
index 4e2e544aceb4..f747dc4aa7fc 100644
--- a/tests/actions/DomainTest.ts
+++ b/tests/actions/DomainTest.ts
@@ -1,11 +1,12 @@
 import Onyx from 'react-native-onyx';
-import {clearDomainErrors, createDomain, resetCreateDomainForm, resetDomain} from '@libs/actions/Domain';
+import {clearDomainErrors, clearDomainMemberError, closeUserAccount, createDomain, resetCreateDomainForm, resetDomain} from '@libs/actions/Domain';
 import {WRITE_COMMANDS} from '@libs/API/types';
 import CONST from '@src/CONST';
 import IntlStore from '@src/languages/IntlStore';
 import OnyxUpdateManager from '@src/libs/actions/OnyxUpdateManager';
 import ONYXKEYS from '@src/ONYXKEYS';
-import type {Domain} from '@src/types/onyx';
+import type {Domain, DomainSecurityGroup} from '@src/types/onyx';
+import PrefixedRecord from '@src/types/utils/PrefixedRecord';
 import * as TestHelper from '../utils/TestHelper';
 import waitForBatchedUpdates from '../utils/waitForBatchedUpdates';
 
@@ -117,4 +118,149 @@ describe('actions/Domain', () => {
             },
         });
     });
+
+    it('closeUserAccount - sends DELETE_DOMAIN_MEMBER API request with correct data', () => {
+        const apiWriteSpy = jest.spyOn(require('@libs/API'), 'write').mockImplementation(() => Promise.resolve());
+        const domainAccountID = 123;
+        const domainName = 'test.com';
+        const accountID = 456;
+        const targetEmail = 'user@test.com';
+        const securityGroupKey = `${CONST.DOMAIN.DOMAIN_SECURITY_GROUP_PREFIX}1` as const;
+        const securityGroupIDs = [securityGroupKey];
+        const securityGroups = {
+            [securityGroupKey]: {
+                shared: {
+                    [accountID]: 'read' as const,
+                },
+            },
+        } as unknown as PrefixedRecord<typeof CONST.DOMAIN.DOMAIN_SECURITY_GROUP_PREFIX, DomainSecurityGroup>;
+
+        closeUserAccount(domainAccountID, domainName, accountID, targetEmail, securityGroupIDs, securityGroups);
+
+        expect(apiWriteSpy).toHaveBeenCalledWith(
+            WRITE_COMMANDS.DELETE_DOMAIN_MEMBER,
+            {domain: domainName, targetEmail, overrideProcessingReports: false},
+            {
+                optimisticData: [
+                    expect.objectContaining({
+                        key: `${ONYXKEYS.COLLECTION.DOMAIN}${domainAccountID}`,
+                        value: {[securityGroupKey]: {shared: {[accountID]: null}}},
+                    }),
+                    expect.objectContaining({
+                        key: `${ONYXKEYS.COLLECTION.DOMAIN_PENDING_ACTIONS}${domainAccountID}`,
+                        value: {members: {[targetEmail]: CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE}},
+                    }),
+                ],
+                successData: [
+                    expect.objectContaining({
+                        key: `${ONYXKEYS.COLLECTION.DOMAIN_PENDING_ACTIONS}${domainAccountID}`,
+                        value: {members: {[targetEmail]: null}},
+                    }),
+                ],
+                failureData: [
+                    expect.objectContaining({
+                        key: `${ONYXKEYS.COLLECTION.DOMAIN_ERRORS}${domainAccountID}`,
+                        // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
+                        value: {memberErrors: {[targetEmail]: expect.any(Object)}},
+                    }),
+                    expect.objectContaining({
+                        key: `${ONYXKEYS.COLLECTION.DOMAIN}${domainAccountID}`,
+                        value: {[securityGroupKey]: {shared: {[accountID]: 'read'}}},
+                    }),
+                    expect.objectContaining({
+                        key: `${ONYXKEYS.COLLECTION.DOMAIN_PENDING_ACTIONS}${domainAccountID}`,
+                        value: {members: {[targetEmail]: null}},
+                    }),
+                ],
+            },
+        );
+
+        apiWriteSpy.mockRestore();
+    });
+
+    it('closeUserAccount - handles multiple security groups', () => {
+        const apiWriteSpy = jest.spyOn(require('@libs/API'), 'write').mockImplementation(() => Promise.resolve());
+        const domainAccountID = 123;
+        const domainName = 'test.com';
+        const accountID = 456;
+        const targetEmail = 'user@test.com';
+        const securityGroupKey1 = `${CONST.DOMAIN.DOMAIN_SECURITY_GROUP_PREFIX}1` as const;
+        const securityGroupKey2 = `${CONST.DOMAIN.DOMAIN_SECURITY_GROUP_PREFIX}2` as const;
+        const securityGroupIDs = [securityGroupKey1, securityGroupKey2];
+        const securityGroups = {
+            [securityGroupKey1]: {shared: {[accountID]: 'read' as const}},
+            [securityGroupKey2]: {shared: {[accountID]: 'read' as const}},
+        } as unknown as PrefixedRecord<typeof CONST.DOMAIN.DOMAIN_SECURITY_GROUP_PREFIX, DomainSecurityGroup>;
+        closeUserAccount(domainAccountID, domainName, accountID, targetEmail, securityGroupIDs, securityGroups);
+
+        expect(apiWriteSpy).toHaveBeenCalledWith(
+            WRITE_COMMANDS.DELETE_DOMAIN_MEMBER,
+            expect.any(Object),
+            expect.objectContaining({
+                optimisticData: expect.arrayContaining([
+                    expect.objectContaining({
+                        key: `${ONYXKEYS.COLLECTION.DOMAIN}${domainAccountID}`,
+                        value: {
+                            [securityGroupKey1]: {shared: {[accountID]: null}},
+                            [securityGroupKey2]: {shared: {[accountID]: null}},
+                        },
+                    }),
+                ]),
+            }),
+        );
+
+        apiWriteSpy.mockRestore();
+    });
+
+    it('closeUserAccount - handles overrideProcessingReports flag', () => {
+        const apiWriteSpy = jest.spyOn(require('@libs/API'), 'write').mockImplementation(() => Promise.resolve());
+        const domainAccountID = 123;
+        const domainName = 'test.com';
+        const accountID = 456;
+        const targetEmail = 'user@test.com';
+
+        closeUserAccount(domainAccountID, domainName, accountID, targetEmail, [], {}, true);
+
+        expect(apiWriteSpy).toHaveBeenCalledWith(WRITE_COMMANDS.DELETE_DOMAIN_MEMBER, {domain: domainName, targetEmail, overrideProcessingReports: true}, expect.any(Object));
+
+        apiWriteSpy.mockRestore();
+    });
+
+    it('clearDomainMemberError - clears member errors and pending actions', async () => {
+        const domainAccountID = 123;
+        const accountID = 456;
+        const email = 'user@test.com';
+
+        await Onyx.set(`${ONYXKEYS.COLLECTION.DOMAIN_ERRORS}${domainAccountID}` as const, {
+            memberErrors: {
+                // eslint-disable-next-line @typescript-eslint/naming-convention
+                [accountID]: {errors: {123: 'error'}},
+                // eslint-disable-next-line @typescript-eslint/naming-convention
+                [email]: {errors: {456: 'error'}},
+            },
+        });
+
+        await Onyx.set(`${ONYXKEYS.COLLECTION.DOMAIN_PENDING_ACTIONS}${domainAccountID}` as const, {
+            members: {[email]: CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE},
+        });
+
+        clearDomainMemberError(domainAccountID, accountID, email);
+
+        await TestHelper.getOnyxData({
+            key: `${ONYXKEYS.COLLECTION.DOMAIN_ERRORS}${domainAccountID}`,
+            waitForCollectionCallback: false,
+            callback: (errors) => {
+                expect(errors?.memberErrors?.[accountID]).toBeFalsy();
+                expect(errors?.memberErrors?.[email]).toBeFalsy();
+            },
+        });
+
+        await TestHelper.getOnyxData({
+            key: `${ONYXKEYS.COLLECTION.DOMAIN_PENDING_ACTIONS}${domainAccountID}`,
+            waitForCollectionCallback: false,
+            callback: (pendingActions) => {
+                expect(pendingActions?.members?.[email]).toBeFalsy();
+            },
+        });
+    });
 });
diff --git a/tests/unit/DomainSelectorsTest.ts b/tests/unit/DomainSelectorsTest.ts
index 2d9d6e0eecb6..54f0839ddbbf 100644
--- a/tests/unit/DomainSelectorsTest.ts
+++ b/tests/unit/DomainSelectorsTest.ts
@@ -4,6 +4,7 @@ import {
     domainEmailSelector,
     domainSettingsPrimaryContactSelector,
     memberAccountIDsSelector,
+    selectSecurityGroupsForAccount,
     technicalContactSettingsSelector,
 } from '@selectors/Domain';
 import type {OnyxEntry} from 'react-native-onyx';
@@ -271,4 +272,65 @@ describe('domainSelectors', () => {
             expect(memberAccountIDsSelector(domain).sort()).toEqual([123, 456]);
         });
     });
+
+    describe('selectSecurityGroupsForAccount', () => {
+        it('Should return empty arrays when domain has no security groups', () => {
+            const domain = {
+                validated: true,
+                accountID: 1,
+                email: 'test@example.com',
+            } as Domain;
+
+            const result = selectSecurityGroupsForAccount(domain, 123);
+
+            expect(result.keys).toEqual([]);
+            expect(result.securityGroups).toEqual({});
+        });
+
+        it('Should return empty arrays when account is not in any security group', () => {
+            const domain = {
+                validated: true,
+                accountID: 1,
+                email: 'test@example.com',
+                [`${CONST.DOMAIN.DOMAIN_SECURITY_GROUP_PREFIX}1`]: {
+                    shared: {
+                        // eslint-disable-next-line @typescript-eslint/naming-convention
+                        '456': 'read',
+                        // eslint-disable-next-line @typescript-eslint/naming-convention
+                        '789': 'read',
+                    },
+                },
+            } as unknown as Domain;
+
+            const result = selectSecurityGroupsForAccount(domain, 123);
+
+            expect(result.keys).toEqual([]);
+            expect(result.securityGroups).toEqual({});
+        });
+
+        it('Should return multiple security groups when account belongs to several', () => {
+            /* eslint-disable @typescript-eslint/naming-convention */
+            const group1 = {shared: {'123': 'read', '456': 'read'}};
+            const group2 = {shared: {'123': 'read', '789': 'read'}};
+            const group3 = {shared: {'999': 'read'}};
+            /* eslint-enable @typescript-eslint/naming-convention */
+            const domain = {
+                validated: true,
+                accountID: 1,
+                email: 'test@example.com',
+                [`${CONST.DOMAIN.DOMAIN_SECURITY_GROUP_PREFIX}1`]: group1,
+                [`${CONST.DOMAIN.DOMAIN_SECURITY_GROUP_PREFIX}2`]: group2,
+                [`${CONST.DOMAIN.DOMAIN_SECURITY_GROUP_PREFIX}3`]: group3,
+            } as unknown as Domain;
+
+            const result = selectSecurityGroupsForAccount(domain, 123);
+
+            expect(result.keys).toHaveLength(2);
+            expect(result.keys).toContain(`${CONST.DOMAIN.DOMAIN_SECURITY_GROUP_PREFIX}1`);
+            expect(result.keys).toContain(`${CONST.DOMAIN.DOMAIN_SECURITY_GROUP_PREFIX}2`);
+            expect(result.securityGroups[`${CONST.DOMAIN.DOMAIN_SECURITY_GROUP_PREFIX}1`]).toEqual(group1);
+            expect(result.securityGroups[`${CONST.DOMAIN.DOMAIN_SECURITY_GROUP_PREFIX}2`]).toEqual(group2);
+            expect(result.securityGroups[`${CONST.DOMAIN.DOMAIN_SECURITY_GROUP_PREFIX}3`]).toBeUndefined();
+        });
+    });
 });

From ed8b441df82587667c167af45650461101e9168d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jan=20Musia=C5=82?= <jan.musial@swmansion.com>
Date: Mon, 19 Jan 2026 22:19:24 +0100
Subject: [PATCH 11/50] fix lint

---
 src/libs/actions/Domain.ts  | 4 ++--
 tests/actions/DomainTest.ts | 2 +-
 2 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/src/libs/actions/Domain.ts b/src/libs/actions/Domain.ts
index a229eba0c503..ec2eb8233ebc 100644
--- a/src/libs/actions/Domain.ts
+++ b/src/libs/actions/Domain.ts
@@ -792,7 +792,7 @@ function closeUserAccount(
     accountID: number,
     targetEmail: string,
     securityGroupIDs: Array<`${typeof CONST.DOMAIN.DOMAIN_SECURITY_GROUP_PREFIX}${string}`> = [],
-    securityGroups: PrefixedRecord<typeof CONST.DOMAIN.DOMAIN_SECURITY_GROUP_PREFIX, DomainSecurityGroup> = {},
+    securityGroups: PrefixedRecord<typeof CONST.DOMAIN.DOMAIN_SECURITY_GROUP_PREFIX, Partial<DomainSecurityGroup>> = {},
     overrideProcessingReports = false,
 ) {
     const optimisticValue: PrefixedRecord<typeof CONST.DOMAIN.DOMAIN_SECURITY_GROUP_PREFIX, Partial<DomainSecurityGroup>> = {};
@@ -805,7 +805,7 @@ function closeUserAccount(
         };
         failureValue[groupID] = {
             shared: {
-                [accountID]: securityGroups?.[groupID]?.shared?.[accountID],
+                [accountID]: securityGroups?.[groupID]?.shared?.[accountID] ?? null,
             },
         };
     }
diff --git a/tests/actions/DomainTest.ts b/tests/actions/DomainTest.ts
index f747dc4aa7fc..e2f1b4df6f3d 100644
--- a/tests/actions/DomainTest.ts
+++ b/tests/actions/DomainTest.ts
@@ -6,7 +6,7 @@ import IntlStore from '@src/languages/IntlStore';
 import OnyxUpdateManager from '@src/libs/actions/OnyxUpdateManager';
 import ONYXKEYS from '@src/ONYXKEYS';
 import type {Domain, DomainSecurityGroup} from '@src/types/onyx';
-import PrefixedRecord from '@src/types/utils/PrefixedRecord';
+import type PrefixedRecord from '@src/types/utils/PrefixedRecord';
 import * as TestHelper from '../utils/TestHelper';
 import waitForBatchedUpdates from '../utils/waitForBatchedUpdates';
 

From a86bfdc1a2be1c27c51130be157de600c81fa302 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jan=20Musia=C5=82?= <jan.musial@swmansion.com>
Date: Tue, 20 Jan 2026 18:10:46 +0100
Subject: [PATCH 12/50] add translations

---
 src/languages/de.ts                            | 12 +++++++++++-
 src/languages/es.ts                            |  7 +++++++
 src/languages/fr.ts                            | 12 +++++++++++-
 src/languages/it.ts                            | 12 +++++++++++-
 src/languages/ja.ts                            | 12 +++++++++++-
 src/languages/nl.ts                            | 12 +++++++++++-
 src/languages/pl.ts                            | 12 +++++++++++-
 src/languages/pt-BR.ts                         | 12 +++++++++++-
 src/languages/zh-hans.ts                       | 12 +++++++++++-
 src/pages/domain/Members/DomainMembersPage.tsx |  7 ++++---
 10 files changed, 99 insertions(+), 11 deletions(-)

diff --git a/src/languages/de.ts b/src/languages/de.ts
index 8606d4fde2bf..1a15a50604f2 100644
--- a/src/languages/de.ts
+++ b/src/languages/de.ts
@@ -8007,7 +8007,17 @@ Hier ist ein *Testbeleg*, um dir zu zeigen, wie es funktioniert:`,
             enterDomainName: 'Geben Sie hier Ihren Domänennamen ein',
             resetDomainInfo: `Diese Aktion ist <strong>dauerhaft</strong> und die folgenden Daten werden gelöscht: <br/> <ul><li>Firmenkarten-Verbindungen und alle nicht eingereichten Ausgaben von diesen Karten</li> <li>SAML- und Gruppeneinstellungen</li> </ul> Alle Konten, Workspaces, Berichte, Ausgaben und anderen Daten bleiben erhalten. <br/><br/>Hinweis: Sie können diese Domain aus Ihrer Domainliste entfernen, indem Sie die zugehörige E-Mail aus Ihren <a href="#">Kontaktmethoden</a> löschen.`,
         },
-        members: {title: 'Mitglieder', findMember: 'Mitglied suchen'},
+        members: {
+            title: 'Mitglieder',
+            findMember: 'Mitglied suchen',
+            closeAccount: 'Konto schließen',
+            closeAccountPrompt: 'Bist du sicher? Diese Aktion ist dauerhaft.',
+            forceCloseAccount: 'Konto zwangsweise schließen',
+            safeCloseAccount: 'Konto sicher schließen',
+            closeAccountInfo:
+                'Wir empfehlen, das Konto sicher zu schließen, um das Schließen zu überspringen, falls Folgendes vorliegt: <ul><li>Ausstehende Genehmigungen</li><li>Aktive Erstattungen</li><li>Keine alternativen Anmeldemethoden</li></ul> Andernfalls können Sie die oben genannten Sicherheitsvorkehrungen ignorieren und das ausgewählte Konto zwangsweise schließen.',
+            error: {removeMember: 'Dieser Benutzer kann nicht entfernt werden. Bitte versuche es erneut.'},
+        },
     },
     gps: {
         tooltip: 'GPS-Verfolgung läuft! Wenn du fertig bist, stoppe die Verfolgung unten.',
diff --git a/src/languages/es.ts b/src/languages/es.ts
index da14daedbbbd..f209f9967398 100644
--- a/src/languages/es.ts
+++ b/src/languages/es.ts
@@ -8107,6 +8107,13 @@ ${amount} para ${merchant} - ${date}`,
         members: {
             title: 'Miembros',
             findMember: 'Buscar miembro',
+            closeAccount: 'Cerrar cuenta',
+            closeAccountPrompt: '¿Estás seguro? Esta acción es permanente.',
+            forceCloseAccount: 'Forzar cierre de cuenta',
+            safeCloseAccount: 'Cierre seguro de cuenta',
+            closeAccountInfo:
+                'Recomendamos cerrar la cuenta de forma segura para omitir el cierre si hay: <ul><li>Aprobaciones pendientes</li><li>Reembolsos en curso</li><li>No hay otro método de inicio de sesión</li></ul>De lo contrario, puedes ignorar las precauciones de seguridad anteriores y forzar el cierre de las cuentas seleccionadas.',
+            error: {removeMember: 'No se pudo eliminar a este usuario. Por favor, inténtalo de nuevo.'},
         },
     },
     gps: {
diff --git a/src/languages/fr.ts b/src/languages/fr.ts
index 73a350ac602b..5bbf86f24de9 100644
--- a/src/languages/fr.ts
+++ b/src/languages/fr.ts
@@ -8014,7 +8014,17 @@ Voici un *reçu test* pour vous montrer comment cela fonctionne :`,
             enterDomainName: 'Saisissez votre nom de domaine ici',
             resetDomainInfo: `Cette action est <strong>définitive</strong> et les données suivantes seront supprimées : <br/> <ul><li>Connexions aux cartes d'entreprise et toutes les dépenses non déclarées de ces cartes</li> <li>Paramètres SAML et de groupe</li> </ul> Tous les comptes, espaces de travail, rapports, dépenses et autres données seront conservés. <br/><br/>Remarque : Vous pouvez supprimer ce domaine de votre liste de domaines en retirant l'adresse e-mail associée de vos <a href="#">méthodes de contact</a>.`,
         },
-        members: {title: 'Membres', findMember: 'Rechercher un membre'},
+        members: {
+            title: 'Membres',
+            findMember: 'Rechercher un membre',
+            closeAccount: 'Fermer le compte',
+            closeAccountPrompt: 'Êtes-vous sûr(e) ? Cette action est définitive.',
+            forceCloseAccount: 'Forcer la fermeture du compte',
+            safeCloseAccount: 'Fermer le compte en toute sécurité',
+            closeAccountInfo:
+                'Nous recommandons de fermer le compte en toute sécurité afin d’éviter de le fermer dans le cas où il y aurait : <ul><li>Des validations en attente</li><li>Des remboursements actifs</li><li>Aucune autre méthode de connexion</li></ul> Sinon, vous pouvez ignorer les précautions de sécurité ci-dessus et forcer la fermeture du compte sélectionné.',
+            error: {removeMember: 'Impossible de supprimer cet utilisateur. Veuillez réessayer.'},
+        },
     },
     gps: {
         tooltip: 'Suivi GPS en cours ! Quand vous avez terminé, arrêtez le suivi ci-dessous.',
diff --git a/src/languages/it.ts b/src/languages/it.ts
index ea7c9d93b6d7..53e8d5fcb71d 100644
--- a/src/languages/it.ts
+++ b/src/languages/it.ts
@@ -7995,7 +7995,17 @@ Ecco una *ricevuta di prova* per mostrarti come funziona:`,
             enterDomainName: 'Inserisci qui il tuo nome di dominio',
             resetDomainInfo: `Questa azione è <strong>permanente</strong> e i seguenti dati verranno eliminati: <br/> <ul><li>Connessioni alle carte aziendali e tutte le spese non riportate da tali carte</li> <li>Impostazioni SAML e di gruppo</li> </ul> Tutti gli account, gli spazi di lavoro, i report, le spese e gli altri dati rimarranno. <br/><br/>Nota: puoi rimuovere questo dominio dall'elenco dei tuoi domini eliminando l'email associata dalle tue <a href="#">modalità di contatto</a>.`,
         },
-        members: {title: 'Membri', findMember: 'Trova membro'},
+        members: {
+            title: 'Membri',
+            findMember: 'Trova membro',
+            closeAccount: 'Chiudi account',
+            closeAccountPrompt: 'Sei sicuro? Questa azione è permanente.',
+            forceCloseAccount: 'Forza chiusura account',
+            safeCloseAccount: 'Chiudi il conto in sicurezza',
+            closeAccountInfo:
+                'Consigliamo di chiudere l’account in modo sicuro per evitare problemi in caso di: <ul><li>Approvazioni in sospeso</li><li>Rimborsi attivi</li><li>Nessun metodo di accesso alternativo</li></ul> In caso contrario, puoi ignorare le precauzioni di sicurezza sopra indicate e forzare la chiusura dell’account selezionato.',
+            error: {removeMember: 'Impossibile rimuovere questo utente. Riprova.'},
+        },
     },
     gps: {
         tooltip: 'Monitoraggio GPS in corso! Quando hai finito, interrompi il monitoraggio qui sotto.',
diff --git a/src/languages/ja.ts b/src/languages/ja.ts
index 4f4633ca97c3..81db4475dbf2 100644
--- a/src/languages/ja.ts
+++ b/src/languages/ja.ts
@@ -7929,7 +7929,17 @@ Expensify の使い方をお見せするための*テストレシート*がこ
             enterDomainName: 'ここにドメイン名を入力してください',
             resetDomainInfo: `この操作は<strong>永久的</strong>であり、次のデータが削除されます：<br/> <ul><li>会社カードの接続およびそれらのカードからの未報告の経費</li> <li>SAML とグループ設定</li> </ul> すべてのアカウント、ワークスペース、レポート、経費、およびその他のデータは保持されます。<br/><br/>注：関連付けられているメールアドレスを<a href="#">連絡先方法</a>から削除することで、このドメインをドメイン一覧から消去できます。`,
         },
-        members: {title: 'メンバー', findMember: 'メンバーを検索'},
+        members: {
+            title: 'メンバー',
+            findMember: 'メンバーを検索',
+            closeAccount: 'アカウントを閉じる',
+            closeAccountPrompt: '本当によろしいですか？この操作は元に戻せません。',
+            forceCloseAccount: 'アカウントを強制的に閉鎖',
+            safeCloseAccount: 'アカウントを安全に閉じる',
+            closeAccountInfo:
+                '保留中の承認、処理中の精算、代替ログイン方法がない場合などでもアカウントを閉鎖できるように、安全にアカウントを閉鎖することを推奨します: <ul><li>保留中の承認</li><li>進行中の払い戻し</li><li>代替ログイン方法なし</li></ul> それ以外の場合は、上記の安全上の注意を無視して、選択したアカウントを強制的に閉鎖できます。',
+            error: {removeMember: 'このユーザーを削除できません。もう一度お試しください。'},
+        },
     },
     gps: {
         tooltip: 'GPS 追跡を進行中です！完了したら、下で追跡を停止してください。',
diff --git a/src/languages/nl.ts b/src/languages/nl.ts
index a827f018a614..59c1cebaf217 100644
--- a/src/languages/nl.ts
+++ b/src/languages/nl.ts
@@ -7980,7 +7980,17 @@ Hier is een *testbon* om je te laten zien hoe het werkt:`,
             enterDomainName: 'Voer hier uw domeinnaam in',
             resetDomainInfo: `Deze actie is <strong>definitief</strong> en de volgende gegevens worden verwijderd: <br/> <ul><li>Bedrijfskaartverbindingen en niet-ingediende uitgaven van die kaarten</li> <li>SAML- en groepsinstellingen</li> </ul> Alle accounts, werkruimten, rapporten, uitgaven en andere gegevens blijven behouden. <br/><br/>Opmerking: je kunt dit domein uit je domeinenlijst verwijderen door het gekoppelde e-mailadres uit je <a href="#">contactmethoden</a> te verwijderen.`,
         },
-        members: {title: 'Leden', findMember: 'Lid zoeken'},
+        members: {
+            title: 'Leden',
+            findMember: 'Lid zoeken',
+            closeAccount: 'Account sluiten',
+            closeAccountPrompt: 'Weet je het zeker? Deze actie is permanent.',
+            forceCloseAccount: 'Account geforceerd sluiten',
+            safeCloseAccount: 'Account veilig sluiten',
+            closeAccountInfo:
+                'We raden aan om de account veilig te sluiten om te vermijden dat je deze moet sluiten als er zijn: <ul><li>Openstaande goedkeuringen</li><li>Actieve terugbetalingen</li><li>Geen alternatieve inlogmethoden</li></ul> Anders kun je de bovenstaande veiligheidsmaatregelen negeren en de geselecteerde account geforceerd sluiten.',
+            error: {removeMember: 'Kan deze gebruiker niet verwijderen. Probeer het opnieuw.'},
+        },
     },
     gps: {
         tooltip: 'GPS-tracking bezig! Als je klaar bent, stop dan hieronder met tracken.',
diff --git a/src/languages/pl.ts b/src/languages/pl.ts
index 6c2f75012c66..3d4e6bb72bfe 100644
--- a/src/languages/pl.ts
+++ b/src/languages/pl.ts
@@ -7961,7 +7961,17 @@ Oto *paragon testowy*, który pokazuje, jak to działa:`,
             enterDomainName: 'Wpisz tutaj swoją nazwę domeny',
             resetDomainInfo: `Ta akcja jest <strong>trwała</strong> i następujące dane zostaną usunięte: <br/> <ul><li>Połączenia kart firmowych i wszystkie nierozliczone wydatki z tych kart</li> <li>Ustawienia SAML i grup</li> </ul> Wszystkie konta, przestrzenie robocze, raporty, wydatki i inne dane pozostaną bez zmian. <br/><br/>Uwaga: Możesz usunąć tę domenę z listy swoich domen, usuwając powiązany adres e-mail z <a href="#">metod kontaktu</a>.`,
         },
-        members: {title: 'Członkowie', findMember: 'Znajdź członka'},
+        members: {
+            title: 'Członkowie',
+            findMember: 'Znajdź członka',
+            closeAccount: 'Zamknij konto',
+            closeAccountPrompt: 'Czy na pewno? Ta czynność jest nieodwracalna.',
+            forceCloseAccount: 'Wymuś zamknięcie konta',
+            safeCloseAccount: 'Zamknij konto bezpiecznie',
+            closeAccountInfo:
+                'Zalecamy bezpieczne zamknięcie konta, aby pominąć jego zamykanie w przypadku, gdy występują: <ul><li>Oczekujące zatwierdzenia</li><li>Aktywne zwroty kosztów</li><li>Brak alternatywnych metod logowania</li></ul> W przeciwnym razie możesz zignorować powyższe środki ostrożności i wymusić zamknięcie wybranego konta.',
+            error: {removeMember: 'Nie można usunąć tego użytkownika. Spróbuj ponownie.'},
+        },
     },
     gps: {
         tooltip: 'Śledzenie GPS w toku! Gdy skończysz, zatrzymaj śledzenie poniżej.',
diff --git a/src/languages/pt-BR.ts b/src/languages/pt-BR.ts
index 4ab1bc7fcfb5..451719b30a10 100644
--- a/src/languages/pt-BR.ts
+++ b/src/languages/pt-BR.ts
@@ -7966,7 +7966,17 @@ Aqui está um *recibo de teste* para mostrar como funciona:`,
             enterDomainName: 'Insira seu nome de domínio aqui',
             resetDomainInfo: `Esta ação é <strong>permanente</strong> e os seguintes dados serão excluídos: <br/> <ul><li>Conexões de cartão corporativo e quaisquer despesas não reportadas desses cartões</li> <li>Configurações de SAML e de grupo</li> </ul> Todas as contas, workspaces, relatórios, despesas e outros dados permanecerão. <br/><br/>Observação: Você pode remover este domínio da sua lista de domínios excluindo o e-mail associado dos seus <a href="#">métodos de contato</a>.`,
         },
-        members: {title: 'Membros', findMember: 'Encontrar membro'},
+        members: {
+            title: 'Membros',
+            findMember: 'Encontrar membro',
+            closeAccount: 'Encerrar conta',
+            closeAccountPrompt: 'Tem certeza? Esta ação é permanente.',
+            forceCloseAccount: 'Forçar encerramento da conta',
+            safeCloseAccount: 'Fechar conta com segurança',
+            closeAccountInfo:
+                'Recomendamos fechar a conta com segurança para evitar o fechamento caso haja: <ul><li>aprovações pendentes</li><li>reembolsos ativos</li><li>nenhum método de login alternativo</li></ul> Caso contrário, você pode ignorar as precauções de segurança acima e forçar o fechamento da conta selecionada.',
+            error: {removeMember: 'Não foi possível remover este usuário. Tente novamente.'},
+        },
     },
     gps: {
         tooltip: 'Rastreamento por GPS em andamento! Quando terminar, pare o rastreamento abaixo.',
diff --git a/src/languages/zh-hans.ts b/src/languages/zh-hans.ts
index a96bf7150c15..667075a868fd 100644
--- a/src/languages/zh-hans.ts
+++ b/src/languages/zh-hans.ts
@@ -7779,7 +7779,17 @@ ${reportName}
             enterDomainName: '在此输入您的域名',
             resetDomainInfo: `此操作是<strong>永久性的</strong>，并且以下数据将被删除：<br/> <ul><li>公司卡连接以及这些卡片上所有未报销的费用</li> <li>SAML 和群组设置</li> </ul> 所有账户、工作区、报表、费用以及其他数据将会保留。<br/><br/>注意：您可以通过从<a href="#">联系方法</a>中移除关联的邮箱，将此域名从您的域名列表中清除。`,
         },
-        members: {title: '成员', findMember: '查找成员'},
+        members: {
+            title: '成员',
+            findMember: '查找成员',
+            closeAccount: '关闭账户',
+            closeAccountPrompt: '您确定吗？此操作是永久性的。',
+            forceCloseAccount: '强制关闭账户',
+            safeCloseAccount: '安全关闭账户',
+            closeAccountInfo:
+                '我们建议安全地关闭该账户，以便在存在以下情况时跳过关闭操作：<ul><li>待处理的审批</li><li>正在进行的报销</li><li>没有其他登录方式</li></ul>否则，您可以忽略上述安全预防措施并强制关闭所选账户。',
+            error: {removeMember: '无法移除此用户。请重试。'},
+        },
     },
     gps: {
         tooltip: 'GPS 跟踪进行中！完成后，请在下方停止跟踪。',
diff --git a/src/pages/domain/Members/DomainMembersPage.tsx b/src/pages/domain/Members/DomainMembersPage.tsx
index 4a253d908ff2..25370a335530 100644
--- a/src/pages/domain/Members/DomainMembersPage.tsx
+++ b/src/pages/domain/Members/DomainMembersPage.tsx
@@ -4,6 +4,7 @@ import {useMemoizedLazyIllustrations} from '@hooks/useLazyAsset';
 import useLocalize from '@hooks/useLocalize';
 import useOnyx from '@hooks/useOnyx';
 import {clearDomainMemberError} from '@libs/actions/Domain';
+import {getLatestError} from '@libs/ErrorUtils';
 import Navigation from '@navigation/Navigation';
 import type {PlatformStackScreenProps} from '@navigation/PlatformStackNavigation/types';
 import type {DomainSplitNavigatorParamList} from '@navigation/types';
@@ -33,9 +34,9 @@ function DomainMembersPage({route}: DomainMembersPageProps) {
         selector: memberPendingActionSelector,
     });
 
-    const getCustomRowProps = (accountID: number, accountEmail?: string) => ({
-        errors: accountEmail && domainErrors?.memberErrors?.[accountEmail]?.errors ? domainErrors?.memberErrors?.[accountEmail]?.errors : domainErrors?.memberErrors?.[accountID]?.errors,
-        pendingAction: accountEmail ? domainPendingAction?.[accountEmail] : undefined,
+    const getCustomRowProps = (accountID: number, email?: string) => ({
+        errors: email ? getLatestError(domainErrors?.memberErrors?.[email]?.errors) : (getLatestError(domainErrors?.memberErrors?.[accountID]?.errors) ?? undefined),
+        pendingAction: email ? domainPendingAction?.[email] : undefined,
     });
 
     return (

From cef1fb31e03a119ad59370a8d39336258ecf5ba9 Mon Sep 17 00:00:00 2001
From: sumo_slonik <jakub.nowakowski@swmansion.com>
Date: Thu, 22 Jan 2026 12:48:44 +0100
Subject: [PATCH 13/50] ready for review

---
 src/pages/domain/DomainInitialPage.tsx | 14 --------------
 1 file changed, 14 deletions(-)

diff --git a/src/pages/domain/DomainInitialPage.tsx b/src/pages/domain/DomainInitialPage.tsx
index 12ea98b5e2bf..aabcf2ccafae 100644
--- a/src/pages/domain/DomainInitialPage.tsx
+++ b/src/pages/domain/DomainInitialPage.tsx
@@ -73,20 +73,6 @@ function DomainInitialPage({route}: DomainInitialPageProps) {
             action: singleExecution(waitForNavigate(() => Navigation.navigate(ROUTES.DOMAIN_SAML.getRoute(domainAccountID)))),
             screenName: SCREENS.DOMAIN.SAML,
         },
-        // --- DEV only below this line
-        {
-            translationKey: 'domain.admins.title',
-            icon: icons.UserLock,
-            action: singleExecution(waitForNavigate(() => Navigation.navigate(ROUTES.DOMAIN_ADMINS.getRoute(domainAccountID)))),
-            screenName: SCREENS.DOMAIN.ADMINS,
-        },
-        {
-            translationKey: 'domain.members.title',
-            icon: icons.UserLock,
-            action: singleExecution(waitForNavigate(() => Navigation.navigate(ROUTES.DOMAIN_MEMBERS.getRoute(domainAccountID)))),
-            screenName: SCREENS.DOMAIN.MEMBERS,
-        },
-        // --- DEV only above this line
     ];
 
     const fetchDomainData = useCallback(() => {

From 167e2cdff0fbbdcb2bdc1c708963b6251625f9a5 Mon Sep 17 00:00:00 2001
From: sumo_slonik <jakub.nowakowski@swmansion.com>
Date: Fri, 23 Jan 2026 12:17:44 +0100
Subject: [PATCH 14/50] fixed comments after review

---
 src/CONST/index.ts                            |  2 +-
 src/components/DecisionModal.tsx              | 32 +++++-----
 src/libs/actions/Domain.ts                    |  4 +-
 .../Members/DomainMemberDetailsPage.tsx       | 14 ++---
 .../domain/Members/DomainMembersPage.tsx      | 11 ++--
 src/selectors/Domain.ts                       | 62 +++++++++----------
 src/selectors/PersonalDetails.ts              |  4 +-
 tests/actions/DomainTest.ts                   | 18 +++---
 tests/unit/DomainSelectorsTest.ts             | 42 +++++++------
 9 files changed, 100 insertions(+), 89 deletions(-)

diff --git a/src/CONST/index.ts b/src/CONST/index.ts
index 7aa056864273..96147ba552ae 100755
--- a/src/CONST/index.ts
+++ b/src/CONST/index.ts
@@ -1888,7 +1888,7 @@ const CONST = {
         PROCESS_REQUEST_DELAY_MS: 1000,
         MAX_PENDING_TIME_MS: 10 * 1000,
         RECHECK_INTERVAL_MS: 60 * 1000,
-        MAX_REQUEST_RETRIES: 10,
+        MAX_REQUEST_RETRIES: 2,
         MAX_OPEN_APP_REQUEST_RETRIES: 2,
         NETWORK_STATUS: {
             ONLINE: 'online',
diff --git a/src/components/DecisionModal.tsx b/src/components/DecisionModal.tsx
index c249a4451070..d05b5c866949 100644
--- a/src/components/DecisionModal.tsx
+++ b/src/components/DecisionModal.tsx
@@ -20,17 +20,17 @@ type DecisionModalProps = {
     /** Text content used in second button */
     secondOptionText: string;
 
-    /** Weather first option should use a success themed button */
-    firstOptionSuccess?: boolean;
+    /** Whether the first option uses a success-themed button */
+    isFirstOptionSuccess?: boolean;
 
-    /** Weather second option should use a success themed button  */
-    secondOptionSuccess?: boolean;
+    /** Whether the second option uses a success-themed button */
+    isSecondOptionSuccess?: boolean;
 
-    /** Weather first option should use a danger themed button  */
-    firstOptionDanger?: boolean;
+    /** Whether the first option uses a danger-themed button */
+    isFirstOptionDanger?: boolean;
 
-    /** Weather second option should use a danger themed button  */
-    secondOptionDanger?: boolean;
+    /** Whether the second option uses a danger-themed button */
+    isSecondOptionDanger?: boolean;
 
     /** onSubmit callback fired after clicking on first button */
     onFirstOptionSubmit?: () => void;
@@ -58,10 +58,10 @@ function DecisionModal({
     isSmallScreenWidth,
     onClose,
     isVisible,
-    firstOptionDanger = false,
-    firstOptionSuccess = true,
-    secondOptionSuccess = false,
-    secondOptionDanger = false,
+    isFirstOptionDanger = false,
+    isFirstOptionSuccess = true,
+    isSecondOptionSuccess = false,
+    isSecondOptionDanger = false,
 }: DecisionModalProps) {
     const styles = useThemeStyles();
 
@@ -84,8 +84,8 @@ function DecisionModal({
                 </View>
                 {!!firstOptionText && (
                     <Button
-                        success={firstOptionSuccess}
-                        danger={firstOptionDanger}
+                        success={isFirstOptionSuccess}
+                        danger={isFirstOptionDanger}
                         style={styles.mt5}
                         onPress={onFirstOptionSubmit}
                         pressOnEnter
@@ -97,8 +97,8 @@ function DecisionModal({
                     style={[firstOptionText ? styles.mt3 : styles.mt5, styles.noSelect]}
                     onPress={onSecondOptionSubmit}
                     text={secondOptionText}
-                    success={secondOptionSuccess}
-                    danger={secondOptionDanger}
+                    success={isSecondOptionSuccess}
+                    danger={isSecondOptionDanger}
                     large
                 />
             </View>
diff --git a/src/libs/actions/Domain.ts b/src/libs/actions/Domain.ts
index 25d52a49724f..1e2fe8bf069e 100644
--- a/src/libs/actions/Domain.ts
+++ b/src/libs/actions/Domain.ts
@@ -778,7 +778,7 @@ function resetDomain(domainAccountID: number, domainName: string, domain: Domain
 }
 
 /**
- * Clears errors after trying to reset domain
+ * Clears errors after trying to reset the domain
  */
 function clearDomainErrors(domainAccountID: number) {
     Onyx.merge(`${ONYXKEYS.COLLECTION.DOMAIN_ERRORS}${domainAccountID}`, {
@@ -789,7 +789,7 @@ function clearDomainErrors(domainAccountID: number) {
     });
 }
 
-/** Sends a request to remove user from a domain and close their account */
+/** Sends a request to remove a user from a domain and close their account */
 function closeUserAccount(
     domainAccountID: number,
     domain: string,
diff --git a/src/pages/domain/Members/DomainMemberDetailsPage.tsx b/src/pages/domain/Members/DomainMemberDetailsPage.tsx
index d97ece099fa5..04da76616f60 100644
--- a/src/pages/domain/Members/DomainMemberDetailsPage.tsx
+++ b/src/pages/domain/Members/DomainMemberDetailsPage.tsx
@@ -1,4 +1,5 @@
 import {adminAccountIDsSelector, domainNameSelector, selectSecurityGroupsForAccount} from '@selectors/Domain';
+import {personalDetailsSelector} from '@selectors/PersonalDetails';
 import React, {useState} from 'react';
 import type {OnyxEntry} from 'react-native-onyx';
 import Button from '@components/Button';
@@ -28,8 +29,9 @@ function DomainMemberDetailsPage({route}: DomainMemberDetailsPageProps) {
     const {domainAccountID, accountID} = route.params;
     const styles = useThemeStyles();
     const {translate} = useLocalize();
-    const icons = useMemoizedLazyExpensifyIcons(['Info', 'RemoveMembers'] as const);
+    const icons = useMemoizedLazyExpensifyIcons(['RemoveMembers'] as const);
     const [isModalVisible, setIsModalVisible] = useState(false);
+    // using isSmallScreenWidth here because DecisionModal expects isSmallScreen instead of houldUseNarrowLayout
     // eslint-disable-next-line rulesdir/prefer-shouldUseNarrowLayout-instead-of-isSmallScreenWidth
     const {isSmallScreenWidth} = useResponsiveLayout();
     const {showConfirmModal} = useConfirmModal();
@@ -39,16 +41,14 @@ function DomainMemberDetailsPage({route}: DomainMemberDetailsPageProps) {
         selector: adminAccountIDsSelector,
     });
 
-    // eslint-disable-next-line rulesdir/no-inline-useOnyx-selector
     const [securityGroupsData] = useOnyx(`${ONYXKEYS.COLLECTION.DOMAIN}${domainAccountID}`, {
         canBeMissing: true,
-        selector: (d: OnyxEntry<Domain>) => selectSecurityGroupsForAccount(d, accountID),
+        selector: selectSecurityGroupsForAccount(accountID),
     });
 
-    // eslint-disable-next-line rulesdir/no-inline-useOnyx-selector
     const [personalDetails] = useOnyx(ONYXKEYS.PERSONAL_DETAILS_LIST, {
         canBeMissing: true,
-        selector: (personalDetailsList: OnyxEntry<PersonalDetailsList>) => personalDetailsList?.[accountID],
+        selector: personalDetailsSelector(accountID),
     });
 
     const [domainName] = useOnyx(`${ONYXKEYS.COLLECTION.DOMAIN}${domainAccountID}`, {canBeMissing: false, selector: domainNameSelector});
@@ -114,8 +114,8 @@ function DomainMemberDetailsPage({route}: DomainMemberDetailsPageProps) {
                 firstOptionText={translate('domain.members.forceCloseAccount')}
                 isVisible={isModalVisible}
                 onClose={() => setIsModalVisible(false)}
-                firstOptionDanger
-                secondOptionSuccess
+                isFirstOptionDanger
+                isSecondOptionSuccess
             />
         </>
     );
diff --git a/src/pages/domain/Members/DomainMembersPage.tsx b/src/pages/domain/Members/DomainMembersPage.tsx
index 25370a335530..45c0197be293 100644
--- a/src/pages/domain/Members/DomainMembersPage.tsx
+++ b/src/pages/domain/Members/DomainMembersPage.tsx
@@ -34,10 +34,13 @@ function DomainMembersPage({route}: DomainMembersPageProps) {
         selector: memberPendingActionSelector,
     });
 
-    const getCustomRowProps = (accountID: number, email?: string) => ({
-        errors: email ? getLatestError(domainErrors?.memberErrors?.[email]?.errors) : (getLatestError(domainErrors?.memberErrors?.[accountID]?.errors) ?? undefined),
-        pendingAction: email ? domainPendingAction?.[email] : undefined,
-    });
+    const getCustomRowProps = (accountID: number, email?: string) => {
+        const errorKey = email ?? accountID;
+        const errors = getLatestError(domainErrors?.memberErrors?.[errorKey]?.errors) ?? undefined;
+        const pendingAction = email ? domainPendingAction?.[email] : undefined;
+
+        return {errors, pendingAction};
+    };
 
     return (
         <BaseDomainMembersPage
diff --git a/src/selectors/Domain.ts b/src/selectors/Domain.ts
index b1f8cda75f09..32d3dd01f28f 100644
--- a/src/selectors/Domain.ts
+++ b/src/selectors/Domain.ts
@@ -101,46 +101,44 @@ function isSecurityGroupEntry(entry: [string, unknown]): entry is [SecurityGroup
     const [key, value] = entry;
     return key.startsWith(CONST.DOMAIN.DOMAIN_SECURITY_GROUP_PREFIX) && typeof value === 'object' && value !== null && 'shared' in value;
 }
-
 /**
- * Gets all security groups for a given account ID.
- * Searches through all security groups in the domain and returns those
- * where the account ID appears in the 'shared' property.
+ * Creates a selector function to get all security groups for a specific account ID.
+ * The returned function searches through a domain and returns groups where
+ * the account ID is present in the 'shared' property.
  *
- * @param domain - The domain object from Onyx
- * @param accountID - The account ID to search for
- * @returns Object containing:
- *   - keys: Array of security group keys the account belongs to
- *   - securityGroups: Record of security group data keyed by their prefixed IDs
+ * @param accountID - The account ID to filter by
+ * @returns A function that takes a domain and returns the filtered keys and security group data
  */
-function selectSecurityGroupsForAccount(
-    domain: Domain | undefined,
-    accountID: number,
-): {
-    keys: SecurityGroupKey[];
-    securityGroups: PrefixedRecord<typeof CONST.DOMAIN.DOMAIN_SECURITY_GROUP_PREFIX, Partial<DomainSecurityGroup>>;
-} {
-    if (!domain) {
-        return {keys: [], securityGroups: {}};
-    }
+function selectSecurityGroupsForAccount(accountID: number) {
+    return (
+        domain: Domain | undefined,
+    ): {
+        keys: SecurityGroupKey[];
+        securityGroups: PrefixedRecord<typeof CONST.DOMAIN.DOMAIN_SECURITY_GROUP_PREFIX, Partial<DomainSecurityGroup>>;
+    } => {
+        if (!domain) {
+            return {keys: [], securityGroups: {}};
+        }
 
-    const accountIDStr = String(accountID);
-    const keys: SecurityGroupKey[] = [];
-    const securityGroups: PrefixedRecord<typeof CONST.DOMAIN.DOMAIN_SECURITY_GROUP_PREFIX, Partial<DomainSecurityGroup>> = {};
+        const accountIDStr = String(accountID);
+        const keys: SecurityGroupKey[] = [];
+        const securityGroups: PrefixedRecord<typeof CONST.DOMAIN.DOMAIN_SECURITY_GROUP_PREFIX, Partial<DomainSecurityGroup>> = {};
 
-    for (const entry of Object.entries(domain)) {
-        if (!isSecurityGroupEntry(entry)) {
-            continue;
-        }
+        for (const entry of Object.entries(domain)) {
+            if (!isSecurityGroupEntry(entry)) {
+                continue;
+            }
+
+            const [key, group] = entry;
 
-        const [key, group] = entry;
-        if (accountIDStr in group.shared) {
-            keys.push(key);
-            securityGroups[key] = group;
+            if (group.shared && accountIDStr in group.shared) {
+                keys.push(key);
+                securityGroups[key] = group;
+            }
         }
-    }
 
-    return {keys, securityGroups};
+        return {keys, securityGroups};
+    };
 }
 
 const memberPendingActionSelector = (pendingAction: OnyxEntry<DomainPendingActions>) => pendingAction?.members ?? {};
diff --git a/src/selectors/PersonalDetails.ts b/src/selectors/PersonalDetails.ts
index a11d3ff3dea6..eb1975294558 100644
--- a/src/selectors/PersonalDetails.ts
+++ b/src/selectors/PersonalDetails.ts
@@ -11,4 +11,6 @@ const createPersonalDetailsSelector = <T>(personalDetails: OnyxEntry<PersonalDet
 const personalDetailsByEmailSelector = (personalDetails: OnyxEntry<PersonalDetailsList>) =>
     personalDetails ? lodashMapKeys(personalDetails, (value, key) => value?.login ?? key) : undefined;
 
-export {createPersonalDetailsSelector, personalDetailsByEmailSelector};
+const personalDetailsSelector = (accountID: number) => (personalDetailsList: OnyxEntry<PersonalDetailsList>) => personalDetailsList?.[accountID];
+
+export {createPersonalDetailsSelector, personalDetailsByEmailSelector, personalDetailsSelector};
diff --git a/tests/actions/DomainTest.ts b/tests/actions/DomainTest.ts
index e2f1b4df6f3d..9810ec4be361 100644
--- a/tests/actions/DomainTest.ts
+++ b/tests/actions/DomainTest.ts
@@ -127,13 +127,13 @@ describe('actions/Domain', () => {
         const targetEmail = 'user@test.com';
         const securityGroupKey = `${CONST.DOMAIN.DOMAIN_SECURITY_GROUP_PREFIX}1` as const;
         const securityGroupIDs = [securityGroupKey];
-        const securityGroups = {
+        const securityGroups: PrefixedRecord<typeof CONST.DOMAIN.DOMAIN_SECURITY_GROUP_PREFIX, Partial<DomainSecurityGroup>> = {
             [securityGroupKey]: {
                 shared: {
-                    [accountID]: 'read' as const,
+                    [accountID]: 'read',
                 },
             },
-        } as unknown as PrefixedRecord<typeof CONST.DOMAIN.DOMAIN_SECURITY_GROUP_PREFIX, DomainSecurityGroup>;
+        };
 
         closeUserAccount(domainAccountID, domainName, accountID, targetEmail, securityGroupIDs, securityGroups);
 
@@ -187,10 +187,14 @@ describe('actions/Domain', () => {
         const securityGroupKey1 = `${CONST.DOMAIN.DOMAIN_SECURITY_GROUP_PREFIX}1` as const;
         const securityGroupKey2 = `${CONST.DOMAIN.DOMAIN_SECURITY_GROUP_PREFIX}2` as const;
         const securityGroupIDs = [securityGroupKey1, securityGroupKey2];
-        const securityGroups = {
-            [securityGroupKey1]: {shared: {[accountID]: 'read' as const}},
-            [securityGroupKey2]: {shared: {[accountID]: 'read' as const}},
-        } as unknown as PrefixedRecord<typeof CONST.DOMAIN.DOMAIN_SECURITY_GROUP_PREFIX, DomainSecurityGroup>;
+        const securityGroups: PrefixedRecord<typeof CONST.DOMAIN.DOMAIN_SECURITY_GROUP_PREFIX, Partial<DomainSecurityGroup>> = {
+            [securityGroupKey1]: {
+                shared: {[accountID]: 'read'},
+            },
+            [securityGroupKey2]: {
+                shared: {[accountID]: 'read'},
+            },
+        };
         closeUserAccount(domainAccountID, domainName, accountID, targetEmail, securityGroupIDs, securityGroups);
 
         expect(apiWriteSpy).toHaveBeenCalledWith(
diff --git a/tests/unit/DomainSelectorsTest.ts b/tests/unit/DomainSelectorsTest.ts
index 54f0839ddbbf..5e5a432061f4 100644
--- a/tests/unit/DomainSelectorsTest.ts
+++ b/tests/unit/DomainSelectorsTest.ts
@@ -9,7 +9,7 @@ import {
 } from '@selectors/Domain';
 import type {OnyxEntry} from 'react-native-onyx';
 import CONST from '@src/CONST';
-import type {CardFeeds, Domain, DomainPendingActions, DomainSettings} from '@src/types/onyx';
+import type {CardFeeds, Domain, DomainPendingActions, DomainSecurityGroup, DomainSettings} from '@src/types/onyx';
 
 describe('domainSelectors', () => {
     const userID1 = 123;
@@ -281,39 +281,43 @@ describe('domainSelectors', () => {
                 email: 'test@example.com',
             } as Domain;
 
-            const result = selectSecurityGroupsForAccount(domain, 123);
+            const result = selectSecurityGroupsForAccount(123)(domain);
 
             expect(result.keys).toEqual([]);
             expect(result.securityGroups).toEqual({});
         });
 
         it('Should return empty arrays when account is not in any security group', () => {
+            const securityGroup = {
+                enableRestrictedPrimaryLogin: false,
+                enableRestrictedPolicyCreation: false,
+                shared: {
+                    // eslint-disable-next-line @typescript-eslint/naming-convention
+                    '456': 'read',
+                    // eslint-disable-next-line @typescript-eslint/naming-convention
+                    '789': 'read',
+                },
+            } as DomainSecurityGroup;
+
             const domain = {
                 validated: true,
                 accountID: 1,
                 email: 'test@example.com',
-                [`${CONST.DOMAIN.DOMAIN_SECURITY_GROUP_PREFIX}1`]: {
-                    shared: {
-                        // eslint-disable-next-line @typescript-eslint/naming-convention
-                        '456': 'read',
-                        // eslint-disable-next-line @typescript-eslint/naming-convention
-                        '789': 'read',
-                    },
-                },
-            } as unknown as Domain;
+                [`${CONST.DOMAIN.DOMAIN_SECURITY_GROUP_PREFIX}1`]: securityGroup,
+            } as Domain;
 
-            const result = selectSecurityGroupsForAccount(domain, 123);
+            const result = selectSecurityGroupsForAccount(123)(domain);
 
-            expect(result.keys).toEqual([]);
+            expect(result.keys).toHaveLength(0);
             expect(result.securityGroups).toEqual({});
         });
 
         it('Should return multiple security groups when account belongs to several', () => {
             /* eslint-disable @typescript-eslint/naming-convention */
-            const group1 = {shared: {'123': 'read', '456': 'read'}};
-            const group2 = {shared: {'123': 'read', '789': 'read'}};
-            const group3 = {shared: {'999': 'read'}};
-            /* eslint-enable @typescript-eslint/naming-convention */
+            const group1 = {shared: {'123': 'read', '456': 'read'}, enableRestrictedPrimaryLogin: true, enableRestrictedPolicyCreation: true} as DomainSecurityGroup;
+            const group2 = {shared: {'123': 'read', '789': 'read'}, enableRestrictedPrimaryLogin: true, enableRestrictedPolicyCreation: true} as DomainSecurityGroup;
+            const group3 = {shared: {'999': 'read'}, enableRestrictedPrimaryLogin: true, enableRestrictedPolicyCreation: true} as DomainSecurityGroup;
+
             const domain = {
                 validated: true,
                 accountID: 1,
@@ -321,9 +325,9 @@ describe('domainSelectors', () => {
                 [`${CONST.DOMAIN.DOMAIN_SECURITY_GROUP_PREFIX}1`]: group1,
                 [`${CONST.DOMAIN.DOMAIN_SECURITY_GROUP_PREFIX}2`]: group2,
                 [`${CONST.DOMAIN.DOMAIN_SECURITY_GROUP_PREFIX}3`]: group3,
-            } as unknown as Domain;
+            } as Domain;
 
-            const result = selectSecurityGroupsForAccount(domain, 123);
+            const result = selectSecurityGroupsForAccount(123)(domain);
 
             expect(result.keys).toHaveLength(2);
             expect(result.keys).toContain(`${CONST.DOMAIN.DOMAIN_SECURITY_GROUP_PREFIX}1`);

From 1835e11f44486c5bea1a3a9ad26d8d913a40c57e Mon Sep 17 00:00:00 2001
From: sumo_slonik <jakub.nowakowski@swmansion.com>
Date: Fri, 23 Jan 2026 12:42:17 +0100
Subject: [PATCH 15/50] fixed comments after review

---
 src/pages/domain/Members/DomainMemberDetailsPage.tsx | 4 +---
 1 file changed, 1 insertion(+), 3 deletions(-)

diff --git a/src/pages/domain/Members/DomainMemberDetailsPage.tsx b/src/pages/domain/Members/DomainMemberDetailsPage.tsx
index 04da76616f60..056facbdfac4 100644
--- a/src/pages/domain/Members/DomainMemberDetailsPage.tsx
+++ b/src/pages/domain/Members/DomainMemberDetailsPage.tsx
@@ -1,7 +1,6 @@
 import {adminAccountIDsSelector, domainNameSelector, selectSecurityGroupsForAccount} from '@selectors/Domain';
 import {personalDetailsSelector} from '@selectors/PersonalDetails';
 import React, {useState} from 'react';
-import type {OnyxEntry} from 'react-native-onyx';
 import Button from '@components/Button';
 import DecisionModal from '@components/DecisionModal';
 import FullScreenLoadingIndicator from '@components/FullscreenLoadingIndicator';
@@ -20,7 +19,6 @@ import type {SettingsNavigatorParamList} from '@navigation/types';
 import BaseDomainMemberDetailsComponent from '@pages/domain/BaseDomainMemberDetailsComponent';
 import ONYXKEYS from '@src/ONYXKEYS';
 import type SCREENS from '@src/SCREENS';
-import type {Domain, PersonalDetailsList} from '@src/types/onyx';
 import isLoadingOnyxValue from '@src/types/utils/isLoadingOnyxValue';
 
 type DomainMemberDetailsPageProps = PlatformStackScreenProps<SettingsNavigatorParamList, typeof SCREENS.DOMAIN.MEMBER_DETAILS>;
@@ -31,7 +29,7 @@ function DomainMemberDetailsPage({route}: DomainMemberDetailsPageProps) {
     const {translate} = useLocalize();
     const icons = useMemoizedLazyExpensifyIcons(['RemoveMembers'] as const);
     const [isModalVisible, setIsModalVisible] = useState(false);
-    // using isSmallScreenWidth here because DecisionModal expects isSmallScreen instead of houldUseNarrowLayout
+    // using isSmallScreenWidth here because DecisionModal expects isSmallScreen instead of shouldUseNarrowLayout
     // eslint-disable-next-line rulesdir/prefer-shouldUseNarrowLayout-instead-of-isSmallScreenWidth
     const {isSmallScreenWidth} = useResponsiveLayout();
     const {showConfirmModal} = useConfirmModal();

From 46585376975fa4f9f9d9cbe29d674e1e2f2d74f0 Mon Sep 17 00:00:00 2001
From: sumo_slonik <jakub.nowakowski@swmansion.com>
Date: Fri, 23 Jan 2026 12:44:24 +0100
Subject: [PATCH 16/50] fix MAX_REQUEST_RETRIES

---
 src/CONST/index.ts | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/CONST/index.ts b/src/CONST/index.ts
index 96147ba552ae..7aa056864273 100755
--- a/src/CONST/index.ts
+++ b/src/CONST/index.ts
@@ -1888,7 +1888,7 @@ const CONST = {
         PROCESS_REQUEST_DELAY_MS: 1000,
         MAX_PENDING_TIME_MS: 10 * 1000,
         RECHECK_INTERVAL_MS: 60 * 1000,
-        MAX_REQUEST_RETRIES: 2,
+        MAX_REQUEST_RETRIES: 10,
         MAX_OPEN_APP_REQUEST_RETRIES: 2,
         NETWORK_STATUS: {
             ONLINE: 'online',

From 60b407ad5bbc2941cbb227e2a9e1808c890dce58 Mon Sep 17 00:00:00 2001
From: sumo_slonik <jakub.nowakowski@swmansion.com>
Date: Fri, 23 Jan 2026 13:22:32 +0100
Subject: [PATCH 17/50] fix eslints and prietter

---
 src/libs/actions/Domain.ts                    | 15 ++++---------
 .../Members/DomainMemberDetailsPage.tsx       | 11 +++++-----
 src/selectors/Domain.ts                       | 10 ++-------
 src/types/onyx/Domain.ts                      | 21 +++++++++++++++++-
 tests/actions/DomainTest.ts                   | 22 ++++++++++++++++---
 5 files changed, 51 insertions(+), 28 deletions(-)

diff --git a/src/libs/actions/Domain.ts b/src/libs/actions/Domain.ts
index 1e2fe8bf069e..4b3d27a89e39 100644
--- a/src/libs/actions/Domain.ts
+++ b/src/libs/actions/Domain.ts
@@ -14,6 +14,7 @@ import {getMicroSecondOnyxErrorWithTranslationKey} from '@libs/ErrorUtils';
 import CONST from '@src/CONST';
 import ONYXKEYS from '@src/ONYXKEYS';
 import type {Domain, DomainSecurityGroup} from '@src/types/onyx';
+import type {SecurityGroupsData} from '@src/types/onyx/Domain';
 import type PrefixedRecord from '@src/types/utils/PrefixedRecord';
 import type {ScimTokenWithState} from './ScimToken/ScimTokenUtils';
 import {ScimTokenState} from './ScimToken/ScimTokenUtils';
@@ -790,18 +791,10 @@ function clearDomainErrors(domainAccountID: number) {
 }
 
 /** Sends a request to remove a user from a domain and close their account */
-function closeUserAccount(
-    domainAccountID: number,
-    domain: string,
-    accountID: number,
-    targetEmail: string,
-    securityGroupIDs: Array<`${typeof CONST.DOMAIN.DOMAIN_SECURITY_GROUP_PREFIX}${string}`> = [],
-    securityGroups: PrefixedRecord<typeof CONST.DOMAIN.DOMAIN_SECURITY_GROUP_PREFIX, Partial<DomainSecurityGroup>> = {},
-    overrideProcessingReports = false,
-) {
+function closeUserAccount(domainAccountID: number, domain: string, accountID: number, targetEmail: string, securityGroupsData: SecurityGroupsData, overrideProcessingReports = false) {
     const optimisticValue: PrefixedRecord<typeof CONST.DOMAIN.DOMAIN_SECURITY_GROUP_PREFIX, Partial<DomainSecurityGroup>> = {};
     const failureValue: PrefixedRecord<typeof CONST.DOMAIN.DOMAIN_SECURITY_GROUP_PREFIX, Partial<DomainSecurityGroup>> = {};
-    for (const groupID of securityGroupIDs) {
+    for (const groupID of securityGroupsData.keys) {
         optimisticValue[groupID] = {
             shared: {
                 [accountID]: null,
@@ -809,7 +802,7 @@ function closeUserAccount(
         };
         failureValue[groupID] = {
             shared: {
-                [accountID]: securityGroups?.[groupID]?.shared?.[accountID] ?? null,
+                [accountID]: securityGroupsData.securityGroups?.[groupID]?.shared?.[accountID] ?? null,
             },
         };
     }
diff --git a/src/pages/domain/Members/DomainMemberDetailsPage.tsx b/src/pages/domain/Members/DomainMemberDetailsPage.tsx
index 056facbdfac4..06b7e4131a12 100644
--- a/src/pages/domain/Members/DomainMemberDetailsPage.tsx
+++ b/src/pages/domain/Members/DomainMemberDetailsPage.tsx
@@ -29,9 +29,7 @@ function DomainMemberDetailsPage({route}: DomainMemberDetailsPageProps) {
     const {translate} = useLocalize();
     const icons = useMemoizedLazyExpensifyIcons(['RemoveMembers'] as const);
     const [isModalVisible, setIsModalVisible] = useState(false);
-    // using isSmallScreenWidth here because DecisionModal expects isSmallScreen instead of shouldUseNarrowLayout
-    // eslint-disable-next-line rulesdir/prefer-shouldUseNarrowLayout-instead-of-isSmallScreenWidth
-    const {isSmallScreenWidth} = useResponsiveLayout();
+    const {shouldUseNarrowLayout} = useResponsiveLayout();
     const {showConfirmModal} = useConfirmModal();
 
     const [adminAccountIDs, domainMetadata] = useOnyx(`${ONYXKEYS.COLLECTION.DOMAIN}${domainAccountID}`, {
@@ -61,6 +59,9 @@ function DomainMemberDetailsPage({route}: DomainMemberDetailsPageProps) {
     }
 
     const handleCloseAccount = async (force: boolean) => {
+        if (!securityGroupsData) {
+            return;
+        }
         setIsModalVisible(false);
         const result = await showConfirmModal({
             title: translate('domain.members.closeAccount'),
@@ -74,7 +75,7 @@ function DomainMemberDetailsPage({route}: DomainMemberDetailsPageProps) {
             setIsModalVisible(true);
             return;
         }
-        closeUserAccount(domainAccountID, domainName ?? '', accountID, memberLogin, securityGroupsData?.keys ?? [], securityGroupsData?.securityGroups, force);
+        closeUserAccount(domainAccountID, domainName ?? '', accountID, memberLogin, securityGroupsData, force);
         Navigation.dismissModal();
     };
 
@@ -105,7 +106,7 @@ function DomainMemberDetailsPage({route}: DomainMemberDetailsPageProps) {
             <DecisionModal
                 title={translate('domain.members.closeAccount')}
                 prompt={translate('domain.members.closeAccountInfo')}
-                isSmallScreenWidth={isSmallScreenWidth}
+                isSmallScreenWidth={shouldUseNarrowLayout}
                 onSecondOptionSubmit={handleSafeCloseAccount}
                 onFirstOptionSubmit={handleForceCloseAccount}
                 secondOptionText={translate('domain.members.safeCloseAccount')}
diff --git a/src/selectors/Domain.ts b/src/selectors/Domain.ts
index 32d3dd01f28f..502ab8cbbd09 100644
--- a/src/selectors/Domain.ts
+++ b/src/selectors/Domain.ts
@@ -2,6 +2,7 @@ import {Str} from 'expensify-common';
 import type {OnyxEntry} from 'react-native-onyx';
 import CONST from '@src/CONST';
 import type {CardFeeds, Domain, DomainPendingActions, DomainSecurityGroup, DomainSettings, SamlMetadata} from '@src/types/onyx';
+import type {SecurityGroupKey, SecurityGroupsData} from '@src/types/onyx/Domain';
 import getEmptyArray from '@src/types/utils/getEmptyArray';
 import type PrefixedRecord from '@src/types/utils/PrefixedRecord';
 
@@ -92,8 +93,6 @@ function memberAccountIDsSelector(domain: OnyxEntry<Domain>): number[] {
     return uniqueIDs.length > 0 ? uniqueIDs : getEmptyArray<number>();
 }
 
-type SecurityGroupKey = `${typeof CONST.DOMAIN.DOMAIN_SECURITY_GROUP_PREFIX}${string}`;
-
 /**
  * Type guard to check if a domain entry is a security group entry.
  */
@@ -110,12 +109,7 @@ function isSecurityGroupEntry(entry: [string, unknown]): entry is [SecurityGroup
  * @returns A function that takes a domain and returns the filtered keys and security group data
  */
 function selectSecurityGroupsForAccount(accountID: number) {
-    return (
-        domain: Domain | undefined,
-    ): {
-        keys: SecurityGroupKey[];
-        securityGroups: PrefixedRecord<typeof CONST.DOMAIN.DOMAIN_SECURITY_GROUP_PREFIX, Partial<DomainSecurityGroup>>;
-    } => {
+    return (domain: Domain | undefined): SecurityGroupsData => {
         if (!domain) {
             return {keys: [], securityGroups: {}};
         }
diff --git a/src/types/onyx/Domain.ts b/src/types/onyx/Domain.ts
index b89c6efe56cc..0e3d45724dd1 100644
--- a/src/types/onyx/Domain.ts
+++ b/src/types/onyx/Domain.ts
@@ -101,6 +101,25 @@ type DomainSecurityGroup = SecurityGroup & {
     shared: Record<string, 'read' | null>;
 };
 
-export {type SamlMetadata, type DomainSecurityGroup};
+/**
+ * Security group key prefix.
+ * @example `${CONST.DOMAIN.DOMAIN_SECURITY_GROUP_PREFIX}1234567890`
+ */
+type SecurityGroupKey = `${typeof CONST.DOMAIN.DOMAIN_SECURITY_GROUP_PREFIX}${string}`;
+
+/**
+ * Combination of security group keys and security groups that are shared with the domain.
+ */
+type SecurityGroupsData = {
+    /**
+     * IDs of the security groups that are shared with the domain.
+     */
+    keys: SecurityGroupKey[];
+    /**
+     * Security groups that are shared with the domain.
+     */
+    securityGroups: PrefixedRecord<typeof CONST.DOMAIN.DOMAIN_SECURITY_GROUP_PREFIX, Partial<DomainSecurityGroup>>;
+};
+export {type SamlMetadata, type DomainSecurityGroup, type SecurityGroupsData, type SecurityGroupKey};
 
 export default Domain;
diff --git a/tests/actions/DomainTest.ts b/tests/actions/DomainTest.ts
index 9810ec4be361..2aa1be325f68 100644
--- a/tests/actions/DomainTest.ts
+++ b/tests/actions/DomainTest.ts
@@ -135,7 +135,10 @@ describe('actions/Domain', () => {
             },
         };
 
-        closeUserAccount(domainAccountID, domainName, accountID, targetEmail, securityGroupIDs, securityGroups);
+        closeUserAccount(domainAccountID, domainName, accountID, targetEmail, {
+            keys: securityGroupIDs,
+            securityGroups,
+        });
 
         expect(apiWriteSpy).toHaveBeenCalledWith(
             WRITE_COMMANDS.DELETE_DOMAIN_MEMBER,
@@ -195,7 +198,10 @@ describe('actions/Domain', () => {
                 shared: {[accountID]: 'read'},
             },
         };
-        closeUserAccount(domainAccountID, domainName, accountID, targetEmail, securityGroupIDs, securityGroups);
+        closeUserAccount(domainAccountID, domainName, accountID, targetEmail, {
+            keys: securityGroupIDs,
+            securityGroups,
+        });
 
         expect(apiWriteSpy).toHaveBeenCalledWith(
             WRITE_COMMANDS.DELETE_DOMAIN_MEMBER,
@@ -223,7 +229,17 @@ describe('actions/Domain', () => {
         const accountID = 456;
         const targetEmail = 'user@test.com';
 
-        closeUserAccount(domainAccountID, domainName, accountID, targetEmail, [], {}, true);
+        closeUserAccount(
+            domainAccountID,
+            domainName,
+            accountID,
+            targetEmail,
+            {
+                keys: [],
+                securityGroups: {},
+            },
+            true,
+        );
 
         expect(apiWriteSpy).toHaveBeenCalledWith(WRITE_COMMANDS.DELETE_DOMAIN_MEMBER, {domain: domainName, targetEmail, overrideProcessingReports: true}, expect.any(Object));
 

From 6e714fec27d5a8203ca91b98382b10c633691020 Mon Sep 17 00:00:00 2001
From: sumo_slonik <jakub.nowakowski@swmansion.com>
Date: Tue, 27 Jan 2026 14:21:15 +0100
Subject: [PATCH 18/50] add unit test to new selectors

---
 src/languages/en.ts                       |  2 +-
 src/libs/actions/Domain.ts                |  6 +--
 src/selectors/Domain.ts                   |  1 +
 src/types/onyx/index.ts                   | 66 ++++++++++++-----------
 tests/unit/DomainSelectorsTest.ts         | 28 ++++++++++
 tests/unit/PersonalDetailsSelectorTest.ts | 30 +++++++++++
 6 files changed, 96 insertions(+), 37 deletions(-)
 create mode 100644 tests/unit/PersonalDetailsSelectorTest.ts

diff --git a/src/languages/en.ts b/src/languages/en.ts
index b00a2ffb8b39..137b9595b7cf 100755
--- a/src/languages/en.ts
+++ b/src/languages/en.ts
@@ -8018,7 +8018,7 @@ const translations = {
             safeCloseAccount: 'Close account safely',
             closeAccountInfo:
                 'We recommend closing the account safely to skip closing it in case there are: <ul><li>Pending approvals</li><li>Active reimbursements</li><li>No alternative login methods</li></ul> Otherwise, you can ignore the safety precautions above and force close the selected account.',
-            errors: {
+            error: {
                 addMember: 'Unable to add this member. Please try again.',
                 removeMember: 'Unable to remove this user. Please try again.',
             },
diff --git a/src/libs/actions/Domain.ts b/src/libs/actions/Domain.ts
index 9c48e126950b..0d09706cc6cb 100644
--- a/src/libs/actions/Domain.ts
+++ b/src/libs/actions/Domain.ts
@@ -15,9 +15,7 @@ import {getMicroSecondOnyxErrorWithTranslationKey} from '@libs/ErrorUtils';
 import {generateAccountID} from '@libs/UserUtils';
 import CONST from '@src/CONST';
 import ONYXKEYS from '@src/ONYXKEYS';
-import type {Domain, DomainSecurityGroup} from '@src/types/onyx';
-import type {SecurityGroupsData} from '@src/types/onyx/Domain';
-import type {DomainSecurityGroup} from '@src/types/onyx/Domain';
+import type {Domain, DomainSecurityGroup, SecurityGroupsData} from '@src/types/onyx';
 import type PrefixedRecord from '@src/types/utils/PrefixedRecord';
 import type {ScimTokenWithState} from './ScimToken/ScimTokenUtils';
 import {ScimTokenState} from './ScimToken/ScimTokenUtils';
@@ -894,7 +892,7 @@ function addMemberToDomain(domainAccountID: number, email: string, defaultSecuri
             value: {
                 memberErrors: {
                     [email]: {
-                        errors: getMicroSecondOnyxErrorWithTranslationKey('domain.members.errors.addMember'),
+                        errors: getMicroSecondOnyxErrorWithTranslationKey('domain.members.error.addMember'),
                     },
                 },
             },
diff --git a/src/selectors/Domain.ts b/src/selectors/Domain.ts
index 94f3534aa909..bf52b717c789 100644
--- a/src/selectors/Domain.ts
+++ b/src/selectors/Domain.ts
@@ -155,4 +155,5 @@ export {
     defaultSecurityGroupIDSelector,
     selectSecurityGroupsForAccount,
     memberPendingActionSelector,
+    isSecurityGroupEntry,
 };
diff --git a/src/types/onyx/index.ts b/src/types/onyx/index.ts
index 698337f0a3f3..29e0b785a7c3 100644
--- a/src/types/onyx/index.ts
+++ b/src/types/onyx/index.ts
@@ -1,11 +1,11 @@
-import type {OnboardingPurpose} from '@libs/actions/Welcome/OnboardingFlow';
-import type {FileObject} from '@src/types/utils/Attachment';
+import type { OnboardingPurpose } from '@libs/actions/Welcome/OnboardingFlow';
+import type { FileObject } from '@src/types/utils/Attachment';
 import type Account from './Account';
 import type AccountData from './AccountData';
 import type AppReview from './AppReview';
-import type {ApprovalWorkflowOnyx} from './ApprovalWorkflow';
-import type {AssignCard, AssignCardData} from './AssignCard';
-import type {BankAccountList} from './BankAccount';
+import type { ApprovalWorkflowOnyx } from './ApprovalWorkflow';
+import type { AssignCard, AssignCardData } from './AssignCard';
+import type { BankAccountList } from './BankAccount';
 import type BankAccount from './BankAccount';
 import type BankAccountShareDetails from './BankAccountShareDetails';
 import type Beta from './Beta';
@@ -16,23 +16,23 @@ import type BillingStatus from './BillingStatus';
 import type BlockedFromConcierge from './BlockedFromConcierge';
 import type CancellationDetails from './CancellationDetails';
 import type Card from './Card';
-import type {CardList, FailedCompanyCardAssignment, FailedCompanyCardAssignments, IssueNewCard, ProvisioningCardData, WorkspaceCardsList} from './Card';
+import type { CardList, FailedCompanyCardAssignment, FailedCompanyCardAssignments, IssueNewCard, ProvisioningCardData, WorkspaceCardsList } from './Card';
 import type CardContinuousReconciliation from './CardContinuousReconciliation';
 import type CardFeeds from './CardFeeds';
-import type {AddNewCompanyCardFeed, CombinedCardFeed, CombinedCardFeeds, CompanyCardFeed, CompanyCardFeedWithDomainID, DomainSettings, FundID} from './CardFeeds';
+import type { AddNewCompanyCardFeed, CombinedCardFeed, CombinedCardFeeds, CompanyCardFeed, CompanyCardFeedWithDomainID, DomainSettings, FundID } from './CardFeeds';
 import type CardOnWaitlist from './CardOnWaitlist';
-import type {CapturedLogs, Log} from './Console';
-import type {CorpayFields, CorpayFormField} from './CorpayFields';
-import type {CorpayOnboardingFields} from './CorpayOnboardingFields';
+import type { CapturedLogs, Log } from './Console';
+import type { CorpayFields, CorpayFormField } from './CorpayFields';
+import type { CorpayOnboardingFields } from './CorpayOnboardingFields';
 import type Credentials from './Credentials';
 import type Currency from './Currency';
-import type {CurrencyList} from './Currency';
+import type { CurrencyList } from './Currency';
 import type CustomStatusDraft from './CustomStatusDraft';
-import type {CardFeedErrorsDerivedValue, OutstandingReportsByPolicyIDDerivedValue, ReportAttributesDerivedValue, ReportTransactionsAndViolationsDerivedValue} from './DerivedValues';
+import type { CardFeedErrorsDerivedValue, OutstandingReportsByPolicyIDDerivedValue, ReportAttributesDerivedValue, ReportTransactionsAndViolationsDerivedValue } from './DerivedValues';
 import type DismissedProductTraining from './DismissedProductTraining';
 import type DismissedReferralBanners from './DismissedReferralBanners';
 import type Domain from './Domain';
-import type {DomainSecurityGroup, SamlMetadata} from './Domain';
+import type {DomainSecurityGroup, SamlMetadata, SecurityGroupsData} from './Domain';
 import type DomainErrors from './DomainErrors';
 import type DomainPendingActions from './DomainPendingActions';
 import type Download from './Download';
@@ -42,7 +42,7 @@ import type ExpensifyCardBankAccountMetadata from './ExpensifyCardBankAccountMet
 import type ExpensifyCardSettings from './ExpensifyCardSettings';
 import type ExportTemplate from './ExportTemplate';
 import type FrequentlyUsedEmoji from './FrequentlyUsedEmoji';
-import type {FundList} from './Fund';
+import type { FundList } from './Fund';
 import type Fund from './Fund';
 import type GpsDraftDetails from './GpsDraftDetails';
 import type HybridApp from './HybridApp';
@@ -52,11 +52,11 @@ import type IntroSelected from './IntroSelected';
 import type InvitedEmailsToAccountIDs from './InvitedEmailsToAccountIDs';
 import type JoinablePolicies from './JoinablePolicies';
 import type LastExportMethod from './LastExportMethod';
-import type {LastPaymentMethod, LastPaymentMethodType} from './LastPaymentMethod';
+import type { LastPaymentMethod, LastPaymentMethodType } from './LastPaymentMethod';
 import type LastSelectedDistanceRates from './LastSelectedDistanceRates';
 import type Locale from './Locale';
 import type LockAccountDetails from './LockAccountDetails';
-import type {LoginList} from './Login';
+import type { LoginList } from './Login';
 import type Login from './Login';
 import type MapboxAccessToken from './MapboxAccessToken';
 import type MergeTransaction from './MergeTransaction';
@@ -66,22 +66,22 @@ import type NewGroupChatDraft from './NewGroupChatDraft';
 import type Onboarding from './Onboarding';
 import type OnboardingRHPVariant from './OnboardingRHPVariant';
 import type OnyxInputOrEntry from './OnyxInputOrEntry';
-import type {OnyxUpdateEvent, OnyxUpdatesFromServer} from './OnyxUpdatesFromServer';
-import type {DecisionName, OriginalMessageIOU} from './OriginalMessage';
+import type { OnyxUpdateEvent, OnyxUpdatesFromServer } from './OnyxUpdatesFromServer';
+import type { DecisionName, OriginalMessageIOU } from './OriginalMessage';
 import type Pages from './Pages';
-import type {PendingContactAction} from './PendingContactAction';
+import type { PendingContactAction } from './PendingContactAction';
 import type PersonalBankAccount from './PersonalBankAccount';
-import type {PersonalDetailsList, PersonalDetailsMetadata} from './PersonalDetails';
+import type { PersonalDetailsList, PersonalDetailsMetadata } from './PersonalDetails';
 import type PersonalDetails from './PersonalDetails';
 import type PlaidData from './PlaidData';
 import type Policy from './Policy';
-import type {PolicyConnectionName, PolicyConnectionSyncProgress, PolicyReportField, TaxRate, TaxRates, TaxRatesWithDefault} from './Policy';
-import type {PolicyCategories, PolicyCategory} from './PolicyCategory';
-import type {PolicyEmployeeList} from './PolicyEmployee';
+import type { PolicyConnectionName, PolicyConnectionSyncProgress, PolicyReportField, TaxRate, TaxRates, TaxRatesWithDefault } from './Policy';
+import type { PolicyCategories, PolicyCategory } from './PolicyCategory';
+import type { PolicyEmployeeList } from './PolicyEmployee';
 import type PolicyEmployee from './PolicyEmployee';
 import type PolicyJoinMember from './PolicyJoinMember';
 import type PolicyOwnershipChangeChecks from './PolicyOwnershipChangeChecks';
-import type {PolicyTag, PolicyTagLists, PolicyTags} from './PolicyTag';
+import type { PolicyTag, PolicyTagLists, PolicyTags } from './PolicyTag';
 import type PrivatePersonalDetails from './PrivatePersonalDetails';
 import type PrivateSubscription from './PrivateSubscription';
 import type PurchaseList from './PurchaseList';
@@ -89,27 +89,27 @@ import type QuickAction from './QuickAction';
 import type RecentlyUsedCategories from './RecentlyUsedCategories';
 import type RecentlyUsedReportFields from './RecentlyUsedReportFields';
 import type RecentlyUsedTags from './RecentlyUsedTags';
-import type {RecentSearchItem} from './RecentSearch';
+import type { RecentSearchItem } from './RecentSearch';
 import type RecentWaypoint from './RecentWaypoint';
 import type ReimbursementAccount from './ReimbursementAccount';
 import type Report from './Report';
-import type {ReportActions} from './ReportAction';
+import type { ReportActions } from './ReportAction';
 import type ReportAction from './ReportAction';
 import type ReportActionReactions from './ReportActionReactions';
 import type ReportActionsDraft from './ReportActionsDraft';
 import type ReportActionsDrafts from './ReportActionsDrafts';
-import type {GroupedTransactions, ReportLayoutGroupBy} from './ReportLayout';
+import type { GroupedTransactions, ReportLayoutGroupBy } from './ReportLayout';
 import type ReportMetadata from './ReportMetadata';
 import type ReportNameValuePairs from './ReportNameValuePairs';
 import type LastSearchParams from './ReportNavigation';
 import type ReportNextStepDeprecated from './ReportNextStepDeprecated';
 import type ReportUserIsTyping from './ReportUserIsTyping';
-import type {ReportFieldsViolations, ReportViolationName} from './ReportViolation';
+import type { ReportFieldsViolations, ReportViolationName } from './ReportViolation';
 import type ReportViolations from './ReportViolation';
 import type Request from './Request';
 import type Response from './Response';
 import type ReviewDuplicates from './ReviewDuplicates';
-import type {SaveSearch} from './SaveSearch';
+import type { SaveSearch } from './SaveSearch';
 import type ScheduleCallDraft from './ScheduleCallDraft';
 import type ScreenShareRequest from './ScreenShareRequest';
 import type SearchContext from './SearchContext';
@@ -124,10 +124,10 @@ import type StripeCustomerID from './StripeCustomerID';
 import type SupportalPermissionDenied from './SupportalPermissionDenied';
 import type Task from './Task';
 import type Transaction from './Transaction';
-import type {TransactionViolation, ViolationName} from './TransactionViolation';
+import type { TransactionViolation, ViolationName } from './TransactionViolation';
 import type TransactionViolations from './TransactionViolation';
 import type TravelProvisioning from './TravelProvisioning';
-import type {TravelSettings} from './TravelSettings';
+import type { TravelSettings } from './TravelSettings';
 import type TryNewDot from './TryNewDot';
 import type UnshareBankAccount from './UnshareBankAccount';
 import type UserLocation from './UserLocation';
@@ -137,12 +137,13 @@ import type VacationDelegate from './VacationDelegate';
 import type ValidateMagicCodeAction from './ValidateMagicCodeAction';
 import type ValidateUserAndGetAccessiblePolicies from './ValidateUserAndGetAccessiblePolicies';
 import type WalletAdditionalDetails from './WalletAdditionalDetails';
-import type {WalletAdditionalQuestionDetails} from './WalletAdditionalDetails';
+import type { WalletAdditionalQuestionDetails } from './WalletAdditionalDetails';
 import type WalletOnfido from './WalletOnfido';
 import type WalletStatement from './WalletStatement';
 import type WalletTerms from './WalletTerms';
 import type WalletTransfer from './WalletTransfer';
 
+
 export type {
     FileObject,
     TryNewDot,
@@ -321,4 +322,5 @@ export type {
     DomainErrors,
     DomainPendingActions,
     DomainSecurityGroup,
+    SecurityGroupsData
 };
diff --git a/tests/unit/DomainSelectorsTest.ts b/tests/unit/DomainSelectorsTest.ts
index 1bab27e6dc4c..751e21fd87b6 100644
--- a/tests/unit/DomainSelectorsTest.ts
+++ b/tests/unit/DomainSelectorsTest.ts
@@ -4,6 +4,7 @@ import {
     defaultSecurityGroupIDSelector,
     domainEmailSelector,
     domainSettingsPrimaryContactSelector,
+    isSecurityGroupEntry,
     memberAccountIDsSelector,
     selectSecurityGroupsForAccount,
     technicalContactSettingsSelector,
@@ -363,4 +364,31 @@ describe('domainSelectors', () => {
             expect(result.securityGroups[`${CONST.DOMAIN.DOMAIN_SECURITY_GROUP_PREFIX}3`]).toBeUndefined();
         });
     });
+
+    describe('isSecurityGroupEntry', () => {
+        it('should return true for a valid security group entry', () => {
+            const entry: [string, unknown] = [`${CONST.DOMAIN.DOMAIN_SECURITY_GROUP_PREFIX}123`, {shared: {}}];
+            expect(isSecurityGroupEntry(entry)).toBe(true);
+        });
+
+        it('should return false if the key does not start with the security group prefix', () => {
+            const entry: [string, unknown] = ['invalid_prefix_123', {shared: {}}];
+            expect(isSecurityGroupEntry(entry)).toBe(false);
+        });
+
+        it('should return false if the value is not an object', () => {
+            const entry: [string, unknown] = [`${CONST.DOMAIN.DOMAIN_SECURITY_GROUP_PREFIX}123`, 'not an object'];
+            expect(isSecurityGroupEntry(entry)).toBe(false);
+        });
+
+        it('should return false if the value is null', () => {
+            const entry: [string, unknown] = [`${CONST.DOMAIN.DOMAIN_SECURITY_GROUP_PREFIX}123`, null];
+            expect(isSecurityGroupEntry(entry)).toBe(false);
+        });
+
+        it('should return false if the value does not have a "shared" property', () => {
+            const entry: [string, unknown] = [`${CONST.DOMAIN.DOMAIN_SECURITY_GROUP_PREFIX}123`, {other: {}}];
+            expect(isSecurityGroupEntry(entry)).toBe(false);
+        });
+    });
 });
diff --git a/tests/unit/PersonalDetailsSelectorTest.ts b/tests/unit/PersonalDetailsSelectorTest.ts
new file mode 100644
index 000000000000..36e47e72b331
--- /dev/null
+++ b/tests/unit/PersonalDetailsSelectorTest.ts
@@ -0,0 +1,30 @@
+import {personalDetailsSelector} from '@selectors/PersonalDetails';
+import type {PersonalDetailsList} from '@src/types/onyx';
+
+describe('PersonalDetailsSelector', () => {
+    describe('personalDetailsSelector', () => {
+        const accountID = 123;
+        const personalDetails = {
+            accountID,
+            displayName: 'Test User',
+        };
+        const personalDetailsList = {
+            [accountID]: personalDetails,
+        } as unknown as PersonalDetailsList;
+
+        it('should return the personal details for the given accountID', () => {
+            const result = personalDetailsSelector(accountID)(personalDetailsList);
+            expect(result).toEqual(personalDetails);
+        });
+
+        it('should return undefined if the accountID is not in the list', () => {
+            const result = personalDetailsSelector(999)(personalDetailsList);
+            expect(result).toBeUndefined();
+        });
+
+        it('should return undefined if the personalDetailsList is undefined', () => {
+            const result = personalDetailsSelector(accountID)(undefined);
+            expect(result).toBeUndefined();
+        });
+    });
+});

From 5bbc20d49deb3079f067d227a09b819be68e6b41 Mon Sep 17 00:00:00 2001
From: sumo_slonik <jakub.nowakowski@swmansion.com>
Date: Tue, 27 Jan 2026 15:19:23 +0100
Subject: [PATCH 19/50] run prettier

---
 src/languages/de.ts      |  4 +--
 src/languages/fr.ts      |  4 +--
 src/languages/it.ts      |  3 +-
 src/languages/ja.ts      |  4 +--
 src/languages/nl.ts      |  4 +--
 src/languages/pl.ts      |  4 +--
 src/languages/pt-BR.ts   |  4 +--
 src/languages/zh-hans.ts |  6 ++--
 src/types/onyx/index.ts  | 65 ++++++++++++++++++++--------------------
 9 files changed, 49 insertions(+), 49 deletions(-)

diff --git a/src/languages/de.ts b/src/languages/de.ts
index 10d783aef956..f074edd16636 100644
--- a/src/languages/de.ts
+++ b/src/languages/de.ts
@@ -8080,8 +8080,8 @@ Hier ist ein *Testbeleg*, um dir zu zeigen, wie es funktioniert:`,
                 'Wir empfehlen, das Konto sicher zu schließen, um das Schließen zu überspringen, falls Folgendes vorliegt: <ul><li>Ausstehende Genehmigungen</li><li>Aktive Erstattungen</li><li>Keine alternativen Anmeldemethoden</li></ul> Andernfalls können Sie die oben genannten Sicherheitsvorkehrungen ignorieren und das ausgewählte Konto zwangsweise schließen.',
             error: {
                 removeMember: 'Dieser Benutzer kann nicht entfernt werden. Bitte versuche es erneut.',
-                addMember: 'Dieses Mitglied kann nicht hinzugefügt werden. Bitte versuche es erneut.'
-                },
+                addMember: 'Dieses Mitglied kann nicht hinzugefügt werden. Bitte versuche es erneut.',
+            },
         },
         domainAdmins: 'Domain-Admins',
     },
diff --git a/src/languages/fr.ts b/src/languages/fr.ts
index 9c9d44b56a57..2798ade76d86 100644
--- a/src/languages/fr.ts
+++ b/src/languages/fr.ts
@@ -8085,8 +8085,8 @@ Voici un *reçu test* pour vous montrer comment cela fonctionne :`,
                 'Nous recommandons de fermer le compte en toute sécurité afin d’éviter de le fermer dans le cas où il y aurait : <ul><li>Des validations en attente</li><li>Des remboursements actifs</li><li>Aucune autre méthode de connexion</li></ul> Sinon, vous pouvez ignorer les précautions de sécurité ci-dessus et forcer la fermeture du compte sélectionné.',
             error: {
                 removeMember: 'Impossible de supprimer cet utilisateur. Veuillez réessayer.',
-                addMember: 'Impossible d’ajouter ce membre. Veuillez réessayer.'
-                },
+                addMember: 'Impossible d’ajouter ce membre. Veuillez réessayer.',
+            },
         },
         domainAdmins: 'Administrateurs de domaine',
     },
diff --git a/src/languages/it.ts b/src/languages/it.ts
index 60370f8deea3..fcd90cbb61d0 100644
--- a/src/languages/it.ts
+++ b/src/languages/it.ts
@@ -8067,7 +8067,8 @@ Ecco una *ricevuta di prova* per mostrarti come funziona:`,
                 'Consigliamo di chiudere l’account in modo sicuro per evitare problemi in caso di: <ul><li>Approvazioni in sospeso</li><li>Rimborsi attivi</li><li>Nessun metodo di accesso alternativo</li></ul> In caso contrario, puoi ignorare le precauzioni di sicurezza sopra indicate e forzare la chiusura dell’account selezionato.',
             error: {
                 removeMember: 'Impossibile rimuovere questo utente. Riprova.',
-                addMember: 'Impossibile aggiungere questo membro. Riprova.'},
+                addMember: 'Impossibile aggiungere questo membro. Riprova.',
+            },
         },
         domainAdmins: 'Amministratori di dominio',
     },
diff --git a/src/languages/ja.ts b/src/languages/ja.ts
index a219ed57ae6f..407cd3bdf8de 100644
--- a/src/languages/ja.ts
+++ b/src/languages/ja.ts
@@ -8001,8 +8001,8 @@ Expensify の使い方をお見せするための*テストレシート*がこ
                 '保留中の承認、処理中の精算、代替ログイン方法がない場合などでもアカウントを閉鎖できるように、安全にアカウントを閉鎖することを推奨します: <ul><li>保留中の承認</li><li>進行中の払い戻し</li><li>代替ログイン方法なし</li></ul> それ以外の場合は、上記の安全上の注意を無視して、選択したアカウントを強制的に閉鎖できます。',
             error: {
                 removeMember: 'このユーザーを削除できません。もう一度お試しください。',
-                addMember: 'このメンバーを追加できませんでした。もう一度お試しください。'
-                },
+                addMember: 'このメンバーを追加できませんでした。もう一度お試しください。',
+            },
         },
         domainAdmins: 'ドメイン管理者',
     },
diff --git a/src/languages/nl.ts b/src/languages/nl.ts
index 3a2abe29cf59..1522c768f0fe 100644
--- a/src/languages/nl.ts
+++ b/src/languages/nl.ts
@@ -8049,8 +8049,8 @@ Hier is een *testbon* om je te laten zien hoe het werkt:`,
                 'We raden aan om de account veilig te sluiten om te vermijden dat je deze moet sluiten als er zijn: <ul><li>Openstaande goedkeuringen</li><li>Actieve terugbetalingen</li><li>Geen alternatieve inlogmethoden</li></ul> Anders kun je de bovenstaande veiligheidsmaatregelen negeren en de geselecteerde account geforceerd sluiten.',
             error: {
                 removeMember: 'Kan deze gebruiker niet verwijderen. Probeer het opnieuw.',
-                addMember: 'Kan dit lid niet toevoegen. Probeer het opnieuw.'
-                },
+                addMember: 'Kan dit lid niet toevoegen. Probeer het opnieuw.',
+            },
         },
         domainAdmins: 'Domeinbeheerders',
     },
diff --git a/src/languages/pl.ts b/src/languages/pl.ts
index 6f936512ea62..fbd5fbb9463e 100644
--- a/src/languages/pl.ts
+++ b/src/languages/pl.ts
@@ -8033,8 +8033,8 @@ Oto *paragon testowy*, który pokazuje, jak to działa:`,
                 'Zalecamy bezpieczne zamknięcie konta, aby pominąć jego zamykanie w przypadku, gdy występują: <ul><li>Oczekujące zatwierdzenia</li><li>Aktywne zwroty kosztów</li><li>Brak alternatywnych metod logowania</li></ul> W przeciwnym razie możesz zignorować powyższe środki ostrożności i wymusić zamknięcie wybranego konta.',
             error: {
                 removeMember: 'Nie można usunąć tego użytkownika. Spróbuj ponownie.',
-                addMember: 'Nie można dodać tego członka. Spróbuj ponownie.'
-                },
+                addMember: 'Nie można dodać tego członka. Spróbuj ponownie.',
+            },
         },
         domainAdmins: 'Administratorzy domeny',
     },
diff --git a/src/languages/pt-BR.ts b/src/languages/pt-BR.ts
index 43f9bead9409..fab87ecade35 100644
--- a/src/languages/pt-BR.ts
+++ b/src/languages/pt-BR.ts
@@ -8044,8 +8044,8 @@ Aqui está um *recibo de teste* para mostrar como funciona:`,
                 'Recomendamos fechar a conta com segurança para evitar o fechamento caso haja: <ul><li>aprovações pendentes</li><li>reembolsos ativos</li><li>nenhum método de login alternativo</li></ul> Caso contrário, você pode ignorar as precauções de segurança acima e forçar o fechamento da conta selecionada.',
             error: {
                 removeMember: 'Não foi possível remover este usuário. Tente novamente.',
-                addMember: 'Não foi possível adicionar este membro. Tente novamente.'
-                },
+                addMember: 'Não foi possível adicionar este membro. Tente novamente.',
+            },
         },
         domainAdmins: 'Administradores de domínio',
     },
diff --git a/src/languages/zh-hans.ts b/src/languages/zh-hans.ts
index c97f9df07dab..d4898906c0e7 100644
--- a/src/languages/zh-hans.ts
+++ b/src/languages/zh-hans.ts
@@ -7834,7 +7834,7 @@ ${reportName}
             title: '成员',
             findMember: '查找成员',
             addMember: '添加成员',
-             email: '电子邮箱地址',
+            email: '电子邮箱地址',
             closeAccount: '关闭账户',
             closeAccountPrompt: '您确定吗？此操作是永久性的。',
             forceCloseAccount: '强制关闭账户',
@@ -7843,8 +7843,8 @@ ${reportName}
                 '我们建议安全地关闭该账户，以便在存在以下情况时跳过关闭操作：<ul><li>待处理的审批</li><li>正在进行的报销</li><li>没有其他登录方式</li></ul>否则，您可以忽略上述安全预防措施并强制关闭所选账户。',
             error: {
                 removeMember: '无法移除此用户。请重试。',
-                addMember: '无法添加此成员。请重试。'
-                },
+                addMember: '无法添加此成员。请重试。',
+            },
         },
         domainAdmins: '域管理员',
     },
diff --git a/src/types/onyx/index.ts b/src/types/onyx/index.ts
index 29e0b785a7c3..625bf2b48b29 100644
--- a/src/types/onyx/index.ts
+++ b/src/types/onyx/index.ts
@@ -1,11 +1,11 @@
-import type { OnboardingPurpose } from '@libs/actions/Welcome/OnboardingFlow';
-import type { FileObject } from '@src/types/utils/Attachment';
+import type {OnboardingPurpose} from '@libs/actions/Welcome/OnboardingFlow';
+import type {FileObject} from '@src/types/utils/Attachment';
 import type Account from './Account';
 import type AccountData from './AccountData';
 import type AppReview from './AppReview';
-import type { ApprovalWorkflowOnyx } from './ApprovalWorkflow';
-import type { AssignCard, AssignCardData } from './AssignCard';
-import type { BankAccountList } from './BankAccount';
+import type {ApprovalWorkflowOnyx} from './ApprovalWorkflow';
+import type {AssignCard, AssignCardData} from './AssignCard';
+import type {BankAccountList} from './BankAccount';
 import type BankAccount from './BankAccount';
 import type BankAccountShareDetails from './BankAccountShareDetails';
 import type Beta from './Beta';
@@ -16,19 +16,19 @@ import type BillingStatus from './BillingStatus';
 import type BlockedFromConcierge from './BlockedFromConcierge';
 import type CancellationDetails from './CancellationDetails';
 import type Card from './Card';
-import type { CardList, FailedCompanyCardAssignment, FailedCompanyCardAssignments, IssueNewCard, ProvisioningCardData, WorkspaceCardsList } from './Card';
+import type {CardList, FailedCompanyCardAssignment, FailedCompanyCardAssignments, IssueNewCard, ProvisioningCardData, WorkspaceCardsList} from './Card';
 import type CardContinuousReconciliation from './CardContinuousReconciliation';
 import type CardFeeds from './CardFeeds';
-import type { AddNewCompanyCardFeed, CombinedCardFeed, CombinedCardFeeds, CompanyCardFeed, CompanyCardFeedWithDomainID, DomainSettings, FundID } from './CardFeeds';
+import type {AddNewCompanyCardFeed, CombinedCardFeed, CombinedCardFeeds, CompanyCardFeed, CompanyCardFeedWithDomainID, DomainSettings, FundID} from './CardFeeds';
 import type CardOnWaitlist from './CardOnWaitlist';
-import type { CapturedLogs, Log } from './Console';
-import type { CorpayFields, CorpayFormField } from './CorpayFields';
-import type { CorpayOnboardingFields } from './CorpayOnboardingFields';
+import type {CapturedLogs, Log} from './Console';
+import type {CorpayFields, CorpayFormField} from './CorpayFields';
+import type {CorpayOnboardingFields} from './CorpayOnboardingFields';
 import type Credentials from './Credentials';
 import type Currency from './Currency';
-import type { CurrencyList } from './Currency';
+import type {CurrencyList} from './Currency';
 import type CustomStatusDraft from './CustomStatusDraft';
-import type { CardFeedErrorsDerivedValue, OutstandingReportsByPolicyIDDerivedValue, ReportAttributesDerivedValue, ReportTransactionsAndViolationsDerivedValue } from './DerivedValues';
+import type {CardFeedErrorsDerivedValue, OutstandingReportsByPolicyIDDerivedValue, ReportAttributesDerivedValue, ReportTransactionsAndViolationsDerivedValue} from './DerivedValues';
 import type DismissedProductTraining from './DismissedProductTraining';
 import type DismissedReferralBanners from './DismissedReferralBanners';
 import type Domain from './Domain';
@@ -42,7 +42,7 @@ import type ExpensifyCardBankAccountMetadata from './ExpensifyCardBankAccountMet
 import type ExpensifyCardSettings from './ExpensifyCardSettings';
 import type ExportTemplate from './ExportTemplate';
 import type FrequentlyUsedEmoji from './FrequentlyUsedEmoji';
-import type { FundList } from './Fund';
+import type {FundList} from './Fund';
 import type Fund from './Fund';
 import type GpsDraftDetails from './GpsDraftDetails';
 import type HybridApp from './HybridApp';
@@ -52,11 +52,11 @@ import type IntroSelected from './IntroSelected';
 import type InvitedEmailsToAccountIDs from './InvitedEmailsToAccountIDs';
 import type JoinablePolicies from './JoinablePolicies';
 import type LastExportMethod from './LastExportMethod';
-import type { LastPaymentMethod, LastPaymentMethodType } from './LastPaymentMethod';
+import type {LastPaymentMethod, LastPaymentMethodType} from './LastPaymentMethod';
 import type LastSelectedDistanceRates from './LastSelectedDistanceRates';
 import type Locale from './Locale';
 import type LockAccountDetails from './LockAccountDetails';
-import type { LoginList } from './Login';
+import type {LoginList} from './Login';
 import type Login from './Login';
 import type MapboxAccessToken from './MapboxAccessToken';
 import type MergeTransaction from './MergeTransaction';
@@ -66,22 +66,22 @@ import type NewGroupChatDraft from './NewGroupChatDraft';
 import type Onboarding from './Onboarding';
 import type OnboardingRHPVariant from './OnboardingRHPVariant';
 import type OnyxInputOrEntry from './OnyxInputOrEntry';
-import type { OnyxUpdateEvent, OnyxUpdatesFromServer } from './OnyxUpdatesFromServer';
-import type { DecisionName, OriginalMessageIOU } from './OriginalMessage';
+import type {OnyxUpdateEvent, OnyxUpdatesFromServer} from './OnyxUpdatesFromServer';
+import type {DecisionName, OriginalMessageIOU} from './OriginalMessage';
 import type Pages from './Pages';
-import type { PendingContactAction } from './PendingContactAction';
+import type {PendingContactAction} from './PendingContactAction';
 import type PersonalBankAccount from './PersonalBankAccount';
-import type { PersonalDetailsList, PersonalDetailsMetadata } from './PersonalDetails';
+import type {PersonalDetailsList, PersonalDetailsMetadata} from './PersonalDetails';
 import type PersonalDetails from './PersonalDetails';
 import type PlaidData from './PlaidData';
 import type Policy from './Policy';
-import type { PolicyConnectionName, PolicyConnectionSyncProgress, PolicyReportField, TaxRate, TaxRates, TaxRatesWithDefault } from './Policy';
-import type { PolicyCategories, PolicyCategory } from './PolicyCategory';
-import type { PolicyEmployeeList } from './PolicyEmployee';
+import type {PolicyConnectionName, PolicyConnectionSyncProgress, PolicyReportField, TaxRate, TaxRates, TaxRatesWithDefault} from './Policy';
+import type {PolicyCategories, PolicyCategory} from './PolicyCategory';
+import type {PolicyEmployeeList} from './PolicyEmployee';
 import type PolicyEmployee from './PolicyEmployee';
 import type PolicyJoinMember from './PolicyJoinMember';
 import type PolicyOwnershipChangeChecks from './PolicyOwnershipChangeChecks';
-import type { PolicyTag, PolicyTagLists, PolicyTags } from './PolicyTag';
+import type {PolicyTag, PolicyTagLists, PolicyTags} from './PolicyTag';
 import type PrivatePersonalDetails from './PrivatePersonalDetails';
 import type PrivateSubscription from './PrivateSubscription';
 import type PurchaseList from './PurchaseList';
@@ -89,27 +89,27 @@ import type QuickAction from './QuickAction';
 import type RecentlyUsedCategories from './RecentlyUsedCategories';
 import type RecentlyUsedReportFields from './RecentlyUsedReportFields';
 import type RecentlyUsedTags from './RecentlyUsedTags';
-import type { RecentSearchItem } from './RecentSearch';
+import type {RecentSearchItem} from './RecentSearch';
 import type RecentWaypoint from './RecentWaypoint';
 import type ReimbursementAccount from './ReimbursementAccount';
 import type Report from './Report';
-import type { ReportActions } from './ReportAction';
+import type {ReportActions} from './ReportAction';
 import type ReportAction from './ReportAction';
 import type ReportActionReactions from './ReportActionReactions';
 import type ReportActionsDraft from './ReportActionsDraft';
 import type ReportActionsDrafts from './ReportActionsDrafts';
-import type { GroupedTransactions, ReportLayoutGroupBy } from './ReportLayout';
+import type {GroupedTransactions, ReportLayoutGroupBy} from './ReportLayout';
 import type ReportMetadata from './ReportMetadata';
 import type ReportNameValuePairs from './ReportNameValuePairs';
 import type LastSearchParams from './ReportNavigation';
 import type ReportNextStepDeprecated from './ReportNextStepDeprecated';
 import type ReportUserIsTyping from './ReportUserIsTyping';
-import type { ReportFieldsViolations, ReportViolationName } from './ReportViolation';
+import type {ReportFieldsViolations, ReportViolationName} from './ReportViolation';
 import type ReportViolations from './ReportViolation';
 import type Request from './Request';
 import type Response from './Response';
 import type ReviewDuplicates from './ReviewDuplicates';
-import type { SaveSearch } from './SaveSearch';
+import type {SaveSearch} from './SaveSearch';
 import type ScheduleCallDraft from './ScheduleCallDraft';
 import type ScreenShareRequest from './ScreenShareRequest';
 import type SearchContext from './SearchContext';
@@ -124,10 +124,10 @@ import type StripeCustomerID from './StripeCustomerID';
 import type SupportalPermissionDenied from './SupportalPermissionDenied';
 import type Task from './Task';
 import type Transaction from './Transaction';
-import type { TransactionViolation, ViolationName } from './TransactionViolation';
+import type {TransactionViolation, ViolationName} from './TransactionViolation';
 import type TransactionViolations from './TransactionViolation';
 import type TravelProvisioning from './TravelProvisioning';
-import type { TravelSettings } from './TravelSettings';
+import type {TravelSettings} from './TravelSettings';
 import type TryNewDot from './TryNewDot';
 import type UnshareBankAccount from './UnshareBankAccount';
 import type UserLocation from './UserLocation';
@@ -137,13 +137,12 @@ import type VacationDelegate from './VacationDelegate';
 import type ValidateMagicCodeAction from './ValidateMagicCodeAction';
 import type ValidateUserAndGetAccessiblePolicies from './ValidateUserAndGetAccessiblePolicies';
 import type WalletAdditionalDetails from './WalletAdditionalDetails';
-import type { WalletAdditionalQuestionDetails } from './WalletAdditionalDetails';
+import type {WalletAdditionalQuestionDetails} from './WalletAdditionalDetails';
 import type WalletOnfido from './WalletOnfido';
 import type WalletStatement from './WalletStatement';
 import type WalletTerms from './WalletTerms';
 import type WalletTransfer from './WalletTransfer';
 
-
 export type {
     FileObject,
     TryNewDot,
@@ -322,5 +321,5 @@ export type {
     DomainErrors,
     DomainPendingActions,
     DomainSecurityGroup,
-    SecurityGroupsData
+    SecurityGroupsData,
 };

From 177e05993dede94ab443db2dc460cf6617df8a0d Mon Sep 17 00:00:00 2001
From: sumo_slonik <jakub.nowakowski@swmansion.com>
Date: Tue, 27 Jan 2026 15:24:58 +0100
Subject: [PATCH 20/50] fix eslint

---
 src/pages/domain/Members/DomainMembersPage.tsx | 13 +++++++------
 1 file changed, 7 insertions(+), 6 deletions(-)

diff --git a/src/pages/domain/Members/DomainMembersPage.tsx b/src/pages/domain/Members/DomainMembersPage.tsx
index deb6e834d25d..612e8b86b7f4 100644
--- a/src/pages/domain/Members/DomainMembersPage.tsx
+++ b/src/pages/domain/Members/DomainMembersPage.tsx
@@ -1,21 +1,22 @@
 import {defaultSecurityGroupIDSelector, memberAccountIDsSelector, memberPendingActionSelector} from '@selectors/Domain';
 import React from 'react';
 import Button from '@components/Button';
-import {useMemoizedLazyExpensifyIcons, useMemoizedLazyIllustrations} from '@hooks/useLazyAsset';
+import { useMemoizedLazyExpensifyIcons, useMemoizedLazyIllustrations } from '@hooks/useLazyAsset';
 import useLocalize from '@hooks/useLocalize';
 import useOnyx from '@hooks/useOnyx';
 import useResponsiveLayout from '@hooks/useResponsiveLayout';
 import useThemeStyles from '@hooks/useThemeStyles';
-import {clearAddMemberError, clearDomainMemberError} from '@libs/actions/Domain';
-import {getLatestError} from '@libs/ErrorUtils';
+import { clearAddMemberError } from '@libs/actions/Domain';
+import { getLatestError } from '@libs/ErrorUtils';
 import Navigation from '@navigation/Navigation';
-import type {PlatformStackScreenProps} from '@navigation/PlatformStackNavigation/types';
-import type {DomainSplitNavigatorParamList} from '@navigation/types';
+import type { PlatformStackScreenProps } from '@navigation/PlatformStackNavigation/types';
+import type { DomainSplitNavigatorParamList } from '@navigation/types';
 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<DomainSplitNavigatorParamList, typeof SCREENS.DOMAIN.MEMBERS>;
 
 function DomainMembersPage({route}: DomainMembersPageProps) {
@@ -27,7 +28,7 @@ function DomainMembersPage({route}: DomainMembersPageProps) {
     const styles = useThemeStyles();
 
     const [domainErrors] = useOnyx(`${ONYXKEYS.COLLECTION.DOMAIN_ERRORS}${domainAccountID}`, {canBeMissing: true});
-    const [domainPendingActions] = useOnyx(`${ONYXKEYS.COLLECTION.DOMAIN_PENDING_ACTIONS}${domainAccountID}`, {canBeMissing: true});
+    const [domainPendingAction] = useOnyx(`${ONYXKEYS.COLLECTION.DOMAIN_PENDING_ACTIONS}${domainAccountID}`, {canBeMissing: true, selector: memberPendingActionSelector});
     const [defaultSecurityGroupID] = useOnyx(`${ONYXKEYS.COLLECTION.DOMAIN}${domainAccountID}`, {canBeMissing: true, selector: defaultSecurityGroupIDSelector});
 
     const [memberIDs] = useOnyx(`${ONYXKEYS.COLLECTION.DOMAIN}${domainAccountID}`, {

From fe9a617fe5e8df97962ff07f8a1aa8e88fa9f646 Mon Sep 17 00:00:00 2001
From: sumo_slonik <jakub.nowakowski@swmansion.com>
Date: Tue, 27 Jan 2026 15:28:24 +0100
Subject: [PATCH 21/50] fix prettier

---
 src/pages/domain/Members/DomainMembersPage.tsx | 11 +++++------
 1 file changed, 5 insertions(+), 6 deletions(-)

diff --git a/src/pages/domain/Members/DomainMembersPage.tsx b/src/pages/domain/Members/DomainMembersPage.tsx
index 612e8b86b7f4..c024e33e7272 100644
--- a/src/pages/domain/Members/DomainMembersPage.tsx
+++ b/src/pages/domain/Members/DomainMembersPage.tsx
@@ -1,22 +1,21 @@
 import {defaultSecurityGroupIDSelector, memberAccountIDsSelector, memberPendingActionSelector} from '@selectors/Domain';
 import React from 'react';
 import Button from '@components/Button';
-import { useMemoizedLazyExpensifyIcons, useMemoizedLazyIllustrations } from '@hooks/useLazyAsset';
+import {useMemoizedLazyExpensifyIcons, useMemoizedLazyIllustrations} from '@hooks/useLazyAsset';
 import useLocalize from '@hooks/useLocalize';
 import useOnyx from '@hooks/useOnyx';
 import useResponsiveLayout from '@hooks/useResponsiveLayout';
 import useThemeStyles from '@hooks/useThemeStyles';
-import { clearAddMemberError } from '@libs/actions/Domain';
-import { getLatestError } from '@libs/ErrorUtils';
+import {clearAddMemberError} from '@libs/actions/Domain';
+import {getLatestError} from '@libs/ErrorUtils';
 import Navigation from '@navigation/Navigation';
-import type { PlatformStackScreenProps } from '@navigation/PlatformStackNavigation/types';
-import type { DomainSplitNavigatorParamList } from '@navigation/types';
+import type {PlatformStackScreenProps} from '@navigation/PlatformStackNavigation/types';
+import type {DomainSplitNavigatorParamList} from '@navigation/types';
 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<DomainSplitNavigatorParamList, typeof SCREENS.DOMAIN.MEMBERS>;
 
 function DomainMembersPage({route}: DomainMembersPageProps) {

From eacb1ed1bea0274e86a43fcd6028ad009bfec845 Mon Sep 17 00:00:00 2001
From: sumo_slonik <jakub.nowakowski@swmansion.com>
Date: Tue, 27 Jan 2026 15:43:29 +0100
Subject: [PATCH 22/50] fix ts

---
 src/languages/pt-BR.ts            |  6 ------
 tests/unit/DomainSelectorsTest.ts | 29 ++++++++++++-----------------
 2 files changed, 12 insertions(+), 23 deletions(-)

diff --git a/src/languages/pt-BR.ts b/src/languages/pt-BR.ts
index fab87ecade35..989822d0bb42 100644
--- a/src/languages/pt-BR.ts
+++ b/src/languages/pt-BR.ts
@@ -8025,12 +8025,6 @@ Aqui está um *recibo de teste* para mostrar como funciona:`,
             enterDomainName: 'Insira seu nome de domínio aqui',
             resetDomainInfo: `Esta ação é <strong>permanente</strong> e os seguintes dados serão excluídos: <br/> <ul><li>Conexões de cartão corporativo e quaisquer despesas não reportadas desses cartões</li> <li>Configurações de SAML e de grupo</li> </ul> Todas as contas, workspaces, relatórios, despesas e outros dados permanecerão. <br/><br/>Observação: Você pode remover este domínio da sua lista de domínios excluindo o e-mail associado dos seus <a href="#">métodos de contato</a>.`,
         },
-        members: {
-            title: 'Membros',
-            findMember: 'Encontrar membro',
-
-            errors: {},
-        },
         members: {
             title: 'Membros',
             findMember: 'Encontrar membro',
diff --git a/tests/unit/DomainSelectorsTest.ts b/tests/unit/DomainSelectorsTest.ts
index 751e21fd87b6..6e41db72b038 100644
--- a/tests/unit/DomainSelectorsTest.ts
+++ b/tests/unit/DomainSelectorsTest.ts
@@ -1,17 +1,8 @@
-import {
-    adminAccountIDsSelector,
-    adminPendingActionSelector,
-    defaultSecurityGroupIDSelector,
-    domainEmailSelector,
-    domainSettingsPrimaryContactSelector,
-    isSecurityGroupEntry,
-    memberAccountIDsSelector,
-    selectSecurityGroupsForAccount,
-    technicalContactSettingsSelector,
-} from '@selectors/Domain';
-import type {OnyxEntry} from 'react-native-onyx';
+import { adminAccountIDsSelector, adminPendingActionSelector, defaultSecurityGroupIDSelector, domainEmailSelector, domainSettingsPrimaryContactSelector, isSecurityGroupEntry, memberAccountIDsSelector, selectSecurityGroupsForAccount, technicalContactSettingsSelector } from '@selectors/Domain';
+import type { OnyxEntry } from 'react-native-onyx';
 import CONST from '@src/CONST';
-import type {CardFeeds, Domain, DomainPendingActions, DomainSecurityGroup, DomainSettings} from '@src/types/onyx';
+import type { CardFeeds, Domain, DomainPendingActions, DomainSecurityGroup, DomainSettings } from '@src/types/onyx';
+
 
 describe('domainSelectors', () => {
     const userID1 = 123;
@@ -326,12 +317,14 @@ describe('domainSelectors', () => {
                 },
             } as DomainSecurityGroup;
 
-            const domain = {
+            const domain: Domain = {
                 validated: true,
                 accountID: 1,
                 email: 'test@example.com',
+                // eslint-disable-next-line @typescript-eslint/naming-convention
+                domain_defaultSecurityGroupID: '1',
                 [`${CONST.DOMAIN.DOMAIN_SECURITY_GROUP_PREFIX}1`]: securityGroup,
-            } as Domain;
+            };
 
             const result = selectSecurityGroupsForAccount(123)(domain);
 
@@ -345,14 +338,16 @@ describe('domainSelectors', () => {
             const group2 = {shared: {'123': 'read', '789': 'read'}, enableRestrictedPrimaryLogin: true, enableRestrictedPolicyCreation: true} as DomainSecurityGroup;
             const group3 = {shared: {'999': 'read'}, enableRestrictedPrimaryLogin: true, enableRestrictedPolicyCreation: true} as DomainSecurityGroup;
 
-            const domain = {
+            const domain: Domain = {
                 validated: true,
                 accountID: 1,
                 email: 'test@example.com',
+                // eslint-disable-next-line @typescript-eslint/naming-convention
+                domain_defaultSecurityGroupID: '1',
                 [`${CONST.DOMAIN.DOMAIN_SECURITY_GROUP_PREFIX}1`]: group1,
                 [`${CONST.DOMAIN.DOMAIN_SECURITY_GROUP_PREFIX}2`]: group2,
                 [`${CONST.DOMAIN.DOMAIN_SECURITY_GROUP_PREFIX}3`]: group3,
-            } as Domain;
+            };
 
             const result = selectSecurityGroupsForAccount(123)(domain);
 

From 7492a30075bf8483f5813468256cc7ed61d778cb Mon Sep 17 00:00:00 2001
From: sumo_slonik <jakub.nowakowski@swmansion.com>
Date: Wed, 28 Jan 2026 11:37:37 +0100
Subject: [PATCH 23/50] fix ts and prettier

---
 tests/unit/DomainSelectorsTest.ts | 21 +++++++++++++++------
 1 file changed, 15 insertions(+), 6 deletions(-)

diff --git a/tests/unit/DomainSelectorsTest.ts b/tests/unit/DomainSelectorsTest.ts
index 6e41db72b038..7e21cfb018d6 100644
--- a/tests/unit/DomainSelectorsTest.ts
+++ b/tests/unit/DomainSelectorsTest.ts
@@ -1,8 +1,17 @@
-import { adminAccountIDsSelector, adminPendingActionSelector, defaultSecurityGroupIDSelector, domainEmailSelector, domainSettingsPrimaryContactSelector, isSecurityGroupEntry, memberAccountIDsSelector, selectSecurityGroupsForAccount, technicalContactSettingsSelector } from '@selectors/Domain';
-import type { OnyxEntry } from 'react-native-onyx';
+import {
+    adminAccountIDsSelector,
+    adminPendingActionSelector,
+    defaultSecurityGroupIDSelector,
+    domainEmailSelector,
+    domainSettingsPrimaryContactSelector,
+    isSecurityGroupEntry,
+    memberAccountIDsSelector,
+    selectSecurityGroupsForAccount,
+    technicalContactSettingsSelector,
+} from '@selectors/Domain';
+import type {OnyxEntry} from 'react-native-onyx';
 import CONST from '@src/CONST';
-import type { CardFeeds, Domain, DomainPendingActions, DomainSecurityGroup, DomainSettings } from '@src/types/onyx';
-
+import type {CardFeeds, Domain, DomainPendingActions, DomainSecurityGroup, DomainSettings} from '@src/types/onyx';
 
 describe('domainSelectors', () => {
     const userID1 = 123;
@@ -324,7 +333,7 @@ describe('domainSelectors', () => {
                 // eslint-disable-next-line @typescript-eslint/naming-convention
                 domain_defaultSecurityGroupID: '1',
                 [`${CONST.DOMAIN.DOMAIN_SECURITY_GROUP_PREFIX}1`]: securityGroup,
-            };
+            } as unknown as Domain;
 
             const result = selectSecurityGroupsForAccount(123)(domain);
 
@@ -347,7 +356,7 @@ describe('domainSelectors', () => {
                 [`${CONST.DOMAIN.DOMAIN_SECURITY_GROUP_PREFIX}1`]: group1,
                 [`${CONST.DOMAIN.DOMAIN_SECURITY_GROUP_PREFIX}2`]: group2,
                 [`${CONST.DOMAIN.DOMAIN_SECURITY_GROUP_PREFIX}3`]: group3,
-            };
+            } as unknown as Domain;
 
             const result = selectSecurityGroupsForAccount(123)(domain);
 

From 8c9015fc74de83eb469ca2257c56264c264a2528 Mon Sep 17 00:00:00 2001
From: sumo_slonik <jakub.nowakowski@swmansion.com>
Date: Wed, 28 Jan 2026 11:43:05 +0100
Subject: [PATCH 24/50] Sync Mobile-Expensify

---
 Mobile-Expensify | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Mobile-Expensify b/Mobile-Expensify
index 5c176e3d4768..096193436135 160000
--- a/Mobile-Expensify
+++ b/Mobile-Expensify
@@ -1 +1 @@
-Subproject commit 5c176e3d4768276bec5f47341ae9e1fcdb67de55
+Subproject commit 0961934361353ab8d4b10b7b45d509da523713b9

From 94178b6cd88afe8e5d73ba0c2161b833e0baa25d Mon Sep 17 00:00:00 2001
From: sumo_slonik <jakub.nowakowski@swmansion.com>
Date: Wed, 28 Jan 2026 14:34:54 +0100
Subject: [PATCH 25/50] fix responsive layout

---
 src/pages/domain/Members/DomainMemberDetailsPage.tsx | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)

diff --git a/src/pages/domain/Members/DomainMemberDetailsPage.tsx b/src/pages/domain/Members/DomainMemberDetailsPage.tsx
index 06b7e4131a12..85f67c7d774b 100644
--- a/src/pages/domain/Members/DomainMemberDetailsPage.tsx
+++ b/src/pages/domain/Members/DomainMemberDetailsPage.tsx
@@ -29,7 +29,9 @@ function DomainMemberDetailsPage({route}: DomainMemberDetailsPageProps) {
     const {translate} = useLocalize();
     const icons = useMemoizedLazyExpensifyIcons(['RemoveMembers'] as const);
     const [isModalVisible, setIsModalVisible] = useState(false);
-    const {shouldUseNarrowLayout} = useResponsiveLayout();
+    // we need to use isSmallScreenWidth here because the DecisionModal is opening from RHP and ShouldUseNarrowLayout layout will not work in this place
+    // eslint-disable-next-line rulesdir/prefer-shouldUseNarrowLayout-instead-of-isSmallScreenWidth
+    const {isSmallScreenWidth} = useResponsiveLayout();
     const {showConfirmModal} = useConfirmModal();
 
     const [adminAccountIDs, domainMetadata] = useOnyx(`${ONYXKEYS.COLLECTION.DOMAIN}${domainAccountID}`, {
@@ -106,7 +108,7 @@ function DomainMemberDetailsPage({route}: DomainMemberDetailsPageProps) {
             <DecisionModal
                 title={translate('domain.members.closeAccount')}
                 prompt={translate('domain.members.closeAccountInfo')}
-                isSmallScreenWidth={shouldUseNarrowLayout}
+                isSmallScreenWidth={isSmallScreenWidth}
                 onSecondOptionSubmit={handleSafeCloseAccount}
                 onFirstOptionSubmit={handleForceCloseAccount}
                 secondOptionText={translate('domain.members.safeCloseAccount')}

From b2b6491fb5ac40c6f47dd8412b85473ce6e15120 Mon Sep 17 00:00:00 2001
From: sumo_slonik <jakub.nowakowski@swmansion.com>
Date: Wed, 28 Jan 2026 19:46:16 +0100
Subject: [PATCH 26/50] remove unnecessary as const

---
 src/pages/domain/Members/DomainMemberDetailsPage.tsx | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/pages/domain/Members/DomainMemberDetailsPage.tsx b/src/pages/domain/Members/DomainMemberDetailsPage.tsx
index 85f67c7d774b..497d6c425495 100644
--- a/src/pages/domain/Members/DomainMemberDetailsPage.tsx
+++ b/src/pages/domain/Members/DomainMemberDetailsPage.tsx
@@ -27,7 +27,7 @@ function DomainMemberDetailsPage({route}: DomainMemberDetailsPageProps) {
     const {domainAccountID, accountID} = route.params;
     const styles = useThemeStyles();
     const {translate} = useLocalize();
-    const icons = useMemoizedLazyExpensifyIcons(['RemoveMembers'] as const);
+    const icons = useMemoizedLazyExpensifyIcons(['RemoveMembers']);
     const [isModalVisible, setIsModalVisible] = useState(false);
     // we need to use isSmallScreenWidth here because the DecisionModal is opening from RHP and ShouldUseNarrowLayout layout will not work in this place
     // eslint-disable-next-line rulesdir/prefer-shouldUseNarrowLayout-instead-of-isSmallScreenWidth

From 5528ce0ede14ea72848c7909800b54be91e3a4d8 Mon Sep 17 00:00:00 2001
From: sumo_slonik <jakub.nowakowski@swmansion.com>
Date: Thu, 29 Jan 2026 11:28:00 +0100
Subject: [PATCH 27/50] fix buttons

---
 src/components/DecisionModal.tsx | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/src/components/DecisionModal.tsx b/src/components/DecisionModal.tsx
index d05b5c866949..3ab35584e619 100644
--- a/src/components/DecisionModal.tsx
+++ b/src/components/DecisionModal.tsx
@@ -1,5 +1,5 @@
 import React from 'react';
-import {View} from 'react-native';
+import {ScrollView, View} from 'react-native';
 import useThemeStyles from '@hooks/useThemeStyles';
 import CONST from '@src/CONST';
 import Button from './Button';
@@ -72,7 +72,7 @@ function DecisionModal({
             type={isSmallScreenWidth ? CONST.MODAL.MODAL_TYPE.BOTTOM_DOCKED : CONST.MODAL.MODAL_TYPE.CONFIRM}
             innerContainerStyle={styles.pv0}
         >
-            <View style={styles.m5}>
+            <ScrollView contentContainerStyle={styles.m5}>
                 <View>
                     <View style={[styles.flexRow, styles.mb5]}>
                         <Header
@@ -101,7 +101,7 @@ function DecisionModal({
                     danger={isSecondOptionDanger}
                     large
                 />
-            </View>
+            </ScrollView>
         </Modal>
     );
 }

From e8f6e50d4646aa86d37a7ee9c3b05afb79202611 Mon Sep 17 00:00:00 2001
From: sumo_slonik <jakub.nowakowski@swmansion.com>
Date: Thu, 29 Jan 2026 16:46:17 +0100
Subject: [PATCH 28/50] fix escape button bug

---
 .../Members/DomainMemberDetailsPage.tsx       | 31 ++++++++++---------
 1 file changed, 17 insertions(+), 14 deletions(-)

diff --git a/src/pages/domain/Members/DomainMemberDetailsPage.tsx b/src/pages/domain/Members/DomainMemberDetailsPage.tsx
index 497d6c425495..a6c97940e2ce 100644
--- a/src/pages/domain/Members/DomainMemberDetailsPage.tsx
+++ b/src/pages/domain/Members/DomainMemberDetailsPage.tsx
@@ -74,7 +74,6 @@ function DomainMemberDetailsPage({route}: DomainMemberDetailsPageProps) {
             shouldShowCancelButton: true,
         });
         if (result.action !== ModalActions.CONFIRM) {
-            setIsModalVisible(true);
             return;
         }
         closeUserAccount(domainAccountID, domainName ?? '', accountID, memberLogin, securityGroupsData, force);
@@ -105,19 +104,23 @@ function DomainMemberDetailsPage({route}: DomainMemberDetailsPageProps) {
                 accountID={accountID}
                 avatarButton={avatarButton}
             />
-            <DecisionModal
-                title={translate('domain.members.closeAccount')}
-                prompt={translate('domain.members.closeAccountInfo')}
-                isSmallScreenWidth={isSmallScreenWidth}
-                onSecondOptionSubmit={handleSafeCloseAccount}
-                onFirstOptionSubmit={handleForceCloseAccount}
-                secondOptionText={translate('domain.members.safeCloseAccount')}
-                firstOptionText={translate('domain.members.forceCloseAccount')}
-                isVisible={isModalVisible}
-                onClose={() => setIsModalVisible(false)}
-                isFirstOptionDanger
-                isSecondOptionSuccess
-            />
+            {/* I added this condition here because on this screen we have two modals, and that causes
+            the Esc button not to work properly when using isVisible props. */}
+            {isModalVisible && (
+                <DecisionModal
+                    title={translate('domain.members.closeAccount')}
+                    prompt={translate('domain.members.closeAccountInfo')}
+                    isSmallScreenWidth={isSmallScreenWidth}
+                    onSecondOptionSubmit={handleSafeCloseAccount}
+                    onFirstOptionSubmit={handleForceCloseAccount}
+                    secondOptionText={translate('domain.members.safeCloseAccount')}
+                    firstOptionText={translate('domain.members.forceCloseAccount')}
+                    isVisible
+                    onClose={() => setIsModalVisible(false)}
+                    isFirstOptionDanger
+                    isSecondOptionSuccess
+                />
+            )}
         </>
     );
 }

From e3ee3979e8a10318277b29a2e9186f8db2e14da9 Mon Sep 17 00:00:00 2001
From: war-in <war-in@users.noreply.github.com>
Date: Thu, 29 Jan 2026 18:04:35 +0100
Subject: [PATCH 29/50] fix: show second modal only after the first disappeared

---
 src/components/DecisionModal.tsx              |  5 ++
 .../Members/DomainMemberDetailsPage.tsx       | 51 ++++++++++---------
 2 files changed, 33 insertions(+), 23 deletions(-)

diff --git a/src/components/DecisionModal.tsx b/src/components/DecisionModal.tsx
index 3ab35584e619..3361df45996d 100644
--- a/src/components/DecisionModal.tsx
+++ b/src/components/DecisionModal.tsx
@@ -44,6 +44,9 @@ type DecisionModalProps = {
     /** Callback for closing modal */
     onClose: () => void;
 
+    /** Callback when modal has fully disappeared */
+    onModalHide: () => void;
+
     /** Whether modal is visible */
     isVisible: boolean;
 };
@@ -57,6 +60,7 @@ function DecisionModal({
     onSecondOptionSubmit,
     isSmallScreenWidth,
     onClose,
+    onModalHide,
     isVisible,
     isFirstOptionDanger = false,
     isFirstOptionSuccess = true,
@@ -71,6 +75,7 @@ function DecisionModal({
             isVisible={isVisible}
             type={isSmallScreenWidth ? CONST.MODAL.MODAL_TYPE.BOTTOM_DOCKED : CONST.MODAL.MODAL_TYPE.CONFIRM}
             innerContainerStyle={styles.pv0}
+            onModalHide={onModalHide}
         >
             <ScrollView contentContainerStyle={styles.m5}>
                 <View>
diff --git a/src/pages/domain/Members/DomainMemberDetailsPage.tsx b/src/pages/domain/Members/DomainMemberDetailsPage.tsx
index a6c97940e2ce..54b7a948d3b0 100644
--- a/src/pages/domain/Members/DomainMemberDetailsPage.tsx
+++ b/src/pages/domain/Members/DomainMemberDetailsPage.tsx
@@ -29,6 +29,7 @@ function DomainMemberDetailsPage({route}: DomainMemberDetailsPageProps) {
     const {translate} = useLocalize();
     const icons = useMemoizedLazyExpensifyIcons(['RemoveMembers']);
     const [isModalVisible, setIsModalVisible] = useState(false);
+    const [shouldForceCloseAccount, setShouldForceCloseAccount] = useState<boolean>();
     // we need to use isSmallScreenWidth here because the DecisionModal is opening from RHP and ShouldUseNarrowLayout layout will not work in this place
     // eslint-disable-next-line rulesdir/prefer-shouldUseNarrowLayout-instead-of-isSmallScreenWidth
     const {isSmallScreenWidth} = useResponsiveLayout();
@@ -60,11 +61,11 @@ function DomainMemberDetailsPage({route}: DomainMemberDetailsPageProps) {
         return <FullScreenLoadingIndicator shouldUseGoBackButton />;
     }
 
-    const handleCloseAccount = async (force: boolean) => {
-        if (!securityGroupsData) {
+    const handleCloseAccount = async () => {
+        if (!securityGroupsData || shouldForceCloseAccount === undefined) {
             return;
         }
-        setIsModalVisible(false);
+
         const result = await showConfirmModal({
             title: translate('domain.members.closeAccount'),
             prompt: translate('domain.members.closeAccountPrompt'),
@@ -74,18 +75,23 @@ function DomainMemberDetailsPage({route}: DomainMemberDetailsPageProps) {
             shouldShowCancelButton: true,
         });
         if (result.action !== ModalActions.CONFIRM) {
+            setIsModalVisible(true);
+            setShouldForceCloseAccount(undefined);
             return;
         }
-        closeUserAccount(domainAccountID, domainName ?? '', accountID, memberLogin, securityGroupsData, force);
+        closeUserAccount(domainAccountID, domainName ?? '', accountID, memberLogin, securityGroupsData, shouldForceCloseAccount);
+        setShouldForceCloseAccount(undefined);
         Navigation.dismissModal();
     };
 
     const handleForceCloseAccount = () => {
-        handleCloseAccount(true);
+        setShouldForceCloseAccount(true);
+        setIsModalVisible(false);
     };
 
     const handleSafeCloseAccount = () => {
-        handleCloseAccount(false);
+        setShouldForceCloseAccount(false);
+        setIsModalVisible(false);
     };
 
     const avatarButton = (
@@ -104,23 +110,22 @@ function DomainMemberDetailsPage({route}: DomainMemberDetailsPageProps) {
                 accountID={accountID}
                 avatarButton={avatarButton}
             />
-            {/* I added this condition here because on this screen we have two modals, and that causes
-            the Esc button not to work properly when using isVisible props. */}
-            {isModalVisible && (
-                <DecisionModal
-                    title={translate('domain.members.closeAccount')}
-                    prompt={translate('domain.members.closeAccountInfo')}
-                    isSmallScreenWidth={isSmallScreenWidth}
-                    onSecondOptionSubmit={handleSafeCloseAccount}
-                    onFirstOptionSubmit={handleForceCloseAccount}
-                    secondOptionText={translate('domain.members.safeCloseAccount')}
-                    firstOptionText={translate('domain.members.forceCloseAccount')}
-                    isVisible
-                    onClose={() => setIsModalVisible(false)}
-                    isFirstOptionDanger
-                    isSecondOptionSuccess
-                />
-            )}
+            <DecisionModal
+                title={translate('domain.members.closeAccount')}
+                prompt={translate('domain.members.closeAccountInfo')}
+                isSmallScreenWidth={isSmallScreenWidth}
+                onFirstOptionSubmit={handleForceCloseAccount}
+                onSecondOptionSubmit={handleSafeCloseAccount}
+                secondOptionText={translate('domain.members.safeCloseAccount')}
+                firstOptionText={translate('domain.members.forceCloseAccount')}
+                isVisible={isModalVisible}
+                onClose={() => setIsModalVisible(false)}
+                onModalHide={() => {
+                    handleCloseAccount();
+                }}
+                isFirstOptionDanger
+                isSecondOptionSuccess
+            />
         </>
     );
 }

From e632d5eeea73e8a74e93e7879f2c99a633f3c651 Mon Sep 17 00:00:00 2001
From: war-in <war-in@users.noreply.github.com>
Date: Thu, 29 Jan 2026 18:12:13 +0100
Subject: [PATCH 30/50] fix: typecheck

---
 src/components/DecisionModal.tsx | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/components/DecisionModal.tsx b/src/components/DecisionModal.tsx
index 3361df45996d..c46df4783509 100644
--- a/src/components/DecisionModal.tsx
+++ b/src/components/DecisionModal.tsx
@@ -45,7 +45,7 @@ type DecisionModalProps = {
     onClose: () => void;
 
     /** Callback when modal has fully disappeared */
-    onModalHide: () => void;
+    onModalHide?: () => void;
 
     /** Whether modal is visible */
     isVisible: boolean;

From 2e3d0160e28e5c17f00e40025c5d45789e3b83dd Mon Sep 17 00:00:00 2001
From: war-in <war-in@users.noreply.github.com>
Date: Fri, 30 Jan 2026 09:31:12 +0100
Subject: [PATCH 31/50] fix: eslint

---
 src/components/DecisionModal.tsx | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/src/components/DecisionModal.tsx b/src/components/DecisionModal.tsx
index c46df4783509..d1c13e4149ad 100644
--- a/src/components/DecisionModal.tsx
+++ b/src/components/DecisionModal.tsx
@@ -1,7 +1,8 @@
 import React from 'react';
-import {ScrollView, View} from 'react-native';
+import {View} from 'react-native';
 import useThemeStyles from '@hooks/useThemeStyles';
 import CONST from '@src/CONST';
+import ScrollView from './ScrollView';
 import Button from './Button';
 import Header from './Header';
 import Modal from './Modal';

From 8248a9a6deaa87989b801e4fcf622933f246a2fa Mon Sep 17 00:00:00 2001
From: war-in <war-in@users.noreply.github.com>
Date: Fri, 30 Jan 2026 09:32:22 +0100
Subject: [PATCH 32/50] fix: prettier

---
 src/components/DecisionModal.tsx | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/components/DecisionModal.tsx b/src/components/DecisionModal.tsx
index d1c13e4149ad..8cf23cb6d2b2 100644
--- a/src/components/DecisionModal.tsx
+++ b/src/components/DecisionModal.tsx
@@ -2,11 +2,11 @@ import React from 'react';
 import {View} from 'react-native';
 import useThemeStyles from '@hooks/useThemeStyles';
 import CONST from '@src/CONST';
-import ScrollView from './ScrollView';
 import Button from './Button';
 import Header from './Header';
 import Modal from './Modal';
 import RenderHTML from './RenderHTML';
+import ScrollView from './ScrollView';
 
 type DecisionModalProps = {
     /** Title describing purpose of modal */

From 139ff4e440c3d38c9b04cbfec6f3b99e8d1e73d4 Mon Sep 17 00:00:00 2001
From: sumo_slonik <jakub.nowakowski@swmansion.com>
Date: Mon, 2 Feb 2026 15:42:33 +0100
Subject: [PATCH 33/50] fix offline mode, fixes after Vit's comments

---
 src/libs/actions/Domain.ts                    | 20 +++------
 .../Members/DomainMemberDetailsPage.tsx       | 19 ++++----
 src/selectors/Domain.ts                       | 19 ++++----
 src/types/onyx/Domain.ts                      | 16 ++++---
 tests/unit/DomainSelectorsTest.ts             | 43 +++++++++----------
 5 files changed, 56 insertions(+), 61 deletions(-)

diff --git a/src/libs/actions/Domain.ts b/src/libs/actions/Domain.ts
index 0d09706cc6cb..70c05940c8cf 100644
--- a/src/libs/actions/Domain.ts
+++ b/src/libs/actions/Domain.ts
@@ -941,28 +941,22 @@ function clearAddMemberError(domainAccountID: number, accountID: number, email:
 }
 
 /** Sends a request to remove a user from a domain and close their account */
-function closeUserAccount(domainAccountID: number, domain: string, accountID: number, targetEmail: string, securityGroupsData: SecurityGroupsData, overrideProcessingReports = false) {
+/** Sends a request to remove a user from a domain and close their account */
+function closeUserAccount(domainAccountID: number, domain: string, accountID: number, targetEmail: string, securityGroupsData: UserSecurityGroupData, overrideProcessingReports = false) {
     const optimisticValue: PrefixedRecord<typeof CONST.DOMAIN.DOMAIN_SECURITY_GROUP_PREFIX, Partial<DomainSecurityGroup>> = {};
     const failureValue: PrefixedRecord<typeof CONST.DOMAIN.DOMAIN_SECURITY_GROUP_PREFIX, Partial<DomainSecurityGroup>> = {};
-    for (const groupID of securityGroupsData.keys) {
-        optimisticValue[groupID] = {
-            shared: {
-                [accountID]: null,
-            },
-        };
+
+    if (securityGroupsData) {
+        const groupID = securityGroupsData.key;
+
         failureValue[groupID] = {
             shared: {
-                [accountID]: securityGroupsData.securityGroups?.[groupID]?.shared?.[accountID] ?? null,
+                [accountID]: 'read',
             },
         };
     }
 
     const optimisticData: OnyxUpdate[] = [
-        {
-            onyxMethod: Onyx.METHOD.MERGE,
-            key: `${ONYXKEYS.COLLECTION.DOMAIN}${domainAccountID}`,
-            value: optimisticValue,
-        },
         {
             onyxMethod: Onyx.METHOD.MERGE,
             key: `${ONYXKEYS.COLLECTION.DOMAIN_PENDING_ACTIONS}${domainAccountID}`,
diff --git a/src/pages/domain/Members/DomainMemberDetailsPage.tsx b/src/pages/domain/Members/DomainMemberDetailsPage.tsx
index 54b7a948d3b0..1ab4ea45b0de 100644
--- a/src/pages/domain/Members/DomainMemberDetailsPage.tsx
+++ b/src/pages/domain/Members/DomainMemberDetailsPage.tsx
@@ -1,4 +1,4 @@
-import {adminAccountIDsSelector, domainNameSelector, selectSecurityGroupsForAccount} from '@selectors/Domain';
+import {adminAccountIDsSelector, domainNameSelector, selectSecurityGroupForAccount} from '@selectors/Domain';
 import {personalDetailsSelector} from '@selectors/PersonalDetails';
 import React, {useState} from 'react';
 import Button from '@components/Button';
@@ -40,9 +40,9 @@ function DomainMemberDetailsPage({route}: DomainMemberDetailsPageProps) {
         selector: adminAccountIDsSelector,
     });
 
-    const [securityGroupsData] = useOnyx(`${ONYXKEYS.COLLECTION.DOMAIN}${domainAccountID}`, {
+    const [userSecurityGroup] = useOnyx(`${ONYXKEYS.COLLECTION.DOMAIN}${domainAccountID}`, {
         canBeMissing: true,
-        selector: selectSecurityGroupsForAccount(accountID),
+        selector: selectSecurityGroupForAccount(accountID),
     });
 
     const [personalDetails] = useOnyx(ONYXKEYS.PERSONAL_DETAILS_LIST, {
@@ -57,12 +57,8 @@ function DomainMemberDetailsPage({route}: DomainMemberDetailsPageProps) {
     const {accountID: currentUserAccountID} = useCurrentUserPersonalDetails();
     const isAdmin = adminAccountIDs?.includes(currentUserAccountID);
 
-    if (isLoadingOnyxValue(domainMetadata)) {
-        return <FullScreenLoadingIndicator shouldUseGoBackButton />;
-    }
-
     const handleCloseAccount = async () => {
-        if (!securityGroupsData || shouldForceCloseAccount === undefined) {
+        if (!userSecurityGroup || shouldForceCloseAccount === undefined) {
             return;
         }
 
@@ -79,7 +75,7 @@ function DomainMemberDetailsPage({route}: DomainMemberDetailsPageProps) {
             setShouldForceCloseAccount(undefined);
             return;
         }
-        closeUserAccount(domainAccountID, domainName ?? '', accountID, memberLogin, securityGroupsData, shouldForceCloseAccount);
+        closeUserAccount(domainAccountID, domainName ?? '', accountID, memberLogin, userSecurityGroup, shouldForceCloseAccount);
         setShouldForceCloseAccount(undefined);
         Navigation.dismissModal();
     };
@@ -103,6 +99,11 @@ function DomainMemberDetailsPage({route}: DomainMemberDetailsPageProps) {
             style={styles.mb5}
         />
     );
+
+    if (isLoadingOnyxValue(domainMetadata)) {
+        return <FullScreenLoadingIndicator shouldUseGoBackButton />;
+    }
+
     return (
         <>
             <BaseDomainMemberDetailsComponent
diff --git a/src/selectors/Domain.ts b/src/selectors/Domain.ts
index bf52b717c789..4497e91d70f5 100644
--- a/src/selectors/Domain.ts
+++ b/src/selectors/Domain.ts
@@ -3,6 +3,7 @@ import type {OnyxEntry} from 'react-native-onyx';
 import CONST from '@src/CONST';
 import type {CardFeeds, Domain, DomainPendingActions, DomainSecurityGroup, DomainSettings, SamlMetadata} from '@src/types/onyx';
 import type {SecurityGroupKey, SecurityGroupsData} from '@src/types/onyx/Domain';
+import {UserSecurityGroupData} from '@src/types/onyx/Domain';
 import getEmptyArray from '@src/types/utils/getEmptyArray';
 import type PrefixedRecord from '@src/types/utils/PrefixedRecord';
 
@@ -108,15 +109,13 @@ function isSecurityGroupEntry(entry: [string, unknown]): entry is [SecurityGroup
  * @param accountID - The account ID to filter by
  * @returns A function that takes a domain and returns the filtered keys and security group data
  */
-function selectSecurityGroupsForAccount(accountID: number) {
-    return (domain: Domain | undefined): SecurityGroupsData => {
+function selectSecurityGroupForAccount(accountID: number) {
+    return (domain: Domain | undefined): UserSecurityGroupData => {
         if (!domain) {
-            return {keys: [], securityGroups: {}};
+            return undefined;
         }
 
         const accountIDStr = String(accountID);
-        const keys: SecurityGroupKey[] = [];
-        const securityGroups: PrefixedRecord<typeof CONST.DOMAIN.DOMAIN_SECURITY_GROUP_PREFIX, Partial<DomainSecurityGroup>> = {};
 
         for (const entry of Object.entries(domain)) {
             if (!isSecurityGroupEntry(entry)) {
@@ -126,12 +125,14 @@ function selectSecurityGroupsForAccount(accountID: number) {
             const [key, group] = entry;
 
             if (group.shared && accountIDStr in group.shared) {
-                keys.push(key);
-                securityGroups[key] = group;
+                return {
+                    key,
+                    securityGroup: group,
+                };
             }
         }
 
-        return {keys, securityGroups};
+        return undefined;
     };
 }
 
@@ -153,7 +154,7 @@ export {
     adminPendingActionSelector,
     technicalContactSettingsSelector,
     defaultSecurityGroupIDSelector,
-    selectSecurityGroupsForAccount,
+    selectSecurityGroupForAccount,
     memberPendingActionSelector,
     isSecurityGroupEntry,
 };
diff --git a/src/types/onyx/Domain.ts b/src/types/onyx/Domain.ts
index f36bc6a05060..f0980636e215 100644
--- a/src/types/onyx/Domain.ts
+++ b/src/types/onyx/Domain.ts
@@ -112,18 +112,20 @@ type DomainSecurityGroup = SecurityGroup & {
 type SecurityGroupKey = `${typeof CONST.DOMAIN.DOMAIN_SECURITY_GROUP_PREFIX}${string}`;
 
 /**
- * Combination of security group keys and security groups that are shared with the domain.
+ * Combination of security group key and security groups that the user belongs to.
  */
-type SecurityGroupsData = {
+type UserSecurityGroupData = {
     /**
-     * IDs of the security groups that are shared with the domain.
+     * The ID (key) of the security group the user belongs to.
      */
-    keys: SecurityGroupKey[];
+    key: SecurityGroupKey;
+
     /**
-     * Security groups that are shared with the domain.
+     * The security group data.
      */
-    securityGroups: PrefixedRecord<typeof CONST.DOMAIN.DOMAIN_SECURITY_GROUP_PREFIX, Partial<DomainSecurityGroup>>;
+    securityGroup: Partial<DomainSecurityGroup>;
 };
-export {type SamlMetadata, type DomainSecurityGroup, type SecurityGroupsData, type SecurityGroupKey};
+
+export {type SamlMetadata, type DomainSecurityGroup, type UserSecurityGroupData, type SecurityGroupKey};
 
 export default Domain;
diff --git a/tests/unit/DomainSelectorsTest.ts b/tests/unit/DomainSelectorsTest.ts
index 7e21cfb018d6..d62d796215bd 100644
--- a/tests/unit/DomainSelectorsTest.ts
+++ b/tests/unit/DomainSelectorsTest.ts
@@ -6,7 +6,7 @@ import {
     domainSettingsPrimaryContactSelector,
     isSecurityGroupEntry,
     memberAccountIDsSelector,
-    selectSecurityGroupsForAccount,
+    selectSecurityGroupForAccount,
     technicalContactSettingsSelector,
 } from '@selectors/Domain';
 import type {OnyxEntry} from 'react-native-onyx';
@@ -300,21 +300,20 @@ describe('domainSelectors', () => {
         });
     });
 
-    describe('selectSecurityGroupsForAccount', () => {
-        it('Should return empty arrays when domain has no security groups', () => {
+    describe('selectSecurityGroupForAccount', () => {
+        it('Should return undefined when domain has no security groups', () => {
             const domain = {
                 validated: true,
                 accountID: 1,
                 email: 'test@example.com',
             } as Domain;
 
-            const result = selectSecurityGroupsForAccount(123)(domain);
+            const result = selectSecurityGroupForAccount(123)(domain);
 
-            expect(result.keys).toEqual([]);
-            expect(result.securityGroups).toEqual({});
+            expect(result).toBeUndefined();
         });
 
-        it('Should return empty arrays when account is not in any security group', () => {
+        it('Should return undefined when account is not in any security group', () => {
             const securityGroup = {
                 enableRestrictedPrimaryLogin: false,
                 enableRestrictedPolicyCreation: false,
@@ -335,17 +334,18 @@ describe('domainSelectors', () => {
                 [`${CONST.DOMAIN.DOMAIN_SECURITY_GROUP_PREFIX}1`]: securityGroup,
             } as unknown as Domain;
 
-            const result = selectSecurityGroupsForAccount(123)(domain);
+            const result = selectSecurityGroupForAccount(123)(domain);
 
-            expect(result.keys).toHaveLength(0);
-            expect(result.securityGroups).toEqual({});
+            expect(result).toBeUndefined();
         });
 
-        it('Should return multiple security groups when account belongs to several', () => {
+        it('Should return the security group data when account belongs to a group', () => {
             /* eslint-disable @typescript-eslint/naming-convention */
             const group1 = {shared: {'123': 'read', '456': 'read'}, enableRestrictedPrimaryLogin: true, enableRestrictedPolicyCreation: true} as DomainSecurityGroup;
-            const group2 = {shared: {'123': 'read', '789': 'read'}, enableRestrictedPrimaryLogin: true, enableRestrictedPolicyCreation: true} as DomainSecurityGroup;
-            const group3 = {shared: {'999': 'read'}, enableRestrictedPrimaryLogin: true, enableRestrictedPolicyCreation: true} as DomainSecurityGroup;
+            const group2 = {shared: {'789': 'read'}, enableRestrictedPrimaryLogin: true, enableRestrictedPolicyCreation: true} as DomainSecurityGroup;
+
+            const key1 = `${CONST.DOMAIN.DOMAIN_SECURITY_GROUP_PREFIX}1`;
+            const key2 = `${CONST.DOMAIN.DOMAIN_SECURITY_GROUP_PREFIX}2`;
 
             const domain: Domain = {
                 validated: true,
@@ -353,19 +353,16 @@ describe('domainSelectors', () => {
                 email: 'test@example.com',
                 // eslint-disable-next-line @typescript-eslint/naming-convention
                 domain_defaultSecurityGroupID: '1',
-                [`${CONST.DOMAIN.DOMAIN_SECURITY_GROUP_PREFIX}1`]: group1,
-                [`${CONST.DOMAIN.DOMAIN_SECURITY_GROUP_PREFIX}2`]: group2,
-                [`${CONST.DOMAIN.DOMAIN_SECURITY_GROUP_PREFIX}3`]: group3,
+                [key1]: group1,
+                [key2]: group2,
             } as unknown as Domain;
 
-            const result = selectSecurityGroupsForAccount(123)(domain);
+            const result = selectSecurityGroupForAccount(123)(domain);
 
-            expect(result.keys).toHaveLength(2);
-            expect(result.keys).toContain(`${CONST.DOMAIN.DOMAIN_SECURITY_GROUP_PREFIX}1`);
-            expect(result.keys).toContain(`${CONST.DOMAIN.DOMAIN_SECURITY_GROUP_PREFIX}2`);
-            expect(result.securityGroups[`${CONST.DOMAIN.DOMAIN_SECURITY_GROUP_PREFIX}1`]).toEqual(group1);
-            expect(result.securityGroups[`${CONST.DOMAIN.DOMAIN_SECURITY_GROUP_PREFIX}2`]).toEqual(group2);
-            expect(result.securityGroups[`${CONST.DOMAIN.DOMAIN_SECURITY_GROUP_PREFIX}3`]).toBeUndefined();
+            expect(result).toEqual({
+                key: key1,
+                securityGroup: group1,
+            });
         });
     });
 

From b36d8bb4b8dc8111c310d43bc0719552c111294a Mon Sep 17 00:00:00 2001
From: sumo_slonik <jakub.nowakowski@swmansion.com>
Date: Mon, 2 Feb 2026 15:51:42 +0100
Subject: [PATCH 34/50] fix types

---
 src/libs/actions/Domain.ts | 25 +++++++++----------------
 src/types/onyx/Domain.ts   |  2 +-
 src/types/onyx/index.ts    |  4 ++--
 3 files changed, 12 insertions(+), 19 deletions(-)

diff --git a/src/libs/actions/Domain.ts b/src/libs/actions/Domain.ts
index 70c05940c8cf..6192a2b83e28 100644
--- a/src/libs/actions/Domain.ts
+++ b/src/libs/actions/Domain.ts
@@ -1,24 +1,17 @@
 import Onyx from 'react-native-onyx';
-import type {OnyxUpdate} from 'react-native-onyx';
+import type { OnyxUpdate } from 'react-native-onyx';
 import * as API from '@libs/API';
-import type {
-    AddAdminToDomainParams,
-    AddMemberToDomainParams,
-    DeleteDomainMemberParams,
-    DeleteDomainParams,
-    RemoveDomainAdminParams,
-    SetTechnicalContactEmailParams,
-    ToggleConsolidatedDomainBillingParams,
-} from '@libs/API/parameters';
-import {READ_COMMANDS, SIDE_EFFECT_REQUEST_COMMANDS, WRITE_COMMANDS} from '@libs/API/types';
-import {getMicroSecondOnyxErrorWithTranslationKey} from '@libs/ErrorUtils';
-import {generateAccountID} from '@libs/UserUtils';
+import type { AddAdminToDomainParams, AddMemberToDomainParams, DeleteDomainMemberParams, DeleteDomainParams, RemoveDomainAdminParams, SetTechnicalContactEmailParams, ToggleConsolidatedDomainBillingParams } from '@libs/API/parameters';
+import { READ_COMMANDS, SIDE_EFFECT_REQUEST_COMMANDS, WRITE_COMMANDS } from '@libs/API/types';
+import { getMicroSecondOnyxErrorWithTranslationKey } from '@libs/ErrorUtils';
+import { generateAccountID } from '@libs/UserUtils';
 import CONST from '@src/CONST';
 import ONYXKEYS from '@src/ONYXKEYS';
-import type {Domain, DomainSecurityGroup, SecurityGroupsData} from '@src/types/onyx';
+import type {Domain, DomainSecurityGroup, UserSecurityGroupData} from '@src/types/onyx';
 import type PrefixedRecord from '@src/types/utils/PrefixedRecord';
-import type {ScimTokenWithState} from './ScimToken/ScimTokenUtils';
-import {ScimTokenState} from './ScimToken/ScimTokenUtils';
+import type { ScimTokenWithState } from './ScimToken/ScimTokenUtils';
+import { ScimTokenState } from './ScimToken/ScimTokenUtils';
+
 
 /**
  * Fetches a validation code that the user is supposed to put in the domain's DNS records to verify it
diff --git a/src/types/onyx/Domain.ts b/src/types/onyx/Domain.ts
index f0980636e215..085598a804a0 100644
--- a/src/types/onyx/Domain.ts
+++ b/src/types/onyx/Domain.ts
@@ -124,7 +124,7 @@ type UserSecurityGroupData = {
      * The security group data.
      */
     securityGroup: Partial<DomainSecurityGroup>;
-};
+} | undefined;
 
 export {type SamlMetadata, type DomainSecurityGroup, type UserSecurityGroupData, type SecurityGroupKey};
 
diff --git a/src/types/onyx/index.ts b/src/types/onyx/index.ts
index a2bbc6fd8b7a..fb08addb5cb7 100644
--- a/src/types/onyx/index.ts
+++ b/src/types/onyx/index.ts
@@ -50,7 +50,7 @@ import type {
 import type DismissedProductTraining from './DismissedProductTraining';
 import type DismissedReferralBanners from './DismissedReferralBanners';
 import type Domain from './Domain';
-import type {DomainSecurityGroup, SamlMetadata, SecurityGroupsData} from './Domain';
+import type {DomainSecurityGroup, SamlMetadata, UserSecurityGroupData} from './Domain';
 import type DomainErrors from './DomainErrors';
 import type DomainPendingActions from './DomainPendingActions';
 import type Download from './Download';
@@ -344,5 +344,5 @@ export type {
     DomainPendingActions,
     DomainSecurityGroup,
     CodingRuleMatchingTransaction,
-    SecurityGroupsData,
+    UserSecurityGroupData,
 };

From a72ca3029e867929f96321041a243b586fca13ca Mon Sep 17 00:00:00 2001
From: sumo_slonik <jakub.nowakowski@swmansion.com>
Date: Mon, 2 Feb 2026 16:28:00 +0100
Subject: [PATCH 35/50] fix ts, es & prettier

---
 src/libs/actions/Domain.ts  |  24 ++++--
 src/selectors/Domain.ts     |   4 +-
 src/types/onyx/Domain.ts    |  24 +++---
 tests/actions/DomainTest.ts | 165 ++++++++++--------------------------
 4 files changed, 76 insertions(+), 141 deletions(-)

diff --git a/src/libs/actions/Domain.ts b/src/libs/actions/Domain.ts
index 6192a2b83e28..ed0ac63d53c9 100644
--- a/src/libs/actions/Domain.ts
+++ b/src/libs/actions/Domain.ts
@@ -1,17 +1,24 @@
 import Onyx from 'react-native-onyx';
-import type { OnyxUpdate } from 'react-native-onyx';
+import type {OnyxUpdate} from 'react-native-onyx';
 import * as API from '@libs/API';
-import type { AddAdminToDomainParams, AddMemberToDomainParams, DeleteDomainMemberParams, DeleteDomainParams, RemoveDomainAdminParams, SetTechnicalContactEmailParams, ToggleConsolidatedDomainBillingParams } from '@libs/API/parameters';
-import { READ_COMMANDS, SIDE_EFFECT_REQUEST_COMMANDS, WRITE_COMMANDS } from '@libs/API/types';
-import { getMicroSecondOnyxErrorWithTranslationKey } from '@libs/ErrorUtils';
-import { generateAccountID } from '@libs/UserUtils';
+import type {
+    AddAdminToDomainParams,
+    AddMemberToDomainParams,
+    DeleteDomainMemberParams,
+    DeleteDomainParams,
+    RemoveDomainAdminParams,
+    SetTechnicalContactEmailParams,
+    ToggleConsolidatedDomainBillingParams,
+} from '@libs/API/parameters';
+import {READ_COMMANDS, SIDE_EFFECT_REQUEST_COMMANDS, WRITE_COMMANDS} from '@libs/API/types';
+import {getMicroSecondOnyxErrorWithTranslationKey} from '@libs/ErrorUtils';
+import {generateAccountID} from '@libs/UserUtils';
 import CONST from '@src/CONST';
 import ONYXKEYS from '@src/ONYXKEYS';
 import type {Domain, DomainSecurityGroup, UserSecurityGroupData} from '@src/types/onyx';
 import type PrefixedRecord from '@src/types/utils/PrefixedRecord';
-import type { ScimTokenWithState } from './ScimToken/ScimTokenUtils';
-import { ScimTokenState } from './ScimToken/ScimTokenUtils';
-
+import type {ScimTokenWithState} from './ScimToken/ScimTokenUtils';
+import {ScimTokenState} from './ScimToken/ScimTokenUtils';
 
 /**
  * Fetches a validation code that the user is supposed to put in the domain's DNS records to verify it
@@ -936,7 +943,6 @@ function clearAddMemberError(domainAccountID: number, accountID: number, email:
 /** Sends a request to remove a user from a domain and close their account */
 /** Sends a request to remove a user from a domain and close their account */
 function closeUserAccount(domainAccountID: number, domain: string, accountID: number, targetEmail: string, securityGroupsData: UserSecurityGroupData, overrideProcessingReports = false) {
-    const optimisticValue: PrefixedRecord<typeof CONST.DOMAIN.DOMAIN_SECURITY_GROUP_PREFIX, Partial<DomainSecurityGroup>> = {};
     const failureValue: PrefixedRecord<typeof CONST.DOMAIN.DOMAIN_SECURITY_GROUP_PREFIX, Partial<DomainSecurityGroup>> = {};
 
     if (securityGroupsData) {
diff --git a/src/selectors/Domain.ts b/src/selectors/Domain.ts
index 4497e91d70f5..73b9a6ae2b21 100644
--- a/src/selectors/Domain.ts
+++ b/src/selectors/Domain.ts
@@ -2,10 +2,8 @@ import {Str} from 'expensify-common';
 import type {OnyxEntry} from 'react-native-onyx';
 import CONST from '@src/CONST';
 import type {CardFeeds, Domain, DomainPendingActions, DomainSecurityGroup, DomainSettings, SamlMetadata} from '@src/types/onyx';
-import type {SecurityGroupKey, SecurityGroupsData} from '@src/types/onyx/Domain';
-import {UserSecurityGroupData} from '@src/types/onyx/Domain';
+import type {SecurityGroupKey, UserSecurityGroupData} from '@src/types/onyx/Domain';
 import getEmptyArray from '@src/types/utils/getEmptyArray';
-import type PrefixedRecord from '@src/types/utils/PrefixedRecord';
 
 const domainMemberSamlSettingsSelector = (domainSettings: OnyxEntry<CardFeeds>) => domainSettings?.settings;
 
diff --git a/src/types/onyx/Domain.ts b/src/types/onyx/Domain.ts
index 085598a804a0..6143c49c2d52 100644
--- a/src/types/onyx/Domain.ts
+++ b/src/types/onyx/Domain.ts
@@ -114,17 +114,19 @@ type SecurityGroupKey = `${typeof CONST.DOMAIN.DOMAIN_SECURITY_GROUP_PREFIX}${st
 /**
  * Combination of security group key and security groups that the user belongs to.
  */
-type UserSecurityGroupData = {
-    /**
-     * The ID (key) of the security group the user belongs to.
-     */
-    key: SecurityGroupKey;
-
-    /**
-     * The security group data.
-     */
-    securityGroup: Partial<DomainSecurityGroup>;
-} | undefined;
+type UserSecurityGroupData =
+    | {
+          /**
+           * The ID (key) of the security group the user belongs to.
+           */
+          key: SecurityGroupKey;
+
+          /**
+           * The security group data.
+           */
+          securityGroup: Partial<DomainSecurityGroup>;
+      }
+    | undefined;
 
 export {type SamlMetadata, type DomainSecurityGroup, type UserSecurityGroupData, type SecurityGroupKey};
 
diff --git a/tests/actions/DomainTest.ts b/tests/actions/DomainTest.ts
index 918dec457bee..d7353c54a7d5 100644
--- a/tests/actions/DomainTest.ts
+++ b/tests/actions/DomainTest.ts
@@ -223,131 +223,60 @@ describe('actions/Domain', () => {
         });
     });
 
-    it('closeUserAccount - sends DELETE_DOMAIN_MEMBER API request with correct data', () => {
-        const apiWriteSpy = jest.spyOn(require('@libs/API'), 'write').mockImplementation(() => Promise.resolve());
-        const domainAccountID = 123;
-        const domainName = 'test.com';
-        const accountID = 456;
-        const targetEmail = 'user@test.com';
-        const securityGroupKey = `${CONST.DOMAIN.DOMAIN_SECURITY_GROUP_PREFIX}1` as const;
-        const securityGroupIDs = [securityGroupKey];
-        const securityGroups: PrefixedRecord<typeof CONST.DOMAIN.DOMAIN_SECURITY_GROUP_PREFIX, Partial<DomainSecurityGroup>> = {
-            [securityGroupKey]: {
-                shared: {
-                    [accountID]: 'read',
+    describe('closeUserAccount', () => {
+        it('closeUserAccount - sends DELETE_DOMAIN_MEMBER API request with correct data', () => {
+            const apiWriteSpy = jest.spyOn(require('@libs/API'), 'write').mockImplementation(() => Promise.resolve());
+            const domainAccountID = 123;
+            const domainName = 'test.com';
+            const targetEmail = 'user@test.com';
+
+            closeUserAccount(domainAccountID, domainName, targetEmail);
+
+            expect(apiWriteSpy).toHaveBeenCalledWith(
+                WRITE_COMMANDS.DELETE_DOMAIN_MEMBER,
+                {domain: domainName, targetEmail, overrideProcessingReports: false},
+                {
+                    optimisticData: [
+                        expect.objectContaining({
+                            key: `${ONYXKEYS.COLLECTION.DOMAIN_PENDING_ACTIONS}${domainAccountID}`,
+                            value: {members: {[targetEmail]: CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE}},
+                        }),
+                    ],
+                    successData: [
+                        expect.objectContaining({
+                            key: `${ONYXKEYS.COLLECTION.DOMAIN_PENDING_ACTIONS}${domainAccountID}`,
+                            value: {members: {[targetEmail]: null}},
+                        }),
+                    ],
+                    failureData: [
+                        expect.objectContaining({
+                            key: `${ONYXKEYS.COLLECTION.DOMAIN_ERRORS}${domainAccountID}`,
+                            // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
+                            value: {memberErrors: {[targetEmail]: expect.any(Object)}},
+                        }),
+                        expect.objectContaining({
+                            key: `${ONYXKEYS.COLLECTION.DOMAIN_PENDING_ACTIONS}${domainAccountID}`,
+                            value: {members: {[targetEmail]: null}},
+                        }),
+                    ],
                 },
-            },
-        };
-
-        closeUserAccount(domainAccountID, domainName, accountID, targetEmail, {
-            keys: securityGroupIDs,
-            securityGroups,
-        });
-
-        expect(apiWriteSpy).toHaveBeenCalledWith(
-            WRITE_COMMANDS.DELETE_DOMAIN_MEMBER,
-            {domain: domainName, targetEmail, overrideProcessingReports: false},
-            {
-                optimisticData: [
-                    expect.objectContaining({
-                        key: `${ONYXKEYS.COLLECTION.DOMAIN}${domainAccountID}`,
-                        value: {[securityGroupKey]: {shared: {[accountID]: null}}},
-                    }),
-                    expect.objectContaining({
-                        key: `${ONYXKEYS.COLLECTION.DOMAIN_PENDING_ACTIONS}${domainAccountID}`,
-                        value: {members: {[targetEmail]: CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE}},
-                    }),
-                ],
-                successData: [
-                    expect.objectContaining({
-                        key: `${ONYXKEYS.COLLECTION.DOMAIN_PENDING_ACTIONS}${domainAccountID}`,
-                        value: {members: {[targetEmail]: null}},
-                    }),
-                ],
-                failureData: [
-                    expect.objectContaining({
-                        key: `${ONYXKEYS.COLLECTION.DOMAIN_ERRORS}${domainAccountID}`,
-                        // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
-                        value: {memberErrors: {[targetEmail]: expect.any(Object)}},
-                    }),
-                    expect.objectContaining({
-                        key: `${ONYXKEYS.COLLECTION.DOMAIN}${domainAccountID}`,
-                        value: {[securityGroupKey]: {shared: {[accountID]: 'read'}}},
-                    }),
-                    expect.objectContaining({
-                        key: `${ONYXKEYS.COLLECTION.DOMAIN_PENDING_ACTIONS}${domainAccountID}`,
-                        value: {members: {[targetEmail]: null}},
-                    }),
-                ],
-            },
-        );
+            );
 
-        apiWriteSpy.mockRestore();
-    });
-
-    it('closeUserAccount - handles multiple security groups', () => {
-        const apiWriteSpy = jest.spyOn(require('@libs/API'), 'write').mockImplementation(() => Promise.resolve());
-        const domainAccountID = 123;
-        const domainName = 'test.com';
-        const accountID = 456;
-        const targetEmail = 'user@test.com';
-        const securityGroupKey1 = `${CONST.DOMAIN.DOMAIN_SECURITY_GROUP_PREFIX}1` as const;
-        const securityGroupKey2 = `${CONST.DOMAIN.DOMAIN_SECURITY_GROUP_PREFIX}2` as const;
-        const securityGroupIDs = [securityGroupKey1, securityGroupKey2];
-        const securityGroups: PrefixedRecord<typeof CONST.DOMAIN.DOMAIN_SECURITY_GROUP_PREFIX, Partial<DomainSecurityGroup>> = {
-            [securityGroupKey1]: {
-                shared: {[accountID]: 'read'},
-            },
-            [securityGroupKey2]: {
-                shared: {[accountID]: 'read'},
-            },
-        };
-        closeUserAccount(domainAccountID, domainName, accountID, targetEmail, {
-            keys: securityGroupIDs,
-            securityGroups,
+            apiWriteSpy.mockRestore();
         });
 
-        expect(apiWriteSpy).toHaveBeenCalledWith(
-            WRITE_COMMANDS.DELETE_DOMAIN_MEMBER,
-            expect.any(Object),
-            expect.objectContaining({
-                optimisticData: expect.arrayContaining([
-                    expect.objectContaining({
-                        key: `${ONYXKEYS.COLLECTION.DOMAIN}${domainAccountID}`,
-                        value: {
-                            [securityGroupKey1]: {shared: {[accountID]: null}},
-                            [securityGroupKey2]: {shared: {[accountID]: null}},
-                        },
-                    }),
-                ]),
-            }),
-        );
-
-        apiWriteSpy.mockRestore();
-    });
-
-    it('closeUserAccount - handles overrideProcessingReports flag', () => {
-        const apiWriteSpy = jest.spyOn(require('@libs/API'), 'write').mockImplementation(() => Promise.resolve());
-        const domainAccountID = 123;
-        const domainName = 'test.com';
-        const accountID = 456;
-        const targetEmail = 'user@test.com';
+        it('closeUserAccount - handles overrideProcessingReports flag', () => {
+            const apiWriteSpy = jest.spyOn(require('@libs/API'), 'write').mockImplementation(() => Promise.resolve());
+            const domainAccountID = 123;
+            const domainName = 'test.com';
+            const targetEmail = 'user@test.com';
 
-        closeUserAccount(
-            domainAccountID,
-            domainName,
-            accountID,
-            targetEmail,
-            {
-                keys: [],
-                securityGroups: {},
-            },
-            true,
-        );
+            closeUserAccount(domainAccountID, domainName, targetEmail, true);
 
-        expect(apiWriteSpy).toHaveBeenCalledWith(WRITE_COMMANDS.DELETE_DOMAIN_MEMBER, {domain: domainName, targetEmail, overrideProcessingReports: true}, expect.any(Object));
+            expect(apiWriteSpy).toHaveBeenCalledWith(WRITE_COMMANDS.DELETE_DOMAIN_MEMBER, {domain: domainName, targetEmail, overrideProcessingReports: true}, expect.any(Object));
 
-        apiWriteSpy.mockRestore();
+            apiWriteSpy.mockRestore();
+        });
     });
 
     it('clearDomainMemberError - clears member errors and pending actions', async () => {

From 56e84b1aaa97b34419857aa0763558826ce715d8 Mon Sep 17 00:00:00 2001
From: sumo_slonik <jakub.nowakowski@swmansion.com>
Date: Mon, 2 Feb 2026 17:48:07 +0100
Subject: [PATCH 36/50] fix close arguments order

---
 src/libs/actions/Domain.ts                    | 38 +++++++------------
 .../domain/Members/DomainMembersPage.tsx      |  4 +-
 tests/actions/DomainTest.ts                   | 25 ++++++++++--
 3 files changed, 36 insertions(+), 31 deletions(-)

diff --git a/src/libs/actions/Domain.ts b/src/libs/actions/Domain.ts
index ed0ac63d53c9..fbb406b7c4ac 100644
--- a/src/libs/actions/Domain.ts
+++ b/src/libs/actions/Domain.ts
@@ -16,6 +16,7 @@ import {generateAccountID} from '@libs/UserUtils';
 import CONST from '@src/CONST';
 import ONYXKEYS from '@src/ONYXKEYS';
 import type {Domain, DomainSecurityGroup, UserSecurityGroupData} from '@src/types/onyx';
+import {PendingAction} from '@src/types/onyx/OnyxCommon';
 import type PrefixedRecord from '@src/types/utils/PrefixedRecord';
 import type {ScimTokenWithState} from './ScimToken/ScimTokenUtils';
 import {ScimTokenState} from './ScimToken/ScimTokenUtils';
@@ -921,26 +922,27 @@ function addMemberToDomain(domainAccountID: number, email: string, defaultSecuri
 /**
  * Removes an error and pending actions after trying to add member. It clears errors for both email and accountID
  */
-function clearAddMemberError(domainAccountID: number, accountID: number, email: string, defaultSecurityGroupID: string) {
+function clearDomainMemberError(domainAccountID: number, accountID: number, email: string, pendingAction: PendingAction, defaultSecurityGroupID: string) {
     const DOMAIN_SECURITY_GROUP = `${CONST.DOMAIN.DOMAIN_SECURITY_GROUP_PREFIX}${defaultSecurityGroupID}`;
 
+    if (pendingAction === CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD) {
+        Onyx.merge(`${ONYXKEYS.COLLECTION.DOMAIN}${domainAccountID}`, {
+            [DOMAIN_SECURITY_GROUP]: {
+                shared: {
+                    [accountID]: null,
+                },
+            },
+        } as PrefixedRecord<typeof CONST.DOMAIN.DOMAIN_SECURITY_GROUP_PREFIX, Partial<DomainSecurityGroup>>);
+    }
+
     Onyx.merge(`${ONYXKEYS.COLLECTION.DOMAIN_ERRORS}${domainAccountID}`, {
         memberErrors: {
             [email]: null,
             [accountID]: null,
         },
     });
-
-    Onyx.merge(`${ONYXKEYS.COLLECTION.DOMAIN}${domainAccountID}`, {
-        [DOMAIN_SECURITY_GROUP]: {
-            shared: {
-                [accountID]: null,
-            },
-        },
-    } as PrefixedRecord<typeof CONST.DOMAIN.DOMAIN_SECURITY_GROUP_PREFIX, Partial<DomainSecurityGroup>>);
 }
 
-/** Sends a request to remove a user from a domain and close their account */
 /** Sends a request to remove a user from a domain and close their account */
 function closeUserAccount(domainAccountID: number, domain: string, accountID: number, targetEmail: string, securityGroupsData: UserSecurityGroupData, overrideProcessingReports = false) {
     const failureValue: PrefixedRecord<typeof CONST.DOMAIN.DOMAIN_SECURITY_GROUP_PREFIX, Partial<DomainSecurityGroup>> = {};
@@ -1008,19 +1010,6 @@ function closeUserAccount(domainAccountID: number, domain: string, accountID: nu
     API.write(WRITE_COMMANDS.DELETE_DOMAIN_MEMBER, parameters, {optimisticData, successData, failureData});
 }
 
-function clearDomainMemberError(domainAccountID: number, accountID: number, email: string) {
-    Onyx.merge(`${ONYXKEYS.COLLECTION.DOMAIN_ERRORS}${domainAccountID}`, {
-        memberErrors: {
-            [accountID]: null,
-            [email]: null,
-        },
-    });
-
-    Onyx.merge(`${ONYXKEYS.COLLECTION.DOMAIN_PENDING_ACTIONS}${domainAccountID}`, {
-        members: {[email]: null},
-    });
-}
-
 export {
     getDomainValidationCode,
     validateDomain,
@@ -1045,7 +1034,6 @@ export {
     resetDomain,
     clearDomainErrors,
     addMemberToDomain,
-    clearAddMemberError,
-    closeUserAccount,
     clearDomainMemberError,
+    closeUserAccount,
 };
diff --git a/src/pages/domain/Members/DomainMembersPage.tsx b/src/pages/domain/Members/DomainMembersPage.tsx
index c024e33e7272..37d8e0329a52 100644
--- a/src/pages/domain/Members/DomainMembersPage.tsx
+++ b/src/pages/domain/Members/DomainMembersPage.tsx
@@ -6,7 +6,7 @@ import useLocalize from '@hooks/useLocalize';
 import useOnyx from '@hooks/useOnyx';
 import useResponsiveLayout from '@hooks/useResponsiveLayout';
 import useThemeStyles from '@hooks/useThemeStyles';
-import {clearAddMemberError} from '@libs/actions/Domain';
+import {clearDomainMemberError} from '@libs/actions/Domain';
 import {getLatestError} from '@libs/ErrorUtils';
 import Navigation from '@navigation/Navigation';
 import type {PlatformStackScreenProps} from '@navigation/PlatformStackNavigation/types';
@@ -68,7 +68,7 @@ function DomainMembersPage({route}: DomainMembersPageProps) {
                 if (!defaultSecurityGroupID) {
                     return;
                 }
-                clearAddMemberError(domainAccountID, item.accountID, item.login, defaultSecurityGroupID);
+                clearDomainMemberError(domainAccountID, item.accountID, item.login, item.pendingAction, defaultSecurityGroupID);
             }}
         />
     );
diff --git a/tests/actions/DomainTest.ts b/tests/actions/DomainTest.ts
index d7353c54a7d5..b024a21ecf0c 100644
--- a/tests/actions/DomainTest.ts
+++ b/tests/actions/DomainTest.ts
@@ -1,5 +1,5 @@
 import Onyx from 'react-native-onyx';
-import {addMemberToDomain, clearAddMemberError, clearDomainErrors, clearDomainMemberError, closeUserAccount, createDomain, resetCreateDomainForm, resetDomain} from '@libs/actions/Domain';
+import {addMemberToDomain, clearDomainErrors, clearDomainMemberError, closeUserAccount, createDomain, resetCreateDomainForm, resetDomain} from '@libs/actions/Domain';
 import {WRITE_COMMANDS} from '@libs/API/types';
 import {generateAccountID} from '@libs/UserUtils';
 import CONST from '@src/CONST';
@@ -202,7 +202,7 @@ describe('actions/Domain', () => {
             } as PrefixedRecord<typeof CONST.DOMAIN.DOMAIN_SECURITY_GROUP_PREFIX, Partial<DomainSecurityGroup>>,
         );
 
-        clearAddMemberError(domainAccountID, optimisticAccountID, email, defaultSecurityGroupID);
+        clearDomainMemberError(domainAccountID, optimisticAccountID, email, defaultSecurityGroupID);
 
         await TestHelper.getOnyxData({
             key: `${ONYXKEYS.COLLECTION.DOMAIN_ERRORS}${domainAccountID}`,
@@ -228,9 +228,20 @@ describe('actions/Domain', () => {
             const apiWriteSpy = jest.spyOn(require('@libs/API'), 'write').mockImplementation(() => Promise.resolve());
             const domainAccountID = 123;
             const domainName = 'test.com';
+            const accountID = 456;
             const targetEmail = 'user@test.com';
+            const securityGroupKey = `${CONST.DOMAIN.DOMAIN_SECURITY_GROUP_PREFIX}1` as const;
 
-            closeUserAccount(domainAccountID, domainName, targetEmail);
+            const securityGroupsData: UserSecurityGroupData = {
+                key: securityGroupKey,
+                securityGroup: {
+                    shared: {
+                        [accountID]: 'read',
+                    },
+                },
+            };
+
+            closeUserAccount(domainAccountID, domainName, accountID, targetEmail, securityGroupsData);
 
             expect(apiWriteSpy).toHaveBeenCalledWith(
                 WRITE_COMMANDS.DELETE_DOMAIN_MEMBER,
@@ -254,6 +265,11 @@ describe('actions/Domain', () => {
                             // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
                             value: {memberErrors: {[targetEmail]: expect.any(Object)}},
                         }),
+                        // This restores the user to the security group if the API call fails
+                        expect.objectContaining({
+                            key: `${ONYXKEYS.COLLECTION.DOMAIN}${domainAccountID}`,
+                            value: {[securityGroupKey]: {shared: {[accountID]: 'read'}}},
+                        }),
                         expect.objectContaining({
                             key: `${ONYXKEYS.COLLECTION.DOMAIN_PENDING_ACTIONS}${domainAccountID}`,
                             value: {members: {[targetEmail]: null}},
@@ -269,9 +285,10 @@ describe('actions/Domain', () => {
             const apiWriteSpy = jest.spyOn(require('@libs/API'), 'write').mockImplementation(() => Promise.resolve());
             const domainAccountID = 123;
             const domainName = 'test.com';
+            const accountID = 456;
             const targetEmail = 'user@test.com';
 
-            closeUserAccount(domainAccountID, domainName, targetEmail, true);
+            closeUserAccount(domainAccountID, domainName, accountID, targetEmail, null, true);
 
             expect(apiWriteSpy).toHaveBeenCalledWith(WRITE_COMMANDS.DELETE_DOMAIN_MEMBER, {domain: domainName, targetEmail, overrideProcessingReports: true}, expect.any(Object));
 

From f7f431d28dd22699b036f12333fba2a28aac21b2 Mon Sep 17 00:00:00 2001
From: sumo_slonik <jakub.nowakowski@swmansion.com>
Date: Mon, 2 Feb 2026 18:36:22 +0100
Subject: [PATCH 37/50] fix jest

---
 src/libs/actions/Domain.ts                     | 11 +++++++++--
 src/pages/domain/Members/DomainMembersPage.tsx |  2 +-
 tests/actions/DomainTest.ts                    | 12 ++++++------
 3 files changed, 16 insertions(+), 9 deletions(-)

diff --git a/src/libs/actions/Domain.ts b/src/libs/actions/Domain.ts
index fbb406b7c4ac..a3411ec6d252 100644
--- a/src/libs/actions/Domain.ts
+++ b/src/libs/actions/Domain.ts
@@ -16,7 +16,7 @@ import {generateAccountID} from '@libs/UserUtils';
 import CONST from '@src/CONST';
 import ONYXKEYS from '@src/ONYXKEYS';
 import type {Domain, DomainSecurityGroup, UserSecurityGroupData} from '@src/types/onyx';
-import {PendingAction} from '@src/types/onyx/OnyxCommon';
+import type {PendingAction} from '@src/types/onyx/OnyxCommon';
 import type PrefixedRecord from '@src/types/utils/PrefixedRecord';
 import type {ScimTokenWithState} from './ScimToken/ScimTokenUtils';
 import {ScimTokenState} from './ScimToken/ScimTokenUtils';
@@ -922,7 +922,7 @@ function addMemberToDomain(domainAccountID: number, email: string, defaultSecuri
 /**
  * Removes an error and pending actions after trying to add member. It clears errors for both email and accountID
  */
-function clearDomainMemberError(domainAccountID: number, accountID: number, email: string, pendingAction: PendingAction, defaultSecurityGroupID: string) {
+function clearDomainMemberError(domainAccountID: number, accountID: number, email: string, defaultSecurityGroupID: string, pendingAction?: PendingAction) {
     const DOMAIN_SECURITY_GROUP = `${CONST.DOMAIN.DOMAIN_SECURITY_GROUP_PREFIX}${defaultSecurityGroupID}`;
 
     if (pendingAction === CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD) {
@@ -941,6 +941,13 @@ function clearDomainMemberError(domainAccountID: number, accountID: number, emai
             [accountID]: null,
         },
     });
+
+    Onyx.merge(`${ONYXKEYS.COLLECTION.DOMAIN_PENDING_ACTIONS}${domainAccountID}`, {
+        member: {
+            [email]: null,
+            [accountID]: null,
+        },
+    });
 }
 
 /** Sends a request to remove a user from a domain and close their account */
diff --git a/src/pages/domain/Members/DomainMembersPage.tsx b/src/pages/domain/Members/DomainMembersPage.tsx
index 37d8e0329a52..0875bbee175a 100644
--- a/src/pages/domain/Members/DomainMembersPage.tsx
+++ b/src/pages/domain/Members/DomainMembersPage.tsx
@@ -68,7 +68,7 @@ function DomainMembersPage({route}: DomainMembersPageProps) {
                 if (!defaultSecurityGroupID) {
                     return;
                 }
-                clearDomainMemberError(domainAccountID, item.accountID, item.login, item.pendingAction, defaultSecurityGroupID);
+                clearDomainMemberError(domainAccountID, item.accountID, item.login, defaultSecurityGroupID, item.pendingAction);
             }}
         />
     );
diff --git a/tests/actions/DomainTest.ts b/tests/actions/DomainTest.ts
index b024a21ecf0c..ca5a51c03705 100644
--- a/tests/actions/DomainTest.ts
+++ b/tests/actions/DomainTest.ts
@@ -6,7 +6,7 @@ import CONST from '@src/CONST';
 import IntlStore from '@src/languages/IntlStore';
 import OnyxUpdateManager from '@src/libs/actions/OnyxUpdateManager';
 import ONYXKEYS from '@src/ONYXKEYS';
-import type {Domain, DomainSecurityGroup} from '@src/types/onyx';
+import type {Domain, DomainSecurityGroup, UserSecurityGroupData} from '@src/types/onyx';
 import type PrefixedRecord from '@src/types/utils/PrefixedRecord';
 import * as TestHelper from '../utils/TestHelper';
 import waitForBatchedUpdates from '../utils/waitForBatchedUpdates';
@@ -202,7 +202,7 @@ describe('actions/Domain', () => {
             } as PrefixedRecord<typeof CONST.DOMAIN.DOMAIN_SECURITY_GROUP_PREFIX, Partial<DomainSecurityGroup>>,
         );
 
-        clearDomainMemberError(domainAccountID, optimisticAccountID, email, defaultSecurityGroupID);
+        clearDomainMemberError(domainAccountID, optimisticAccountID, email, defaultSecurityGroupID, 'add');
 
         await TestHelper.getOnyxData({
             key: `${ONYXKEYS.COLLECTION.DOMAIN_ERRORS}${domainAccountID}`,
@@ -288,7 +288,7 @@ describe('actions/Domain', () => {
             const accountID = 456;
             const targetEmail = 'user@test.com';
 
-            closeUserAccount(domainAccountID, domainName, accountID, targetEmail, null, true);
+            closeUserAccount(domainAccountID, domainName, accountID, targetEmail, undefined, true);
 
             expect(apiWriteSpy).toHaveBeenCalledWith(WRITE_COMMANDS.DELETE_DOMAIN_MEMBER, {domain: domainName, targetEmail, overrideProcessingReports: true}, expect.any(Object));
 
@@ -311,10 +311,10 @@ describe('actions/Domain', () => {
         });
 
         await Onyx.set(`${ONYXKEYS.COLLECTION.DOMAIN_PENDING_ACTIONS}${domainAccountID}` as const, {
-            members: {[email]: CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE},
+            member: {[email]: CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE},
         });
 
-        clearDomainMemberError(domainAccountID, accountID, email);
+        clearDomainMemberError(domainAccountID, accountID, email, '',);
 
         await TestHelper.getOnyxData({
             key: `${ONYXKEYS.COLLECTION.DOMAIN_ERRORS}${domainAccountID}`,
@@ -329,7 +329,7 @@ describe('actions/Domain', () => {
             key: `${ONYXKEYS.COLLECTION.DOMAIN_PENDING_ACTIONS}${domainAccountID}`,
             waitForCollectionCallback: false,
             callback: (pendingActions) => {
-                expect(pendingActions?.members?.[email]).toBeFalsy();
+                expect(pendingActions?.member?.[email]).toBeFalsy();
             },
         });
     });

From ebd5b5f35a20b274610944d739208be3faab8cf2 Mon Sep 17 00:00:00 2001
From: sumo_slonik <jakub.nowakowski@swmansion.com>
Date: Mon, 2 Feb 2026 19:02:52 +0100
Subject: [PATCH 38/50] fix prettier

---
 tests/actions/DomainTest.ts | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/tests/actions/DomainTest.ts b/tests/actions/DomainTest.ts
index ca5a51c03705..d68c2df9ea43 100644
--- a/tests/actions/DomainTest.ts
+++ b/tests/actions/DomainTest.ts
@@ -314,7 +314,7 @@ describe('actions/Domain', () => {
             member: {[email]: CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE},
         });
 
-        clearDomainMemberError(domainAccountID, accountID, email, '',);
+        clearDomainMemberError(domainAccountID, accountID, email, '');
 
         await TestHelper.getOnyxData({
             key: `${ONYXKEYS.COLLECTION.DOMAIN_ERRORS}${domainAccountID}`,

From 14a9a5f071ddbabfecb89460123e237e16865d69 Mon Sep 17 00:00:00 2001
From: sumo_slonik <jakub.nowakowski@swmansion.com>
Date: Tue, 3 Feb 2026 00:05:10 +0100
Subject: [PATCH 39/50] fix ts

---
 tests/actions/DomainTest.ts | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/tests/actions/DomainTest.ts b/tests/actions/DomainTest.ts
index d68c2df9ea43..e163c7db8c44 100644
--- a/tests/actions/DomainTest.ts
+++ b/tests/actions/DomainTest.ts
@@ -311,7 +311,9 @@ describe('actions/Domain', () => {
         });
 
         await Onyx.set(`${ONYXKEYS.COLLECTION.DOMAIN_PENDING_ACTIONS}${domainAccountID}` as const, {
-            member: {[email]: CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE},
+            member: {
+                [email]: {pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE},
+            },
         });
 
         clearDomainMemberError(domainAccountID, accountID, email, '');

From 28eeff2f4eefaf402e7268e229a8f3f1666f3a0a Mon Sep 17 00:00:00 2001
From: sumo_slonik <jakub.nowakowski@swmansion.com>
Date: Tue, 3 Feb 2026 08:07:15 +0100
Subject: [PATCH 40/50] fix marge conflicts

---
 src/languages/de.ts      | 4 ----
 src/languages/fr.ts      | 1 -
 src/languages/ja.ts      | 1 -
 src/languages/nl.ts      | 1 -
 src/languages/pl.ts      | 1 -
 src/languages/zh-hans.ts | 1 -
 6 files changed, 9 deletions(-)

diff --git a/src/languages/de.ts b/src/languages/de.ts
index 00a8bd1627e7..3837e3571ca6 100644
--- a/src/languages/de.ts
+++ b/src/languages/de.ts
@@ -8304,9 +8304,6 @@ Hier ist ein *Testbeleg*, um dir zu zeigen, wie es funktioniert:`,
             findMember: 'Mitglied finden',
             addMember: 'Mitglied hinzufügen',
             email: 'E-Mail-Adresse',
-            errors: {
-                addMember: 'Dieses Mitglied kann nicht hinzugefügt werden. Bitte versuche es erneut.',
-            },
             closeAccount: 'Konto schließen',
             closeAccountPrompt: 'Bist du sicher? Diese Aktion ist dauerhaft.',
             forceCloseAccount: 'Konto zwangsweise schließen',
@@ -8318,7 +8315,6 @@ Hier ist ein *Testbeleg*, um dir zu zeigen, wie es funktioniert:`,
                 addMember: 'Dieses Mitglied kann nicht hinzugefügt werden. Bitte versuche es erneut.',
             },
         },
-        domainAdmins: 'Domain-Admins',
     },
 };
 export default translations;
diff --git a/src/languages/fr.ts b/src/languages/fr.ts
index 57d609f33da9..c2c65a099ae3 100644
--- a/src/languages/fr.ts
+++ b/src/languages/fr.ts
@@ -8329,7 +8329,6 @@ Voici un *reçu test* pour vous montrer comment ça fonctionne :`,
                 addMember: 'Impossible d’ajouter ce membre. Veuillez réessayer.',
             },
         },
-        domainAdmins: 'Administrateurs de domaine',
     },
 };
 export default translations;
diff --git a/src/languages/ja.ts b/src/languages/ja.ts
index 85b5ce5ed46e..1e121209bfed 100644
--- a/src/languages/ja.ts
+++ b/src/languages/ja.ts
@@ -8227,7 +8227,6 @@ ${reportName}
                 addMember: 'このメンバーを追加できませんでした。もう一度お試しください。',
             },
         },
-        domainAdmins: 'ドメイン管理者',
     },
 };
 export default translations;
diff --git a/src/languages/nl.ts b/src/languages/nl.ts
index 9131864f725b..1f7ec693bf8a 100644
--- a/src/languages/nl.ts
+++ b/src/languages/nl.ts
@@ -8283,7 +8283,6 @@ Hier is een *proefbon* om je te laten zien hoe het werkt:`,
                 addMember: 'Kan dit lid niet toevoegen. Probeer het opnieuw.',
             },
         },
-        domainAdmins: 'Domeinbeheerders',
     },
 };
 export default translations;
diff --git a/src/languages/pl.ts b/src/languages/pl.ts
index 0a7aaab9f79b..cc35a57a6792 100644
--- a/src/languages/pl.ts
+++ b/src/languages/pl.ts
@@ -8263,7 +8263,6 @@ Oto *paragon testowy*, żeby pokazać Ci, jak to działa:`,
                 addMember: 'Nie można dodać tego członka. Spróbuj ponownie.',
             },
         },
-        domainAdmins: 'Administratorzy domeny',
     },
 };
 export default translations;
diff --git a/src/languages/zh-hans.ts b/src/languages/zh-hans.ts
index fdc2694927eb..785a0514f828 100644
--- a/src/languages/zh-hans.ts
+++ b/src/languages/zh-hans.ts
@@ -8106,7 +8106,6 @@ ${reportName}
                 addMember: '无法添加此成员。请重试。',
             },
         },
-        domainAdmins: '域管理员',
     }
 };
 export default translations;

From f52e6b0f08c456888d6cf4ebeafe624eb6f41d1c Mon Sep 17 00:00:00 2001
From: sumo_slonik <jakub.nowakowski@swmansion.com>
Date: Tue, 3 Feb 2026 08:28:51 +0100
Subject: [PATCH 41/50] fix prettier

---
 src/languages/it.ts      | 1 -
 src/languages/pt-BR.ts   | 1 -
 src/languages/zh-hans.ts | 2 +-
 3 files changed, 1 insertion(+), 3 deletions(-)

diff --git a/src/languages/it.ts b/src/languages/it.ts
index ada28d2a2e67..d28adcacc1af 100644
--- a/src/languages/it.ts
+++ b/src/languages/it.ts
@@ -8296,7 +8296,6 @@ Ecco una *ricevuta di prova* per mostrarti come funziona:`,
                 addMember: 'Impossibile aggiungere questo membro. Riprova.',
             },
         },
-        domainAdmins: 'Amministratori di dominio',
     },
 };
 export default translations;
diff --git a/src/languages/pt-BR.ts b/src/languages/pt-BR.ts
index e6594a2f6614..3cf8ca87da41 100644
--- a/src/languages/pt-BR.ts
+++ b/src/languages/pt-BR.ts
@@ -8264,7 +8264,6 @@ Aqui está um *comprovante de teste* para mostrar como funciona:`,
                 addMember: 'Não foi possível adicionar este membro. Tente novamente.',
             },
         },
-        domainAdmins: 'Administradores de domínio',
     },
 };
 export default translations;
diff --git a/src/languages/zh-hans.ts b/src/languages/zh-hans.ts
index 785a0514f828..07c88349878c 100644
--- a/src/languages/zh-hans.ts
+++ b/src/languages/zh-hans.ts
@@ -8106,6 +8106,6 @@ ${reportName}
                 addMember: '无法添加此成员。请重试。',
             },
         },
-    }
+    },
 };
 export default translations;

From be02ad7ae27a499625502fa8b1e11c80fd6c1f1e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jan=20Musia=C5=82?= <jan.musial@swmansion.com>
Date: Tue, 3 Feb 2026 12:46:04 +0100
Subject: [PATCH 42/50] change pending action key

---
 src/libs/actions/Domain.ts                    | 54 ++++++++++++-------
 .../domain/Members/DomainMembersPage.tsx      |  2 +-
 src/selectors/Domain.ts                       |  2 +-
 src/types/onyx/DomainPendingActions.ts        |  5 --
 tests/actions/DomainTest.ts                   | 26 +++++----
 5 files changed, 53 insertions(+), 36 deletions(-)

diff --git a/src/libs/actions/Domain.ts b/src/libs/actions/Domain.ts
index a3411ec6d252..96e3b8a5c1b8 100644
--- a/src/libs/actions/Domain.ts
+++ b/src/libs/actions/Domain.ts
@@ -952,24 +952,21 @@ function clearDomainMemberError(domainAccountID: number, accountID: number, emai
 
 /** Sends a request to remove a user from a domain and close their account */
 function closeUserAccount(domainAccountID: number, domain: string, accountID: number, targetEmail: string, securityGroupsData: UserSecurityGroupData, overrideProcessingReports = false) {
-    const failureValue: PrefixedRecord<typeof CONST.DOMAIN.DOMAIN_SECURITY_GROUP_PREFIX, Partial<DomainSecurityGroup>> = {};
-
-    if (securityGroupsData) {
-        const groupID = securityGroupsData.key;
-
-        failureValue[groupID] = {
-            shared: {
-                [accountID]: 'read',
-            },
-        };
-    }
-
     const optimisticData: OnyxUpdate[] = [
         {
             onyxMethod: Onyx.METHOD.MERGE,
             key: `${ONYXKEYS.COLLECTION.DOMAIN_PENDING_ACTIONS}${domainAccountID}`,
             value: {
-                members: {[targetEmail]: CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE},
+                member: {[targetEmail]: {pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE}},
+            },
+        },
+        {
+            onyxMethod: Onyx.METHOD.MERGE,
+            key: `${ONYXKEYS.COLLECTION.DOMAIN_ERRORS}${domainAccountID}`,
+            value: {
+                memberErrors: {
+                    [targetEmail]: null,
+                },
             },
         },
     ];
@@ -979,7 +976,16 @@ function closeUserAccount(domainAccountID: number, domain: string, accountID: nu
             onyxMethod: Onyx.METHOD.MERGE,
             key: `${ONYXKEYS.COLLECTION.DOMAIN_PENDING_ACTIONS}${domainAccountID}`,
             value: {
-                members: {[targetEmail]: null},
+                member: {[targetEmail]: null},
+            },
+        },
+        {
+            onyxMethod: Onyx.METHOD.MERGE,
+            key: `${ONYXKEYS.COLLECTION.DOMAIN_ERRORS}${domainAccountID}`,
+            value: {
+                memberErrors: {
+                    [targetEmail]: null,
+                },
             },
         },
     ];
@@ -994,18 +1000,26 @@ function closeUserAccount(domainAccountID: number, domain: string, accountID: nu
                 },
             },
         },
-        {
-            onyxMethod: Onyx.METHOD.MERGE,
-            key: `${ONYXKEYS.COLLECTION.DOMAIN}${domainAccountID}`,
-            value: failureValue,
-        },
         {
             onyxMethod: Onyx.METHOD.MERGE,
             key: `${ONYXKEYS.COLLECTION.DOMAIN_PENDING_ACTIONS}${domainAccountID}`,
             value: {
-                members: {[targetEmail]: null},
+                member: {[targetEmail]: null},
             },
         },
+        {
+            onyxMethod: Onyx.METHOD.MERGE,
+            key: `${ONYXKEYS.COLLECTION.DOMAIN}${domainAccountID}`,
+            value: (securityGroupsData?.key
+                ? {
+                      [securityGroupsData.key]: {
+                          shared: {
+                              [accountID]: 'read',
+                          },
+                      },
+                  }
+                : {}) as PrefixedRecord<typeof CONST.DOMAIN.DOMAIN_SECURITY_GROUP_PREFIX, Partial<DomainSecurityGroup>>,
+        },
     ];
 
     const parameters: DeleteDomainMemberParams = {
diff --git a/src/pages/domain/Members/DomainMembersPage.tsx b/src/pages/domain/Members/DomainMembersPage.tsx
index 0875bbee175a..0358e06e08c5 100644
--- a/src/pages/domain/Members/DomainMembersPage.tsx
+++ b/src/pages/domain/Members/DomainMembersPage.tsx
@@ -49,7 +49,7 @@ function DomainMembersPage({route}: DomainMembersPageProps) {
     const getCustomRowProps = (accountID: number, email?: string) => {
         const errorKey = email ?? accountID;
         const errors = getLatestError(domainErrors?.memberErrors?.[errorKey]?.errors) ?? undefined;
-        const pendingAction = email ? domainPendingAction?.[email] : undefined;
+        const pendingAction = email ? domainPendingAction?.[email]?.pendingAction : undefined;
 
         return {errors, pendingAction};
     };
diff --git a/src/selectors/Domain.ts b/src/selectors/Domain.ts
index 73b9a6ae2b21..bbbd49d66a7a 100644
--- a/src/selectors/Domain.ts
+++ b/src/selectors/Domain.ts
@@ -134,7 +134,7 @@ function selectSecurityGroupForAccount(accountID: number) {
     };
 }
 
-const memberPendingActionSelector = (pendingAction: OnyxEntry<DomainPendingActions>) => pendingAction?.members ?? {};
+const memberPendingActionSelector = (pendingAction: OnyxEntry<DomainPendingActions>) => pendingAction?.member ?? {};
 
 const adminPendingActionSelector = (pendingAction: OnyxEntry<DomainPendingActions>) => pendingAction?.admin ?? {};
 
diff --git a/src/types/onyx/DomainPendingActions.ts b/src/types/onyx/DomainPendingActions.ts
index fc45099c2c25..e9dc6a62dd83 100644
--- a/src/types/onyx/DomainPendingActions.ts
+++ b/src/types/onyx/DomainPendingActions.ts
@@ -39,11 +39,6 @@ type DomainPendingAction = {
      * Pending action for the domain itself
      */
     pendingAction?: OnyxCommon.PendingAction;
-
-    /**
-     * Pending action for members
-     */
-    members?: Record<string, OnyxCommon.PendingAction>;
 };
 
 export default DomainPendingAction;
diff --git a/tests/actions/DomainTest.ts b/tests/actions/DomainTest.ts
index e163c7db8c44..fa65c1dc4e76 100644
--- a/tests/actions/DomainTest.ts
+++ b/tests/actions/DomainTest.ts
@@ -247,19 +247,27 @@ describe('actions/Domain', () => {
                 WRITE_COMMANDS.DELETE_DOMAIN_MEMBER,
                 {domain: domainName, targetEmail, overrideProcessingReports: false},
                 {
-                    optimisticData: [
+                    optimisticData: expect.arrayContaining([
                         expect.objectContaining({
                             key: `${ONYXKEYS.COLLECTION.DOMAIN_PENDING_ACTIONS}${domainAccountID}`,
-                            value: {members: {[targetEmail]: CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE}},
+                            value: {member: {[targetEmail]: {pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE}}},
                         }),
-                    ],
-                    successData: [
+                        expect.objectContaining({
+                            key: `${ONYXKEYS.COLLECTION.DOMAIN_ERRORS}${domainAccountID}`,
+                            value: {memberErrors: {[targetEmail]: null}},
+                        }),
+                    ]),
+                    successData: expect.arrayContaining([
                         expect.objectContaining({
                             key: `${ONYXKEYS.COLLECTION.DOMAIN_PENDING_ACTIONS}${domainAccountID}`,
-                            value: {members: {[targetEmail]: null}},
+                            value: {member: {[targetEmail]: null}},
+                        }),
+                        expect.objectContaining({
+                            key: `${ONYXKEYS.COLLECTION.DOMAIN_ERRORS}${domainAccountID}`,
+                            value: {memberErrors: {[targetEmail]: null}},
                         }),
-                    ],
-                    failureData: [
+                    ]),
+                    failureData: expect.arrayContaining([
                         expect.objectContaining({
                             key: `${ONYXKEYS.COLLECTION.DOMAIN_ERRORS}${domainAccountID}`,
                             // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
@@ -272,9 +280,9 @@ describe('actions/Domain', () => {
                         }),
                         expect.objectContaining({
                             key: `${ONYXKEYS.COLLECTION.DOMAIN_PENDING_ACTIONS}${domainAccountID}`,
-                            value: {members: {[targetEmail]: null}},
+                            value: {member: {[targetEmail]: null}},
                         }),
-                    ],
+                    ]),
                 },
             );
 

From 2201b2b8ed1c7d7154962bdb7b1a6c4b2ca1b890 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jan=20Musia=C5=82?= <jan.musial@swmansion.com>
Date: Tue, 3 Feb 2026 15:21:35 +0100
Subject: [PATCH 43/50] fix comments v2

---
 .../domain/Members/DomainMemberDetailsPage.tsx | 18 +-----------------
 src/pages/domain/Members/DomainMembersPage.tsx |  6 ++----
 2 files changed, 3 insertions(+), 21 deletions(-)

diff --git a/src/pages/domain/Members/DomainMemberDetailsPage.tsx b/src/pages/domain/Members/DomainMemberDetailsPage.tsx
index 1ab4ea45b0de..7e0d3b8fc4a1 100644
--- a/src/pages/domain/Members/DomainMemberDetailsPage.tsx
+++ b/src/pages/domain/Members/DomainMemberDetailsPage.tsx
@@ -1,12 +1,10 @@
-import {adminAccountIDsSelector, domainNameSelector, selectSecurityGroupForAccount} from '@selectors/Domain';
+import {domainNameSelector, selectSecurityGroupForAccount} from '@selectors/Domain';
 import {personalDetailsSelector} from '@selectors/PersonalDetails';
 import React, {useState} from 'react';
 import Button from '@components/Button';
 import DecisionModal from '@components/DecisionModal';
-import FullScreenLoadingIndicator from '@components/FullscreenLoadingIndicator';
 import {ModalActions} from '@components/Modal/Global/ModalContext';
 import useConfirmModal from '@hooks/useConfirmModal';
-import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails';
 import {useMemoizedLazyExpensifyIcons} from '@hooks/useLazyAsset';
 import useLocalize from '@hooks/useLocalize';
 import useOnyx from '@hooks/useOnyx';
@@ -19,7 +17,6 @@ import type {SettingsNavigatorParamList} from '@navigation/types';
 import BaseDomainMemberDetailsComponent from '@pages/domain/BaseDomainMemberDetailsComponent';
 import ONYXKEYS from '@src/ONYXKEYS';
 import type SCREENS from '@src/SCREENS';
-import isLoadingOnyxValue from '@src/types/utils/isLoadingOnyxValue';
 
 type DomainMemberDetailsPageProps = PlatformStackScreenProps<SettingsNavigatorParamList, typeof SCREENS.DOMAIN.MEMBER_DETAILS>;
 
@@ -35,11 +32,6 @@ function DomainMemberDetailsPage({route}: DomainMemberDetailsPageProps) {
     const {isSmallScreenWidth} = useResponsiveLayout();
     const {showConfirmModal} = useConfirmModal();
 
-    const [adminAccountIDs, domainMetadata] = useOnyx(`${ONYXKEYS.COLLECTION.DOMAIN}${domainAccountID}`, {
-        canBeMissing: true,
-        selector: adminAccountIDsSelector,
-    });
-
     const [userSecurityGroup] = useOnyx(`${ONYXKEYS.COLLECTION.DOMAIN}${domainAccountID}`, {
         canBeMissing: true,
         selector: selectSecurityGroupForAccount(accountID),
@@ -54,9 +46,6 @@ function DomainMemberDetailsPage({route}: DomainMemberDetailsPageProps) {
 
     const memberLogin = personalDetails?.login ?? '';
 
-    const {accountID: currentUserAccountID} = useCurrentUserPersonalDetails();
-    const isAdmin = adminAccountIDs?.includes(currentUserAccountID);
-
     const handleCloseAccount = async () => {
         if (!userSecurityGroup || shouldForceCloseAccount === undefined) {
             return;
@@ -94,16 +83,11 @@ function DomainMemberDetailsPage({route}: DomainMemberDetailsPageProps) {
         <Button
             text={translate('domain.members.closeAccount')}
             onPress={() => setIsModalVisible(true)}
-            isDisabled={!isAdmin}
             icon={icons.RemoveMembers}
             style={styles.mb5}
         />
     );
 
-    if (isLoadingOnyxValue(domainMetadata)) {
-        return <FullScreenLoadingIndicator shouldUseGoBackButton />;
-    }
-
     return (
         <>
             <BaseDomainMemberDetailsComponent
diff --git a/src/pages/domain/Members/DomainMembersPage.tsx b/src/pages/domain/Members/DomainMembersPage.tsx
index 0358e06e08c5..114077f1a5ca 100644
--- a/src/pages/domain/Members/DomainMembersPage.tsx
+++ b/src/pages/domain/Members/DomainMembersPage.tsx
@@ -27,7 +27,7 @@ function DomainMembersPage({route}: DomainMembersPageProps) {
     const styles = useThemeStyles();
 
     const [domainErrors] = useOnyx(`${ONYXKEYS.COLLECTION.DOMAIN_ERRORS}${domainAccountID}`, {canBeMissing: true});
-    const [domainPendingAction] = useOnyx(`${ONYXKEYS.COLLECTION.DOMAIN_PENDING_ACTIONS}${domainAccountID}`, {canBeMissing: true, selector: memberPendingActionSelector});
+    const [domainPendingActions] = useOnyx(`${ONYXKEYS.COLLECTION.DOMAIN_PENDING_ACTIONS}${domainAccountID}`, {canBeMissing: true, selector: memberPendingActionSelector});
     const [defaultSecurityGroupID] = useOnyx(`${ONYXKEYS.COLLECTION.DOMAIN}${domainAccountID}`, {canBeMissing: true, selector: defaultSecurityGroupIDSelector});
 
     const [memberIDs] = useOnyx(`${ONYXKEYS.COLLECTION.DOMAIN}${domainAccountID}`, {
@@ -49,7 +49,7 @@ function DomainMembersPage({route}: DomainMembersPageProps) {
     const getCustomRowProps = (accountID: number, email?: string) => {
         const errorKey = email ?? accountID;
         const errors = getLatestError(domainErrors?.memberErrors?.[errorKey]?.errors) ?? undefined;
-        const pendingAction = email ? domainPendingAction?.[email]?.pendingAction : undefined;
+        const pendingAction = email ? domainPendingActions?.[email]?.pendingAction : undefined;
 
         return {errors, pendingAction};
     };
@@ -74,6 +74,4 @@ function DomainMembersPage({route}: DomainMembersPageProps) {
     );
 }
 
-DomainMembersPage.displayName = 'DomainMembersPage';
-
 export default DomainMembersPage;

From 85f7a1678fe7636910723a03e1c1a63044d82193 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jan=20Musia=C5=82?= <jan.musial@swmansion.com>
Date: Tue, 3 Feb 2026 15:57:43 +0100
Subject: [PATCH 44/50] resolved comments v3

---
 src/pages/domain/Members/DomainMembersPage.tsx | 9 +++++----
 src/selectors/Domain.ts                        | 8 ++++----
 tests/actions/DomainTest.ts                    | 2 +-
 3 files changed, 10 insertions(+), 9 deletions(-)

diff --git a/src/pages/domain/Members/DomainMembersPage.tsx b/src/pages/domain/Members/DomainMembersPage.tsx
index 114077f1a5ca..9e05dd57c5cb 100644
--- a/src/pages/domain/Members/DomainMembersPage.tsx
+++ b/src/pages/domain/Members/DomainMembersPage.tsx
@@ -47,11 +47,12 @@ function DomainMembersPage({route}: DomainMembersPageProps) {
     );
 
     const getCustomRowProps = (accountID: number, email?: string) => {
-        const errorKey = email ?? accountID;
-        const errors = getLatestError(domainErrors?.memberErrors?.[errorKey]?.errors) ?? undefined;
-        const pendingAction = email ? domainPendingActions?.[email]?.pendingAction : undefined;
+        const emailError = email ? getLatestError(domainErrors?.memberErrors?.[email]?.errors) : undefined;
+        const accountIDError = getLatestError(domainErrors?.memberErrors?.[accountID]?.errors);
+        const emailPendingAction = email ? domainPendingActions?.[email]?.pendingAction : undefined;
+        const accountIDPendingAction = domainPendingActions?.[accountID]?.pendingAction;
 
-        return {errors, pendingAction};
+        return {errors: emailError ?? accountIDError, pendingAction: emailPendingAction ?? accountIDPendingAction};
     };
 
     return (
diff --git a/src/selectors/Domain.ts b/src/selectors/Domain.ts
index bbbd49d66a7a..5e834b060dd9 100644
--- a/src/selectors/Domain.ts
+++ b/src/selectors/Domain.ts
@@ -100,15 +100,15 @@ function isSecurityGroupEntry(entry: [string, unknown]): entry is [SecurityGroup
     return key.startsWith(CONST.DOMAIN.DOMAIN_SECURITY_GROUP_PREFIX) && typeof value === 'object' && value !== null && 'shared' in value;
 }
 /**
- * Creates a selector function to get all security groups for a specific account ID.
- * The returned function searches through a domain and returns groups where
+ * Creates a selector function to a single security group for a specific account ID.
+ * The returned function searches through a domain and returns a group where
  * the account ID is present in the 'shared' property.
  *
  * @param accountID - The account ID to filter by
- * @returns A function that takes a domain and returns the filtered keys and security group data
+ * @returns A function that takes a domain and returns the filtered key and security group data
  */
 function selectSecurityGroupForAccount(accountID: number) {
-    return (domain: Domain | undefined): UserSecurityGroupData => {
+    return (domain: OnyxEntry<Domain>): UserSecurityGroupData => {
         if (!domain) {
             return undefined;
         }
diff --git a/tests/actions/DomainTest.ts b/tests/actions/DomainTest.ts
index fa65c1dc4e76..f075d347ef57 100644
--- a/tests/actions/DomainTest.ts
+++ b/tests/actions/DomainTest.ts
@@ -202,7 +202,7 @@ describe('actions/Domain', () => {
             } as PrefixedRecord<typeof CONST.DOMAIN.DOMAIN_SECURITY_GROUP_PREFIX, Partial<DomainSecurityGroup>>,
         );
 
-        clearDomainMemberError(domainAccountID, optimisticAccountID, email, defaultSecurityGroupID, 'add');
+        clearDomainMemberError(domainAccountID, optimisticAccountID, email, defaultSecurityGroupID, CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD);
 
         await TestHelper.getOnyxData({
             key: `${ONYXKEYS.COLLECTION.DOMAIN_ERRORS}${domainAccountID}`,

From 6011cde805a41fe26aaf8a4a069da3b463cbd27f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jan=20Musia=C5=82?= <jan.musial@swmansion.com>
Date: Tue, 3 Feb 2026 16:25:15 +0100
Subject: [PATCH 45/50] update jsdoc

---
 src/libs/actions/Domain.ts | 24 +++++++++++++++++-------
 src/selectors/Domain.ts    |  2 +-
 2 files changed, 18 insertions(+), 8 deletions(-)

diff --git a/src/libs/actions/Domain.ts b/src/libs/actions/Domain.ts
index 96e3b8a5c1b8..72e692e281a3 100644
--- a/src/libs/actions/Domain.ts
+++ b/src/libs/actions/Domain.ts
@@ -781,7 +781,7 @@ function resetDomain(domainAccountID: number, domainName: string, domain: Domain
 }
 
 /**
- * Clears errors after trying to reset the domain
+ * Clears domain related errors and pending actions.
  */
 function clearDomainErrors(domainAccountID: number) {
     Onyx.merge(`${ONYXKEYS.COLLECTION.DOMAIN_ERRORS}${domainAccountID}`, {
@@ -792,6 +792,12 @@ function clearDomainErrors(domainAccountID: number) {
     });
 }
 
+/**
+ * Adds a member to a domain
+ * @param domainAccountID Account ID of a domain
+ * @param email Email of a user to be added
+ * @param defaultSecurityGroupID Security group ip to be used for optimistic updates
+ */
 function addMemberToDomain(domainAccountID: number, email: string, defaultSecurityGroupID: string) {
     const DOMAIN_SECURITY_GROUP = `${CONST.DOMAIN.DOMAIN_SECURITY_GROUP_PREFIX}${defaultSecurityGroupID}`;
     const optimisticAccountID = generateAccountID(email);
@@ -950,7 +956,15 @@ function clearDomainMemberError(domainAccountID: number, accountID: number, emai
     });
 }
 
-/** Sends a request to remove a user from a domain and close their account */
+/**
+ * Sends a request to remove a user from a domain and close their account
+ * @param domainAccountID Account ID of a domain
+ * @param domain Domain name
+ * @param accountID AccountID of a user to be removed
+ * @param targetEmail Email of a user to be removed
+ * @param securityGroupsData Data of a security group user is in
+ * @param overrideProcessingReports "Force" flag. If true user will be removed regardless of if they have outstanding reports
+ */
 function closeUserAccount(domainAccountID: number, domain: string, accountID: number, targetEmail: string, securityGroupsData: UserSecurityGroupData, overrideProcessingReports = false) {
     const optimisticData: OnyxUpdate[] = [
         {
@@ -1012,11 +1026,7 @@ function closeUserAccount(domainAccountID: number, domain: string, accountID: nu
             key: `${ONYXKEYS.COLLECTION.DOMAIN}${domainAccountID}`,
             value: (securityGroupsData?.key
                 ? {
-                      [securityGroupsData.key]: {
-                          shared: {
-                              [accountID]: 'read',
-                          },
-                      },
+                      [securityGroupsData.key]: securityGroupsData.securityGroup,
                   }
                 : {}) as PrefixedRecord<typeof CONST.DOMAIN.DOMAIN_SECURITY_GROUP_PREFIX, Partial<DomainSecurityGroup>>,
         },
diff --git a/src/selectors/Domain.ts b/src/selectors/Domain.ts
index 5e834b060dd9..154b2d6744d2 100644
--- a/src/selectors/Domain.ts
+++ b/src/selectors/Domain.ts
@@ -100,7 +100,7 @@ function isSecurityGroupEntry(entry: [string, unknown]): entry is [SecurityGroup
     return key.startsWith(CONST.DOMAIN.DOMAIN_SECURITY_GROUP_PREFIX) && typeof value === 'object' && value !== null && 'shared' in value;
 }
 /**
- * Creates a selector function to a single security group for a specific account ID.
+ * Creates a selector for a single security group for a specific account ID.
  * The returned function searches through a domain and returns a group where
  * the account ID is present in the 'shared' property.
  *

From 89f907782ebff039215b5a303ef502ccfdc17127 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jan=20Musia=C5=82?= <jan.musial@swmansion.com>
Date: Tue, 3 Feb 2026 16:50:15 +0100
Subject: [PATCH 46/50] add missing optimistic update data

---
 src/libs/actions/Domain.ts | 11 +++++++++++
 src/selectors/Domain.ts    |  1 +
 2 files changed, 12 insertions(+)

diff --git a/src/libs/actions/Domain.ts b/src/libs/actions/Domain.ts
index 72e692e281a3..982f8bc8c440 100644
--- a/src/libs/actions/Domain.ts
+++ b/src/libs/actions/Domain.ts
@@ -967,6 +967,17 @@ function clearDomainMemberError(domainAccountID: number, accountID: number, emai
  */
 function closeUserAccount(domainAccountID: number, domain: string, accountID: number, targetEmail: string, securityGroupsData: UserSecurityGroupData, overrideProcessingReports = false) {
     const optimisticData: OnyxUpdate[] = [
+        {
+            onyxMethod: Onyx.METHOD.MERGE,
+            key: `${ONYXKEYS.COLLECTION.DOMAIN}${domainAccountID}`,
+            value: (securityGroupsData?.key
+                ? {
+                      [securityGroupsData.key]: {
+                          shared: {[accountID]: null},
+                      },
+                  }
+                : {}) as PrefixedRecord<typeof CONST.DOMAIN.DOMAIN_SECURITY_GROUP_PREFIX, Partial<DomainSecurityGroup>>,
+        },
         {
             onyxMethod: Onyx.METHOD.MERGE,
             key: `${ONYXKEYS.COLLECTION.DOMAIN_PENDING_ACTIONS}${domainAccountID}`,
diff --git a/src/selectors/Domain.ts b/src/selectors/Domain.ts
index 154b2d6744d2..bee9c15cc3da 100644
--- a/src/selectors/Domain.ts
+++ b/src/selectors/Domain.ts
@@ -99,6 +99,7 @@ function isSecurityGroupEntry(entry: [string, unknown]): entry is [SecurityGroup
     const [key, value] = entry;
     return key.startsWith(CONST.DOMAIN.DOMAIN_SECURITY_GROUP_PREFIX) && typeof value === 'object' && value !== null && 'shared' in value;
 }
+
 /**
  * Creates a selector for a single security group for a specific account ID.
  * The returned function searches through a domain and returns a group where

From 350144e4ff31bef4340fdd9f77a5a2c6a3ce053b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jan=20Musia=C5=82?= <jan.musial@swmansion.com>
Date: Wed, 4 Feb 2026 09:42:52 +0100
Subject: [PATCH 47/50] revert style change

---
 src/pages/domain/DomainInitialPage.tsx | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/pages/domain/DomainInitialPage.tsx b/src/pages/domain/DomainInitialPage.tsx
index aabcf2ccafae..5973bba18762 100644
--- a/src/pages/domain/DomainInitialPage.tsx
+++ b/src/pages/domain/DomainInitialPage.tsx
@@ -120,7 +120,7 @@ function DomainInitialPage({route}: DomainInitialPageProps) {
                 />
 
                 <ScrollView contentContainerStyle={[styles.flexColumn]}>
-                    <View style={[styles.pb4, styles.mh3, styles.mt3, styles.gap2]}>
+                    <View style={[styles.pb4, styles.mh3, styles.mt3]}>
                         {/*
                             Ideally we should use MenuList component for MenuItems with singleExecution/Navigation actions.
                             In this case where user can click on menu items, we need to have a check for `isExecuting`. So, we are directly mapping menuItems.

From 85a9bb55e8757dc1aafb4aa70729fd8f3d2d3cde Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jan=20Musia=C5=82?= <jan.musial@swmansion.com>
Date: Wed, 4 Feb 2026 12:54:55 +0100
Subject: [PATCH 48/50] fix offline close account row visibility

---
 src/libs/actions/Domain.ts                         | 14 +-------------
 .../domain/Members/DomainMemberDetailsPage.tsx     |  2 +-
 tests/actions/DomainTest.ts                        |  5 ++---
 3 files changed, 4 insertions(+), 17 deletions(-)

diff --git a/src/libs/actions/Domain.ts b/src/libs/actions/Domain.ts
index 982f8bc8c440..b8e420ff0209 100644
--- a/src/libs/actions/Domain.ts
+++ b/src/libs/actions/Domain.ts
@@ -960,24 +960,12 @@ function clearDomainMemberError(domainAccountID: number, accountID: number, emai
  * Sends a request to remove a user from a domain and close their account
  * @param domainAccountID Account ID of a domain
  * @param domain Domain name
- * @param accountID AccountID of a user to be removed
  * @param targetEmail Email of a user to be removed
  * @param securityGroupsData Data of a security group user is in
  * @param overrideProcessingReports "Force" flag. If true user will be removed regardless of if they have outstanding reports
  */
-function closeUserAccount(domainAccountID: number, domain: string, accountID: number, targetEmail: string, securityGroupsData: UserSecurityGroupData, overrideProcessingReports = false) {
+function closeUserAccount(domainAccountID: number, domain: string, targetEmail: string, securityGroupsData: UserSecurityGroupData, overrideProcessingReports = false) {
     const optimisticData: OnyxUpdate[] = [
-        {
-            onyxMethod: Onyx.METHOD.MERGE,
-            key: `${ONYXKEYS.COLLECTION.DOMAIN}${domainAccountID}`,
-            value: (securityGroupsData?.key
-                ? {
-                      [securityGroupsData.key]: {
-                          shared: {[accountID]: null},
-                      },
-                  }
-                : {}) as PrefixedRecord<typeof CONST.DOMAIN.DOMAIN_SECURITY_GROUP_PREFIX, Partial<DomainSecurityGroup>>,
-        },
         {
             onyxMethod: Onyx.METHOD.MERGE,
             key: `${ONYXKEYS.COLLECTION.DOMAIN_PENDING_ACTIONS}${domainAccountID}`,
diff --git a/src/pages/domain/Members/DomainMemberDetailsPage.tsx b/src/pages/domain/Members/DomainMemberDetailsPage.tsx
index 7e0d3b8fc4a1..d2eccbc898bc 100644
--- a/src/pages/domain/Members/DomainMemberDetailsPage.tsx
+++ b/src/pages/domain/Members/DomainMemberDetailsPage.tsx
@@ -64,7 +64,7 @@ function DomainMemberDetailsPage({route}: DomainMemberDetailsPageProps) {
             setShouldForceCloseAccount(undefined);
             return;
         }
-        closeUserAccount(domainAccountID, domainName ?? '', accountID, memberLogin, userSecurityGroup, shouldForceCloseAccount);
+        closeUserAccount(domainAccountID, domainName ?? '', memberLogin, userSecurityGroup, shouldForceCloseAccount);
         setShouldForceCloseAccount(undefined);
         Navigation.dismissModal();
     };
diff --git a/tests/actions/DomainTest.ts b/tests/actions/DomainTest.ts
index f075d347ef57..0f3cf0bc18e5 100644
--- a/tests/actions/DomainTest.ts
+++ b/tests/actions/DomainTest.ts
@@ -241,7 +241,7 @@ describe('actions/Domain', () => {
                 },
             };
 
-            closeUserAccount(domainAccountID, domainName, accountID, targetEmail, securityGroupsData);
+            closeUserAccount(domainAccountID, domainName, targetEmail, securityGroupsData);
 
             expect(apiWriteSpy).toHaveBeenCalledWith(
                 WRITE_COMMANDS.DELETE_DOMAIN_MEMBER,
@@ -293,10 +293,9 @@ describe('actions/Domain', () => {
             const apiWriteSpy = jest.spyOn(require('@libs/API'), 'write').mockImplementation(() => Promise.resolve());
             const domainAccountID = 123;
             const domainName = 'test.com';
-            const accountID = 456;
             const targetEmail = 'user@test.com';
 
-            closeUserAccount(domainAccountID, domainName, accountID, targetEmail, undefined, true);
+            closeUserAccount(domainAccountID, domainName, targetEmail, undefined, true);
 
             expect(apiWriteSpy).toHaveBeenCalledWith(WRITE_COMMANDS.DELETE_DOMAIN_MEMBER, {domain: domainName, targetEmail, overrideProcessingReports: true}, expect.any(Object));
 

From 8173c34fd7a7e2e33d2bd141042d85921ec6ea4b Mon Sep 17 00:00:00 2001
From: war-in <war-in@users.noreply.github.com>
Date: Wed, 4 Feb 2026 18:13:27 +0100
Subject: [PATCH 49/50] fix: remove unwanted space from translations

---
 src/languages/de.ts    | 2 +-
 src/languages/en.ts    | 2 +-
 src/languages/fr.ts    | 2 +-
 src/languages/it.ts    | 2 +-
 src/languages/ja.ts    | 2 +-
 src/languages/nl.ts    | 2 +-
 src/languages/pl.ts    | 2 +-
 src/languages/pt-BR.ts | 2 +-
 8 files changed, 8 insertions(+), 8 deletions(-)

diff --git a/src/languages/de.ts b/src/languages/de.ts
index d11e7dab8a1f..80d03a82eb5b 100644
--- a/src/languages/de.ts
+++ b/src/languages/de.ts
@@ -8311,7 +8311,7 @@ Hier ist ein *Testbeleg*, um dir zu zeigen, wie es funktioniert:`,
             forceCloseAccount: 'Konto zwangsweise schließen',
             safeCloseAccount: 'Konto sicher schließen',
             closeAccountInfo:
-                'Wir empfehlen, das Konto sicher zu schließen, um das Schließen zu überspringen, falls Folgendes vorliegt: <ul><li>Ausstehende Genehmigungen</li><li>Aktive Erstattungen</li><li>Keine alternativen Anmeldemethoden</li></ul> Andernfalls können Sie die oben genannten Sicherheitsvorkehrungen ignorieren und das ausgewählte Konto zwangsweise schließen.',
+                'Wir empfehlen, das Konto sicher zu schließen, um das Schließen zu überspringen, falls Folgendes vorliegt: <ul><li>Ausstehende Genehmigungen</li><li>Aktive Erstattungen</li><li>Keine alternativen Anmeldemethoden</li></ul>Andernfalls können Sie die oben genannten Sicherheitsvorkehrungen ignorieren und das ausgewählte Konto zwangsweise schließen.',
             error: {
                 removeMember: 'Dieser Benutzer kann nicht entfernt werden. Bitte versuche es erneut.',
                 addMember: 'Dieses Mitglied kann nicht hinzugefügt werden. Bitte versuche es erneut.',
diff --git a/src/languages/en.ts b/src/languages/en.ts
index 4dfebc12a82a..f055552c341d 100644
--- a/src/languages/en.ts
+++ b/src/languages/en.ts
@@ -8226,7 +8226,7 @@ const translations = {
             forceCloseAccount: 'Force close account',
             safeCloseAccount: 'Close account safely',
             closeAccountInfo:
-                'We recommend closing the account safely to skip closing it in case there are: <ul><li>Pending approvals</li><li>Active reimbursements</li><li>No alternative login methods</li></ul> Otherwise, you can ignore the safety precautions above and force close the selected account.',
+                'We recommend closing the account safely to skip closing it in case there are: <ul><li>Pending approvals</li><li>Active reimbursements</li><li>No alternative login methods</li></ul>Otherwise, you can ignore the safety precautions above and force close the selected account.',
             error: {
                 addMember: 'Unable to add this member. Please try again.',
                 removeMember: 'Unable to remove this user. Please try again.',
diff --git a/src/languages/fr.ts b/src/languages/fr.ts
index e14d0c443ae3..96d7ca4b7ba0 100644
--- a/src/languages/fr.ts
+++ b/src/languages/fr.ts
@@ -8325,7 +8325,7 @@ Voici un *reçu test* pour vous montrer comment ça fonctionne :`,
             forceCloseAccount: 'Forcer la fermeture du compte',
             safeCloseAccount: 'Fermer le compte en toute sécurité',
             closeAccountInfo:
-                'Nous recommandons de fermer le compte en toute sécurité afin d’éviter de le fermer dans le cas où il y aurait : <ul><li>Des validations en attente</li><li>Des remboursements actifs</li><li>Aucune autre méthode de connexion</li></ul> Sinon, vous pouvez ignorer les précautions de sécurité ci-dessus et forcer la fermeture du compte sélectionné.',
+                'Nous recommandons de fermer le compte en toute sécurité afin d’éviter de le fermer dans le cas où il y aurait : <ul><li>Des validations en attente</li><li>Des remboursements actifs</li><li>Aucune autre méthode de connexion</li></ul>Sinon, vous pouvez ignorer les précautions de sécurité ci-dessus et forcer la fermeture du compte sélectionné.',
             error: {
                 removeMember: 'Impossible de supprimer cet utilisateur. Veuillez réessayer.',
                 addMember: 'Impossible d’ajouter ce membre. Veuillez réessayer.',
diff --git a/src/languages/it.ts b/src/languages/it.ts
index 04ab7b2ae13c..208a171f1f49 100644
--- a/src/languages/it.ts
+++ b/src/languages/it.ts
@@ -8292,7 +8292,7 @@ Ecco una *ricevuta di prova* per mostrarti come funziona:`,
             forceCloseAccount: 'Forza chiusura account',
             safeCloseAccount: 'Chiudi il conto in sicurezza',
             closeAccountInfo:
-                'Consigliamo di chiudere l’account in modo sicuro per evitare problemi in caso di: <ul><li>Approvazioni in sospeso</li><li>Rimborsi attivi</li><li>Nessun metodo di accesso alternativo</li></ul> In caso contrario, puoi ignorare le precauzioni di sicurezza sopra indicate e forzare la chiusura dell’account selezionato.',
+                'Consigliamo di chiudere l’account in modo sicuro per evitare problemi in caso di: <ul><li>Approvazioni in sospeso</li><li>Rimborsi attivi</li><li>Nessun metodo di accesso alternativo</li></ul>In caso contrario, puoi ignorare le precauzioni di sicurezza sopra indicate e forzare la chiusura dell’account selezionato.',
             error: {
                 removeMember: 'Impossibile rimuovere questo utente. Riprova.',
                 addMember: 'Impossibile aggiungere questo membro. Riprova.',
diff --git a/src/languages/ja.ts b/src/languages/ja.ts
index 432c2a1c38bd..d2ee55bc81f9 100644
--- a/src/languages/ja.ts
+++ b/src/languages/ja.ts
@@ -8223,7 +8223,7 @@ ${reportName}
             forceCloseAccount: 'アカウントを強制的に閉鎖',
             safeCloseAccount: 'アカウントを安全に閉じる',
             closeAccountInfo:
-                '保留中の承認、処理中の精算、代替ログイン方法がない場合などでもアカウントを閉鎖できるように、安全にアカウントを閉鎖することを推奨します: <ul><li>保留中の承認</li><li>進行中の払い戻し</li><li>代替ログイン方法なし</li></ul> それ以外の場合は、上記の安全上の注意を無視して、選択したアカウントを強制的に閉鎖できます。',
+                '保留中の承認、処理中の精算、代替ログイン方法がない場合などでもアカウントを閉鎖できるように、安全にアカウントを閉鎖することを推奨します: <ul><li>保留中の承認</li><li>進行中の払い戻し</li><li>代替ログイン方法なし</li></ul>それ以外の場合は、上記の安全上の注意を無視して、選択したアカウントを強制的に閉鎖できます。',
             error: {
                 removeMember: 'このユーザーを削除できません。もう一度お試しください。',
                 addMember: 'このメンバーを追加できませんでした。もう一度お試しください。',
diff --git a/src/languages/nl.ts b/src/languages/nl.ts
index 235e5d415ea1..d920fa997a4f 100644
--- a/src/languages/nl.ts
+++ b/src/languages/nl.ts
@@ -8279,7 +8279,7 @@ Hier is een *proefbon* om je te laten zien hoe het werkt:`,
             forceCloseAccount: 'Account geforceerd sluiten',
             safeCloseAccount: 'Account veilig sluiten',
             closeAccountInfo:
-                'We raden aan om de account veilig te sluiten om te vermijden dat je deze moet sluiten als er zijn: <ul><li>Openstaande goedkeuringen</li><li>Actieve terugbetalingen</li><li>Geen alternatieve inlogmethoden</li></ul> Anders kun je de bovenstaande veiligheidsmaatregelen negeren en de geselecteerde account geforceerd sluiten.',
+                'We raden aan om de account veilig te sluiten om te vermijden dat je deze moet sluiten als er zijn: <ul><li>Openstaande goedkeuringen</li><li>Actieve terugbetalingen</li><li>Geen alternatieve inlogmethoden</li></ul>Anders kun je de bovenstaande veiligheidsmaatregelen negeren en de geselecteerde account geforceerd sluiten.',
             error: {
                 removeMember: 'Kan deze gebruiker niet verwijderen. Probeer het opnieuw.',
                 addMember: 'Kan dit lid niet toevoegen. Probeer het opnieuw.',
diff --git a/src/languages/pl.ts b/src/languages/pl.ts
index b3733b126cf3..1880a1a0bf39 100644
--- a/src/languages/pl.ts
+++ b/src/languages/pl.ts
@@ -8259,7 +8259,7 @@ Oto *paragon testowy*, żeby pokazać Ci, jak to działa:`,
             forceCloseAccount: 'Wymuś zamknięcie konta',
             safeCloseAccount: 'Zamknij konto bezpiecznie',
             closeAccountInfo:
-                'Zalecamy bezpieczne zamknięcie konta, aby pominąć jego zamykanie w przypadku, gdy występują: <ul><li>Oczekujące zatwierdzenia</li><li>Aktywne zwroty kosztów</li><li>Brak alternatywnych metod logowania</li></ul> W przeciwnym razie możesz zignorować powyższe środki ostrożności i wymusić zamknięcie wybranego konta.',
+                'Zalecamy bezpieczne zamknięcie konta, aby pominąć jego zamykanie w przypadku, gdy występują: <ul><li>Oczekujące zatwierdzenia</li><li>Aktywne zwroty kosztów</li><li>Brak alternatywnych metod logowania</li></ul>W przeciwnym razie możesz zignorować powyższe środki ostrożności i wymusić zamknięcie wybranego konta.',
             error: {
                 removeMember: 'Nie można usunąć tego użytkownika. Spróbuj ponownie.',
                 addMember: 'Nie można dodać tego członka. Spróbuj ponownie.',
diff --git a/src/languages/pt-BR.ts b/src/languages/pt-BR.ts
index fa6e4d683a3b..a184302c7acb 100644
--- a/src/languages/pt-BR.ts
+++ b/src/languages/pt-BR.ts
@@ -8260,7 +8260,7 @@ Aqui está um *comprovante de teste* para mostrar como funciona:`,
             forceCloseAccount: 'Forçar encerramento da conta',
             safeCloseAccount: 'Fechar conta com segurança',
             closeAccountInfo:
-                'Recomendamos fechar a conta com segurança para evitar o fechamento caso haja: <ul><li>aprovações pendentes</li><li>reembolsos ativos</li><li>nenhum método de login alternativo</li></ul> Caso contrário, você pode ignorar as precauções de segurança acima e forçar o fechamento da conta selecionada.',
+                'Recomendamos fechar a conta com segurança para evitar o fechamento caso haja: <ul><li>aprovações pendentes</li><li>reembolsos ativos</li><li>nenhum método de login alternativo</li></ul>Caso contrário, você pode ignorar as precauções de segurança acima e forçar o fechamento da conta selecionada.',
             error: {
                 removeMember: 'Não foi possível remover este usuário. Tente novamente.',
                 addMember: 'Não foi possível adicionar este membro. Tente novamente.',

From 4b4d31d8d91708d9b03e9b69ec4cb1f92ff2e828 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jan=20Musia=C5=82?= <jan.musial@swmansion.com>
Date: Thu, 5 Feb 2026 10:06:21 +0100
Subject: [PATCH 50/50] fix typos

---
 src/libs/actions/Domain.ts                           | 2 +-
 src/pages/domain/Members/DomainMemberDetailsPage.tsx | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/libs/actions/Domain.ts b/src/libs/actions/Domain.ts
index b8e420ff0209..c9254533e8b2 100644
--- a/src/libs/actions/Domain.ts
+++ b/src/libs/actions/Domain.ts
@@ -796,7 +796,7 @@ function clearDomainErrors(domainAccountID: number) {
  * Adds a member to a domain
  * @param domainAccountID Account ID of a domain
  * @param email Email of a user to be added
- * @param defaultSecurityGroupID Security group ip to be used for optimistic updates
+ * @param defaultSecurityGroupID Security group ID to be used for optimistic updates
  */
 function addMemberToDomain(domainAccountID: number, email: string, defaultSecurityGroupID: string) {
     const DOMAIN_SECURITY_GROUP = `${CONST.DOMAIN.DOMAIN_SECURITY_GROUP_PREFIX}${defaultSecurityGroupID}`;
diff --git a/src/pages/domain/Members/DomainMemberDetailsPage.tsx b/src/pages/domain/Members/DomainMemberDetailsPage.tsx
index d2eccbc898bc..549f422ee463 100644
--- a/src/pages/domain/Members/DomainMemberDetailsPage.tsx
+++ b/src/pages/domain/Members/DomainMemberDetailsPage.tsx
@@ -27,7 +27,7 @@ function DomainMemberDetailsPage({route}: DomainMemberDetailsPageProps) {
     const icons = useMemoizedLazyExpensifyIcons(['RemoveMembers']);
     const [isModalVisible, setIsModalVisible] = useState(false);
     const [shouldForceCloseAccount, setShouldForceCloseAccount] = useState<boolean>();
-    // we need to use isSmallScreenWidth here because the DecisionModal is opening from RHP and ShouldUseNarrowLayout layout will not work in this place
+    // We need to use isSmallScreenWidth here because the DecisionModal is opening from RHP and ShouldUseNarrowLayout layout will not work in this place
     // eslint-disable-next-line rulesdir/prefer-shouldUseNarrowLayout-instead-of-isSmallScreenWidth
     const {isSmallScreenWidth} = useResponsiveLayout();
     const {showConfirmModal} = useConfirmModal();
