Skip to content

Commit

Permalink
Merge pull request #54771 from jayeshmangwani/plan_changes_to_subscri…
Browse files Browse the repository at this point in the history
…ptions

Plan changes to subscriptions
  • Loading branch information
carlosmiceli authored Jan 10, 2025
2 parents 8abec8e + c4138a6 commit d0a2d5e
Show file tree
Hide file tree
Showing 17 changed files with 339 additions and 155 deletions.
10 changes: 5 additions & 5 deletions src/ROUTES.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1017,13 +1017,13 @@ 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) =>
policyID ? getUrlWithBackToParam(`settings/workspaces/${policyID}/upgrade/${encodeURIComponent(featureName ?? '')}` as const, backTo) : (`settings/workspaces/upgrade` as const),
},
WORKSPACE_DOWNGRADE: {
route: 'settings/workspaces/:policyID/downgrade/',
getRoute: (policyID: string) => `settings/workspaces/${policyID}/downgrade/` as const,
route: 'settings/workspaces/:policyID?/downgrade/',
getRoute: (policyID?: string) => (policyID ? (`settings/workspaces/${policyID}/downgrade/` as const) : `settings/workspaces/downgrade`),
},
WORKSPACE_CATEGORIES_SETTINGS: {
route: 'settings/workspaces/:policyID/categories/settings',
Expand Down
4 changes: 3 additions & 1 deletion src/languages/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2576,6 +2576,7 @@ const translations = {
notAuthorized: `You don't have access to this page. If you're trying to join this workspace, just ask the workspace owner to add you as a member. Something else? Reach out to ${CONST.EMAIL.CONCIERGE}.`,
goToRoom: ({roomName}: GoToRoomParams) => `Go to ${roomName} room`,
goToWorkspace: 'Go to workspace',
goToWorkspaces: 'Go to workspaces',
clearFilter: 'Clear filter',
workspaceName: 'Workspace name',
workspaceOwner: 'Owner',
Expand Down Expand Up @@ -4384,7 +4385,8 @@ const translations = {
title: 'Upgrade to the Control plan',
note: 'Unlock our most powerful features, including:',
benefits: {
note: 'The Control plan starts at $9 per active member per month.',
startsAt: 'The Control plan starts at ',
perMember: 'per active member per month.',
learnMore: 'Learn more',
pricing: 'about our plans and pricing.',
benefit1: 'Advanced accounting connections (NetSuite, Sage Intacct, and more)',
Expand Down
4 changes: 3 additions & 1 deletion src/languages/es.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2599,6 +2599,7 @@ const translations = {
notAuthorized: `No tienes acceso a esta página. Si estás intentando unirte a este espacio de trabajo, pide al dueño del espacio de trabajo que te añada como miembro. ¿Necesitas algo más? Comunícate con ${CONST.EMAIL.CONCIERGE}`,
goToRoom: ({roomName}: GoToRoomParams) => `Ir a la sala ${roomName}`,
goToWorkspace: 'Ir al espacio de trabajo',
goToWorkspaces: 'Ir a espacios de trabajo',
clearFilter: 'Borrar filtro',
workspaceName: 'Nombre del espacio de trabajo',
workspaceOwner: 'Dueño',
Expand Down Expand Up @@ -4450,7 +4451,8 @@ const translations = {
title: 'Mejorar al plan Controlar',
note: 'Desbloquea nuestras funciones más potentes, incluyendo:',
benefits: {
note: 'El plan Controlar comienza desde $9 por miembro activo al mes.',
startsAt: 'El plan Controlar comienza desde ',
perMember: 'por miembro activo al mes.',
learnMore: 'Más información',
pricing: 'sobre nuestros planes y precios.',
benefit1: 'Conexiones avanzadas de contabilidad (NetSuite, Sage Intacct y más)',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ const FULL_SCREEN_TO_RHP_MAPPING: Partial<Record<FullScreenName, string[]>> = {
SCREENS.WORKSPACE.CURRENCY,
SCREENS.WORKSPACE.DESCRIPTION,
SCREENS.WORKSPACE.SHARE,
SCREENS.WORKSPACE.DOWNGRADE,
],
[SCREENS.WORKSPACE.MEMBERS]: [
SCREENS.WORKSPACE.MEMBER_DETAILS,
Expand Down
4 changes: 2 additions & 2 deletions src/libs/Navigation/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -247,13 +247,13 @@ type SettingsNavigatorParamList = {
backTo?: Routes;
};
[SCREENS.WORKSPACE.UPGRADE]: {
policyID: string;
policyID?: string;
featureName?: string;
backTo?: Routes;
categoryId?: string;
};
[SCREENS.WORKSPACE.DOWNGRADE]: {
policyID: string;
policyID?: string;
};
[SCREENS.WORKSPACE.CATEGORIES_SETTINGS]: {
policyID: string;
Expand Down
21 changes: 21 additions & 0 deletions src/libs/PolicyUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import type PolicyEmployee from '@src/types/onyx/PolicyEmployee';
import type {SearchPolicy} from '@src/types/onyx/SearchResults';
import {isEmptyObject} from '@src/types/utils/EmptyObject';
import {hasSynchronizationErrorMessage} from './actions/connections';
import {getCurrentUserAccountID} from './actions/Report';
import {getCategoryApproverRule} from './CategoryUtils';
import * as Localize from './Localize';
import Navigation from './Navigation/Navigation';
Expand Down Expand Up @@ -1185,6 +1186,25 @@ function hasOtherControlWorkspaces(currentPolicyID: string) {
return otherControlWorkspaces.length > 0;
}

// If no policyID is provided, it indicates the workspace upgrade/downgrade URL
// is being accessed from the Subscriptions page without a specific policyID.
// In this case, check if the user is an admin on more than one policy.
// If the user is an admin for multiple policies, we can render the page as it contains a condition
// to navigate them to the Workspaces page when no policyID is provided, instead of showing the Upgrade/Downgrade button.
// If the user is not an admin for multiple policies, they are not allowed to perform this action, and the NotFoundPage is displayed.

function canModifyPlan(policyID?: string) {
const currentUserAccountID = getCurrentUserAccountID();
const ownerPolicies = getOwnedPaidPolicies(allPolicies, currentUserAccountID);

if (!policyID) {
return ownerPolicies.length > 1;
}
const policy = getPolicy(policyID);

return !!policy && isPolicyAdmin(policy);
}

export {
canEditTaxRate,
extractPolicyIDFromPath,
Expand Down Expand Up @@ -1312,6 +1332,7 @@ export {
hasOtherControlWorkspaces,
getManagerAccountEmail,
getRuleApprovers,
canModifyPlan,
};

export type {MemberEmailsToAccountIDs};
107 changes: 0 additions & 107 deletions src/pages/settings/Subscription/SubscriptionPlan.tsx

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import React from 'react';
import {View} from 'react-native';
import type {SvgProps} from 'react-native-svg';
import type {ValueOf} from 'type-fest';
import Icon from '@components/Icon';
import * as Expensicons from '@components/Icon/Expensicons';
import {PressableWithFeedback} from '@components/Pressable';
import SelectCircle from '@components/SelectCircle';
import Text from '@components/Text';
import useTheme from '@hooks/useTheme';
import useThemeStyles from '@hooks/useThemeStyles';
import variables from '@styles/variables';
import type CONST from '@src/CONST';

type PersonalPolicyTypeExludedProps = Exclude<ValueOf<typeof CONST.POLICY.TYPE>, 'personal'>;

type SubscriptionPlanCardProps = {
index: number;
plan: {
title: string;
src: React.FC<SvgProps>;
benefits: string[];
description: string;
isSelected: boolean;
type: PersonalPolicyTypeExludedProps;
};

onPress: (type: PersonalPolicyTypeExludedProps) => void;
};
function SubscriptionPlanCard({plan, index, onPress}: SubscriptionPlanCardProps) {
const styles = useThemeStyles();
const theme = useTheme();

return (
<View style={[styles.borderedContentCard, styles.flex1, styles.mt5, styles.p5, index === 0 && styles.mr3, plan.isSelected && styles.borderColorFocus]}>
<PressableWithFeedback
accessibilityLabel={plan.title}
wrapperStyle={[styles.flex1]}
onPress={() => onPress(plan.type)}
>
<View style={[styles.flexRow, styles.justifyContentBetween]}>
<Icon
src={plan.src}
width={variables.iconHeader}
height={variables.iconHeader}
/>
<View>
<SelectCircle
isChecked={plan.isSelected}
selectCircleStyles={styles.sectionSelectCircle}
/>
</View>
</View>
<Text style={[styles.headerText, styles.mv2]}>{plan.title}</Text>
<Text style={[styles.textLabelSupporting, styles.mb2]}>{plan.description}</Text>
{plan.benefits.map((benefit) => (
<View
style={[styles.flexRow, styles.alignItemsCenter, styles.mt2]}
key={benefit}
>
<Icon
src={Expensicons.Checkmark}
fill={theme.iconSuccessFill}
width={variables.iconSizeSmall}
height={variables.iconSizeSmall}
/>
<Text style={[styles.textMicroSupporting, styles.ml2]}>{benefit}</Text>
</View>
))}
</PressableWithFeedback>
</View>
);
}

SubscriptionPlanCard.displayName = 'SubscriptionPlanCard';

export default SubscriptionPlanCard;
export type {PersonalPolicyTypeExludedProps};
Loading

0 comments on commit d0a2d5e

Please sign in to comment.