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 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
15 changes: 14 additions & 1 deletion src/CONST.ts
Original file line number Diff line number Diff line change
Expand Up @@ -791,6 +791,7 @@ const CONST = {
UPDATE_TIME_ENABLED: 'POLICYCHANGELOG_UPDATE_TIME_ENABLED',
UPDATE_TIME_RATE: 'POLICYCHANGELOG_UPDATE_TIME_RATE',
LEAVE_POLICY: 'POLICYCHANGELOG_LEAVE_POLICY',
CORPORATE_UPGRADE: 'POLICYCHANGELOG_CORPORATE_UPGRADE',
},
ROOM_CHANGE_LOG: {
INVITE_TO_ROOM: 'INVITETOROOM',
Expand Down Expand Up @@ -1981,6 +1982,9 @@ const CONST = {
PAID: 'paid',
ADMIN: 'admin',
},
DEFAULT_MAX_EXPENSE_AGE: 90,
DEFAULT_MAX_EXPENSE_AMOUNT: 200000,
DEFAULT_MAX_AMOUNT_NO_RECEIPT: 2500,
},

CUSTOM_UNITS: {
Expand Down Expand Up @@ -5069,7 +5073,16 @@ const CONST = {
},

EXCLUDE_FROM_LAST_VISITED_PATH: [SCREENS.NOT_FOUND, SCREENS.SAML_SIGN_IN, SCREENS.VALIDATE_LOGIN] as string[],

UPGRADE_FEATURE_INTRO_MAPPING: [
{
id: 'reportFields',
alias: 'report-fields',
name: 'Report Fields',
title: 'workspace.upgrade.reportFields.title',
description: 'workspace.upgrade.reportFields.description',
icon: 'Pencil',
},
],
REPORT_FIELD_TYPES: {
TEXT: 'text',
DATE: 'date',
Expand Down
4 changes: 4 additions & 0 deletions src/ROUTES.ts
Original file line number Diff line number Diff line change
Expand Up @@ -678,6 +678,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 @@ -355,6 +355,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
25 changes: 25 additions & 0 deletions src/languages/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -353,6 +353,7 @@ export default {
shared: 'Shared',
drafts: 'Drafts',
finished: 'Finished',
upgrade: 'Upgrade',
companyID: 'Company ID',
userID: 'User ID',
disable: 'Disable',
Expand Down Expand Up @@ -2968,6 +2969,30 @@ export default {
errorDescriptionPartTwo: 'reach out to Concierge',
errorDescriptionPartThree: 'for help.',
},
upgrade: {
reportFields: {
title: 'Report fields',
description: `Report fields let you specify header-level details, distinct from tags that pertain to expenses on individual line items. These details can encompass specific project names, business trip information, locations, and more.`,
pricing: {
onlyAvailableOnPlan: 'Report fields are only available on the Control plan, starting at ',
amount: '$9 ',
perActiveMember: 'per active member per month.',
},
},
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: `You've upgraded your workspace!`,
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',
},
},
restrictedAction: {
restricted: 'Restricted',
actionsAreCurrentlyRestricted: ({workspaceName}) => `Actions on the ${workspaceName} workspace are currently restricted`,
Expand Down
25 changes: 25 additions & 0 deletions src/languages/es.ts
Original file line number Diff line number Diff line change
Expand Up @@ -344,6 +344,7 @@ export default {
shared: 'Compartidos',
drafts: 'Borradores',
finished: 'Finalizados',
upgrade: 'Mejora',
companyID: 'Empresa ID',
userID: 'Usuario ID',
disable: 'Deshabilitar',
Expand Down Expand Up @@ -3011,6 +3012,30 @@ export default {
errorDescriptionPartTwo: 'contacta con el conserje',
errorDescriptionPartThree: 'por ayuda.',
},
upgrade: {
reportFields: {
title: 'Los campos',
description: `Los campos de informe permiten especificar detalles a nivel de cabecera, distintos de las etiquetas que pertenecen a los gastos en partidas individuales. Estos detalles pueden incluir nombres de proyectos específicos, información sobre viajes de negocios, ubicaciones, etc.`,
pricing: {
onlyAvailableOnPlan: 'Los campos de informe sólo están disponibles en el plan Control, a partir de ',
amount: '$9 ',
perActiveMember: 'por miembro activo al mes.',
},
},
note: {
upgradeWorkspace: 'Mejore su espacio de trabajo para acceder a esta función, o',
learnMore: 'más información',
aboutOurPlans: 'sobre nuestros planes y precios.',
},
upgradeToUnlock: 'Desbloquear esta función',
completed: {
headline: 'Has mejorado tu espacio de trabajo.',
successMessage: (policyName: string) => `Ha mejorado correctamente su espacio de trabajo ${policyName} al plan Control.`,
viewSubscription: 'Ver su suscripción',
moreDetails: 'para obtener más información.',
gotIt: 'Entendido, gracias.',
},
},
restrictedAction: {
restricted: 'Restringido',
actionsAreCurrentlyRestricted: ({workspaceName}) => `Las acciones en el espacio de trabajo ${workspaceName} están actualmente restringidas`,
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 @@ -233,6 +233,7 @@ export type {default as UpdateSubscriptionAutoRenewParams} from './UpdateSubscri
export type {default as UpdateSubscriptionAddNewUsersAutomaticallyParams} from './UpdateSubscriptionAddNewUsersAutomaticallyParams';
export type {default as GenerateSpotnanaTokenParams} from './GenerateSpotnanaTokenParams';
export type {default as UpdateSubscriptionSizeParams} from './UpdateSubscriptionSizeParams';
export type {default as UpgradeToCorporateParams} from './UpgradeToCorporateParams';
export type {default as DeleteMoneyRequestOnSearchParams} from './DeleteMoneyRequestOnSearchParams';
export type {default as HoldMoneyRequestOnSearchParams} from './HoldMoneyRequestOnSearchParams';
export type {default as UnholdMoneyRequestOnSearchParams} from './UnholdMoneyRequestOnSearchParams';
Expand Down
2 changes: 2 additions & 0 deletions src/libs/API/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,7 @@ const WRITE_COMMANDS = {
UPDATE_SUBSCRIPTION_AUTO_RENEW: 'UpdateSubscriptionAutoRenew',
UPDATE_SUBSCRIPTION_ADD_NEW_USERS_AUTOMATICALLY: 'UpdateSubscriptionAddNewUsersAutomatically',
UPDATE_SUBSCRIPTION_SIZE: 'UpdateSubscriptionSize',
UPGRADE_TO_CORPORATE: 'UpgradeToCorporate',
DELETE_MONEY_REQUEST_ON_SEARCH: 'DeleteMoneyRequestOnSearch',
HOLD_MONEY_REQUEST_ON_SEARCH: 'HoldMoneyRequestOnSearch',
UNHOLD_MONEY_REQUEST_ON_SEARCH: 'UnholdMoneyRequestOnSearch',
Expand Down Expand Up @@ -493,6 +494,7 @@ type WriteCommandParameters = {
[WRITE_COMMANDS.REQUEST_REFUND]: null;
[WRITE_COMMANDS.CONNECT_POLICY_TO_SAGE_INTACCT]: Parameters.ConnectPolicyToSageIntacctParams;

[WRITE_COMMANDS.UPGRADE_TO_CORPORATE]: Parameters.UpgradeToCorporateParams;
// Netsuite parameters
[WRITE_COMMANDS.UPDATE_NETSUITE_SUBSIDIARY]: Parameters.UpdateNetSuiteSubsidiaryParams;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,7 @@ const SettingsModalStackNavigator = createModalStackNavigator<SettingsNavigatorP
[SCREENS.WORKSPACE.CATEGORY_SETTINGS]: () => require<ReactComponentModule>('../../../../pages/workspace/categories/CategorySettingsPage').default,
[SCREENS.WORKSPACE.ADDRESS]: () => require<ReactComponentModule>('../../../../pages/workspace/WorkspaceProfileAddressPage').default,
[SCREENS.WORKSPACE.CATEGORIES_SETTINGS]: () => require<ReactComponentModule>('../../../../pages/workspace/categories/WorkspaceCategoriesSettingsPage').default,
[SCREENS.WORKSPACE.UPGRADE]: () => require<ReactComponentModule>('../../../../pages/workspace/upgrade/WorkspaceUpgradePage').default,
[SCREENS.WORKSPACE.MEMBER_DETAILS]: () => require<ReactComponentModule>('../../../../pages/workspace/members/WorkspaceMemberDetailsPage').default,
[SCREENS.WORKSPACE.OWNER_CHANGE_CHECK]: () => require<ReactComponentModule>('@pages/workspace/members/WorkspaceOwnerChangeWrapperPage').default,
[SCREENS.WORKSPACE.OWNER_CHANGE_SUCCESS]: () => require<ReactComponentModule>('../../../../pages/workspace/members/WorkspaceOwnerChangeSuccessPage').default,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,14 @@ import type {FullScreenName} from '@libs/Navigation/types';
import SCREENS from '@src/SCREENS';

const FULL_SCREEN_TO_RHP_MAPPING: Partial<Record<FullScreenName, string[]>> = {
[SCREENS.WORKSPACE.PROFILE]: [SCREENS.WORKSPACE.NAME, SCREENS.WORKSPACE.ADDRESS, SCREENS.WORKSPACE.CURRENCY, SCREENS.WORKSPACE.DESCRIPTION, SCREENS.WORKSPACE.SHARE],
[SCREENS.WORKSPACE.PROFILE]: [
SCREENS.WORKSPACE.NAME,
SCREENS.WORKSPACE.ADDRESS,
SCREENS.WORKSPACE.CURRENCY,
SCREENS.WORKSPACE.DESCRIPTION,
SCREENS.WORKSPACE.SHARE,
SCREENS.WORKSPACE.UPGRADE,
],
[SCREENS.WORKSPACE.REIMBURSE]: [SCREENS.WORKSPACE.RATE_AND_UNIT, SCREENS.WORKSPACE.RATE_AND_UNIT_RATE, SCREENS.WORKSPACE.RATE_AND_UNIT_UNIT],
[SCREENS.WORKSPACE.MEMBERS]: [
SCREENS.WORKSPACE.INVITE,
Expand Down
6 changes: 6 additions & 0 deletions src/libs/Navigation/linkingConfig/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -436,6 +436,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
55 changes: 55 additions & 0 deletions src/libs/actions/Policy/Policy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,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 @@ -3001,6 +3002,59 @@ 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 policy = getPolicy(policyID);
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.

type: CONST.POLICY.TYPE.CORPORATE,
maxExpenseAge: CONST.POLICY.DEFAULT_MAX_EXPENSE_AGE,
maxExpenseAmount: CONST.POLICY.DEFAULT_MAX_EXPENSE_AMOUNT,
maxExpenseAmountNoReceipt: CONST.POLICY.DEFAULT_MAX_AMOUNT_NO_RECEIPT,
glCodes: true,
...(PolicyUtils.isInstantSubmitEnabled(policy) && {
autoReporting: true,
autoReportingFrequency: CONST.POLICY.AUTO_REPORTING_FREQUENCIES.MANUAL,
}),
},
},
];

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,
type: policy?.type,
maxExpenseAge: policy?.maxExpenseAge ?? null,
maxExpenseAmount: policy?.maxExpenseAmount ?? null,
maxExpenseAmountNoReceipt: policy?.maxExpenseAmountNoReceipt ?? null,
glCodes: policy?.glCodes ?? null,
autoReporting: policy?.autoReporting ?? null,
autoReportingFrequency: policy?.autoReportingFrequency ?? null,
},
},
];

const parameters: UpgradeToCorporateParams = {policyID, featureName};

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

function getPoliciesConnectedToSageIntacct(): Policy[] {
return Object.values(allPolicies ?? {}).filter<Policy>((policy): policy is Policy => !!policy && !!policy?.connections?.intacct);
}
Expand Down Expand Up @@ -3067,6 +3121,7 @@ export {
buildPolicyData,
enableExpensifyCard,
createPolicyExpenseChats,
upgradeToCorporate,
openPolicyExpensifyCardsPage,
requestExpensifyCardLimitIncrease,
getPoliciesConnectedToSageIntacct,
Expand Down
40 changes: 40 additions & 0 deletions src/pages/workspace/upgrade/UpgradeConfirmation.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
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';
import ROUTES from '@src/ROUTES';

type Props = {
policyName: string;
policyID: string;
};

function UpgradeConfirmation({policyName, policyID}: 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}
onPress={() => Navigation.navigate(ROUTES.SETTINGS_SUBSCRIPTION)}
>
{translate('workspace.upgrade.completed.viewSubscription')}
</TextLink>{' '}
{translate('workspace.upgrade.completed.moreDetails')}
</>
}
shouldShowButton
onButtonPress={() => Navigation.goBack(ROUTES.WORKSPACE_PROFILE.getRoute(policyID))}
buttonText={translate('workspace.upgrade.completed.gotIt')}
/>
);
}

export default UpgradeConfirmation;
Loading
Loading