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

Add workspace upgrade flow #43822

Merged
merged 20 commits into from
Jul 5, 2024
Merged
Show file tree
Hide file tree
Changes from 3 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
8 changes: 8 additions & 0 deletions src/CONST.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import dateSubtract from 'date-fns/sub';
import Config from 'react-native-config';
import * as KeyCommand from 'react-native-key-command';
import type {ValueOf} from 'type-fest';
import * as Illustrations from './components/Icon/Illustrations';
import BankAccount from './libs/models/BankAccount';
import * as Url from './libs/Url';
import SCREENS from './SCREENS';
Expand Down Expand Up @@ -4858,6 +4859,13 @@ const CONST = {
},

EXCLUDE_FROM_LAST_VISITED_PATH: [SCREENS.NOT_FOUND, SCREENS.SAML_SIGN_IN, SCREENS.VALIDATE_LOGIN] as string[],
UPGRADE_FEATURE_INTRO_MAPPING: {
reportFields: {
title: 'workspace.upgrade.reportFields.title',
description: 'workspace.upgrade.reportFields.description',
icon: Illustrations.Tag,
},
},
} as const;

type Country = keyof typeof CONST.ALL_COUNTRIES;
Expand Down
4 changes: 4 additions & 0 deletions src/ROUTES.ts
Original file line number Diff line number Diff line change
Expand Up @@ -675,6 +675,10 @@ const ROUTES = {
route: 'settings/workspaces/:policyID/categories/:categoryName',
getRoute: (policyID: string, categoryName: string) => `settings/workspaces/${policyID}/categories/${encodeURIComponent(categoryName)}` as const,
},
WORKSPACE_UPGRADE: {
route: 'settings/workspaces/:policyID/upgrade/:featureName',
getRoute: (policyID: string, featureName: string) => `settings/workspaces/${policyID}/upgrade/${encodeURIComponent(featureName)}` as const,
},
WORKSPACE_CATEGORIES_SETTINGS: {
route: 'settings/workspaces/:policyID/categories/settings',
getRoute: (policyID: string) => `settings/workspaces/${policyID}/categories/settings` as const,
Expand Down
1 change: 1 addition & 0 deletions src/SCREENS.ts
Original file line number Diff line number Diff line change
Expand Up @@ -320,6 +320,7 @@ const SCREENS = {
DISTANCE_RATE_EDIT: 'Distance_Rate_Edit',
DISTANCE_RATE_TAX_RECLAIMABLE_ON_EDIT: 'Distance_Rate_Tax_Reclaimable_On_Edit',
DISTANCE_RATE_TAX_RATE_EDIT: 'Distance_Rate_Tax_Rate_Edit',
UPGRADE: 'Workspace_Upgrade',
},

EDIT_REQUEST: {
Expand Down
2 changes: 1 addition & 1 deletion src/components/ConfirmationPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ type ConfirmationPageProps = {
heading: string;

/** Description of the confirmation page */
description: string;
description: React.ReactNode;

/** The text for the button label */
buttonText?: string;
Expand Down
20 changes: 20 additions & 0 deletions src/languages/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -340,6 +340,7 @@ export default {
shared: 'Shared',
drafts: 'Drafts',
finished: 'Finished',
upgrade: 'Upgrade',
},
location: {
useCurrent: 'Use current location',
Expand Down Expand Up @@ -2682,6 +2683,25 @@ export default {
errorDescriptionPartTwo: 'reach out to Concierge',
errorDescriptionPartThree: 'for help.',
},
upgrade: {
reportFields: {
title: 'Report fields',
description: 'Set up custom fields for spend.',
},
note: {
upgradeWorkspace: 'Upgrade your workspace to access this feature, or',
learnMore: 'learn more',
aboutOurPlans: 'about our plans and pricing.',
},
upgradeToUnlock: 'Unlock this feature',
completed: {
headline: 'Upgrade complete!',
successMessage: (policyName: string) => `You've successfully upgraded your ${policyName} workspace to the Control plan!`,
viewSubscription: 'View your subscription',
moreDetails: 'for more details.',
gotIt: 'Got it, thanks',
},
},
},
getAssistancePage: {
title: 'Get assistance',
Expand Down
20 changes: 20 additions & 0 deletions src/languages/es.ts
Original file line number Diff line number Diff line change
Expand Up @@ -331,6 +331,7 @@ export default {
shared: 'Compartidos',
drafts: 'Borradores',
finished: 'Finalizados',
upgrade: 'Mejora',
},
connectionComplete: {
title: 'Conexión Completa',
Expand Down Expand Up @@ -2720,6 +2721,25 @@ export default {
errorDescriptionPartTwo: 'contacta con el conserje',
errorDescriptionPartThree: 'por ayuda.',
},
upgrade: {
reportFields: {
title: 'Report fields',
description: 'Set up custom fields for spend.',
},
note: {
upgradeWorkspace: 'Upgrade your workspace to access this feature, or',
learnMore: 'learn more',
aboutOurPlans: 'about our plans and pricing.',
},
upgradeToUnlock: 'Unlock this feature',
completed: {
headline: 'Upgrade complete!',
successMessage: (policyName: string) => `You've successfully upgraded your ${policyName} workspace to the Control plan!`,
viewSubscription: 'View your subscription',
moreDetails: 'for more details.',
gotIt: 'Got it, thanks',
},
},
allroundexperts marked this conversation as resolved.
Show resolved Hide resolved
},
getAssistancePage: {
title: 'Obtener ayuda',
Expand Down
6 changes: 6 additions & 0 deletions src/libs/API/parameters/UpgradeToCorporateParams.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
type UpgradeToCorporateParams = {
policyID: string;
featureName: string;
};

export default UpgradeToCorporateParams;
1 change: 1 addition & 0 deletions src/libs/API/parameters/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -228,3 +228,4 @@ export type {default as MarkAsCashParams} from './MarkAsCashParams';
export type {default as UpdateSubscriptionTypeParams} from './UpdateSubscriptionTypeParams';
export type {default as SignUpUserParams} from './SignUpUserParams';
export type {default as UpdateSubscriptionSizeParams} from './UpdateSubscriptionSizeParams';
export type {default as UpgradeToCorporateParams} from './UpgradeToCorporateParams';
3 changes: 3 additions & 0 deletions src/libs/API/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,7 @@ const WRITE_COMMANDS = {
UPDATE_SUBSCRIPTION_TYPE: 'UpdateSubscriptionType',
SIGN_UP_USER: 'SignUpUser',
UPDATE_SUBSCRIPTION_SIZE: 'UpdateSubscriptionSize',
UPGRADE_TO_CORPORATE: 'UpgradeToCorporate',
} as const;

type WriteCommand = ValueOf<typeof WRITE_COMMANDS>;
Expand Down Expand Up @@ -450,6 +451,8 @@ type WriteCommandParameters = {
[WRITE_COMMANDS.UPDATE_SUBSCRIPTION_TYPE]: Parameters.UpdateSubscriptionTypeParams;
[WRITE_COMMANDS.SIGN_UP_USER]: Parameters.SignUpUserParams;
[WRITE_COMMANDS.UPDATE_SUBSCRIPTION_SIZE]: Parameters.UpdateSubscriptionSizeParams;

[WRITE_COMMANDS.UPGRADE_TO_CORPORATE]: Parameters.UpgradeToCorporateParams;
};

const READ_COMMANDS = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,7 @@ const SettingsModalStackNavigator = createModalStackNavigator<SettingsNavigatorP
[SCREENS.WORKSPACE.SHARE]: () => require('../../../../pages/workspace/WorkspaceProfileSharePage').default as React.ComponentType,
[SCREENS.WORKSPACE.CURRENCY]: () => require('../../../../pages/workspace/WorkspaceProfileCurrencyPage').default as React.ComponentType,
[SCREENS.WORKSPACE.CATEGORY_SETTINGS]: () => require('../../../../pages/workspace/categories/CategorySettingsPage').default as React.ComponentType,
[SCREENS.WORKSPACE.UPGRADE]: () => require('../../../../pages/workspace/upgrade/WorkspaceUpgradePage').default as React.ComponentType,
[SCREENS.WORKSPACE.ADDRESS]: () => require('../../../../pages/workspace/WorkspaceProfileAddressPage').default as React.ComponentType,
[SCREENS.WORKSPACE.CATEGORIES_SETTINGS]: () => require('../../../../pages/workspace/categories/WorkspaceCategoriesSettingsPage').default as React.ComponentType,
[SCREENS.WORKSPACE.MEMBER_DETAILS]: () => require('../../../../pages/workspace/members/WorkspaceMemberDetailsPage').default as React.ComponentType,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ const FULL_SCREEN_TO_RHP_MAPPING: Partial<Record<FullScreenName, string[]>> = {
SCREENS.WORKSPACE.WORKFLOWS_AUTO_REPORTING_FREQUENCY,
SCREENS.WORKSPACE.WORKFLOWS_AUTO_REPORTING_MONTHLY_OFFSET,
SCREENS.WORKSPACE.WORKFLOWS_PAYER,
SCREENS.WORKSPACE.UPGRADE,
],
[SCREENS.WORKSPACE.ACCOUNTING.ROOT]: [
SCREENS.WORKSPACE.ACCOUNTING.QUICKBOOKS_ONLINE_IMPORT,
Expand Down Expand Up @@ -54,6 +55,7 @@ const FULL_SCREEN_TO_RHP_MAPPING: Partial<Record<FullScreenName, string[]>> = {
SCREENS.WORKSPACE.ACCOUNTING.XERO_EXPORT_PREFERRED_EXPORTER_SELECT,
SCREENS.WORKSPACE.ACCOUNTING.XERO_BILL_PAYMENT_ACCOUNT_SELECTOR,
SCREENS.WORKSPACE.ACCOUNTING.XERO_EXPORT_BANK_ACCOUNT_SELECT,
SCREENS.WORKSPACE.UPGRADE,
],
[SCREENS.WORKSPACE.TAXES]: [
SCREENS.WORKSPACE.TAXES_SETTINGS,
Expand Down Expand Up @@ -83,6 +85,7 @@ const FULL_SCREEN_TO_RHP_MAPPING: Partial<Record<FullScreenName, string[]>> = {
SCREENS.WORKSPACE.DISTANCE_RATE_TAX_RATE_EDIT,
SCREENS.WORKSPACE.DISTANCE_RATE_DETAILS,
],
[SCREENS.WORKSPACE.MORE_FEATURES]: [SCREENS.WORKSPACE.UPGRADE],
};

export default FULL_SCREEN_TO_RHP_MAPPING;
6 changes: 6 additions & 0 deletions src/libs/Navigation/linkingConfig/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -373,6 +373,12 @@ const config: LinkingOptions<RootStackParamList>['config'] = {
categoryName: (categoryName: string) => decodeURIComponent(categoryName),
},
},
[SCREENS.WORKSPACE.UPGRADE]: {
path: ROUTES.WORKSPACE_UPGRADE.route,
parse: {
featureName: (featureName: string) => decodeURIComponent(featureName),
},
},
[SCREENS.WORKSPACE.CATEGORIES_SETTINGS]: {
path: ROUTES.WORKSPACE_CATEGORIES_SETTINGS.route,
},
Expand Down
5 changes: 5 additions & 0 deletions src/libs/Navigation/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,11 @@ type SettingsNavigatorParamList = {
categoryName: string;
backTo?: Routes;
};
[SCREENS.WORKSPACE.UPGRADE]: {
policyID: string;
featureName: string;
backTo?: Routes;
};
[SCREENS.WORKSPACE.CATEGORIES_SETTINGS]: {
policyID: string;
backTo?: Routes;
Expand Down
38 changes: 38 additions & 0 deletions src/libs/actions/Policy/Policy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import type {
UpdateWorkspaceCustomUnitAndRateParams,
UpdateWorkspaceDescriptionParams,
UpdateWorkspaceGeneralSettingsParams,
UpgradeToCorporateParams,
} from '@libs/API/parameters';
import type UpdatePolicyAddressParams from '@libs/API/parameters/UpdatePolicyAddressParams';
import {READ_COMMANDS, WRITE_COMMANDS} from '@libs/API/types';
Expand Down Expand Up @@ -2934,6 +2935,42 @@ function setForeignCurrencyDefault(policyID: string, taxCode: string) {
API.write(WRITE_COMMANDS.SET_POLICY_TAXES_FOREIGN_CURRENCY_DEFAULT, parameters, onyxData);
}

function upgradeToCorporate(policyID: string, featureName: string) {
const optimisticData: OnyxUpdate[] = [
{
onyxMethod: Onyx.METHOD.MERGE,
key: `policy_${policyID}`,
value: {
isPendingUpgrade: true,
Copy link
Contributor

@youssef-lr youssef-lr Jun 26, 2024

Choose a reason for hiding this comment

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

let's add these optimistic values and their keys in CONST if they're not there:

type=CORPORATE
maxExpenseAge=CONST.POLICY.DEFAULT_MAX_EXPENSE_AGE=90
maxExpenseAmount=CONST.POLICY.DEFAULT_MAX_EXPENSE_AMOUNT=200000
maxExpenseAmountNoReceipt=CONST.POLICY.DEFAULT_MAX_AMOUNT_NO_RECEIPT=2500
glCodes=true

And then add these values to the optimistic data if instant submit is enabled:

if (isInstantSubmitEnabled(policy)) {
    // set autoReporting=false
    // set autoReportingFrequency=CONST.POLICY.AUTO_REPORTING_FREQUENCIES.MANUAL
}

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Handled.

},
},
];

const successData: OnyxUpdate[] = [
{
onyxMethod: Onyx.METHOD.MERGE,
key: `policy_${policyID}`,
value: {
isPendingUpgrade: false,
},
},
];

const failureData: OnyxUpdate[] = [
{
onyxMethod: Onyx.METHOD.MERGE,
key: `policy_${policyID}`,
value: {
isPendingUpgrade: false,
},
},
];
allroundexperts marked this conversation as resolved.
Show resolved Hide resolved

const parameters: UpgradeToCorporateParams = {policyID, featureName};

API.write(WRITE_COMMANDS.UPGRADE_TO_CORPORATE, parameters, {optimisticData, successData, failureData});
}

export {
leaveWorkspace,
addBillingCardAndRequestPolicyOwnerChange,
Expand Down Expand Up @@ -2996,6 +3033,7 @@ export {
createDraftWorkspace,
buildPolicyData,
createPolicyExpenseChats,
upgradeToCorporate,
};

export type {NewCustomUnit};
38 changes: 38 additions & 0 deletions src/pages/workspace/upgrade/UpgradeConfirmation.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import React from 'react';
import ConfirmationPage from '@components/ConfirmationPage';
import TextLink from '@components/TextLink';
import useLocalize from '@hooks/useLocalize';
import useThemeStyles from '@hooks/useThemeStyles';
import Navigation from '@libs/Navigation/Navigation';

type Props = {
policyName: string;
};

function UpgradeConfirmation({policyName}: Props) {
const {translate} = useLocalize();
const styles = useThemeStyles();

return (
<ConfirmationPage
heading={translate('workspace.upgrade.completed.headline')}
description={
<>
{translate('workspace.upgrade.completed.successMessage', policyName)}{' '}
<TextLink
style={styles.link}
href=""
allroundexperts marked this conversation as resolved.
Show resolved Hide resolved
>
{translate('workspace.upgrade.completed.viewSubscription')}
</TextLink>{' '}
{translate('workspace.upgrade.completed.moreDetails')}
</>
}
shouldShowButton
onButtonPress={() => Navigation.goBack()}
allroundexperts marked this conversation as resolved.
Show resolved Hide resolved
buttonText={translate('workspace.upgrade.completed.gotIt')}
/>
);
}

export default UpgradeConfirmation;
74 changes: 74 additions & 0 deletions src/pages/workspace/upgrade/UpgradeIntro.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import React from 'react';
import {View} from 'react-native';
import Badge from '@components/Badge';
import Button from '@components/Button';
import Icon from '@components/Icon';
import * as Expensicon from '@components/Icon/Expensicons';
import Text from '@components/Text';
import TextLink from '@components/TextLink';
import useLocalize from '@hooks/useLocalize';
import useResponsiveLayout from '@hooks/useResponsiveLayout';
import useThemeStyles from '@hooks/useThemeStyles';
import variables from '@styles/variables';
import type IconAsset from '@src/types/utils/IconAsset';

type Props = {
buttonDisabled?: boolean;
loading?: boolean;
title: string;
description: string;
icon: IconAsset;
onUpgrade: () => void;
};

function UpgradeIntro({title, description, icon, onUpgrade, buttonDisabled, loading}: Props) {
const styles = useThemeStyles();
const {isExtraSmallScreenWidth, isSmallScreenWidth} = useResponsiveLayout();
const {translate} = useLocalize();

return (
<View style={styles.p5}>
Copy link
Contributor

Choose a reason for hiding this comment

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

Sorry for not noticing this before but we do have a Section component can we reuse it here @allroundexperts

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Can you provide the full name of this component?

Copy link
Contributor

Choose a reason for hiding this comment

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

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Hm... Would this be plug in play? I think we'll need to do some changes within this component to meet our needs.

Copy link
Contributor

Choose a reason for hiding this comment

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

Hm... Would this be plug in play? I think we'll need to do some changes within this component to meet our needs.

Ideally we should reuse but we def. need to make changes to component to meet our needs here, I dont think this is super important change, current component is readable as it is. Consider this as a optional comment 👍

<View style={styles.workspaceUpgradeIntroBox({isExtraSmallScreenWidth, isSmallScreenWidth})}>
<View style={[styles.mb3, styles.flexRow, styles.justifyContentBetween]}>
<Icon
src={icon}
width={variables.iconSizeExtraLarge}
height={variables.iconSizeExtraLarge}
/>
<Badge
icon={Expensicon.Unlock}
text={translate('workspace.upgrade.upgradeToUnlock')}
success
/>
</View>
<View style={styles.mb4}>
<Text style={styles.textHeadlineH1}>{title}</Text>
</View>
<View style={styles.mb5}>
<Text style={[styles.textNormal, styles.textSupporting]}>{description}</Text>
</View>
allroundexperts marked this conversation as resolved.
Show resolved Hide resolved
<Button
isLoading={loading}
text={translate('common.upgrade')}
success
onPress={onUpgrade}
isDisabled={buttonDisabled}
/>
</View>
<View style={styles.mt6}>
<Text style={[styles.textNormal, styles.textSupporting]}>
{translate('workspace.upgrade.note.upgradeWorkspace')}{' '}
<TextLink
style={[styles.link]}
href=""
>
{translate('workspace.upgrade.note.learnMore')}
</TextLink>{' '}
{translate('workspace.upgrade.note.aboutOurPlans')}
</Text>
</View>
</View>
);
}

export default UpgradeIntro;
Loading
Loading