Fix Submit/Collect upgrade RHP to reflect the selected plan#93464
Fix Submit/Collect upgrade RHP to reflect the selected plan#93464MelvinBot wants to merge 2 commits into
Conversation
Propagate the plan chosen in the Plan RHP to the Upgrade RHP and the upgrade action so display copy, pricing, and the upgrade targetType match the selected plan instead of always defaulting to Control. Co-authored-by: Abdelrahman Khattab <abzokhattab@users.noreply.github.com>
This comment has been minimized.
This comment has been minimized.
Code Review@MelvinBot — solid fix for the root cause in #93326. The approach of threading What works well
Issues / concerns1. Collect upgrade still shows Control subtitle copy (minor UX bug)
translate('workspace.upgrade.commonFeatures.note')That string is "Unlock our most powerful features, including:" — reads as Control copy, not Collect. Consider adding a 2. Non-beta Submit → Collect upgrade path not fixed The issue proposal called out fixing both 3. No unit test coverage for the new behavior
4. No validation of
5. Draft PR completeness Before marking ready for review:
Regression risk
VerdictApprove with minor changes — the core fix is correct and well-scoped for the reported bug (Submit workspace + Suggested test plan
|
- Show Collect-specific subtitle copy in GenericFeaturesView (collect.note in all 10 locales) - Whitelist upgradePlanType from the URL to only TEAM/CORPORATE - Add unit tests asserting upgradePlanType=team renders Collect title + Team pricing and upgradePlanType=corporate renders Control title + Corporate pricing Co-authored-by: Abdelrahman Khattab <abzokhattab@users.noreply.github.com>
🦜 Polyglot Parrot! 🦜Squawk! Looks like you added some shiny new English strings. Allow me to parrot them back to you in other tongues: View the translation diffdiff --git a/src/languages/de.ts b/src/languages/de.ts
index 59dd2e09a48..d4b20d22e42 100644
--- a/src/languages/de.ts
+++ b/src/languages/de.ts
@@ -6995,10 +6995,10 @@ 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',
+ title: 'Zum Collect-Tarif upgraden',
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:',
+ `<muted-text>Der Collect-Tarif beginnt bei <strong>${formattedPrice}</strong> ${hasTeam2025Pricing ? `pro Mitglied pro Monat.` : `pro aktivem Mitglied und Monat.`} <a href="${learnMoreMethodsRoute}">Erfahren Sie mehr</a> über unsere Tarife und Preise.</muted-text>`,
+ note: 'Schalten Sie essentielle Funktionen für den Betrieb Ihres Unternehmens frei, darunter:',
},
note: 'Schalte unsere leistungsstärksten Funktionen frei, darunter:',
benefits: {
diff --git a/src/languages/es.ts b/src/languages/es.ts
index 18be0c27b65..e0aeedee2f8 100644
--- a/src/languages/es.ts
+++ b/src/languages/es.ts
@@ -1753,7 +1753,6 @@ const translations: TranslationDeepObject<typeof en> = {
}
},
[CONST.NEXT_STEP.MESSAGE_KEY.WAITING_TO_MARK_AS_DONE]: (actor, actorType, _eta, _etaType) => {
- // eslint-disable-next-line default-case
switch (actorType) {
case CONST.NEXT_STEP.ACTOR_TYPE.CURRENT_USER:
return `Esperando a que <strong>tú</strong> lo marques como listo.`;
@@ -6979,9 +6978,9 @@ ${amount} para ${merchant} - ${date}`,
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:',
+ startsAtFull: (learnMoreMethodsRoute: string, formattedPrice: string, hasTeam2025Pricing: boolean) =>
+ `<muted-text>El plan Recopilar empieza 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 funciones esenciales para gestionar tu negocio, como:',
},
note: 'Desbloquea nuestras funciones más potentes, incluyendo:',
benefits: {
diff --git a/src/languages/fr.ts b/src/languages/fr.ts
index 54d928aef65..572e56cd864 100644
--- a/src/languages/fr.ts
+++ b/src/languages/fr.ts
@@ -1,6 +1,7 @@
/**
* _____ __ __
* / ___/__ ___ ___ _______ _/ /____ ___/ /
+ * / (_ / -_) _ \/ -_) __/ _ \`/ __/ -_) _ /
* \___/\__/_//_/\__/_/ \_,_/\__/\__/\_,_/
*
* This file was automatically generated. Please consider these alternatives before manually editing it:
@@ -862,7 +863,7 @@ const translations: TranslationDeepObject<typeof en> = {
beginningOfChatHistory: (users: string) => `Cette discussion est avec ${users}.`,
beginningOfChatHistoryPolicyExpenseChat: (workspaceName: string, submitterDisplayName: string) =>
`C’est ici que <strong>${submitterDisplayName}</strong> soumettra des dépenses à <strong>${workspaceName}</strong>. Utilisez simplement le bouton +.`,
- beginningOfChatHistoryPolicyExpenseChatTrack: 'C\u2019est ici que vous suivrez vos dépenses',
+ beginningOfChatHistoryPolicyExpenseChatTrack: 'C’est ici que vous suivrez vos dépenses',
beginningOfChatHistorySelfDM: 'Ceci est votre espace personnel. Utilisez-le pour vos notes, tâches, brouillons et rappels.',
beginningOfChatHistorySystemDM: 'Bienvenue ! Procédons à la configuration.',
chatWithAccountManager: 'Discutez avec votre gestionnaire de compte ici',
@@ -7022,8 +7023,8 @@ Rendez obligatoires des informations de dépense comme les reçus et les descrip
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 :',
+ `<muted-text>Le forfait 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 forfaits et nos tarifs.</muted-text>`,
+ note: 'Débloquez les fonctionnalités essentielles pour gérer votre entreprise, notamment :',
},
note: 'Débloquez nos fonctionnalités les plus puissantes, notamment :',
benefits: {
diff --git a/src/languages/it.ts b/src/languages/it.ts
index 9492635af08..8ed0a7ae5f1 100644
--- a/src/languages/it.ts
+++ b/src/languages/it.ts
@@ -6980,8 +6980,8 @@ Richiedi dettagli sulle spese come ricevute e descrizioni, imposta limiti e valo
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:',
+ `<muted-text>Il piano Collect parte da <strong>${formattedPrice}</strong> ${hasTeam2025Pricing ? `per membro 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 gestire la tua attività, tra cui:',
},
note: 'Sblocca le nostre funzioni più potenti, tra cui:',
benefits: {
diff --git a/src/languages/ja.ts b/src/languages/ja.ts
index 89795fcd605..4837b5037f4 100644
--- a/src/languages/ja.ts
+++ b/src/languages/ja.ts
@@ -6904,10 +6904,10 @@ ${reportName}
commonFeatures: {
title: 'Controlプランにアップグレード',
collect: {
- title: '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: '以下を含む、ビジネスに欠かせない機能をアンロックしましょう:',
+ `<muted-text>Collect プランは<strong>${formattedPrice}</strong> ${hasTeam2025Pricing ? `メンバー1人あたり月額` : `アクティブメンバー1人あたり月額`}からご利用いただけます。プランと料金の詳細は<a href="${learnMoreMethodsRoute}">こちら</a>をご覧ください。</muted-text>`,
+ note: 'ビジネス運営に欠かせない、次の機能を利用できるようになります。',
},
note: '以下を含む、最も強力な機能をアンロックしましょう:',
benefits: {
diff --git a/src/languages/nl.ts b/src/languages/nl.ts
index a69686930e7..da062afc996 100644
--- a/src/languages/nl.ts
+++ b/src/languages/nl.ts
@@ -6960,7 +6960,7 @@ Vereis onkostendetails zoals bonnen en beschrijvingen, stel limieten en standaar
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 essentiële functies om je bedrijf te runnen, waaronder:',
},
note: 'Ontgrendel onze krachtigste functies, waaronder:',
benefits: {
diff --git a/src/languages/pl.ts b/src/languages/pl.ts
index d353a9a9aca..f18b419e226 100644
--- a/src/languages/pl.ts
+++ b/src/languages/pl.ts
@@ -6953,8 +6953,8 @@ Wymagaj szczegółów wydatków, takich jak paragony i opisy, ustawiaj limity i
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:',
+ `<muted-text>Plan Collect zaczyna się od <strong>${formattedPrice}</strong> ${hasTeam2025Pricing ? `za członka miesięcznie.` : `za aktywnego członka miesięcznie.`} <a href="${learnMoreMethodsRoute}">Dowiedz się więcej</a> o naszych planach i cenach.</muted-text>`,
+ note: 'Odblokuj kluczowe funkcje do prowadzenia firmy, w tym:',
},
note: 'Odblokuj nasze najpotężniejsze funkcje, w tym:',
benefits: {
diff --git a/src/languages/pt-BR.ts b/src/languages/pt-BR.ts
index 4ad3cfce0bf..53f6bd1b4b5 100644
--- a/src/languages/pt-BR.ts
+++ b/src/languages/pt-BR.ts
@@ -6955,10 +6955,10 @@ 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',
+ title: 'Fazer 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 recursos essenciais para administrar seu negócio, incluindo:',
},
note: 'Desbloqueie nossos recursos mais avançados, incluindo:',
benefits: {
diff --git a/src/languages/zh-hans.ts b/src/languages/zh-hans.ts
index d5bfb573152..241fc15cb88 100644
--- a/src/languages/zh-hans.ts
+++ b/src/languages/zh-hans.ts
@@ -6784,7 +6784,7 @@ ${reportName}
title: '升级到 Collect 方案',
startsAtFull: (learnMoreMethodsRoute: string, formattedPrice: string, hasTeam2025Pricing: boolean) =>
`<muted-text>Collect 方案起价为 <strong>${formattedPrice}</strong> ${hasTeam2025Pricing ? `每位成员每月。` : `每位活跃成员每月。`},<a href="${learnMoreMethodsRoute}">了解更多</a>我们的方案和定价。</muted-text>`,
- note: '解锁助力您业务发展的核心功能,包括:',
+ note: '解锁运营业务所需的核心功能,包括:',
},
note: '解锁我们最强大的功能,包括:',
benefits: {
Note You can apply these changes to your branch by copying the patch to your clipboard, then running |
|
Thanks for the thorough review! I pushed 1. Collect upgrade showed Control subtitle copy ✅ FixedAdded a Collect-specific
2. Non-beta Submit → Collect path — confirmed out of scopeThis is intentional, and there isn't a clean way to fix it in this PR. The branch that honors canAccessSubmitWorkspaceFeatures = isSubmitPolicy(policy) && isSubmit2026BetaEnabled;When the beta is off, the fallback is 3. No unit test coverage ✅ AddedAdded two tests to
All 6 tests in the suite pass locally. 4. No validation of
|
Explanation of Change
On a Submit workspace, choosing Collect in the Plan RHP and pressing Upgrade opened the Control upgrade RHP (wrong copy, wrong pricing, and the upgrade itself targeted Control). The same root cause produced the wrong RHP when a Collect workspace tried to upgrade. The upgrade screen never knew which plan the user actually picked — it always fell back to Control's intro view.
This change threads the plan the user selected in the Plan RHP through to the Upgrade RHP and the upgrade action:
ROUTES.WORKSPACE_UPGRADEnow accepts an optionalupgradePlanTypequery param, and the corresponding navigation param was added inNavigation/types.ts.DynamicWorkspaceOverviewPlanTypePagepasses the selected plan (currentPlan) when navigating to the upgrade screen for Submit→Team/Corporate and Team→Corporate.WorkspaceUpgradePagereadsupgradePlanTypeand uses it to drive the actualupgradeSubmittargetType, and forwards it toUpgradeIntro.UpgradeIntrousesupgradePlanTypeto select Team (Collect) pricing, andGenericFeaturesViewrenders Collect-specific title, "starts at" copy, and benefit bullets when the selected plan is Collect.workspace.upgrade.commonFeatures.collect.title/.startsAtFull) to all 10 locale files; Collect benefit bullets reuse the existingsubscription.yourPlan.collect.*strings.Fixed Issues
$ #93326
PROPOSAL: #93326 (comment)
Tests
// TODO: The human co-author must fill out the tests you ran before marking this PR as "ready for review".
// Please describe what tests you performed that validate your changes worked.
submit2026beta).Offline tests
Same as Tests.
QA Steps
// TODO: These must be filled out, or the issue title must include "[No QA]."
PR Author Checklist
### Fixed Issuessection aboveTestssectionOffline stepssectionQA stepssectiontoggleReportand notonIconClick)src/languages/*files and using the translation methodSTYLE.md) were followedAvatar, I verified the components usingAvatarare working as expected)StyleUtils.getBackgroundAndBorderStyle(theme.componentBG))npm run compress-svg)Avataris modified, I verified thatAvataris working as expected in all cases)Designlabel and/or tagged@Expensify/designso the design team can review the changes.ScrollViewcomponent to make it scrollable when more elements are added to the page.mainbranch was merged into this PR after a review, I tested again and verified the outcome was still expected according to theTeststeps.Screenshots/Videos
Android: Native
Android: mWeb Chrome
iOS: Native
iOS: mWeb Safari
MacOS: Chrome / Safari