Add persistent bottom-right product marketing window - #96141
Conversation
|
Hey, I noticed you changed If you want to automatically generate translations for other locales, an Expensify employee will have to:
Alternatively, if you are an external contributor, you can run the translation script locally with your own OpenAI API key. To learn more, try running: npx ts-node ./scripts/generateTranslations.ts --helpTypically, you'd want to translate only what you changed by running |
8546c9a to
292fa34
Compare
Codecov Report✅ Changes either increased or maintained existing code coverage, great job!
|
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 292fa34b95
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
|
This PR uses productMarketingWindow:expensePolicyPdf-2026-07 to persist the card dismissal. The DismissProductTraining API needs to support namespaced values in the format productMarketingWindow:. It currently returns jsonCode: 402, so the dismissal is not persisted by the backend. Could an internal engineer confirm whether this backend support is already being handled? |
|
Let us know when this is ready for a @Expensify/design review, thanks! |
The frontend UI is ready for Design review. Please take a look, backend work is being handled separately. Thanks! cc: @shawnborton @Expensify/design |
|
🚧 shawnborton has triggered a test Expensify/App build. You can view the workflow run here. |
|
First thing I notice right off the bat in the videos: don't we want these to be themed opposite the user's current setting? |
|
@shawnborton Ops, i missed to push few commits, can you please retrigger the build ? |
|
Agree Danny, we should match the mockups. I'm heading out for the day shortly but @dannymcclain can help trigger new builds! |
🦜 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 e883beb145c..010ad2a6a10 100644
--- a/src/languages/de.ts
+++ b/src/languages/de.ts
@@ -10293,9 +10293,9 @@ Fügen Sie weitere Ausgabelimits hinzu, um den Cashflow Ihres Unternehmens zu sc
productMarketingWindow: {
roleTypes: {
admin: {
- heading: 'Neue Rollentypen für Administratoren',
- body: 'Gib deinem Team mit neuen Rollen für Karten-, Personen- und Zahlungsadministratoren detailliertere Berechtigungen.',
- cta: 'Ausprobieren',
+ heading: 'Neue Rollentypen für Admins',
+ body: 'Geben Sie Ihrem Team detailliertere Berechtigungen mit neuen Administratorrollen für Karten, Personen und Zahlungen.',
+ cta: 'Probieren Sie es aus',
},
},
},
diff --git a/src/languages/es.ts b/src/languages/es.ts
index 979af3e5110..1fbe3e7c74a 100644
--- a/src/languages/es.ts
+++ b/src/languages/es.ts
@@ -10454,8 +10454,8 @@ El plan Controlar empieza en 9 $ por miembro activo al mes.`,
productMarketingWindow: {
roleTypes: {
admin: {
- heading: 'Nuevos tipos de roles para administradores',
- body: 'Otorga a tu equipo permisos más granulares con los nuevos roles de administrador de tarjetas, personas y pagos.',
+ heading: 'Nuevos tipos de roles para admins',
+ body: 'Ofrece a tu equipo permisos más granulares con los nuevos roles de administrador de tarjetas, personas y pagos.',
cta: 'Pruébalo',
},
},
diff --git a/src/languages/fr.ts b/src/languages/fr.ts
index f0e9fbdbccc..7f3349f7b1a 100644
--- a/src/languages/fr.ts
+++ b/src/languages/fr.ts
@@ -10328,8 +10328,8 @@ Ajoutez davantage de règles de dépenses pour protéger la trésorerie de l’e
roleTypes: {
admin: {
heading: 'Nouveaux types de rôles pour les administrateurs',
- body: 'Accordez à votre équipe des autorisations plus granulaires grâce aux nouveaux rôles d’administrateur des cartes, des personnes et des paiements.',
- cta: 'Essayer',
+ body: 'Offrez à votre équipe des autorisations plus granulaires avec de nouveaux rôles d’administrateur pour les cartes, les personnes et les paiements.',
+ cta: 'EssayerEssaye',
},
},
},
diff --git a/src/languages/it.ts b/src/languages/it.ts
index f1d3e2f4860..159bb9669db 100644
--- a/src/languages/it.ts
+++ b/src/languages/it.ts
@@ -10270,8 +10270,8 @@ Aggiungi altre regole di spesa per proteggere il flusso di cassa aziendale.`,
roleTypes: {
admin: {
heading: 'Nuovi tipi di ruolo per gli amministratori',
- body: 'Offri al tuo team autorizzazioni più granulari con i nuovi ruoli di amministratore delle carte, delle persone e dei pagamenti.',
- cta: 'Prova ora',
+ body: 'Offri al tuo team autorizzazioni più granulari con i nuovi ruoli di amministratore per carte, persone e pagamenti.',
+ cta: 'Provalo',
},
},
},
diff --git a/src/languages/ja.ts b/src/languages/ja.ts
index 7a48b6b64e8..c8fd8084f12 100644
--- a/src/languages/ja.ts
+++ b/src/languages/ja.ts
@@ -10128,13 +10128,7 @@ ${reportName}`,
},
},
productMarketingWindow: {
- roleTypes: {
- admin: {
- heading: '管理者向けの新しい役割タイプ',
- body: '新しいカード管理者、ユーザー管理者、支払い管理者の役割により、チームによりきめ細かな権限を付与できます。',
- cta: 'お試しください',
- },
- },
+ roleTypes: {admin: {heading: '管理者向けの新しいロールタイプ', body: '新しいカード、メンバー、支払いの各管理者ロールで、チームにより細かな権限を付与できます。', cta: '試してみる'}},
},
productTrainingTooltip: {
conciergeLHNGBR: '<tooltip>まずは<strong>こちらから!</strong></tooltip>',
diff --git a/src/languages/nl.ts b/src/languages/nl.ts
index b62dad32a24..38026a2fe68 100644
--- a/src/languages/nl.ts
+++ b/src/languages/nl.ts
@@ -10240,7 +10240,7 @@ er bestedingsregels toe om de kasstroom van het bedrijf te beschermen.`,
roleTypes: {
admin: {
heading: 'Nieuwe roltypen voor beheerders',
- body: 'Geef je team gedetailleerdere rechten met de nieuwe beheerdersrollen voor kaarten, personen en betalingen.',
+ body: 'Geef je team meer gedetailleerde toegangsrechten met nieuwe kaart-, personen- en betalingsbeheerrollen.',
cta: 'Probeer het uit',
},
},
diff --git a/src/languages/pl.ts b/src/languages/pl.ts
index 614bd9dd823..c7ff7a962bd 100644
--- a/src/languages/pl.ts
+++ b/src/languages/pl.ts
@@ -10208,9 +10208,9 @@ Dodaj więcej zasad wydatków, żeby chronić płynność finansową firmy.`,
productMarketingWindow: {
roleTypes: {
admin: {
- heading: 'Nowe typy ról administratora',
- body: 'Nadaj zespołowi bardziej szczegółowe uprawnienia dzięki nowym rolom administratora kart, osób i płatności.',
- cta: 'Wypróbuj',
+ heading: 'Nowe typy ról dla administratorów',
+ body: 'Daj swojemu zespołowi bardziej szczegółowe uprawnienia dzięki nowym rolom administratora kart, osób i płatności.',
+ cta: 'Wypróbuj to',
},
},
},
diff --git a/src/languages/pt-BR.ts b/src/languages/pt-BR.ts
index 9fba459c752..c90af64ed27 100644
--- a/src/languages/pt-BR.ts
+++ b/src/languages/pt-BR.ts
@@ -10229,7 +10229,7 @@ Adicione mais regras de gasto para proteger o fluxo de caixa da empresa.`,
roleTypes: {
admin: {
heading: 'Novos tipos de função para administradores',
- body: 'Dê à sua equipe permissões mais granulares com as novas funções de administrador de cartões, pessoas e pagamentos.',
+ body: 'Dê à sua equipe permissões mais granulares com novos papéis de administrador para cartões, pessoas e pagamentos.',
cta: 'Experimente',
},
},
diff --git a/src/languages/zh-hans.ts b/src/languages/zh-hans.ts
index 236425a94d9..ab712395ded 100644
--- a/src/languages/zh-hans.ts
+++ b/src/languages/zh-hans.ts
@@ -9894,15 +9894,7 @@ ${reportName}`,
inviteBoss: '准备好后即可邀请你的上司',
},
},
- productMarketingWindow: {
- roleTypes: {
- admin: {
- heading: '新的管理员角色类型',
- body: '通过新的卡片、成员和付款管理员角色,为团队分配更精细的权限。',
- cta: '试用一下',
- },
- },
- },
+ productMarketingWindow: {roleTypes: {admin: {heading: '新的管理员角色类型', body: '通过新增卡片、成员和付款管理员角色,为你的团队提供更精细的权限控制。', cta: '试用一下'}}},
productTrainingTooltip: {
conciergeLHNGBR: '<tooltip>从<strong>这里</strong>开始!</tooltip>',
accountSwitcher: '<tooltip>您现在可以协同操作另一位用户的账户了!</tooltip>',
Note You can apply these changes to your branch by copying the patch to your clipboard, then running |
|
failing typechecks |
|
@KJ21-ENG pull main please |
|
on it 🏃🏻♂️ |
|
|
|
✋ This PR was not deployed to staging yet because QA is ongoing. It will be automatically deployed to staging after the next production release. |
|
🚧 JS00001 has triggered a test Expensify/App build. You can view the workflow run here. |
|
🧪🧪 Use the links below to test this adhoc build on Android, iOS, and Web. Happy testing! 🧪🧪
|
|
🚀 Deployed to staging by https://github.com/JS00001 in version: 9.4.50-0 🚀
|
|
🤖 No help site changes are required — no draft PR created. I reviewed the changes in this PR against the help site articles under Why:
If the intent was to document the marketing window itself, that's not typically a help-site topic — let me know if you'd like me to reconsider or if there's a specific article you had in mind. @KJ21-ENG — since no help site PR was created, there's nothing to review here. If you disagree and think an article should change, reply with |
|
Deploy Blocker #97834 was identified to be related to this PR. |
|
Deploy Blocker #97837 was identified to be related to this PR. |
|
Looking into these blockers. I haven't investigated them thoroughly yet, but whatever I have seen till now, IMO, we can demote these. Though I will let you know if I find any blocker that is actually some major issue and we should revert the PR or not. cc: @JS00001 |
|
Deploy Blocker #97846 was identified to be related to this PR. |
Explanation of Change
This PR adds a reusable product-marketing window that remains visible across authenticated app navigation until the user dismisses it or completes its CTA.
productUpdateJuly2026; dismissing it does not prevent a later campaign with a different key from appearing.Try it outstores the dismissal first and then opens the selected workspace's Members page. PressingDismissstores the same update key without navigating.nvp_lastDismissedMarketingWindowNVP throughSetNameValuePair, with optimistic Onyx data and rollback data if the request fails.16px,2px, and16pxrespectively.Unit and navigation tests cover audience eligibility, workspace selection, dismissal and rollback behavior, CTA ordering and routing, modal precedence, startup loading, responsive styling, inverse themes, and persistence across route changes.
Fixed Issues
$ #95791
PROPOSAL: #95791 (comment)
Tests
Use eligible accounts that have not previously dismissed
productUpdateJuly2026. Since dismissal is stored at account level, use a second fresh eligible account for the separate dismissal test.Dismiss, andTry it out.Try it out, and verify the window closes before navigation and the selected workspace's Members page opens.Dismiss, and verify the window closes, stays closed during navigation, and remains dismissed after a refresh or restart.Offline tests
Use another eligible admin account that has not dismissed the current update.
Dismissand verify the window closes immediately from the optimistic Onyx update.QA Steps
Same as the Tests and Offline tests sections above on staging. Use fresh eligible admin accounts for the CTA, dismissal, and offline legs, plus one account that is not an admin of any active workspace.
PR Author Checklist
### Fixed Issuessection aboveTestssectionOffline stepssectionQA stepssectionAvatar, I verified the components usingAvatarare working as expected)StyleUtils.getBackgroundAndBorderStyle(theme.componentBG))npm run compress-svg)Avatar, I verified the components usingAvatarare working as expected in all cases)Designlabel and/or tagged@Expensify/designso the design team can review the changes.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
Record_2026-07-30-01-40-24_40d2c9e2d8fb5b5aba3904834590021e.mp4
Android: mWeb Chrome
Record_2026-07-30-02-02-32.mp4
iOS: Native
Screen.Recording.2026-07-30.at.1.44.11.AM.mov
iOS: mWeb Safari
Screen.Recording.2026-07-30.at.1.59.59.AM.mov
MacOS: Chrome / Safari
Screen.Recording.2026-07-30.at.1.51.50.AM.mov