Skip to content

Frontend: Private Domain Onboarding Check #54186

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 73 commits into from
Apr 16, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
73 commits
Select commit Hold shift + click to select a range
b2fe9ca
add privateDomainOnboardingCheck beta
allgandalf Dec 16, 2024
a11d3ee
add new onyx props to Onboarding key
allgandalf Dec 16, 2024
55caf76
add onboardingWorkEmailForm form and its onyx key
allgandalf Dec 16, 2024
7b65e57
add basic screen to add work email
allgandalf Dec 16, 2024
d9b000b
fix prettier and type errors and store error under correct input_name
allgandalf Dec 16, 2024
bc520d7
add template api
allgandalf Dec 16, 2024
e364d53
merge main
allgandalf Dec 27, 2024
d5fa647
remove error field
allgandalf Dec 27, 2024
6f1de7c
add template validate code onboarding screen
allgandalf Dec 27, 2024
003b1b4
Merge branch 'main' into privateDomainScreen
allgandalf Dec 27, 2024
0196179
try to fix failing jest
allgandalf Dec 27, 2024
f131cfe
fix faling tests
allgandalf Dec 27, 2024
8b92ded
cleanup code
allgandalf Dec 27, 2024
3a7c689
Merge branch 'main' into privateDomainScreen
allgandalf Jan 1, 2025
7d141ff
Merge branch 'main' into privateDomainScreen
allgandalf Jan 13, 2025
45406eb
Update types.ts
allgandalf Jan 13, 2025
fca30aa
udate MergeIntoAccountAndLogInParams
allgandalf Jan 13, 2025
22bff38
Rename MergeIntoAccountAndLoginParams.ts to MergeIntoAccountAndLogInP…
allgandalf Jan 13, 2025
08ccc94
Merge branch 'Expensify:main' into privateDomainScreen
allgandalf Jan 14, 2025
196232d
Merge branch 'Expensify:main' into privateDomainScreen
allgandalf Jan 17, 2025
1882887
Merge branch 'main' into privateDomainScreen
allgandalf Jan 28, 2025
8cd262c
fix lint
allgandalf Jan 28, 2025
9a65908
Update src/CONST.ts
allgandalf Jan 28, 2025
09b7552
fix minor places
allgandalf Jan 28, 2025
294136f
fix issues
allgandalf Jan 28, 2025
9a246fc
Merge branch 'Expensify:main' into privateDomainScreen
allgandalf Jan 31, 2025
ede0c4e
remove beta
allgandalf Jan 31, 2025
595e308
Merge branch 'main' into privateDomainScreen
allgandalf Feb 1, 2025
f553af4
fix failing tests
allgandalf Feb 4, 2025
c388c09
Merge branch 'main' into privateDomainScreen
allgandalf Feb 7, 2025
62c985c
push improvements for onboarding flow
allgandalf Feb 7, 2025
044dea2
Merge branch 'Expensify:main' into privateDomainScreen
allgandalf Feb 9, 2025
6553e04
add changes for better app functionality
allgandalf Feb 10, 2025
b824df7
fix lint
allgandalf Feb 10, 2025
e649630
Merge branch 'main' into privateDomainScreen
allgandalf Feb 18, 2025
8991c38
update screens
allgandalf Feb 18, 2025
a01f031
fix input focus
allgandalf Feb 18, 2025
6fe37c3
Merge branch 'main' into privateDomainScreen
allgandalf Mar 11, 2025
2d297bb
fix prettier
allgandalf Mar 11, 2025
9368b86
use WRITE command for addWorkEmail
allgandalf Mar 11, 2025
546602f
Merge branch 'main' into privateDomainScreen
allgandalf Mar 22, 2025
e5f241a
Merge branch 'main' into privateDomainScreen
allgandalf Mar 26, 2025
02de55e
update auth token after API response
allgandalf Mar 26, 2025
8b3b6d4
Merge branch 'main' into privateDomainScreen
allgandalf Mar 28, 2025
6c4bc7c
fix rendering
allgandalf Mar 28, 2025
e5c9ffa
Merge branch 'Expensify:main' into privateDomainScreen
allgandalf Apr 4, 2025
ef6229b
fix: onboarding purpose screen is blank
allgandalf Apr 4, 2025
d1e05dd
fix navigation logic after updating auth token
allgandalf Apr 4, 2025
b731fe6
fix persistent error message on merge blocked screen and clear should…
allgandalf Apr 4, 2025
6190d6a
make CI Green
allgandalf Apr 4, 2025
00af78f
make CI green: Part 2
allgandalf Apr 4, 2025
eb2a9f1
make CI green: Part 3
allgandalf Apr 4, 2025
9200110
Merge branch 'Expensify:main' into privateDomainScreen
allgandalf Apr 7, 2025
29eabbf
Merge branch 'main' into privateDomainScreen
allgandalf Apr 7, 2025
07cc62b
fix prettier
allgandalf Apr 7, 2025
f299815
introduce invalid code error message
allgandalf Apr 7, 2025
c101d8c
remove console log
allgandalf Apr 7, 2025
badb765
assign empty string to error messgae than null value
allgandalf Apr 8, 2025
761a1b0
Merge branch 'Expensify:main' into privateDomainScreen
allgandalf Apr 8, 2025
88ddec0
fix brower back button navigation bug
allgandalf Apr 8, 2025
36577f0
fix navigating back from validate code page
allgandalf Apr 8, 2025
ce10ca9
add UI and navigation test for Private email flow
allgandalf Apr 9, 2025
49bed75
fix lint errors in tests
allgandalf Apr 9, 2025
20cf98b
fix prettier
allgandalf Apr 9, 2025
02c2873
Refactor: make the code readable
allgandalf Apr 9, 2025
3d2788c
Merge branch 'Expensify:main' into privateDomainScreen
allgandalf Apr 10, 2025
e92f93f
fix failing jest test by mocking libraries
allgandalf Apr 10, 2025
2ad6557
Merge branch 'main' into privateDomainScreen
allgandalf Apr 14, 2025
03acbd5
Merge branch 'main' into privateDomainScreen
allgandalf Apr 15, 2025
ef64f91
call OpenApp after successfully merging account
allgandalf Apr 15, 2025
6a0a535
Merge branch 'main' into privateDomainScreen
blimpich Apr 15, 2025
c42c335
Merge branch 'Expensify:main' into privateDomainScreen
allgandalf Apr 16, 2025
109b1c2
fix changed files esLint
allgandalf Apr 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
3 changes: 3 additions & 0 deletions src/CONST.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5615,6 +5615,9 @@ const CONST = {
DEBUG: 'DEBUG',
},
},

// We need to store this server side error in order to not show the blocking screen when the error is for invalid code
MERGE_ACCOUNT_INVALID_CODE_ERROR: '401 Not authorized - Invalid validateCode',
REIMBURSEMENT_ACCOUNT: {
DEFAULT_DATA: {
achData: {
Expand Down
3 changes: 3 additions & 0 deletions src/ONYXKEYS.ts
Original file line number Diff line number Diff line change
Expand Up @@ -786,6 +786,8 @@ const ONYXKEYS = {
RULES_CUSTOM_FORM_DRAFT: 'rulesCustomFormDraft',
DEBUG_DETAILS_FORM: 'debugDetailsForm',
DEBUG_DETAILS_FORM_DRAFT: 'debugDetailsFormDraft',
ONBOARDING_WORK_EMAIL_FORM: 'onboardingWorkEmailForm',
ONBOARDING_WORK_EMAIL_FORM_DRAFT: 'onboardingWorkEmailFormDraft',
MERGE_ACCOUNT_DETAILS_FORM: 'mergeAccountDetailsForm',
MERGE_ACCOUNT_DETAILS_FORM_DRAFT: 'mergeAccountDetailsFormDraft',
WORKSPACE_PER_DIEM_FORM: 'workspacePerDiemForm',
Expand Down Expand Up @@ -890,6 +892,7 @@ type OnyxFormValuesMapping = {
[ONYXKEYS.FORMS.RULES_CUSTOM_FORM]: FormTypes.RulesCustomForm;
[ONYXKEYS.FORMS.SEARCH_SAVED_SEARCH_RENAME_FORM]: FormTypes.SearchSavedSearchRenameForm;
[ONYXKEYS.FORMS.DEBUG_DETAILS_FORM]: FormTypes.DebugReportForm | FormTypes.DebugReportActionForm | FormTypes.DebugTransactionForm | FormTypes.DebugTransactionViolationForm;
[ONYXKEYS.FORMS.ONBOARDING_WORK_EMAIL_FORM]: FormTypes.OnboardingWorkEmailForm;
[ONYXKEYS.FORMS.MERGE_ACCOUNT_DETAILS_FORM]: FormTypes.MergeAccountDetailsForm;
[ONYXKEYS.FORMS.INTERNATIONAL_BANK_ACCOUNT_FORM]: FormTypes.InternationalBankAccountForm;
[ONYXKEYS.FORMS.WORKSPACE_PER_DIEM_FORM]: FormTypes.WorkspacePerDiemForm;
Expand Down
8 changes: 8 additions & 0 deletions src/ROUTES.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1879,6 +1879,14 @@ const ROUTES = {
route: 'onboarding/join-workspaces',
getRoute: (backTo?: string) => getUrlWithBackToParam(`onboarding/join-workspaces`, backTo),
},
ONBOARDING_WORK_EMAIL: {
route: 'onboarding/work-email',
getRoute: (backTo?: string) => getUrlWithBackToParam(`onboarding/work-email`, backTo),
},
ONBOARDING_WORK_EMAIL_VALIDATION: {
route: 'onboarding/work-email-validation',
getRoute: (backTo?: string) => getUrlWithBackToParam(`onboarding/work-email-validation`, backTo),
},
WELCOME_VIDEO_ROOT: 'onboarding/welcome-video',
EXPLANATION_MODAL_ROOT: 'onboarding/explanation',
WORKSPACE_CONFIRMATION: {
Expand Down
2 changes: 2 additions & 0 deletions src/SCREENS.ts
Original file line number Diff line number Diff line change
Expand Up @@ -641,6 +641,8 @@ const SCREENS = {
EMPLOYEES: 'Onboarding_Employees',
ACCOUNTING: 'Onboarding_Accounting',
WORKSPACES: 'Onboarding_Workspaces',
WORK_EMAIL: 'Onboarding_Work_Email',
WORK_EMAIL_VALIDATION: 'Onboarding_Work_Email_Validation',
},

WELCOME_VIDEO: {
Expand Down
47 changes: 29 additions & 18 deletions src/components/AutoEmailLink.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,32 +18,43 @@ type AutoEmailLinkProps = {

function AutoEmailLink({text, style}: AutoEmailLinkProps) {
const styles = useThemeStyles();
const emailRegex = COMMON_CONST.REG_EXP.EXTRACT_EMAIL;
const matches = [...text.matchAll(emailRegex)];

if (matches.length === 0) {
return <Text style={style}>{text}</Text>;
}

let lastIndex = 0;

return (
<Text style={style}>
{text.split(COMMON_CONST.REG_EXP.EXTRACT_EMAIL).map((str, index) => {
if (COMMON_CONST.REG_EXP.EMAIL.test(str)) {
return (
<TextLink
// eslint-disable-next-line react/no-array-index-key
key={`${index}-${str}`}
href={`mailto:${str}`}
style={styles.link}
>
{str}
</TextLink>
);
{matches.flatMap((match, index) => {
const email = match[0];
const startIndex = match.index ?? 0;
const elements = [];

// Push plain text before email
if (startIndex > lastIndex) {
elements.push(text.slice(lastIndex, startIndex));
}

return (
<Text
style={style}
// Push email as a link
elements.push(
<TextLink
// eslint-disable-next-line react/no-array-index-key
key={`${index}-${str}`}
key={`email-${index}`}
href={`mailto:${email}`}
style={styles.link}
>
{str}
</Text>
{email}
</TextLink>,
);

lastIndex = startIndex + email.length;
return elements;
})}
{lastIndex < text.length && text.slice(lastIndex)}
</Text>
);
}
Expand Down
11 changes: 8 additions & 3 deletions src/components/Form/FormProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,9 @@ type FormProviderProps<TFormID extends OnyxFormKey = OnyxFormKey> = FormProps<TF
/** Whether HTML is allowed in form inputs */
allowHTML?: boolean;

/** Whether to render the submit button above the footer. */
shouldRenderFooterAboveSubmit?: boolean;

/** Whether the form is loading */
isLoading?: boolean;

Expand All @@ -98,13 +101,14 @@ function FormProvider(
shouldTrimValues = true,
allowHTML = false,
isLoading = false,
shouldRenderFooterAboveSubmit = false,
...rest
}: FormProviderProps,
forwardedRef: ForwardedRef<FormRef>,
) {
const [network] = useOnyx(ONYXKEYS.NETWORK);
const [formState] = useOnyx<OnyxFormKey, Form>(`${formID}`);
const [draftValues] = useOnyx<OnyxFormDraftKey, Form>(`${formID}Draft`);
const [network] = useOnyx(ONYXKEYS.NETWORK, {canBeMissing: true});
const [formState] = useOnyx<OnyxFormKey, Form>(`${formID}`, {canBeMissing: true});
const [draftValues] = useOnyx<OnyxFormDraftKey, Form>(`${formID}Draft`, {canBeMissing: true});
const {preferredLocale, translate} = useLocalize();
const inputRefs = useRef<InputRefs>({});
const touchedInputs = useRef<Record<string, boolean>>({});
Expand Down Expand Up @@ -432,6 +436,7 @@ function FormProvider(
errors={errors}
isLoading={isLoading}
enabledWhenOffline={enabledWhenOffline}
shouldRenderFooterAboveSubmit={shouldRenderFooterAboveSubmit}
>
{typeof children === 'function' ? children({inputValues}) : children}
</FormWrapper>
Expand Down
8 changes: 7 additions & 1 deletion src/components/Form/FormWrapper.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,9 @@ type FormWrapperProps = ChildrenProps &
/** Callback to submit the form */
onSubmit: () => void;

/** should render the extra button above submit button */
shouldRenderFooterAboveSubmit?: boolean;

/** Whether the form is loading */
isLoading?: boolean;

Expand Down Expand Up @@ -76,6 +79,7 @@ function FormWrapper({
shouldHideFixErrorsAlert = false,
disablePressOnEnter = false,
isSubmitDisabled = false,
shouldRenderFooterAboveSubmit = false,
isLoading = false,
shouldScrollToEnd = false,
addBottomSafeAreaPadding,
Expand All @@ -87,7 +91,7 @@ function FormWrapper({
const formRef = useRef<RNScrollView>(null);
const formContentRef = useRef<View>(null);

const [formState] = useOnyx<OnyxFormKey, Form>(`${formID}`);
const [formState] = useOnyx<OnyxFormKey, Form>(`${formID}`, {canBeMissing: true});

const errorMessage = useMemo(() => (formState ? getLatestErrorMessage(formState) : undefined), [formState]);

Expand Down Expand Up @@ -168,6 +172,7 @@ function FormWrapper({
isSubmitActionDangerous={isSubmitActionDangerous}
disablePressOnEnter={disablePressOnEnter}
enterKeyEventListenerPriority={1}
shouldRenderFooterAboveSubmit={shouldRenderFooterAboveSubmit}
shouldBlendOpacity={shouldSubmitButtonBlendOpacity}
/>
),
Expand Down Expand Up @@ -196,6 +201,7 @@ function FormWrapper({
submitButtonStylesWithBottomSafeAreaPadding,
submitButtonText,
submitFlexEnabled,
shouldRenderFooterAboveSubmit,
],
);

Expand Down
2 changes: 2 additions & 0 deletions src/components/Form/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,8 @@ type FormProps<TFormID extends OnyxFormKey = OnyxFormKey> = {
/** Disable press on enter for submit button */
disablePressOnEnter?: boolean;

/** Render extra button above submit button */
shouldRenderFooterAboveSubmit?: boolean;
/**
* Determines whether the form should automatically scroll to the end upon rendering or when the value changes.
* If `true`, the form will smoothly scroll to the bottom after interactions have completed.
Expand Down
7 changes: 6 additions & 1 deletion src/components/FormAlertWithSubmitButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,9 @@ type FormAlertWithSubmitButtonProps = {
/** The priority to assign the enter key event listener to buttons. 0 is the highest priority. */
enterKeyEventListenerPriority?: number;

/** should render the extra button above submit button */
shouldRenderFooterAboveSubmit?: boolean;

/**
* Whether the button should have a background layer in the color of theme.appBG.
* This is needed for buttons that allow content to display under them.
Expand Down Expand Up @@ -92,6 +95,7 @@ function FormAlertWithSubmitButton({
useSmallerSubmitButtonSize = false,
errorMessageStyle,
enterKeyEventListenerPriority = 0,
shouldRenderFooterAboveSubmit = false,
shouldBlendOpacity = false,
addButtonBottomPadding = true,
}: FormAlertWithSubmitButtonProps) {
Expand All @@ -115,6 +119,7 @@ function FormAlertWithSubmitButton({
>
{(isOffline: boolean | undefined) => (
<View>
{shouldRenderFooterAboveSubmit && footerContent}
{isOffline && !enabledWhenOffline ? (
<Button
success
Expand Down Expand Up @@ -143,7 +148,7 @@ function FormAlertWithSubmitButton({
large={!useSmallerSubmitButtonSize}
/>
)}
{footerContent}
{!shouldRenderFooterAboveSubmit && footerContent}
</View>
)}
</FormAlertWrapper>
Expand Down
6 changes: 6 additions & 0 deletions src/components/ValidateCodeActionForm/type.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,12 @@ type ValidateCodeActionFormProps = {
/** Ref for validate code form */
forwardedRef: ForwardedRef<ValidateCodeFormHandle>;

/** Whether to show the skip button */
shouldShowSkipButton?: boolean;

/** Function to call when the skip button is pressed */
handleSkipButtonPress?: () => void;

/** Text for submit button */
submitButtonText?: string;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,12 @@ type ValidateCodeFormProps = {

/** Whether the form is loading or not */
isLoading?: boolean;

/** Whether to show skip button */
shouldShowSkipButton?: boolean;

/** Function to call when skip button is pressed */
handleSkipButtonPress?: () => void;
};

function BaseValidateCodeForm({
Expand All @@ -91,6 +97,8 @@ function BaseValidateCodeForm({
hideSubmitButton,
submitButtonText,
isLoading,
shouldShowSkipButton = false,
handleSkipButtonPress,
}: ValidateCodeFormProps) {
const {translate} = useLocalize();
const {isOffline} = useNetwork();
Expand All @@ -100,7 +108,7 @@ function BaseValidateCodeForm({
const [formError, setFormError] = useState<ValidateCodeFormError>({});
const [validateCode, setValidateCode] = useState('');
const inputValidateCodeRef = useRef<MagicCodeInputHandle>(null);
const [account = {}] = useOnyx(ONYXKEYS.ACCOUNT);
const [account = {}] = useOnyx(ONYXKEYS.ACCOUNT, {canBeMissing: true});
// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing -- nullish coalescing doesn't achieve the same result in this case
const shouldDisableResendValidateCode = !!isOffline || account?.isLoading;
const focusTimeoutRef = useRef<NodeJS.Timeout | null>(null);
Expand Down Expand Up @@ -277,14 +285,23 @@ function BaseValidateCodeForm({
messages={{0: translate('validateCodeModal.successfulNewCodeRequest')}}
/>
)}

<OfflineWithFeedback
shouldDisplayErrorAbove
pendingAction={validatePendingAction}
errors={canShowError ? validateError : undefined}
errorRowStyles={[styles.mt2]}
errorRowStyles={[styles.mt2, styles.textWrap]}
onClose={() => clearError()}
style={buttonStyles}
>
{shouldShowSkipButton && (
<Button
text={translate('common.skip')}
onPress={handleSkipButtonPress}
success={false}
large
/>
)}
{!hideSubmitButton && (
<Button
isDisabled={isOffline}
Expand Down
25 changes: 25 additions & 0 deletions src/languages/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,8 @@ import type {
WelcomeEnterMagicCodeParams,
WelcomeToRoomParams,
WeSentYouMagicSignInLinkParams,
WorkEmailMergingBlockedParams,
WorkEmailResendCodeParams,
WorkspaceLockedPlanTypeParams,
WorkspaceMemberList,
WorkspaceOwnerWillNeedToAddOrUpdatePaymentCardParams,
Expand Down Expand Up @@ -532,6 +534,7 @@ const translations = {
skip: 'Skip',
chatWithAccountManager: ({accountManagerDisplayName}: ChatWithAccountManagerParams) => `Need something specific? Chat with your account manager, ${accountManagerDisplayName}.`,
chatNow: 'Chat now',
workEmail: 'Work email',
destination: 'Destination',
subrate: 'Subrate',
perDiem: 'Per diem',
Expand Down Expand Up @@ -2021,6 +2024,28 @@ const translations = {
error: {
requiredFirstName: 'Please input your first name to continue',
},
workEmail: {
title: 'What’s your work email?',
subtitle: 'Expensify works best when you connect your work email.',
explanationModal: {
descriptionOne: 'Forward to [email protected] for scanning',
descriptionTwo: 'Join your colleagues already using Expensify',
descriptionThree: 'Enjoy a more customized experience',
},
addWorkEmail: 'Add work email',
},
workEmailValidation: {
title: 'Verify your work email',
magicCodeSent: ({workEmail}: WorkEmailResendCodeParams) => `Please enter the magic code sent to ${workEmail}. It should arrive in a minute or two.`,
},
workEmailValidationError: {
publicEmail: 'Please enter a valid work email from a private domain e.g. [email protected]',
offline: 'We couldn’t add your work email as you appear to be offline',
},
mergeBlockScreen: {
title: 'Couldn’t add work email',
subtitle: ({workEmail}: WorkEmailMergingBlockedParams) => `We couldn’t add ${workEmail}. Please try again later in Settings or chat with Concierge for guidance.`,
},
},
featureTraining: {
doNotShowAgain: "Don't show me this again",
Expand Down
26 changes: 26 additions & 0 deletions src/languages/es.ts
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,8 @@ import type {
WelcomeEnterMagicCodeParams,
WelcomeToRoomParams,
WeSentYouMagicSignInLinkParams,
WorkEmailMergingBlockedParams,
WorkEmailResendCodeParams,
WorkspaceLockedPlanTypeParams,
WorkspaceMemberList,
WorkspaceOwnerWillNeedToAddOrUpdatePaymentCardParams,
Expand Down Expand Up @@ -523,6 +525,7 @@ const translations = {
minuteAbbreviation: 'm',
chatWithAccountManager: ({accountManagerDisplayName}: ChatWithAccountManagerParams) => `¿Necesitas algo específico? Habla con tu gerente de cuenta, ${accountManagerDisplayName}.`,
chatNow: 'Chatear ahora',
workEmail: 'correo electrónico de trabajo',
destination: 'Destino',
subrate: 'Subtasa',
perDiem: 'Per diem',
Expand Down Expand Up @@ -2023,6 +2026,29 @@ const translations = {
error: {
requiredFirstName: 'Introduce tu nombre para continuar',
},
workEmail: {
title: 'Cuál es tu correo electrónico de trabajo',
subtitle: 'Expensify funciona mejor cuando conectas tu correo electrónico de trabajo.',
explanationModal: {
descriptionOne: 'Reenvía a [email protected] para escanear',
descriptionTwo: 'Únete a tus compañeros de trabajo que ya están usando Expensify',
descriptionThree: 'Disfruta de una experiencia más personalizada',
},
addWorkEmail: 'Añadir correo electrónico de trabajo',
},
workEmailValidation: {
title: 'Verifica tu correo electrónico de trabajo',
magicCodeSent: ({workEmail}: WorkEmailResendCodeParams) => `Por favor, introduce el código mágico enviado a ${workEmail}. Debería llegar en uno o dos minutos.`,
},
workEmailValidationError: {
publicEmail: 'Por favor, introduce un correo electrónico laboral válido de un dominio privado, por ejemplo: [email protected]',
offline: 'No pudimos añadir tu correo electrónico laboral porque parece que estás sin conexión.',
},
mergeBlockScreen: {
title: 'No se pudo añadir el correo electrónico de trabajo',
subtitle: ({workEmail}: WorkEmailMergingBlockedParams) =>
`No pudimos añadir ${workEmail}. Por favor, inténtalo de nuevo más tarde en Configuración o chatea con Concierge para obtener ayuda.`,
},
},
featureTraining: {
doNotShowAgain: 'No muestres esto otra vez',
Expand Down
Loading