Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
4 changes: 4 additions & 0 deletions src/ONYXKEYS.ts
Original file line number Diff line number Diff line change
Expand Up @@ -516,6 +516,9 @@ const ONYXKEYS = {
/** Onboarding user reported integration selected by the user during Onboarding flow */
ONBOARDING_USER_REPORTED_INTEGRATION: 'onboardingUserReportedIntegration',

/** Onboarding user reported integration name entered when Other is selected */
ONBOARDING_USER_REPORTED_INTEGRATION_NAME: 'onboardingUserReportedIntegrationName',

/** Onboarding Purpose selected by the user during Onboarding flow */
ONBOARDING_ADMINS_CHAT_REPORT_ID: 'onboardingAdminsChatReportID',

Expand Down Expand Up @@ -1714,6 +1717,7 @@ type OnyxValuesMapping = {
[ONYXKEYS.TRANSACTION_THREAD_NAVIGATION_THREAD_REPORT_IDS]: Record<string, TransactionThreadNavigationDescriptor>;
[ONYXKEYS.NVP_INTEGRATION_SERVER_EXPORT_TEMPLATES]: OnyxTypes.ExportTemplate[];
[ONYXKEYS.ONBOARDING_USER_REPORTED_INTEGRATION]: OnboardingAccounting;
[ONYXKEYS.ONBOARDING_USER_REPORTED_INTEGRATION_NAME]: string;
[ONYXKEYS.HYBRID_APP]: OnyxTypes.HybridApp;
[ONYXKEYS.NVP_CSV_EXPORT_LAYOUTS]: Record<string, OnyxTypes.ExportTemplate>;
[ONYXKEYS.NVP_EXPENSE_RULES]: OnyxTypes.ExpenseRule[];
Expand Down
1 change: 1 addition & 0 deletions src/languages/de.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3148,6 +3148,7 @@ ${amount} für ${merchant} – ${date}`,
accounting: {
title: 'Verwendest du eine Buchhaltungssoftware?',
none: 'Keine',
otherAccountingSoftware: 'Deine Buchhaltungssoftware',
},
interestedFeatures: {
title: 'An welchen Funktionen bist du interessiert?',
Expand Down
1 change: 1 addition & 0 deletions src/languages/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3231,6 +3231,7 @@ const translations = {
accounting: {
title: 'Do you use any accounting software?',
none: 'None',
otherAccountingSoftware: 'Your accounting software',
},
interestedFeatures: {
title: 'What features are you interested in?',
Expand Down
1 change: 1 addition & 0 deletions src/languages/es.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3010,6 +3010,7 @@ ${amount} para ${merchant} - ${date}`,
accounting: {
title: '¿Utilizas algún software de contabilidad?',
none: 'Ninguno',
otherAccountingSoftware: 'Tu software de contabilidad',
},
interestedFeatures: {
title: '¿Qué funciones te interesan?',
Expand Down
1 change: 1 addition & 0 deletions src/languages/fr.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3155,6 +3155,7 @@ ${amount} pour ${merchant} - ${date}`,
accounting: {
title: 'Utilisez-vous un logiciel de comptabilité ?',
none: 'Aucun',
otherAccountingSoftware: 'Votre logiciel de comptabilité',
},
interestedFeatures: {
title: 'Quelles fonctionnalités vous intéressent ?',
Expand Down
1 change: 1 addition & 0 deletions src/languages/it.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3140,6 +3140,7 @@ ${amount} per ${merchant} - ${date}`,
accounting: {
title: 'Usi un software di contabilità?',
none: 'Nessuno',
otherAccountingSoftware: 'Il tuo software di contabilità',
},
interestedFeatures: {
title: 'A quali funzionalità sei interessato?',
Expand Down
1 change: 1 addition & 0 deletions src/languages/ja.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3109,6 +3109,7 @@ ${date} の ${merchant} への ${amount}`,
accounting: {
title: '会計ソフトを利用していますか?',
none: 'なし',
otherAccountingSoftware: 'ご利用の会計ソフト',
},
interestedFeatures: {
title: 'どの機能にご興味がありますか?',
Expand Down
1 change: 1 addition & 0 deletions src/languages/nl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3138,6 +3138,7 @@ ${amount} voor ${merchant} - ${date}`,
accounting: {
title: 'Gebruikt u een boekhoudprogramma?',
none: 'Geen',
otherAccountingSoftware: 'Je boekhoudsoftware',
},
interestedFeatures: {
title: 'In welke functies ben je geïnteresseerd?',
Expand Down
1 change: 1 addition & 0 deletions src/languages/pl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3124,6 +3124,7 @@ ${amount} dla ${merchant} - ${date}`,
accounting: {
title: 'Czy korzystasz z jakiegoś oprogramowania księgowego?',
none: 'Brak',
otherAccountingSoftware: 'Twoje oprogramowanie księgowe',
},
interestedFeatures: {
title: 'Jakie funkcje Cię interesują?',
Expand Down
1 change: 1 addition & 0 deletions src/languages/pt-BR.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3127,6 +3127,7 @@ ${amount} para ${merchant} - ${date}`,
accounting: {
title: 'Você usa algum software de contabilidade?',
none: 'Nenhum',
otherAccountingSoftware: 'Seu software de contabilidade',
},
interestedFeatures: {
title: 'Em quais recursos você tem interesse?',
Expand Down
1 change: 1 addition & 0 deletions src/languages/zh-hans.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3039,6 +3039,7 @@ ${amount},商户:${merchant} - 日期:${date}`,
accounting: {
title: '你是否使用任何会计软件?',
none: '无',
otherAccountingSoftware: '你的会计软件',
},
interestedFeatures: {
title: '你对哪些功能感兴趣?',
Expand Down
1 change: 1 addition & 0 deletions src/libs/API/parameters/CompleteGuidedSetupParams.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ type CompleteGuidedSetupParams = {
paymentSelected?: string;
companySize?: OnboardingCompanySize;
userReportedIntegration?: OnboardingAccounting;
userReportedIntegrationName?: string;
policyID?: string;
selfDMReportID?: string;
selfDMCreatedReportActionID?: string;
Expand Down
1 change: 1 addition & 0 deletions src/libs/API/parameters/CreateWorkspaceParams.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ type CreateWorkspaceParams = {
file?: File;
companySize?: string;
userReportedIntegration?: string;
userReportedIntegrationName?: string;
memberData?: string;
features?: string;
shouldAddGuideWelcomeMessage?: boolean;
Expand Down
1 change: 1 addition & 0 deletions src/libs/ExportOnyxState/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -405,6 +405,7 @@ const onyxKeysToMaskFragileData = new Set<string>([
// Same as LAST_VISITED_PATH above, just limited to onboarding routes
ONYXKEYS.ONBOARDING_LAST_VISITED_PATH,
ONYXKEYS.ONBOARDING_PERSONAL_TRACK_GOAL,
ONYXKEYS.ONBOARDING_USER_REPORTED_INTEGRATION_NAME,
ONYXKEYS.ONYX_UPDATES_FROM_SERVER,
ONYXKEYS.ONYX_UPDATES_LAST_UPDATE_ID_APPLIED_TO_CLIENT,
ONYXKEYS.PENDING_CONTACT_ACTION,
Expand Down
3 changes: 3 additions & 0 deletions src/libs/actions/Policy/Policy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,7 @@ type BuildPolicyDataOptions = {
shouldAddOnboardingTasks?: boolean;
companySize?: OnboardingCompanySize;
userReportedIntegration?: OnboardingAccounting;
userReportedIntegrationName?: string;
isAnnualSubscription?: boolean;
featuresMap?: Array<Pick<Feature, 'id' | 'enabled' | 'enabledByDefault' | 'requiresUpdate'>>;
lastUsedPaymentMethod?: LastPaymentMethodType;
Expand Down Expand Up @@ -2617,6 +2618,7 @@ function buildPolicyData(options: BuildPolicyDataOptions): OnyxData<BuildPolicyD
shouldAddOnboardingTasks = true,
companySize,
userReportedIntegration,
userReportedIntegrationName,
isAnnualSubscription = false,
featuresMap,
lastUsedPaymentMethod,
Expand Down Expand Up @@ -3085,6 +3087,7 @@ function buildPolicyData(options: BuildPolicyDataOptions): OnyxData<BuildPolicyD
file: clonedFile,
companySize,
userReportedIntegration: userReportedIntegration ?? undefined,
userReportedIntegrationName,
features: features ? JSON.stringify(features) : undefined,
shouldAddGuideWelcomeMessage,
areDistanceRatesEnabled,
Expand Down
3 changes: 3 additions & 0 deletions src/libs/actions/Report/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5510,6 +5510,7 @@ type CompleteOnboardingProps = {
paymentSelected?: string;
companySize?: OnboardingCompanySize;
userReportedIntegration?: OnboardingAccounting;
userReportedIntegrationName?: string;
wasInvited?: boolean;
selectedInterestedFeatures?: string[];
isInvitedAccountant?: boolean;
Expand All @@ -5536,6 +5537,7 @@ async function completeOnboarding({
paymentSelected,
companySize,
userReportedIntegration,
userReportedIntegrationName,
wasInvited,
selectedInterestedFeatures,
isInvitedAccountant,
Expand Down Expand Up @@ -5579,6 +5581,7 @@ async function completeOnboarding({
paymentSelected,
companySize,
userReportedIntegration,
userReportedIntegrationName,
policyID: onboardingPolicyID,
selfDMReportID: selfDMParameters.reportID,
selfDMCreatedReportActionID: selfDMParameters.createdReportActionID,
Expand Down
5 changes: 5 additions & 0 deletions src/libs/actions/Welcome/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,10 @@ function setOnboardingUserReportedIntegration(value: OnboardingAccounting | null
Onyx.set(ONYXKEYS.ONBOARDING_USER_REPORTED_INTEGRATION, value);
}

function setOnboardingUserReportedIntegrationName(value?: string) {
Onyx.set(ONYXKEYS.ONBOARDING_USER_REPORTED_INTEGRATION_NAME, value ?? null);
}

function setOnboardingPersonalTrackGoal(value: string) {
Onyx.set(ONYXKEYS.ONBOARDING_PERSONAL_TRACK_GOAL, value);
}
Expand Down Expand Up @@ -222,6 +226,7 @@ export {
setOnboardingMergeAccountStepValue,
updateOnboardingValuesAndNavigation,
setOnboardingUserReportedIntegration,
setOnboardingUserReportedIntegrationName,
setOnboardingPersonalTrackGoal,
addWorkEmailFormError,
clearWorkEmailFormErrors,
Expand Down
35 changes: 31 additions & 4 deletions src/pages/OnboardingAccounting/BaseOnboardingAccounting.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import ScreenWrapper from '@components/ScreenWrapper';
import ScrollView from '@components/ScrollView';
import type {ListItem} from '@components/SelectionList/types';
import Text from '@components/Text';
import TextInput from '@components/TextInput';

import {useMemoizedLazyExpensifyIcons} from '@hooks/useLazyAsset';
import useLocalize from '@hooks/useLocalize';
Expand All @@ -19,7 +20,7 @@ import useStyleUtils from '@hooks/useStyleUtils';
import useTheme from '@hooks/useTheme';
import useThemeStyles from '@hooks/useThemeStyles';

import {setOnboardingAdminsChatReportID, setOnboardingPolicyID, setOnboardingUserReportedIntegration} from '@libs/actions/Welcome';
import {setOnboardingAdminsChatReportID, setOnboardingPolicyID, setOnboardingUserReportedIntegration, setOnboardingUserReportedIntegrationName} from '@libs/actions/Welcome';
import Navigation from '@libs/Navigation/Navigation';
import {isPaidGroupPolicy, isPolicyAdmin} from '@libs/PolicyUtils';

Expand Down Expand Up @@ -115,10 +116,13 @@ function BaseOnboardingAccounting({shouldUseNativeStyles}: BaseOnboardingAccount
const [allPolicies] = useOnyx(ONYXKEYS.COLLECTION.POLICY);
const [session] = useOnyx(ONYXKEYS.SESSION);
const [onboardingUserReportedIntegration] = useOnyx(ONYXKEYS.ONBOARDING_USER_REPORTED_INTEGRATION);
const [onboardingUserReportedIntegrationName] = useOnyx(ONYXKEYS.ONBOARDING_USER_REPORTED_INTEGRATION_NAME);
const onboardingStep = useOnboardingStepCounter(SCREENS.ONBOARDING.ACCOUNTING);

const [userReportedIntegration, setUserReportedIntegration] = useState<OnboardingAccounting | undefined>(onboardingUserReportedIntegration ?? undefined);
const [userReportedIntegrationName, setUserReportedIntegrationName] = useState(onboardingUserReportedIntegrationName ?? '');
const [error, setError] = useState('');
const isOtherSelected = userReportedIntegration === 'other';

const paidGroupPolicy = Object.values(allPolicies ?? {}).find((policy) => isPaidGroupPolicy(policy) && isPolicyAdmin(policy, session?.email));
// Set onboardingPolicyID and onboardingAdminsChatReportID if a workspace is created by the backend for OD signup
Expand Down Expand Up @@ -174,7 +178,7 @@ function BaseOnboardingAccounting({shouldUseNativeStyles}: BaseOnboardingAccount
additionalStyles={[StyleUtils.getAvatarBorderStyle(CONST.AVATAR_SIZE.DEFAULT, CONST.ICON_TYPE_AVATAR), styles.mr3, styles.onboardingSmallIcon]}
/>
),
isSelected: userReportedIntegration === 'other',
isSelected: isOtherSelected,
};

const accountingOptions: OnboardingListItem[] = [...integrations.map(createAccountingOption), othersAccountingOption, noneAccountingOption];
Expand All @@ -186,13 +190,18 @@ function BaseOnboardingAccounting({shouldUseNativeStyles}: BaseOnboardingAccount
}

setOnboardingUserReportedIntegration(userReportedIntegration);
const trimmedIntegrationName = userReportedIntegrationName.trim();
setOnboardingUserReportedIntegrationName(userReportedIntegration === 'other' && trimmedIntegrationName ? trimmedIntegrationName : undefined);

// Navigate to the next onboarding step interested features with the selected integration
Navigation.navigate(ROUTES.ONBOARDING_INTERESTED_FEATURES.getRoute());
};

const handleIntegrationSelect = (integrationKey: OnboardingListItem['keyForList']) => {
setUserReportedIntegration(integrationKey === 'none' ? null : integrationKey);
if (integrationKey !== 'other') {
setUserReportedIntegrationName('');
}
setError('');
};

Expand Down Expand Up @@ -246,8 +255,26 @@ function BaseOnboardingAccounting({shouldUseNativeStyles}: BaseOnboardingAccount
{translate('onboarding.accounting.title')}
</Text>
</View>
<ScrollView style={[onboardingIsMediumOrLargerScreenWidth ? styles.mh8 : styles.mh5, styles.pt3, styles.pb8]}>
<View style={[styles.flexRow, styles.flexWrap, styles.gap3, styles.mb3]}>{accountingOptions.map(renderOption)}</View>
<ScrollView
style={[styles.flex1, styles.flexGrow1, onboardingIsMediumOrLargerScreenWidth ? styles.mh8 : styles.mh5]}
contentContainerStyle={[styles.pt0, styles.pb2]}
keyboardShouldPersistTaps="handled"
showsVerticalScrollIndicator={false}
>
<View style={[styles.flexRow, styles.flexWrap, styles.gap3]}>{accountingOptions.map(renderOption)}</View>
{isOtherSelected && (
<View style={styles.mt2}>
<TextInput
accessibilityLabel={translate('onboarding.accounting.otherAccountingSoftware')}
label={translate('onboarding.accounting.otherAccountingSoftware')}
value={userReportedIntegrationName}
onChangeText={(text) => {
setUserReportedIntegrationName(text);
setError('');
}}
/>
</View>
)}
</ScrollView>
<FixedFooter style={[styles.pt3, styles.ph5]}>
{!!error && (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ function BaseOnboardingInterestedFeatures({shouldUseNativeStyles}: BaseOnboardin
const [onboardingAdminsChatReportID] = useOnyx(ONYXKEYS.ONBOARDING_ADMINS_CHAT_REPORT_ID);
const [onboardingCompanySize] = useOnyx(ONYXKEYS.ONBOARDING_COMPANY_SIZE);
const [userReportedIntegration] = useOnyx(ONYXKEYS.ONBOARDING_USER_REPORTED_INTEGRATION);
const [userReportedIntegrationName] = useOnyx(ONYXKEYS.ONBOARDING_USER_REPORTED_INTEGRATION_NAME);
const [introSelected] = useOnyx(ONYXKEYS.NVP_INTRO_SELECTED);
const [betas] = useOnyx(ONYXKEYS.BETAS);
const [isSelfTourViewed] = useOnyx(ONYXKEYS.NVP_ONBOARDING, {selector: hasSeenTourSelector});
Expand Down Expand Up @@ -192,6 +193,7 @@ function BaseOnboardingInterestedFeatures({shouldUseNativeStyles}: BaseOnboardin

const shouldCreateWorkspace = !onboardingPolicyID && !paidGroupPolicy;
const newUserReportedIntegration = selectedFeatures.some((feature) => feature === CONST.POLICY.MORE_FEATURES.ARE_CONNECTIONS_ENABLED) ? userReportedIntegration : undefined;
const newUserReportedIntegrationName = newUserReportedIntegration === 'other' && userReportedIntegrationName?.trim() ? userReportedIntegrationName.trim() : undefined;
const featuresMap = features.map((feature) => ({
...feature,
enabled: selectedFeatures.includes(feature.id),
Expand All @@ -212,6 +214,7 @@ function BaseOnboardingInterestedFeatures({shouldUseNativeStyles}: BaseOnboardin
shouldAddOnboardingTasks: false,
companySize: onboardingCompanySize,
userReportedIntegration: newUserReportedIntegration,
userReportedIntegrationName: newUserReportedIntegrationName,
featuresMap,
introSelected,
activePolicy,
Expand All @@ -236,6 +239,7 @@ function BaseOnboardingInterestedFeatures({shouldUseNativeStyles}: BaseOnboardin
onboardingPolicyID: policyID,
companySize: onboardingCompanySize,
userReportedIntegration: newUserReportedIntegration,
userReportedIntegrationName: newUserReportedIntegrationName,
firstName: currentUserPersonalDetails?.firstName,
lastName: currentUserPersonalDetails?.lastName,
selectedInterestedFeatures: featuresMap.filter((feature) => feature.enabled).map((feature) => feature.id),
Expand Down Expand Up @@ -286,6 +290,7 @@ function BaseOnboardingInterestedFeatures({shouldUseNativeStyles}: BaseOnboardin
paidGroupPolicy,
session?.email,
userReportedIntegration,
userReportedIntegrationName,
features,
selectedFeatures,
currentUserPersonalDetails?.firstName,
Expand Down
61 changes: 61 additions & 0 deletions tests/actions/PolicyTest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,67 @@ describe('actions/Policy', () => {
mockFetch?.resume?.();
});

it('forwards the user-reported integration name to CreateWorkspace', async () => {
const apiWriteSpy = jest.spyOn(require('@libs/API'), 'write').mockImplementation(() => Promise.resolve());

Policy.createWorkspace({
policyOwnerEmail: ESH_EMAIL,
makeMeAdmin: true,
policyName: WORKSPACE_NAME,
policyID: Policy.generatePolicyID(),
engagementChoice: CONST.ONBOARDING_CHOICES.MANAGE_TEAM,
introSelected: {choice: CONST.ONBOARDING_CHOICES.MANAGE_TEAM},
currentUserAccountIDParam: ESH_ACCOUNT_ID,
currentUserEmailParam: ESH_EMAIL,
currency: CONST.CURRENCY.USD,
userReportedIntegration: 'other',
userReportedIntegrationName: 'Acme Books',
isSelfTourViewed: false,
betas: undefined,
hasActiveAdminPolicies: false,
activePolicy: undefined,
});
await waitForBatchedUpdates();

expect(apiWriteSpy).toHaveBeenCalledWith(
WRITE_COMMANDS.CREATE_WORKSPACE,
expect.objectContaining({
userReportedIntegration: 'other',
userReportedIntegrationName: 'Acme Books',
}),
expect.anything(),
);

apiWriteSpy.mockRestore();
});

it('omits the user-reported integration name from CreateWorkspace when it is undefined', async () => {
Policy.createWorkspace({
policyOwnerEmail: ESH_EMAIL,
makeMeAdmin: true,
policyName: WORKSPACE_NAME,
policyID: Policy.generatePolicyID(),
engagementChoice: CONST.ONBOARDING_CHOICES.MANAGE_TEAM,
introSelected: {choice: CONST.ONBOARDING_CHOICES.MANAGE_TEAM},
currentUserAccountIDParam: ESH_ACCOUNT_ID,
currentUserEmailParam: ESH_EMAIL,
currency: CONST.CURRENCY.USD,
userReportedIntegration: 'other',
isSelfTourViewed: false,
betas: undefined,
hasActiveAdminPolicies: false,
activePolicy: undefined,
});
await waitForBatchedUpdates();

const calls = TestHelper.getFetchMockCalls(WRITE_COMMANDS.CREATE_WORKSPACE);
expect(calls.length).toBeGreaterThan(0);
const body = calls.at(-1)?.[1]?.body;
expect(body).toBeInstanceOf(FormData);
const formEntries = body instanceof FormData ? Object.fromEntries(body) : {};
expect(formEntries.userReportedIntegrationName).toBeUndefined();
});

it('creates a new workspace', async () => {
(fetch as MockFetch)?.pause?.();
await Onyx.set(ONYXKEYS.SESSION, {email: ESH_EMAIL, accountID: ESH_ACCOUNT_ID});
Expand Down
Loading
Loading