Skip to content
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
6 changes: 4 additions & 2 deletions src/ROUTES.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2470,8 +2470,10 @@ const ROUTES = {
},
WORKSPACE_UPGRADE: {
route: 'workspaces/:policyID?/upgrade/:featureName?',
getRoute: (policyID?: string, featureName?: string, backTo?: string) =>
getUrlWithBackToParam(policyID ? (`workspaces/${policyID}/upgrade/${encodeURIComponent(featureName ?? '')}` as const) : (`workspaces/upgrade` as const), backTo),
getRoute: (policyID?: string, featureName?: string, backTo?: string, upgradePlanType?: string) => {
const baseRoute = policyID ? (`workspaces/${policyID}/upgrade/${encodeURIComponent(featureName ?? '')}` as const) : (`workspaces/upgrade` as const);
return getUrlWithBackToParam(upgradePlanType ? (`${baseRoute}?upgradePlanType=${upgradePlanType}` as const) : baseRoute, backTo);
},
},
WORKSPACE_DOWNGRADE: {
route: 'workspaces/:policyID?/downgrade/',
Expand Down
6 changes: 6 additions & 0 deletions src/languages/de.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6994,6 +6994,12 @@ Fordern Sie Spesendetails wie Belege und Beschreibungen an, legen Sie Limits und
},
commonFeatures: {
title: 'Upgrade auf den Control-Tarif',
collect: {
title: 'Upgrade auf den Collect-Tarif',
startsAtFull: (learnMoreMethodsRoute: string, formattedPrice: string, hasTeam2025Pricing: boolean) =>
`<muted-text>Der Collect-Tarif beginnt bei <strong>${formattedPrice}</strong> ${hasTeam2025Pricing ? `pro Mitglied und Monat.` : `pro aktivem Mitglied und Monat.`}. <a href="${learnMoreMethodsRoute}">Erfahre mehr</a> über unsere Tarife und Preise.</muted-text>`,
note: 'Schalte wichtige Funktionen für dein Unternehmen frei, darunter:',
},
note: 'Schalte unsere leistungsstärksten Funktionen frei, darunter:',
benefits: {
startsAtFull: (learnMoreMethodsRoute: string, formattedPrice: string, hasTeam2025Pricing: boolean) =>
Expand Down
6 changes: 6 additions & 0 deletions src/languages/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7183,6 +7183,12 @@ const translations = {
},
commonFeatures: {
title: 'Upgrade to the Control plan',
collect: {
title: 'Upgrade to the Collect plan',
startsAtFull: (learnMoreMethodsRoute: string, formattedPrice: string, hasTeam2025Pricing: boolean) =>
`<muted-text>The Collect plan starts at <strong>${formattedPrice}</strong> ${hasTeam2025Pricing ? `per member per month.` : `per active member per month.`} <a href="${learnMoreMethodsRoute}">Learn more</a> about our plans and pricing.</muted-text>`,
note: 'Unlock essential features to run your business, including:',
},
note: 'Unlock our most powerful features, including:',
benefits: {
startsAtFull: (learnMoreMethodsRoute: string, formattedPrice: string, hasTeam2025Pricing: boolean) =>
Expand Down
6 changes: 6 additions & 0 deletions src/languages/es.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6977,6 +6977,12 @@ ${amount} para ${merchant} - ${date}`,
},
commonFeatures: {
title: 'Mejorar al plan Controlar',
collect: {
title: 'Mejorar al plan Recopilar',
startsAtFull: (learnMoreMethodsRoute, formattedPrice, hasTeam2025Pricing) =>
`<muted-text>El plan Recopilar comienza desde <strong>${formattedPrice}</strong> ${hasTeam2025Pricing ? `por miembro al mes.` : `por miembro activo al mes.`} <a href="${learnMoreMethodsRoute}">Más información</a> sobre nuestros planes y precios.</muted-text>`,
note: 'Desbloquea las funciones esenciales para tu negocio, incluyendo:',
},
note: 'Desbloquea nuestras funciones más potentes, incluyendo:',
benefits: {
startsAtFull: (learnMoreMethodsRoute, formattedPrice, hasTeam2025Pricing) =>
Expand Down
6 changes: 6 additions & 0 deletions src/languages/fr.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7019,6 +7019,12 @@ Rendez obligatoires des informations de dépense comme les reçus et les descrip
},
commonFeatures: {
title: 'Passer au forfait Contrôle',
collect: {
title: 'Passer au forfait Collect',
startsAtFull: (learnMoreMethodsRoute: string, formattedPrice: string, hasTeam2025Pricing: boolean) =>
`<muted-text>Le plan Collect commence à <strong>${formattedPrice}</strong> ${hasTeam2025Pricing ? `par membre et par mois.` : `par membre actif et par mois.`} <a href="${learnMoreMethodsRoute}">En savoir plus</a> sur nos plans et nos tarifs.</muted-text>`,
note: 'Débloquez les fonctionnalités essentielles pour votre entreprise, notamment :',
},
note: 'Débloquez nos fonctionnalités les plus puissantes, notamment :',
benefits: {
startsAtFull: (learnMoreMethodsRoute: string, formattedPrice: string, hasTeam2025Pricing: boolean) =>
Expand Down
6 changes: 6 additions & 0 deletions src/languages/it.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6977,6 +6977,12 @@ Richiedi dettagli sulle spese come ricevute e descrizioni, imposta limiti e valo
},
commonFeatures: {
title: 'Passa al piano Control',
collect: {
title: 'Passa al piano Collect',
startsAtFull: (learnMoreMethodsRoute: string, formattedPrice: string, hasTeam2025Pricing: boolean) =>
`<muted-text>Il piano Collect parte da <strong>${formattedPrice}</strong> ${hasTeam2025Pricing ? `per utente al mese.` : `per membro attivo al mese.`} <a href="${learnMoreMethodsRoute}">Scopri di più</a> sui nostri piani e prezzi.</muted-text>`,
note: 'Sblocca le funzioni essenziali per la tua attività, tra cui:',
},
note: 'Sblocca le nostre funzioni più potenti, tra cui:',
benefits: {
startsAtFull: (learnMoreMethodsRoute: string, formattedPrice: string, hasTeam2025Pricing: boolean) =>
Expand Down
6 changes: 6 additions & 0 deletions src/languages/ja.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6903,6 +6903,12 @@ ${reportName}
},
commonFeatures: {
title: 'Controlプランにアップグレード',
collect: {
title: 'Collectプランにアップグレード',
startsAtFull: (learnMoreMethodsRoute: string, formattedPrice: string, hasTeam2025Pricing: boolean) =>
`<muted-text>Collect プランは <strong>${formattedPrice}</strong> ${hasTeam2025Pricing ? `メンバー1人あたり月額` : `アクティブメンバー1人あたり月額`} からご利用いただけます。プランと料金の詳細は <a href="${learnMoreMethodsRoute}">こちら</a> をご覧ください。</muted-text>`,
note: '以下を含む、ビジネスに欠かせない機能をアンロックしましょう:',
},
note: '以下を含む、最も強力な機能をアンロックしましょう:',
benefits: {
startsAtFull: (learnMoreMethodsRoute: string, formattedPrice: string, hasTeam2025Pricing: boolean) =>
Expand Down
6 changes: 6 additions & 0 deletions src/languages/nl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6956,6 +6956,12 @@ Vereis onkostendetails zoals bonnen en beschrijvingen, stel limieten en standaar
},
commonFeatures: {
title: 'Upgrade naar het Control-abonnement',
collect: {
title: 'Upgrade naar het Collect-abonnement',
startsAtFull: (learnMoreMethodsRoute: string, formattedPrice: string, hasTeam2025Pricing: boolean) =>
`<muted-text>Het Collect-abonnement begint bij <strong>${formattedPrice}</strong> ${hasTeam2025Pricing ? `per lid per maand.` : `per actieve deelnemer per maand.`} <a href="${learnMoreMethodsRoute}">Meer informatie</a> over onze abonnementen en prijzen.</muted-text>`,
note: 'Ontgrendel essentiële functies voor je bedrijf, waaronder:',
},
note: 'Ontgrendel onze krachtigste functies, waaronder:',
benefits: {
startsAtFull: (learnMoreMethodsRoute: string, formattedPrice: string, hasTeam2025Pricing: boolean) =>
Expand Down
6 changes: 6 additions & 0 deletions src/languages/pl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6950,6 +6950,12 @@ Wymagaj szczegółów wydatków, takich jak paragony i opisy, ustawiaj limity i
},
commonFeatures: {
title: 'Ulepsz do planu Control',
collect: {
title: 'Ulepsz do planu Collect',
startsAtFull: (learnMoreMethodsRoute: string, formattedPrice: string, hasTeam2025Pricing: boolean) =>
`<muted-text>Plan Collect zaczyna się od <strong>${formattedPrice}</strong> ${hasTeam2025Pricing ? `za użytkownika miesięcznie.` : `na aktywnego członka miesięcznie.`} <a href="${learnMoreMethodsRoute}">Dowiedz się więcej</a> o naszych planach i cenach.</muted-text>`,
note: 'Odblokuj kluczowe funkcje dla swojej firmy, w tym:',
},
note: 'Odblokuj nasze najpotężniejsze funkcje, w tym:',
benefits: {
startsAtFull: (learnMoreMethodsRoute: string, formattedPrice: string, hasTeam2025Pricing: boolean) =>
Expand Down
6 changes: 6 additions & 0 deletions src/languages/pt-BR.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6954,6 +6954,12 @@ Exija dados de despesas como recibos e descrições, defina limites e padrões e
},
commonFeatures: {
title: 'Faça upgrade para o plano Control',
collect: {
title: 'Faça upgrade para o plano Collect',
startsAtFull: (learnMoreMethodsRoute: string, formattedPrice: string, hasTeam2025Pricing: boolean) =>
`<muted-text>O plano Collect começa em <strong>${formattedPrice}</strong> ${hasTeam2025Pricing ? `por membro por mês.` : `por membro ativo por mês.`} <a href="${learnMoreMethodsRoute}">Saiba mais</a> sobre nossos planos e preços.</muted-text>`,
note: 'Desbloqueie os recursos essenciais para o seu negócio, incluindo:',
},
note: 'Desbloqueie nossos recursos mais avançados, incluindo:',
benefits: {
startsAtFull: (learnMoreMethodsRoute: string, formattedPrice: string, hasTeam2025Pricing: boolean) =>
Expand Down
6 changes: 6 additions & 0 deletions src/languages/zh-hans.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6780,6 +6780,12 @@ ${reportName}
},
commonFeatures: {
title: '升级到 Control 方案',
collect: {
title: '升级到 Collect 方案',
startsAtFull: (learnMoreMethodsRoute: string, formattedPrice: string, hasTeam2025Pricing: boolean) =>
`<muted-text>Collect 方案起价为 <strong>${formattedPrice}</strong> ${hasTeam2025Pricing ? `每位成员每月。` : `每位活跃成员每月。`},<a href="${learnMoreMethodsRoute}">了解更多</a>我们的方案和定价。</muted-text>`,
note: '解锁助力您业务发展的核心功能,包括:',
},
note: '解锁我们最强大的功能,包括:',
benefits: {
startsAtFull: (learnMoreMethodsRoute: string, formattedPrice: string, hasTeam2025Pricing: boolean) =>
Expand Down
1 change: 1 addition & 0 deletions src/libs/Navigation/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -429,6 +429,7 @@ type SettingsNavigatorParamList = {
// eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md
backTo?: Routes;
categoryId?: string;
upgradePlanType?: ValueOf<typeof CONST.POLICY.TYPE>;
};
[SCREENS.WORKSPACE.DOWNGRADE]: {
policyID?: string;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -98,12 +98,12 @@ function DynamicWorkspaceOverviewPlanTypePage({policy}: WithPolicyProps) {
// still pick Team/Corporate. Route any selection from a Submit policy to the
// upgrade screen — the polished Submit-specific upgrade UX ships in #87263.
if (policyID && policy?.type === CONST.POLICY.TYPE.SUBMIT && (currentPlan === CONST.POLICY.TYPE.TEAM || currentPlan === CONST.POLICY.TYPE.CORPORATE)) {
Navigation.navigate(ROUTES.WORKSPACE_UPGRADE.getRoute(policyID));
Navigation.navigate(ROUTES.WORKSPACE_UPGRADE.getRoute(policyID, undefined, undefined, currentPlan));
return;
}

if (policyID && policy?.type === CONST.POLICY.TYPE.TEAM && currentPlan === CONST.POLICY.TYPE.CORPORATE) {
Navigation.navigate(ROUTES.WORKSPACE_UPGRADE.getRoute(policyID));
Navigation.navigate(ROUTES.WORKSPACE_UPGRADE.getRoute(policyID, undefined, undefined, currentPlan));
return;
}

Expand Down
38 changes: 28 additions & 10 deletions src/pages/workspace/upgrade/GenericFeaturesView.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import React from 'react';
import {View} from 'react-native';
import type {ValueOf} from 'type-fest';
import Button from '@components/Button';
import Icon from '@components/Icon';
import {loadIllustration} from '@components/Icon/IllustrationLoader';
Expand All @@ -13,6 +14,7 @@ import useLocalize from '@hooks/useLocalize';
import useResponsiveLayout from '@hooks/useResponsiveLayout';
import useThemeStyles from '@hooks/useThemeStyles';
import Navigation from '@libs/Navigation/Navigation';
import CONST from '@src/CONST';
import ROUTES from '@src/ROUTES';
import type {Route} from '@src/ROUTES';

Expand All @@ -23,9 +25,11 @@ type GenericFeaturesViewProps = {
formattedPrice: string;
policyID?: string;
backTo?: Route;
/** Target plan the user chose to upgrade to. When Collect (TEAM), render Collect copy instead of the default Control copy */
upgradePlanType?: ValueOf<typeof CONST.POLICY.TYPE>;
};

function GenericFeaturesView({onUpgrade, buttonDisabled, loading, formattedPrice, backTo, policyID}: GenericFeaturesViewProps) {
function GenericFeaturesView({onUpgrade, buttonDisabled, loading, formattedPrice, backTo, policyID, upgradePlanType}: GenericFeaturesViewProps) {
const styles = useThemeStyles();
const {translate} = useLocalize();
const {environmentURL} = useEnvironment();
Expand All @@ -34,12 +38,26 @@ function GenericFeaturesView({onUpgrade, buttonDisabled, loading, formattedPrice
const {isExtraSmallScreenWidth} = useResponsiveLayout();
const hasTeam2025Pricing = useHasTeam2025Pricing();

const benefits = [
translate('workspace.upgrade.commonFeatures.benefits.benefit1'),
translate('workspace.upgrade.commonFeatures.benefits.benefit2'),
translate('workspace.upgrade.commonFeatures.benefits.benefit3'),
translate('workspace.upgrade.commonFeatures.benefits.benefit4'),
];
const isCollectUpgrade = upgradePlanType === CONST.POLICY.TYPE.TEAM;
const title = isCollectUpgrade ? translate('workspace.upgrade.commonFeatures.collect.title') : translate('workspace.upgrade.commonFeatures.title');
const startsAtFull = isCollectUpgrade
? translate('workspace.upgrade.commonFeatures.collect.startsAtFull', learnMoreMethodsRoute, formattedPrice, hasTeam2025Pricing)
: translate('workspace.upgrade.commonFeatures.benefits.startsAtFull', learnMoreMethodsRoute, formattedPrice, hasTeam2025Pricing);
const note = isCollectUpgrade ? translate('workspace.upgrade.commonFeatures.collect.note') : translate('workspace.upgrade.commonFeatures.note');

const benefits = isCollectUpgrade
? [
translate('subscription.yourPlan.collect.benefit1'),
translate('subscription.yourPlan.collect.benefit2'),
translate('subscription.yourPlan.collect.benefit3'),
translate('subscription.yourPlan.collect.benefit4'),
]
: [
translate('workspace.upgrade.commonFeatures.benefits.benefit1'),
translate('workspace.upgrade.commonFeatures.benefits.benefit2'),
translate('workspace.upgrade.commonFeatures.benefits.benefit3'),
translate('workspace.upgrade.commonFeatures.benefits.benefit4'),
];

return (
<View style={[styles.m5, styles.highlightBG, styles.br4, styles.workspaceUpgradeIntroBox({isExtraSmallScreenWidth})]}>
Expand All @@ -51,8 +69,8 @@ function GenericFeaturesView({onUpgrade, buttonDisabled, loading, formattedPrice
/>
</View>
<View style={policyID ? styles.mb5 : styles.mb4}>
<Text style={[styles.textHeadlineH1, styles.mb4]}>{translate('workspace.upgrade.commonFeatures.title')}</Text>
<Text style={[styles.textNormal, styles.textSupporting, styles.mb4]}>{translate('workspace.upgrade.commonFeatures.note')}</Text>
<Text style={[styles.textHeadlineH1, styles.mb4]}>{title}</Text>
<Text style={[styles.textNormal, styles.textSupporting, styles.mb4]}>{note}</Text>
{benefits.map((benefit) => (
<View
key={benefit}
Expand All @@ -63,7 +81,7 @@ function GenericFeaturesView({onUpgrade, buttonDisabled, loading, formattedPrice
</View>
))}
<View style={[styles.textNormal, styles.textSupporting, styles.mt4, styles.flexRow]}>
<RenderHTML html={translate('workspace.upgrade.commonFeatures.benefits.startsAtFull', learnMoreMethodsRoute, formattedPrice, hasTeam2025Pricing)} />
<RenderHTML html={startsAtFull} />
</View>
</View>
{!policyID && (
Expand Down
11 changes: 8 additions & 3 deletions src/pages/workspace/upgrade/UpgradeIntro.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,11 @@ type Props = {
isDistanceRateUpgrade?: boolean;
policyID?: string;
backTo?: Route;
/** Target plan the user chose to upgrade to (drives Collect vs Control copy/pricing in the generic view) */
upgradePlanType?: ValueOf<typeof CONST.POLICY.TYPE>;
};

function UpgradeIntro({feature, onUpgrade, buttonDisabled, loading, isCategorizing, isDistanceRateUpgrade, isReporting, policyID, backTo}: Props) {
function UpgradeIntro({feature, onUpgrade, buttonDisabled, loading, isCategorizing, isDistanceRateUpgrade, isReporting, policyID, backTo, upgradePlanType}: Props) {
const styles = useThemeStyles();
const {isExtraSmallScreenWidth} = useResponsiveLayout();
const [policy] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY}${policyID}`);
Expand All @@ -58,12 +60,14 @@ function UpgradeIntro({feature, onUpgrade, buttonDisabled, loading, isCategorizi
const formattedPrice = useMemo(() => {
const upgradeCurrency = Object.hasOwn(CONST.SUBSCRIPTION_PRICES, preferredCurrency) ? preferredCurrency : CONST.PAYMENT_CARD_CURRENCY.USD;
// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
const shouldUseTeamPricing = isCategorizing || isDistanceRateUpgrade || isReporting || isSubmitFeature;
const matchesTeamPricingHeuristics = isCategorizing || isDistanceRateUpgrade || isReporting || isSubmitFeature || upgradePlanType === CONST.POLICY.TYPE.TEAM;
// An explicit upgradePlanType (chosen in the Plan RHP) is authoritative over the feature-based heuristics, so pricing matches the plan title shown in GenericFeaturesView.
const shouldUseTeamPricing = upgradePlanType === CONST.POLICY.TYPE.CORPORATE ? false : matchesTeamPricingHeuristics;
return `${convertToShortDisplayString(
CONST.SUBSCRIPTION_PRICES[upgradeCurrency][shouldUseTeamPricing ? CONST.POLICY.TYPE.TEAM : CONST.POLICY.TYPE.CORPORATE][CONST.SUBSCRIPTION.TYPE.ANNUAL],
upgradeCurrency,
)} `;
}, [preferredCurrency, isCategorizing, isDistanceRateUpgrade, isReporting, isSubmitFeature]);
}, [preferredCurrency, isCategorizing, isDistanceRateUpgrade, isReporting, isSubmitFeature, upgradePlanType]);

const allIconNames = Object.values(CONST.UPGRADE_FEATURE_INTRO_MAPPING)
.map((feat) => feat?.icon)
Expand Down Expand Up @@ -117,6 +121,7 @@ function UpgradeIntro({feature, onUpgrade, buttonDisabled, loading, isCategorizi
loading={loading}
policyID={policyID}
backTo={backTo}
upgradePlanType={upgradePlanType}
/>
);
}
Expand Down
Loading
Loading