Skip to content
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

Give users on a domain the ability to join their colleagues when the company is already using Expensify #51681

Merged
merged 25 commits into from
Dec 13, 2024
Merged
Show file tree
Hide file tree
Changes from 19 commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
ad6007b
onboarding flow without errors
allroundexperts Nov 3, 2024
fa273c3
Merge branch 'main' into feat-48189
allroundexperts Nov 6, 2024
7c5c611
working version
allroundexperts Nov 6, 2024
3cba659
Merge branch 'main' into feat-48189
allroundexperts Nov 6, 2024
938bdf2
WIP: ad6007b6398 onboarding flow without errors
allroundexperts Nov 9, 2024
a107528
fix: handle comments
allroundexperts Nov 10, 2024
79cdc1c
merge with main
allroundexperts Nov 13, 2024
4737f1a
add onboarding wrapper
allroundexperts Nov 13, 2024
dd29fd8
Merge branch 'main' into feat-48189
allroundexperts Nov 24, 2024
467f79d
Merge branch 'main' into feat-48189
allroundexperts Nov 26, 2024
9672b4a
fix errors
allroundexperts Nov 27, 2024
5a8d697
fix lint errors
allroundexperts Nov 27, 2024
cc7ce8c
complete onboarding
allroundexperts Nov 27, 2024
3fa2478
Merge branch 'main' into feat-48189
allroundexperts Nov 27, 2024
e633f90
merge with main
allroundexperts Dec 2, 2024
b2859e0
Merge branch 'main' into feat-48189
allroundexperts Dec 5, 2024
dc13807
make the whole row not clickable
allroundexperts Dec 5, 2024
ebbbbdd
merge with main
allroundexperts Dec 9, 2024
1572b2e
merge with main
allroundexperts Dec 9, 2024
44b73c4
Merge branch 'main' into feat-48189
allroundexperts Dec 12, 2024
cf70577
handle comments
allroundexperts Dec 12, 2024
3c497ac
fix: lint checks
allroundexperts Dec 12, 2024
9bf5636
disable private flow
allroundexperts Dec 12, 2024
0dd74d0
remove unwanted files
allroundexperts Dec 13, 2024
c71dacd
Merge branch 'main' into feat-48189
marcaaron Dec 13, 2024
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 @@ -104,6 +104,12 @@ const ONYXKEYS = {
/** Store the information of magic code */
VALIDATE_ACTION_CODE: 'validate_action_code',

/** A list of policies that a user can join */
JOINABLE_POLICIES: 'joinablePolicies',

/** Flag to indicate if the joinablePolicies are loading */
JOINABLE_POLICIES_LOADING: 'joinablePoliciesLoading',

/** Information about the current session (authToken, accountID, email, loading, error) */
SESSION: 'session',
STASHED_SESSION: 'stashedSession',
Expand Down Expand Up @@ -916,6 +922,8 @@ type OnyxValuesMapping = {
[ONYXKEYS.LOGIN_LIST]: OnyxTypes.LoginList;
[ONYXKEYS.PENDING_CONTACT_ACTION]: OnyxTypes.PendingContactAction;
[ONYXKEYS.VALIDATE_ACTION_CODE]: OnyxTypes.ValidateMagicCodeAction;
[ONYXKEYS.JOINABLE_POLICIES]: OnyxTypes.JoinablePolicies;
[ONYXKEYS.JOINABLE_POLICIES_LOADING]: boolean;
[ONYXKEYS.SESSION]: OnyxTypes.Session;
[ONYXKEYS.USER_METADATA]: OnyxTypes.UserMetadata;
[ONYXKEYS.STASHED_SESSION]: OnyxTypes.Session;
Expand Down
8 changes: 8 additions & 0 deletions src/ROUTES.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1373,6 +1373,10 @@ const ROUTES = {
route: 'onboarding/personal-details',
getRoute: (backTo?: string) => getUrlWithBackToParam(`onboarding/personal-details`, backTo),
},
ONBOARDING_PRIVATE_DOMAIN: {
route: 'onboarding/private-domain',
getRoute: (backTo?: string) => getUrlWithBackToParam(`onboarding/private-domain`, backTo),
},
ONBOARDING_EMPLOYEES: {
route: 'onboarding/employees',
getRoute: (backTo?: string) => getUrlWithBackToParam(`onboarding/employees`, backTo),
Expand All @@ -1385,6 +1389,10 @@ const ROUTES = {
route: 'onboarding/purpose',
getRoute: (backTo?: string) => getUrlWithBackToParam(`onboarding/purpose`, backTo),
},
ONBOARDING_WORKSPACES: {
route: 'onboarding/join-workspaces',
getRoute: (backTo?: string) => getUrlWithBackToParam(`onboarding/join-workspaces`, backTo),
},
WELCOME_VIDEO_ROOT: 'onboarding/welcome-video',
EXPLANATION_MODAL_ROOT: 'onboarding/explanation',
WORKSPACE_CONFIRMATION: '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 @@ -582,8 +582,10 @@ const SCREENS = {
ONBOARDING: {
PERSONAL_DETAILS: 'Onboarding_Personal_Details',
PURPOSE: 'Onboarding_Purpose',
PRIVATE_DOMAIN: 'Onboarding_Private_Domain',
EMPLOYEES: 'Onboarding_Employees',
ACCOUNTING: 'Onboarding_Accounting',
WORKSPACES: 'Onboarding_Workspaces',
},

WELCOME_VIDEO: {
Expand Down
23 changes: 23 additions & 0 deletions src/components/OnboardingWrapper.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import React from 'react';
import {View} from 'react-native';
import useThemeStyles from '@hooks/useThemeStyles';
import FocusTrapForScreens from './FocusTrap/FocusTrapForScreen';

type OnboardingWrapperProps = {
/** Rendered child component */
children: React.ReactNode;
};

function OnboardingWrapper({children}: OnboardingWrapperProps) {
const styles = useThemeStyles();

return (
<FocusTrapForScreens>
<View style={styles.h100}>{children}</View>
</FocusTrapForScreens>
);
}

OnboardingWrapper.displayName = 'OnboardingWrapper';

export default OnboardingWrapper;
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,9 @@ type ValidateCodeFormProps = {
/** Function to clear error of the form */
clearError: () => void;

/** Whether to show the verify button (hidden in private domain onboarding) */
allroundexperts marked this conversation as resolved.
Show resolved Hide resolved
hideSubmitButton?: boolean;

/** Function is called when validate code modal is mounted and on magic code resend */
sendValidateCode: () => void;

Expand All @@ -82,6 +85,7 @@ function BaseValidateCodeForm({
clearError,
sendValidateCode,
buttonStyles,
hideSubmitButton,
isLoading,
}: ValidateCodeFormProps) {
const {translate} = useLocalize();
Expand Down Expand Up @@ -264,16 +268,18 @@ function BaseValidateCodeForm({
onClose={() => clearError()}
style={buttonStyles}
>
<Button
isDisabled={isOffline}
text={translate('common.verify')}
onPress={validateAndSubmitForm}
style={[styles.mt4]}
success
large
// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
isLoading={account?.isLoading || isLoading}
/>
{!hideSubmitButton && (
<Button
isDisabled={isOffline}
text={translate('common.verify')}
onPress={validateAndSubmitForm}
style={[styles.mt4]}
success
large
// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
isLoading={account?.isLoading || isLoading}
/>
)}
</OfflineWithFeedback>
</>
);
Expand Down
9 changes: 7 additions & 2 deletions src/hooks/useOnboardingFlow.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import {useEffect} from 'react';
import {NativeModules} from 'react-native';
import {useOnyx} from 'react-native-onyx';
import * as LoginUtils from '@libs/LoginUtils';
import Navigation from '@libs/Navigation/Navigation';
import {hasCompletedGuidedSetupFlowSelector, tryNewDotOnyxSelector} from '@libs/onboardingSelectors';
import Permissions from '@libs/Permissions';
Expand All @@ -25,6 +26,9 @@ function useOnboardingFlowRouter() {

const [dismissedProductTraining, dismissedProductTrainingMetadata] = useOnyx(ONYXKEYS.NVP_DISMISSED_PRODUCT_TRAINING);

const [session] = useOnyx(ONYXKEYS.SESSION);
const isPrivateDomain = !!session?.email && !LoginUtils.isEmailPublicDomain(session?.email);

const [isSingleNewDotEntry, isSingleNewDotEntryMetadata] = useOnyx(ONYXKEYS.IS_SINGLE_NEW_DOT_ENTRY);
const [allBetas, allBetasMetadata] = useOnyx(ONYXKEYS.BETAS);
useEffect(() => {
Expand Down Expand Up @@ -55,13 +59,13 @@ function useOnboardingFlowRouter() {
// But if the hybrid app onboarding is completed, but NewDot onboarding is not completed, we start NewDot onboarding flow
// This is a special case when user created an account from NewDot without finishing the onboarding flow and then logged in from OldDot
if (isHybridAppOnboardingCompleted === true && isOnboardingCompleted === false) {
OnboardingFlow.startOnboardingFlow();
OnboardingFlow.startOnboardingFlow(isPrivateDomain);
}
}

// If the user is not transitioning from OldDot to NewDot, we should start NewDot onboarding flow if it's not completed yet
if (!NativeModules.HybridAppModule && isOnboardingCompleted === false) {
OnboardingFlow.startOnboardingFlow();
OnboardingFlow.startOnboardingFlow(isPrivateDomain);
}
}, [
isOnboardingCompleted,
Expand All @@ -76,6 +80,7 @@ function useOnboardingFlowRouter() {
dismissedProductTraining,
allBetasMetadata,
allBetas,
isPrivateDomain,
]);

return {isOnboardingCompleted, isHybridAppOnboardingCompleted};
Expand Down
12 changes: 12 additions & 0 deletions src/languages/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -192,7 +192,9 @@ import type {
WelcomeNoteParams,
WelcomeToRoomParams,
WeSentYouMagicSignInLinkParams,
WorkspaceMemberList,
WorkspaceOwnerWillNeedToAddOrUpdatePaymentCardParams,
WorkspaceYouMayJoin,
YourPlanPriceParams,
ZipCodeExampleFormatParams,
} from './params';
Expand Down Expand Up @@ -477,6 +479,7 @@ const translations = {
links: 'Links',
days: 'days',
rename: 'Rename',
skip: 'Skip',
chatWithAccountManager: ({accountManagerDisplayName}: ChatWithAccountManagerParams) => `Need something specific? Chat with your account manager, ${accountManagerDisplayName}.`,
chatNow: 'Chat now',
},
Expand Down Expand Up @@ -1758,6 +1761,11 @@ const translations = {
},
getStarted: 'Get started',
whatsYourName: "What's your name?",
peopleYouMayKnow: 'People you may know are already here! Verify your email to join them.',
workspaceYouMayJoin: ({domain, email}: WorkspaceYouMayJoin) => `Someone from ${domain} has already created a workspace. Please enter the magic code sent to ${email}.`,
joinAWorkspace: 'Join a workspace',
listOfWorkspaces: "Here's the list of workspaces you can join. Don't worry, you can always join them later if you prefer.",
workspaceMemberList: ({employeeCount, policyOwner}: WorkspaceMemberList) => `${employeeCount} member${employeeCount > 1 ? 's' : ''} • ${policyOwner}`,
whereYouWork: 'Where do you work?',
errorSelection: 'Please make a selection to continue.',
purpose: {
Expand Down Expand Up @@ -2771,6 +2779,10 @@ const translations = {
noAccountsFound: 'No accounts found',
noAccountsFoundDescription: 'Add the account in QuickBooks Online and sync the connection again.',
},
workspaceList: {
joinNow: 'Join now',
askToJoin: 'Ask to join',
},
xero: {
organization: 'Xero organization',
organizationDescription: "Choose the Xero organization that you'd like to import data from.",
Expand Down
12 changes: 12 additions & 0 deletions src/languages/es.ts
Original file line number Diff line number Diff line change
Expand Up @@ -192,7 +192,9 @@ import type {
WelcomeNoteParams,
WelcomeToRoomParams,
WeSentYouMagicSignInLinkParams,
WorkspaceMemberList,
WorkspaceOwnerWillNeedToAddOrUpdatePaymentCardParams,
WorkspaceYouMayJoin,
YourPlanPriceParams,
ZipCodeExampleFormatParams,
} from './params';
Expand Down Expand Up @@ -348,6 +350,7 @@ const translations = {
semicolon: 'el punto y coma',
please: 'Por favor',
rename: 'Renombrar',
skip: 'Saltarse',
contactUs: 'contáctenos',
pleaseEnterEmailOrPhoneNumber: 'Por favor, escribe un email o número de teléfono',
fixTheErrors: 'corrige los errores',
Expand Down Expand Up @@ -1761,6 +1764,11 @@ const translations = {
},
getStarted: 'Comenzar',
whatsYourName: '¿Cómo te llamas?',
peopleYouMayKnow: 'Las personas que tal vez conozcas ya están aquí. Verifica tu correo electrónico para unirte a ellos.',
workspaceMemberList: ({employeeCount, policyOwner}: WorkspaceMemberList) => `${employeeCount} miembro${employeeCount > 1 ? 's' : ''} • ${policyOwner}`,
workspaceYouMayJoin: ({domain, email}: WorkspaceYouMayJoin) => `Alguien de ${domain} ya ha creado un espacio de trabajo. Por favor, introduce el código mágico enviado a ${email}.`,
joinAWorkspace: 'Unirse a un espacio de trabajo',
listOfWorkspaces: 'Aquí está la lista de espacios de trabajo a los que puedes unirte. No te preocupes, siempre puedes unirte a ellos más tarde si lo prefieres.',
whereYouWork: '¿Dónde trabajas?',
errorSelection: 'Por favor selecciona una opción para continuar.',
purpose: {
Expand Down Expand Up @@ -2802,6 +2810,10 @@ const translations = {
noAccountsFound: 'No se ha encontrado ninguna cuenta',
noAccountsFoundDescription: 'Añade la cuenta en QuickBooks Online y sincroniza de nuevo la conexión.',
},
workspaceList: {
joinNow: 'Únete ahora',
askToJoin: 'Pedir unirse',
},
xero: {
organization: 'Organización Xero',
organizationDescription: 'Seleccione la organización en Xero desde la que está importando los datos.',
Expand Down
12 changes: 12 additions & 0 deletions src/languages/params.ts
Original file line number Diff line number Diff line change
Expand Up @@ -543,6 +543,16 @@ type ImportedTypesParams = {
importedTypes: string[];
};

type WorkspaceYouMayJoin = {
domain: string;
email: string;
};

type WorkspaceMemberList = {
employeeCount: number;
policyOwner: string;
};

type FileLimitParams = {
fileLimit: number;
};
Expand Down Expand Up @@ -771,6 +781,8 @@ export type {
OptionalParam,
AssignCardParams,
ImportedTypesParams,
WorkspaceYouMayJoin,
WorkspaceMemberList,
ImportPerDiemRatesSuccessfullDescriptionParams,
CurrencyCodeParams,
CompanyNameParams,
Expand Down
5 changes: 5 additions & 0 deletions src/libs/API/parameters/JoinAccessiblePolicyParams.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
type JoinAccessiblePolicyParams = {
policyID: string;
};

export default JoinAccessiblePolicyParams;
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
type ValidateUserAndGetAccessiblePoliciesParams = {
validateCode: string;
};

export default ValidateUserAndGetAccessiblePoliciesParams;
2 changes: 2 additions & 0 deletions src/libs/API/parameters/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -346,10 +346,12 @@ export type {default as ConnectPolicyToQuickBooksDesktopParams} from './ConnectP
export type {default as UpdateInvoiceCompanyNameParams} from './UpdateInvoiceCompanyNameParams';
export type {default as UpdateInvoiceCompanyWebsiteParams} from './UpdateInvoiceCompanyWebsiteParams';
export type {default as UpdateQuickbooksDesktopExpensesExportDestinationTypeParams} from './UpdateQuickbooksDesktopExpensesExportDestinationTypeParams';
export type {default as ValidateUserAndGetAccessiblePoliciesParams} from './ValidateUserAndGetAccessiblePoliciesParams';
export type {default as UpdateQuickbooksDesktopCompanyCardExpenseAccountTypeParams} from './UpdateQuickbooksDesktopCompanyCardExpenseAccountTypeParams';
export type {default as TogglePolicyPerDiemParams} from './TogglePolicyPerDiemParams';
export type {default as OpenPolicyPerDiemRatesPageParams} from './OpenPolicyPerDiemRatesPageParams';
export type {default as TogglePlatformMuteParams} from './TogglePlatformMuteParams';
export type {default as JoinAccessiblePolicyParams} from './JoinAccessiblePolicyParams';
export type {default as ImportPerDiemRatesParams} from './ImportPerDiemRatesParams';
export type {default as ExportPerDiemCSVParams} from './ExportPerDiemCSVParams';
export type {default as DismissProductTrainingParams} from './DismissProductTraining';
4 changes: 4 additions & 0 deletions src/libs/API/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,7 @@ const WRITE_COMMANDS = {
SET_POLICY_TAXES_FOREIGN_CURRENCY_DEFAULT: 'SetPolicyForeignCurrencyDefaultTax',
SET_POLICY_CUSTOM_TAX_NAME: 'SetPolicyCustomTaxName',
JOIN_POLICY_VIA_INVITE_LINK: 'JoinWorkspaceViaInviteLink',
JOIN_ACCESSIBLE_POLICY: 'JoinAccessiblePolicy',
ACCEPT_JOIN_REQUEST: 'AcceptJoinRequest',
DECLINE_JOIN_REQUEST: 'DeclineJoinRequest',
CREATE_POLICY_TAX: 'CreatePolicyTax',
Expand Down Expand Up @@ -438,6 +439,7 @@ const WRITE_COMMANDS = {
SELF_TOUR_VIEWED: 'SelfTourViewed',
UPDATE_INVOICE_COMPANY_NAME: 'UpdateInvoiceCompanyName',
UPDATE_INVOICE_COMPANY_WEBSITE: 'UpdateInvoiceCompanyWebsite',
VALIDATE_USER_AND_GET_ACCESSIBLE_POLICIES: 'ValidateUserAndGetAccessiblePolicies',
DISMISS_PRODUCT_TRAINING: 'DismissProductTraining',
} as const;

Expand Down Expand Up @@ -676,6 +678,7 @@ type WriteCommandParameters = {
[WRITE_COMMANDS.SEARCH]: Parameters.SearchParams;
[WRITE_COMMANDS.SET_POLICY_CATEGORY_TAX]: Parameters.SetPolicyCategoryTaxParams;
[WRITE_COMMANDS.JOIN_POLICY_VIA_INVITE_LINK]: Parameters.JoinPolicyInviteLinkParams;
[WRITE_COMMANDS.VALIDATE_USER_AND_GET_ACCESSIBLE_POLICIES]: Parameters.ValidateUserAndGetAccessiblePoliciesParams;
[WRITE_COMMANDS.ACCEPT_JOIN_REQUEST]: Parameters.AcceptJoinRequestParams;
[WRITE_COMMANDS.DECLINE_JOIN_REQUEST]: Parameters.DeclineJoinRequestParams;
[WRITE_COMMANDS.SET_POLICY_TAXES_CURRENCY_DEFAULT]: Parameters.SetPolicyCurrencyDefaultParams;
Expand Down Expand Up @@ -889,6 +892,7 @@ type WriteCommandParameters = {
[WRITE_COMMANDS.UPDATE_INVOICE_COMPANY_NAME]: Parameters.UpdateInvoiceCompanyNameParams;
[WRITE_COMMANDS.UPDATE_INVOICE_COMPANY_WEBSITE]: Parameters.UpdateInvoiceCompanyWebsiteParams;

[WRITE_COMMANDS.JOIN_ACCESSIBLE_POLICY]: Parameters.JoinAccessiblePolicyParams;
// Dismis Product Training
[WRITE_COMMANDS.DISMISS_PRODUCT_TRAINING]: Parameters.DismissProductTrainingParams;
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,9 @@ import OnboardingRefManager from '@libs/OnboardingRefManager';
import OnboardingAccounting from '@pages/OnboardingAccounting';
import OnboardingEmployees from '@pages/OnboardingEmployees';
import OnboardingPersonalDetails from '@pages/OnboardingPersonalDetails';
import OnboardingPrivateDomain from '@pages/OnboardingPrivateDomain';
import OnboardingPurpose from '@pages/OnboardingPurpose';
import OnboardingWorkspaces from '@pages/OnboardingWorkspaces';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
import SCREENS from '@src/SCREENS';
Expand Down Expand Up @@ -74,6 +76,14 @@ function OnboardingModalNavigator() {
name={SCREENS.ONBOARDING.PERSONAL_DETAILS}
component={OnboardingPersonalDetails}
/>
<Stack.Screen
name={SCREENS.ONBOARDING.PRIVATE_DOMAIN}
component={OnboardingPrivateDomain}
/>
<Stack.Screen
name={SCREENS.ONBOARDING.WORKSPACES}
component={OnboardingWorkspaces}
/>
<Stack.Screen
name={SCREENS.ONBOARDING.EMPLOYEES}
component={OnboardingEmployees}
Expand Down
5 changes: 4 additions & 1 deletion src/libs/Navigation/NavigationRoot.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import useThemePreference from '@hooks/useThemePreference';
import Firebase from '@libs/Firebase';
import {FSPage} from '@libs/Fullstory';
import Log from '@libs/Log';
import * as LoginUtils from '@libs/LoginUtils';
import {hasCompletedGuidedSetupFlowSelector} from '@libs/onboardingSelectors';
import {getPathFromURL} from '@libs/Url';
import {updateLastVisitedPath} from '@userActions/App';
Expand Down Expand Up @@ -92,6 +93,8 @@ function NavigationRoot({authenticated, lastVisitedPath, initialUrl, onReady, sh
const {shouldUseNarrowLayout} = useResponsiveLayout();
const {setActiveWorkspaceID} = useActiveWorkspace();
const [user] = useOnyx(ONYXKEYS.USER);
const [session] = useOnyx(ONYXKEYS.SESSION);
const isPrivateDomain = !!session?.email && !LoginUtils.isEmailPublicDomain(session?.email);

const [isOnboardingCompleted = true] = useOnyx(ONYXKEYS.NVP_ONBOARDING, {
selector: hasCompletedGuidedSetupFlowSelector,
Expand All @@ -105,7 +108,7 @@ function NavigationRoot({authenticated, lastVisitedPath, initialUrl, onReady, sh
// If the user haven't completed the flow, we want to always redirect them to the onboarding flow.
// We also make sure that the user is authenticated.
if (!NativeModules.HybridAppModule && !isOnboardingCompleted && authenticated && !shouldShowRequire2FAModal) {
const {adaptedState} = getAdaptedStateFromPath(getOnboardingInitialPath(), linkingConfig.config);
const {adaptedState} = getAdaptedStateFromPath(getOnboardingInitialPath(isPrivateDomain), linkingConfig.config);
return adaptedState;
}

Expand Down
Loading
Loading