Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
d7be725
feat: add DomainAdminsSettingsPage.tsx & DomainAddPrimaryContactPage.tsx
war-in Dec 12, 2025
1420a04
Merge branch 'war-in/domains/add-admin-page' into war-in/domains/add-…
war-in Dec 12, 2025
ca6536a
chore: post-merge fixes
war-in Dec 12, 2025
efac20c
chore: fix prettier
war-in Dec 12, 2025
c7752c3
Merge branch 'war-in/domains/add-admin-page' into war-in/domains/add-…
war-in Dec 12, 2025
8a1f0cc
refactor: fix component for react compiler
war-in Dec 12, 2025
0c0a650
Merge branch 'war-in/domains/add-admin-page' into war-in/domains/add-…
war-in Dec 12, 2025
d1cfb88
Merge branch 'main' into war-in/domains/add-admins-settings-page
war-in Dec 14, 2025
37c79b9
chore: fix review comments
war-in Dec 14, 2025
b8d2c73
refactor: call SET_TECHNICAL_CONTACT_EMAIL command and fix No results…
war-in Dec 15, 2025
b22fe6a
Merge branch 'main' into war-in/domains/add-admins-settings-page
war-in Dec 15, 2025
a37bd87
refactor: add DomainNotFoundPageWrapper to easily handle not found pa…
war-in Dec 15, 2025
eb5a41a
Merge branch 'main' into war-in/domains/add-admins-settings-page
war-in Dec 15, 2025
284acfe
chore: fix eslint & add spanish translations
war-in Dec 15, 2025
afded76
fix: go back to settings after refresh
war-in Dec 15, 2025
5f23d97
feat: add Primary contact badge
war-in Dec 15, 2025
cee830b
fix: use correct paths
war-in Dec 15, 2025
3001bb5
chore: fix prettier
war-in Dec 15, 2025
684225b
Merge branch 'main' into war-in/domains/add-admins-settings-page
war-in Dec 16, 2025
6ffec0b
refactor: do not allow for unsetting primary contact
war-in Dec 16, 2025
f5b8f17
feat: add translations
war-in Dec 16, 2025
6fdd1aa
refactor: use technicalContactEmailSelector to get primary contact email
war-in Dec 16, 2025
0926952
refactor: use selector to extract admins personal details
war-in Dec 16, 2025
26a9928
Merge branch 'main' into war-in/domains/add-admins-settings-page
war-in Dec 16, 2025
cb6cbb0
refactor: apply `DomainNotFoundPageWrapper` to DomainAdminDetailsPage
war-in Dec 16, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions src/ONYXKEYS.ts
Original file line number Diff line number Diff line change
Expand Up @@ -737,6 +737,12 @@ const ONYXKEYS = {

/** Stores domain admin account ID */
EXPENSIFY_ADMIN_ACCESS_PREFIX: 'expensify_adminPermissions_',

/** Pending actions for a domain */
DOMAIN_PENDING_ACTIONS: 'domainPendingActions_',

/** Errors related to a domain */
DOMAIN_ERRORS: 'domainErrors_',
},

/** List of Form ids */
Expand Down Expand Up @@ -1124,6 +1130,8 @@ type OnyxCollectionValuesMapping = {
[ONYXKEYS.COLLECTION.SHARED_NVP_PRIVATE_ADMIN_ACCESS]: boolean;
[ONYXKEYS.COLLECTION.SAML_METADATA]: OnyxTypes.SamlMetadata;
[ONYXKEYS.COLLECTION.EXPENSIFY_ADMIN_ACCESS_PREFIX]: number;
[ONYXKEYS.COLLECTION.DOMAIN_PENDING_ACTIONS]: OnyxTypes.DomainPendingActions;
[ONYXKEYS.COLLECTION.DOMAIN_ERRORS]: OnyxTypes.DomainErrors;
};

type OnyxValuesMapping = {
Expand Down
8 changes: 8 additions & 0 deletions src/ROUTES.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3456,6 +3456,14 @@ const ROUTES = {
route: 'domain/:domainAccountID/admins/:accountID',
getRoute: (domainAccountID: number, accountID: number) => `domain/${domainAccountID}/admins/${accountID}` as const,
},
DOMAIN_ADMINS_SETTINGS: {
route: 'domain/:domainAccountID/admins/settings',
getRoute: (domainAccountID: number) => `domain/${domainAccountID}/admins/settings` as const,
},
DOMAIN_ADD_PRIMARY_CONTACT: {
route: 'domain/:domainAccountID/admins/settings/primary-contact',
getRoute: (domainAccountID: number) => `domain/${domainAccountID}/admins/settings/primary-contact` as const,
},
} as const;

/**
Expand Down
2 changes: 2 additions & 0 deletions src/SCREENS.ts
Original file line number Diff line number Diff line change
Expand Up @@ -854,6 +854,8 @@ const SCREENS = {
SAML: 'Domain_SAML',
ADMINS: 'Domain_Admins',
ADMIN_DETAILS: 'Domain_Admin_Details',
ADMINS_SETTINGS: 'Admins_Settings',
ADD_PRIMARY_CONTACT: 'Add_Primary_Contact',
},
} as const;

Expand Down
2 changes: 1 addition & 1 deletion src/languages/de.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7968,7 +7968,7 @@ Hier ist ein *Testbeleg*, um dir zu zeigen, wie es funktioniert:`,
subtitle: 'Erzwingen Sie für Mitglieder Ihrer Domain die Anmeldung per Single Sign-On, schränken Sie die Erstellung von Workspaces ein und vieles mehr.',
enable: 'Aktivieren',
},
admins: {title: 'Admins', findAdmin: 'Admin finden'},
admins: {title: 'Admins', findAdmin: 'Admin finden', primaryContact: 'Hauptansprechpartner', addPrimaryContact: 'Primären Kontakt hinzufügen', settings: 'Einstellungen'},
},
};
// IMPORTANT: This line is manually replaced in generate translation files by scripts/generateTranslations.ts,
Expand Down
3 changes: 3 additions & 0 deletions src/languages/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7817,6 +7817,9 @@ const translations = {
admins: {
title: 'Admins',
findAdmin: 'Find admin',
primaryContact: 'Primary contact',
addPrimaryContact: 'Add primary contact',
settings: 'Settings',
},
},
};
Expand Down
3 changes: 3 additions & 0 deletions src/languages/es.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7964,6 +7964,9 @@ ${amount} para ${merchant} - ${date}`,
admins: {
title: 'Administradores',
findAdmin: 'Encontrar administrador',
primaryContact: 'Contacto principal',
addPrimaryContact: 'Añadir contacto principal',
settings: 'Configuración',
},
},
};
Expand Down
2 changes: 1 addition & 1 deletion src/languages/fr.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7973,7 +7973,7 @@ Voici un *reçu test* pour vous montrer comment cela fonctionne :`,
subtitle: "Exiger que les membres de votre domaine se connectent via l'authentification unique, restreindre la création d'espaces de travail, et plus encore.",
enable: 'Activer',
},
admins: {title: 'Admins', findAdmin: 'Trouver un admin'},
admins: {title: 'Admins', findAdmin: 'Trouver un admin', primaryContact: 'Contact principal', addPrimaryContact: 'Ajouter un contact principal', settings: 'Paramètres'},
},
};
// IMPORTANT: This line is manually replaced in generate translation files by scripts/generateTranslations.ts,
Expand Down
8 changes: 7 additions & 1 deletion src/languages/it.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7949,7 +7949,13 @@ Ecco una *ricevuta di prova* per mostrarti come funziona:`,
subtitle: 'Richiedi ai membri del tuo dominio di accedere tramite Single Sign-On, limita la creazione di spazi di lavoro e altro ancora.',
enable: 'Abilita',
},
admins: {title: 'Amministratori', findAdmin: 'Trova amministratore'},
admins: {
title: 'Amministratori',
findAdmin: 'Trova amministratore',
primaryContact: 'Contatto principale',
addPrimaryContact: 'Aggiungi contatto principale',
settings: 'Impostazioni',
},
},
};
// IMPORTANT: This line is manually replaced in generate translation files by scripts/generateTranslations.ts,
Expand Down
2 changes: 1 addition & 1 deletion src/languages/ja.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7888,7 +7888,7 @@ Expensify の使い方をお見せするための*テストレシート*がこ
subtitle: 'ドメインのメンバーにシングルサインオンでのログインを必須化し、ワークスペースの作成を制限するなど、さらに多くのことができます。',
enable: '有効にする',
},
admins: {title: '管理者', findAdmin: '管理者を検索'},
admins: {title: '管理者', findAdmin: '管理者を検索', primaryContact: '主要連絡先', addPrimaryContact: '主要連絡先を追加', settings: '設定'},
},
};
// IMPORTANT: This line is manually replaced in generate translation files by scripts/generateTranslations.ts,
Expand Down
8 changes: 7 additions & 1 deletion src/languages/nl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7935,7 +7935,13 @@ Hier is een *testbon* om je te laten zien hoe het werkt:`,
subtitle: 'Verplicht leden van je domein om in te loggen via single sign-on, beperk het aanmaken van werkruimten en meer.',
enable: 'Inschakelen',
},
admins: {title: 'Beheerders', findAdmin: 'Beheerder zoeken'},
admins: {
title: 'Beheerders',
findAdmin: 'Beheerder zoeken',
primaryContact: 'Primair contactpersoon',
addPrimaryContact: 'Primair contactpersoon toevoegen',
settings: 'Instellingen',
},
},
};
// IMPORTANT: This line is manually replaced in generate translation files by scripts/generateTranslations.ts,
Expand Down
2 changes: 1 addition & 1 deletion src/languages/pl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7920,7 +7920,7 @@ Oto *paragon testowy*, który pokazuje, jak to działa:`,
subtitle: 'Wymagaj, aby członkowie Twojej domeny logowali się przez Single Sign-On (SSO), ograniczaj tworzenie obszarów roboczych i nie tylko.',
enable: 'Włącz',
},
admins: {title: 'Administratorzy', findAdmin: 'Znajdź administratora'},
admins: {title: 'Administratorzy', findAdmin: 'Znajdź administratora', primaryContact: 'Główny kontakt', addPrimaryContact: 'Dodaj główny kontakt', settings: 'Ustawienia'},
},
};
// IMPORTANT: This line is manually replaced in generate translation files by scripts/generateTranslations.ts,
Expand Down
8 changes: 7 additions & 1 deletion src/languages/pt-BR.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7927,7 +7927,13 @@ Aqui está um *recibo de teste* para mostrar como funciona:`,
subtitle: 'Exija que os membros do seu domínio façam login por meio de logon único (SSO), restrinja a criação de espaços de trabalho e muito mais.',
enable: 'Ativar',
},
admins: {title: 'Administradores', findAdmin: 'Encontrar administrador'},
admins: {
title: 'Administradores',
findAdmin: 'Encontrar administrador',
primaryContact: 'Contato principal',
addPrimaryContact: 'Adicionar contato principal',
settings: 'Configurações',
},
},
};
// IMPORTANT: This line is manually replaced in generate translation files by scripts/generateTranslations.ts,
Expand Down
2 changes: 1 addition & 1 deletion src/languages/zh-hans.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7753,7 +7753,7 @@ ${reportName}
addDomain: {title: '添加域', subtitle: '请输入您想访问的私有域名(例如:expensify.com)。', domainName: '域名', newDomain: '新域名'},
domainAdded: {title: '已添加域名', description: '接下来,您需要验证域名的所有权并调整您的安全设置。', configure: '配置'},
enhancedSecurity: {title: '增强的安全性', subtitle: '要求您域内的成员使用单点登录登录、限制工作区创建等。', enable: '启用'},
admins: {title: '管理员', findAdmin: '查找管理员'},
admins: {title: '管理员', findAdmin: '查找管理员', primaryContact: '主要联系人', addPrimaryContact: '添加主要联系人', settings: '设置'},
},
};
// IMPORTANT: This line is manually replaced in generate translation files by scripts/generateTranslations.ts,
Expand Down
7 changes: 7 additions & 0 deletions src/libs/API/parameters/SetTechnicalContactEmailParams.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
type SetTechnicalContactEmailParams = {
authToken?: string | null;
domainAccountID: number;
technicalContactAccountID: number;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We will update this to email, but since its not used yet, we can do that later

};

export default SetTechnicalContactEmailParams;
1 change: 1 addition & 0 deletions src/libs/API/parameters/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -448,3 +448,4 @@ export type {default as SetSamlIdentityParams} from './SetSamlIdentityParams';
export type {default as UpdateSamlEnabledParams} from './UpdateSamlEnabledParams';
export type {default as UpdateSamlRequiredParams} from './UpdateSamlRequiredParams';
export type {default as SetPolicyRequireCompanyCardsEnabledParams} from './SetPolicyRequireCompanyCardsEnabled';
export type {default as SetTechnicalContactEmailParams} from './SetTechnicalContactEmailParams';
2 changes: 2 additions & 0 deletions src/libs/API/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -526,6 +526,7 @@ const WRITE_COMMANDS = {
UPDATE_SAML_ENABLED: 'UpdateSAMLEnabled',
UPDATE_SAML_REQUIRED: 'UpdateSAMLRequired',
CREATE_DOMAIN: 'CreateDomain',
SET_TECHNICAL_CONTACT_EMAIL: 'SetTechnicalContactEmail',
} as const;

type WriteCommand = ValueOf<typeof WRITE_COMMANDS>;
Expand Down Expand Up @@ -1073,6 +1074,7 @@ type WriteCommandParameters = {
[WRITE_COMMANDS.UPDATE_SAML_ENABLED]: Parameters.UpdateSamlEnabledParams;
[WRITE_COMMANDS.UPDATE_SAML_REQUIRED]: Parameters.UpdateSamlRequiredParams;
[WRITE_COMMANDS.CREATE_DOMAIN]: Parameters.DomainParams;
[WRITE_COMMANDS.SET_TECHNICAL_CONTACT_EMAIL]: Parameters.SetTechnicalContactEmailParams;
};

const READ_COMMANDS = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -848,6 +848,8 @@ 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.ADMINS_SETTINGS]: () => require<ReactComponentModule>('../../../../pages/domain/Admins/DomainAdminsSettingsPage').default,
[SCREENS.DOMAIN.ADD_PRIMARY_CONTACT]: () => require<ReactComponentModule>('../../../../pages/domain/Admins/DomainAddPrimaryContactPage').default,
});

const TwoFactorAuthenticatorStackNavigator = createModalStackNavigator<EnablePaymentsNavigatorParamList>({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import SCREENS from '@src/SCREENS';
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.ADMINS]: [SCREENS.DOMAIN.ADMIN_DETAILS, SCREENS.DOMAIN.ADMINS_SETTINGS, SCREENS.DOMAIN.ADD_PRIMARY_CONTACT],
};

export default DOMAIN_TO_RHP;
6 changes: 6 additions & 0 deletions src/libs/Navigation/linkingConfig/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1111,6 +1111,12 @@ const config: LinkingOptions<RootNavigatorParamList>['config'] = {
[SCREENS.DOMAIN.ADMIN_DETAILS]: {
path: ROUTES.DOMAIN_ADMIN_DETAILS.route,
},
[SCREENS.DOMAIN.ADMINS_SETTINGS]: {
path: ROUTES.DOMAIN_ADMINS_SETTINGS.route,
},
[SCREENS.DOMAIN.ADD_PRIMARY_CONTACT]: {
path: ROUTES.DOMAIN_ADD_PRIMARY_CONTACT.route,
},
},
},
[SCREENS.RIGHT_MODAL.TWO_FACTOR_AUTH]: {
Expand Down
6 changes: 6 additions & 0 deletions src/libs/Navigation/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1332,6 +1332,12 @@ type SettingsNavigatorParamList = {
domainAccountID: number;
accountID: number;
};
[SCREENS.DOMAIN.ADMINS_SETTINGS]: {
domainAccountID: number;
};
[SCREENS.DOMAIN.ADD_PRIMARY_CONTACT]: {
domainAccountID: number;
};
} & ReimbursementAccountNavigatorParamList;

type DomainCardNavigatorParamList = {
Expand Down
67 changes: 67 additions & 0 deletions src/libs/actions/Domain.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import Onyx from 'react-native-onyx';
import type {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';
import {getMicroSecondOnyxErrorWithTranslationKey} from '@libs/ErrorUtils';
import {getAuthToken} from '@libs/Network/NetworkStore';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
import type {ScimTokenWithState} from './ScimToken/ScimTokenUtils';
Expand Down Expand Up @@ -351,6 +353,69 @@ function resetCreateDomainForm() {
Onyx.merge(ONYXKEYS.FORMS.CREATE_DOMAIN_FORM, null);
}

function setPrimaryContact(domainAccountID: number, newTechnicalContactAccountID: number, newTechnicalContactEmail: string, currentTechnicalContactEmail?: string) {
const optimisticData: OnyxUpdate[] = [
{
onyxMethod: Onyx.METHOD.MERGE,
key: `${ONYXKEYS.COLLECTION.SHARED_NVP_PRIVATE_DOMAIN_MEMBER}${domainAccountID}`,
value: {
settings: {
technicalContactEmail: newTechnicalContactEmail,
},
},
},
{
onyxMethod: Onyx.METHOD.MERGE,
key: `${ONYXKEYS.COLLECTION.DOMAIN_PENDING_ACTIONS}${domainAccountID}`,
value: {
technicalContactEmail: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE,
},
},
];
const successData: OnyxUpdate[] = [
{
onyxMethod: Onyx.METHOD.MERGE,
key: `${ONYXKEYS.COLLECTION.DOMAIN_PENDING_ACTIONS}${domainAccountID}`,
value: {
technicalContactEmail: null,
},
},
];
const failureData: OnyxUpdate[] = [
{
onyxMethod: Onyx.METHOD.MERGE,
key: `${ONYXKEYS.COLLECTION.SHARED_NVP_PRIVATE_DOMAIN_MEMBER}${domainAccountID}`,
value: {
settings: {
technicalContactEmail: currentTechnicalContactEmail,
},
},
},
{
onyxMethod: Onyx.METHOD.MERGE,
key: `${ONYXKEYS.COLLECTION.DOMAIN_PENDING_ACTIONS}${domainAccountID}`,
value: {
technicalContactEmail: null,
},
},
];

const authToken = getAuthToken();
const params: SetTechnicalContactEmailParams = {
authToken,
domainAccountID,
technicalContactAccountID: newTechnicalContactAccountID,
};

API.write(WRITE_COMMANDS.SET_TECHNICAL_CONTACT_EMAIL, params, {optimisticData, successData, failureData});
}

function clearSetPrimaryContactError(domainAccountID: number) {
Onyx.merge(`${ONYXKEYS.COLLECTION.DOMAIN_ERRORS}${domainAccountID}`, {
technicalContactEmailErrors: null,
});
}

export {
getDomainValidationCode,
validateDomain,
Expand All @@ -365,4 +430,6 @@ export {
getScimToken,
createDomain,
resetCreateDomainForm,
setPrimaryContact,
clearSetPrimaryContactError,
};
Loading
Loading