From bde424e883f610c125de70f7cafc0760c2e8323a Mon Sep 17 00:00:00 2001 From: jayeshmangwani Date: Sun, 1 Dec 2024 02:20:31 +0530 Subject: [PATCH 01/12] added ws plan type page and added upgrade generic features view --- src/ROUTES.ts | 10 +- src/SCREENS.ts | 1 + src/languages/en.ts | 32 +++++ src/languages/es.ts | 32 +++++ src/languages/params.ts | 6 + .../parameters/UpgradeToCorporateParams.ts | 2 +- .../ModalStackNavigators/index.tsx | 1 + .../FULL_SCREEN_TO_RHP_MAPPING.ts | 1 + src/libs/Navigation/linkingConfig/config.ts | 3 + src/libs/Navigation/types.ts | 2 +- src/libs/PolicyUtils.ts | 12 ++ src/libs/actions/Policy/Policy.ts | 6 +- src/pages/workspace/WorkspaceProfilePage.tsx | 17 +++ .../WorkspaceProfilePlanTypePage.tsx | 128 ++++++++++++++++++ src/pages/workspace/WorkspacesListRow.tsx | 28 ++-- .../workspace/upgrade/GenericFeaturesView.tsx | 75 ++++++++++ src/pages/workspace/upgrade/UpgradeIntro.tsx | 13 +- .../upgrade/WorkspaceUpgradePage.tsx | 5 +- 18 files changed, 346 insertions(+), 28 deletions(-) create mode 100644 src/pages/workspace/WorkspaceProfilePlanTypePage.tsx create mode 100644 src/pages/workspace/upgrade/GenericFeaturesView.tsx diff --git a/src/ROUTES.ts b/src/ROUTES.ts index a6eb3c1166df..45a6345622fc 100644 --- a/src/ROUTES.ts +++ b/src/ROUTES.ts @@ -711,6 +711,10 @@ const ROUTES = { route: 'settings/workspaces/:policyID/profile/address', getRoute: (policyID: string, backTo?: string) => getUrlWithBackToParam(`settings/workspaces/${policyID}/profile/address` as const, backTo), }, + WORKSPACE_PROFILE_PLAN: { + route: 'settings/workspaces/:policyID/profile/plan', + getRoute: (policyID: string, backTo?: string) => getUrlWithBackToParam(`settings/workspaces/${policyID}/profile/plan` as const, backTo), + }, WORKSPACE_ACCOUNTING: { route: 'settings/workspaces/:policyID/accounting', getRoute: (policyID: string) => `settings/workspaces/${policyID}/accounting` as const, @@ -974,9 +978,9 @@ const ROUTES = { getRoute: (policyID: string, categoryName: string) => `settings/workspaces/${policyID}/category/${encodeURIComponent(categoryName)}` as const, }, WORKSPACE_UPGRADE: { - route: 'settings/workspaces/:policyID/upgrade/:featureName', - getRoute: (policyID: string, featureName: string, backTo?: string) => - getUrlWithBackToParam(`settings/workspaces/${policyID}/upgrade/${encodeURIComponent(featureName)}` as const, backTo), + route: 'settings/workspaces/:policyID/upgrade/:featureName?', + getRoute: (policyID: string, featureName?: string, backTo?: string) => + getUrlWithBackToParam(`settings/workspaces/${policyID}/upgrade/${encodeURIComponent(featureName ?? '')}` as const, backTo), }, WORKSPACE_DOWNGRADE: { route: 'settings/workspaces/:policyID/downgrade/', diff --git a/src/SCREENS.ts b/src/SCREENS.ts index e4fa03bf4815..1b4ecb1ea1c8 100644 --- a/src/SCREENS.ts +++ b/src/SCREENS.ts @@ -498,6 +498,7 @@ const SCREENS = { TAG_GL_CODE: 'Tag_GL_Code', CURRENCY: 'Workspace_Profile_Currency', ADDRESS: 'Workspace_Profile_Address', + PLAN: 'Workspace_Profile_Plan_Type', WORKFLOWS: 'Workspace_Workflows', WORKFLOWS_PAYER: 'Workspace_Workflows_Payer', WORKFLOWS_APPROVALS_NEW: 'Workspace_Approvals_New', diff --git a/src/languages/en.ts b/src/languages/en.ts index 13ff38a0bc58..54cff7b78575 100755 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -191,6 +191,7 @@ import type { WelcomeNoteParams, WelcomeToRoomParams, WeSentYouMagicSignInLinkParams, + WorkspaceLockedPlanTypeParams, WorkspaceOwnerWillNeedToAddOrUpdatePaymentCardParams, YourPlanPriceParams, ZipCodeExampleFormatParams, @@ -2538,6 +2539,7 @@ const translations = { return 'Member'; } }, + planType: 'Plan type', submitExpense: 'Submit expenses using your workspace chat below:', defaultCategory: 'Default category', }, @@ -4280,6 +4282,19 @@ const translations = { moreDetails: 'for more details.', gotIt: 'Got it, thanks', }, + commonFeatures: { + title: 'Upgrade Workspace to Control', + note: 'Get access to all our most advanced functionality, including:', + benefits: { + note: 'The Control plan starts at $9 per active member per month.', + learnMore: 'Learn more', + pricing: 'about our plans and pricing.', + benefit1: 'Advanced accounting connections (NetSuite, Sage Intacct and more)', + benefit2: 'Expense rules', + benefit3: 'Multiple approval workflows', + benefit4: 'Enhanced security controls', + }, + }, }, restrictedAction: { restricted: 'Restricted', @@ -4383,6 +4398,23 @@ const translations = { andEnableWorkflows: 'and enable workflows, then add approvals to unlock this feature.', }, }, + planTypePage: { + planTypes: { + team: { + label: 'Collect', + description: 'For teams looking to automate their processes.', + }, + corporate: { + label: 'Control', + description: 'For organizations with advanced requirements.', + }, + }, + description: "Choose a plan that's right for you. For a detailed list of features and pricing, check out our", + subscriptionLink: 'plan types and pricing help page', + lockedPlanDescription: ({subscriptionUsersCount, annualSubscriptionEndDate}: WorkspaceLockedPlanTypeParams) => + `You've committed to ${subscriptionUsersCount} active users on the Control plan until your annual subscription ends on ${annualSubscriptionEndDate}. You can switch to pay-per-use subscription and downgrade to the Collect plan starting ${annualSubscriptionEndDate} by disabling auto-renew in`, + subscriptions: 'Subscriptions', + }, }, getAssistancePage: { title: 'Get assistance', diff --git a/src/languages/es.ts b/src/languages/es.ts index 6477827fd0a1..b40a65c6eb6e 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -191,6 +191,7 @@ import type { WelcomeNoteParams, WelcomeToRoomParams, WeSentYouMagicSignInLinkParams, + WorkspaceLockedPlanTypeParams, WorkspaceOwnerWillNeedToAddOrUpdatePaymentCardParams, YourPlanPriceParams, ZipCodeExampleFormatParams, @@ -2562,6 +2563,7 @@ const translations = { return 'Miembro'; } }, + planType: 'Tipo de plan', submitExpense: 'Envíe los gastos utilizando el chat de su espacio de trabajo:', defaultCategory: 'Categoría predeterminada', }, @@ -4247,6 +4249,23 @@ const translations = { confirmText: 'Sí, exportar de nuevo', cancelText: 'Cancelar', }, + planTypePage: { + planTypes: { + team: { + label: 'Collect', + description: 'Para equipos que buscan automatizar sus procesos.', + }, + corporate: { + label: 'Recolectar', + description: 'Para organizaciones con requisitos avanzados.', + }, + }, + description: 'Elige el plan adecuado para ti. Para ver una lista detallada de funciones y precios, consulta nuestra', + subscriptionLink: 'página de ayuda sobre tipos de planes y precios', + lockedPlanDescription: ({subscriptionUsersCount, annualSubscriptionEndDate}: WorkspaceLockedPlanTypeParams) => + `Tienes un compromiso anual de ${subscriptionUsersCount} miembros activos en el plan Control hasta el ${annualSubscriptionEndDate}. Puedes cambiar a una suscripción de pago por uso y desmejorar al plan Recopilar a partir del ${annualSubscriptionEndDate} desactivando la renovación automática en`, + subscriptions: 'Suscripciones', + }, upgrade: { reportFields: { title: 'Los campos', @@ -4328,6 +4347,19 @@ const translations = { moreDetails: 'para obtener más información.', gotIt: 'Entendido, gracias.', }, + commonFeatures: { + title: 'Actualiza tu espacio de trabajo al plan Controlar', + note: 'Obtén acceso a todas nuestras funciones más avanzadas, incluyendo:', + benefits: { + note: 'El plan Controlar comienza en $9 por miembro activo al mes.', + learnMore: 'Obtén más información', + pricing: 'sobre nuestros planes y precios.', + benefit1: 'Conexiones contables avanzadas (NetSuite, Sage Intacct y más)', + benefit2: 'Reglas de gastos', + benefit3: 'Flujos de aprobación múltiples', + benefit4: 'Controles de seguridad mejorados', + }, + }, }, restrictedAction: { restricted: 'Restringido', diff --git a/src/languages/params.ts b/src/languages/params.ts index 90b789efae58..9b9b3aeca092 100644 --- a/src/languages/params.ts +++ b/src/languages/params.ts @@ -559,6 +559,11 @@ type CurrencyCodeParams = { currencyCode: string; }; +type WorkspaceLockedPlanTypeParams = { + subscriptionUsersCount: number; + annualSubscriptionEndDate: string; +}; + type CompanyNameParams = { companyName: string; }; @@ -769,6 +774,7 @@ export type { ImportedTypesParams, ImportPerDiemRatesSuccessfullDescriptionParams, CurrencyCodeParams, + WorkspaceLockedPlanTypeParams, CompanyNameParams, ChatWithAccountManagerParams, }; diff --git a/src/libs/API/parameters/UpgradeToCorporateParams.ts b/src/libs/API/parameters/UpgradeToCorporateParams.ts index ee9d1359c4dd..7b7ff3e0adcc 100644 --- a/src/libs/API/parameters/UpgradeToCorporateParams.ts +++ b/src/libs/API/parameters/UpgradeToCorporateParams.ts @@ -1,6 +1,6 @@ type UpgradeToCorporateParams = { policyID: string; - featureName: string; + featureName?: string; }; export default UpgradeToCorporateParams; diff --git a/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx b/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx index 9822c60faaa8..73b256ae6fbd 100644 --- a/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx +++ b/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx @@ -269,6 +269,7 @@ const SettingsModalStackNavigator = createModalStackNavigator require('../../../../pages/workspace/WorkspaceProfileCurrencyPage').default, [SCREENS.WORKSPACE.CATEGORY_SETTINGS]: () => require('../../../../pages/workspace/categories/CategorySettingsPage').default, [SCREENS.WORKSPACE.ADDRESS]: () => require('../../../../pages/workspace/WorkspaceProfileAddressPage').default, + [SCREENS.WORKSPACE.PLAN]: () => require('../../../../pages/workspace/WorkspaceProfilePlanTypePage').default, [SCREENS.WORKSPACE.CATEGORIES_SETTINGS]: () => require('../../../../pages/workspace/categories/WorkspaceCategoriesSettingsPage').default, [SCREENS.WORKSPACE.CATEGORIES_IMPORT]: () => require('../../../../pages/workspace/categories/ImportCategoriesPage').default, [SCREENS.WORKSPACE.CATEGORIES_IMPORTED]: () => require('../../../../pages/workspace/categories/ImportedCategoriesPage').default, diff --git a/src/libs/Navigation/linkingConfig/FULL_SCREEN_TO_RHP_MAPPING.ts b/src/libs/Navigation/linkingConfig/FULL_SCREEN_TO_RHP_MAPPING.ts index f36f154819f5..4365c5e65e25 100755 --- a/src/libs/Navigation/linkingConfig/FULL_SCREEN_TO_RHP_MAPPING.ts +++ b/src/libs/Navigation/linkingConfig/FULL_SCREEN_TO_RHP_MAPPING.ts @@ -5,6 +5,7 @@ const FULL_SCREEN_TO_RHP_MAPPING: Partial> = { [SCREENS.WORKSPACE.PROFILE]: [ SCREENS.WORKSPACE.NAME, SCREENS.WORKSPACE.ADDRESS, + SCREENS.WORKSPACE.PLAN, SCREENS.WORKSPACE.CURRENCY, SCREENS.WORKSPACE.DESCRIPTION, SCREENS.WORKSPACE.SHARE, diff --git a/src/libs/Navigation/linkingConfig/config.ts b/src/libs/Navigation/linkingConfig/config.ts index 02f7f6950a0d..e9f627ee4341 100644 --- a/src/libs/Navigation/linkingConfig/config.ts +++ b/src/libs/Navigation/linkingConfig/config.ts @@ -347,6 +347,9 @@ const config: LinkingOptions['config'] = { [SCREENS.WORKSPACE.ADDRESS]: { path: ROUTES.WORKSPACE_PROFILE_ADDRESS.route, }, + [SCREENS.WORKSPACE.PLAN]: { + path: ROUTES.WORKSPACE_PROFILE_PLAN.route, + }, [SCREENS.WORKSPACE.ACCOUNTING.QUICKBOOKS_ONLINE_IMPORT]: {path: ROUTES.POLICY_ACCOUNTING_QUICKBOOKS_ONLINE_IMPORT.route}, [SCREENS.WORKSPACE.ACCOUNTING.QUICKBOOKS_ONLINE_CHART_OF_ACCOUNTS]: {path: ROUTES.POLICY_ACCOUNTING_QUICKBOOKS_ONLINE_CHART_OF_ACCOUNTS.route}, [SCREENS.WORKSPACE.ACCOUNTING.QUICKBOOKS_ONLINE_CLASSES]: {path: ROUTES.POLICY_ACCOUNTING_QUICKBOOKS_ONLINE_CLASSES.route}, diff --git a/src/libs/Navigation/types.ts b/src/libs/Navigation/types.ts index 3674a24a907b..2bb786884bbe 100644 --- a/src/libs/Navigation/types.ts +++ b/src/libs/Navigation/types.ts @@ -246,7 +246,7 @@ type SettingsNavigatorParamList = { }; [SCREENS.WORKSPACE.UPGRADE]: { policyID: string; - featureName: string; + featureName?: string; backTo?: Routes; categoryId?: string; }; diff --git a/src/libs/PolicyUtils.ts b/src/libs/PolicyUtils.ts index f6b277d69d6b..562b2ba62198 100644 --- a/src/libs/PolicyUtils.ts +++ b/src/libs/PolicyUtils.ts @@ -1119,6 +1119,17 @@ function getActivePolicy(): OnyxEntry { return getPolicy(activePolicyId); } +function getUserFriendlyWorkspaceType(workspaceType: ValueOf) { + switch (workspaceType) { + case CONST.POLICY.TYPE.CORPORATE: + return Localize.translateLocal('workspace.type.control'); + case CONST.POLICY.TYPE.TEAM: + return Localize.translateLocal('workspace.type.collect'); + default: + return Localize.translateLocal('workspace.type.free'); + } +} + function isPolicyAccessible(policy: OnyxEntry): boolean { return !isEmptyObject(policy) && (Object.keys(policy).length !== 1 || isEmptyObject(policy.errors)) && !!policy?.id; } @@ -1252,6 +1263,7 @@ export { getNetSuiteImportCustomFieldLabel, getAllPoliciesLength, getActivePolicy, + getUserFriendlyWorkspaceType, isPolicyAccessible, areAllGroupPoliciesExpenseChatDisabled, }; diff --git a/src/libs/actions/Policy/Policy.ts b/src/libs/actions/Policy/Policy.ts index 0b72d2de3f98..01a5909de9db 100644 --- a/src/libs/actions/Policy/Policy.ts +++ b/src/libs/actions/Policy/Policy.ts @@ -1663,6 +1663,7 @@ function buildPolicyData(policyOwnerEmail = '', makeMeAdmin = false, policyName outputCurrency: CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD, address: CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD, description: CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD, + type: CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD, }, }, }, @@ -1735,6 +1736,7 @@ function buildPolicyData(policyOwnerEmail = '', makeMeAdmin = false, policyName outputCurrency: null, address: null, description: null, + type: null, }, }, }, @@ -3324,7 +3326,7 @@ function setForeignCurrencyDefault(policyID: string, taxCode: string) { API.write(WRITE_COMMANDS.SET_POLICY_TAXES_FOREIGN_CURRENCY_DEFAULT, parameters, onyxData); } -function upgradeToCorporate(policyID: string, featureName: string) { +function upgradeToCorporate(policyID: string, featureName?: string) { const policy = getPolicy(policyID); const optimisticData: OnyxUpdate[] = [ { @@ -3376,7 +3378,7 @@ function upgradeToCorporate(policyID: string, featureName: string) { }, ]; - const parameters: UpgradeToCorporateParams = {policyID, featureName}; + const parameters: UpgradeToCorporateParams = {policyID, ...(featureName ? {featureName} : {})}; API.write(WRITE_COMMANDS.UPGRADE_TO_CORPORATE, parameters, {optimisticData, successData, failureData}); } diff --git a/src/pages/workspace/WorkspaceProfilePage.tsx b/src/pages/workspace/WorkspaceProfilePage.tsx index 4a4862152d53..f46ac071a81b 100644 --- a/src/pages/workspace/WorkspaceProfilePage.tsx +++ b/src/pages/workspace/WorkspaceProfilePage.tsx @@ -77,6 +77,7 @@ function WorkspaceProfilePage({policyDraft, policy: policyProp, route}: Workspac const onPressName = useCallback(() => Navigation.navigate(ROUTES.WORKSPACE_PROFILE_NAME.getRoute(policy?.id ?? '-1')), [policy?.id]); const onPressDescription = useCallback(() => Navigation.navigate(ROUTES.WORKSPACE_PROFILE_DESCRIPTION.getRoute(policy?.id ?? '-1')), [policy?.id]); const onPressShare = useCallback(() => Navigation.navigate(ROUTES.WORKSPACE_PROFILE_SHARE.getRoute(policy?.id ?? '-1')), [policy?.id]); + const onPressPlanType = useCallback(() => Navigation.navigate(ROUTES.WORKSPACE_PROFILE_PLAN.getRoute(policy?.id ?? '-1')), [policy?.id]); const policyName = policy?.name ?? ''; const policyDescription = @@ -267,6 +268,22 @@ function WorkspaceProfilePage({policyDraft, policy: policyProp, route}: Workspac )} + {!readOnly && !!policy?.type && ( + + + + + + )} {!readOnly && (