Skip to content

Show an error when the default workspace does not have travel enabled - #98484

Open
blimpich wants to merge 7 commits into
mainfrom
blimpich-travelDefaultWorkspaceGuard
Open

Show an error when the default workspace does not have travel enabled#98484
blimpich wants to merge 7 commits into
mainfrom
blimpich-travelDefaultWorkspaceGuard

Conversation

@blimpich

@blimpich blimpich commented Aug 12, 2026

Copy link
Copy Markdown
Contributor

Explanation of Change

Today a member whose default workspace does not have travel enabled can still book travel from a different, travel-enabled workspace. They get sent into Expensify Travel without a travel profile, so they never show up in that company's traveler list and their booking does not behave as expected. This change stops the booking at the point of the click and shows a modal naming their current default workspace, telling them to change it before they book. Setting up travel on a workspace is unaffected — only the booking hand-off is blocked, and the wording of the message comes from the issue.

Fixed Issues

$ #92551
PROPOSAL: #92551 (comment)

Tests

  • Verify that no errors appear in the JS console

Offline tests

QA Steps

Copy pasted from GH issue:

Precondition: expensifail account with enabled Travel feature. Has at least 1 booked trip (e.g. flight) in the past.

  1. In NewDot, navigate to Workspace > Members
  2. Click the "Invite member" button and invite the secondary "New User" email (no Expensify account)
  3. Navigate to the Global Create (+) menu and select "Book Travel" > "Book Travel"
  4. Within the Expensify Travel interface( staging.travel.expensify.com), navigate to Program > Users
  5. Repeat step 2 and invite existing Expensify user.
  6. confirm that you see an error modal telling you that you need to either enable travel on your default workspace or you need to switch your default workspace to one that is travel enabled

Screenshots/Videos

Screenshot 2026-08-12 at 1 21 23 PM

@OSBotify

This comment has been minimized.

@codecov

codecov Bot commented Aug 12, 2026

Copy link
Copy Markdown

Codecov Report

✅ Changes either increased or maintained existing code coverage, great job!

Files with missing lines Coverage Δ
src/components/BookTravelButton.tsx 83.75% <100.00%> (+2.17%) ⬆️
src/libs/PolicyUtils.ts 77.69% <100.00%> (+0.03%) ⬆️
...bar/FABPopoverContent/menuItems/TravelMenuItem.tsx 4.34% <ø> (+0.34%) ⬆️
...nents/Navigation/QuickCreationActionsBar/index.tsx 87.20% <0.00%> (+1.00%) ⬆️
... and 30 files with indirect coverage changes

@OSBotify

Copy link
Copy Markdown
Contributor

🦜 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 diff
diff --git a/src/languages/de.ts b/src/languages/de.ts
index b82889e2..77b778e1 100644
--- a/src/languages/de.ts
+++ b/src/languages/de.ts
@@ -4495,8 +4495,7 @@ ${amount} für ${merchant} – ${date}`,
         },
         defaultWorkspaceTravelDisabled: {
             title: 'Reisen ist nicht aktiviert',
-            message: ({workspaceName}: {workspaceName: string}) =>
-                `Ihr aktueller Standard-Arbeitsbereich ist ${workspaceName}, in dem Reisen nicht aktiviert ist. Bitte ändern Sie Ihren Standard-Arbeitsbereich, um buchen zu können!`,
+            message: 'Um zu buchen, aktivieren Sie bitte Reisen in Ihrem Standard-Arbeitsbereich oder wechseln Sie Ihren Standard-Arbeitsbereich zu einem, in dem Reisen aktiviert ist.',
         },
     },
     workspace: {
diff --git a/src/languages/el.ts b/src/languages/el.ts
index 8b177058..2afc34c9 100644
--- a/src/languages/el.ts
+++ b/src/languages/el.ts
@@ -4565,9 +4565,9 @@ ${amount} για ${merchant} - ${date}`,
                 'Γνωρίζατε ότι μπορείτε να κλείνετε και να διαχειρίζεστε ταξίδια με τρένο απευθείας στο Expensify; Και ότι ανεβάζει αυτόματα τις αποδείξεις για εσάς; Την επόμενη φορά απλώς κάντε κράτηση μέσω του <a href="https://travel.expensify.com">Expensify Travel</a> 🚂',
         },
         defaultWorkspaceTravelDisabled: {
-            title: 'Το ταξίδι δεν είναι ενεργοποιημένο',
-            message: ({workspaceName}: {workspaceName: string}) =>
-                `Ο τρέχων προεπιλεγμένος χώρος εργασίας σας είναι ο ${workspaceName}, στον οποίο δεν είναι ενεργοποιημένα τα ταξίδια. Παρακαλούμε αλλάξτε τον προεπιλεγμένο χώρο εργασίας σας για να κάνετε κράτηση!`,
+            title: 'Η ταξιδιωτική λειτουργία δεν είναι ενεργοποιημένη',
+            message:
+                'Για να κάνετε κράτηση, ενεργοποιήστε τα ταξίδια στον προεπιλεγμένο χώρο εργασίας σας ή αλλάξτε τον προεπιλεγμένο χώρο εργασίας σε κάποιον όπου τα ταξίδια είναι ενεργοποιημένα.',
         },
     },
     proactiveAppReview: {
diff --git a/src/languages/es.ts b/src/languages/es.ts
index fafcc4b8..be76f46e 100644
--- a/src/languages/es.ts
+++ b/src/languages/es.ts
@@ -4399,9 +4399,8 @@ ${amount} para ${merchant} - ${date}`,
                 '¿Sabías que puedes reservar y gestionar viajes en tren directamente en Expensify? ¿Y que además sube los recibos automáticamente por ti? La próxima vez, simplemente reserva a través de <a href="https://travel.expensify.com">Expensify Travel</a> 🚂',
         },
         defaultWorkspaceTravelDisabled: {
-            title: 'Viajes no está habilitado',
-            message: ({workspaceName}: {workspaceName: string}) =>
-                `Tu espacio de trabajo predeterminado actual es ${workspaceName}, que no tiene los viajes habilitados. ¡Cambia tu espacio de trabajo predeterminado para poder reservar!`,
+            title: 'Los viajes no están habilitados',
+            message: 'Para reservar, habilita los viajes en tu espacio de trabajo predeterminado o cambia tu espacio de trabajo predeterminado a uno que tenga los viajes habilitados.',
         },
     },
     proactiveAppReview: {
diff --git a/src/languages/fr.ts b/src/languages/fr.ts
index c2a37904..db93ece4 100644
--- a/src/languages/fr.ts
+++ b/src/languages/fr.ts
@@ -4504,8 +4504,7 @@ ${amount} pour ${merchant} - ${date}`,
         },
         defaultWorkspaceTravelDisabled: {
             title: 'Voyage n’est pas activé',
-            message: ({workspaceName}: {workspaceName: string}) =>
-                `Votre espace de travail par défaut actuel est ${workspaceName}, qui n’a pas le voyage activé. Veuillez changer d’espace de travail par défaut pour pouvoir réserver !`,
+            message: 'Pour réserver, veuillez activer les voyages sur votre espace de travail par défaut ou définir par défaut un espace de travail sur lequel les voyages sont activés.',
         },
     },
     workspace: {
diff --git a/src/languages/it.ts b/src/languages/it.ts
index da2d9929..76f6fd70 100644
--- a/src/languages/it.ts
+++ b/src/languages/it.ts
@@ -4470,9 +4470,8 @@ ${amount} per ${merchant} - ${date}`,
                 'Sapevi che puoi prenotare e gestire i viaggi in treno direttamente in Expensify? E che le ricevute vengono caricate automaticamente per te? La prossima volta prenota semplicemente tramite <a href="https://travel.expensify.com">Expensify Travel</a> 🚂',
         },
         defaultWorkspaceTravelDisabled: {
-            title: 'Viaggi non è abilitato',
-            message: ({workspaceName}: {workspaceName: string}) =>
-                `Il tuo spazio di lavoro predefinito attuale è ${workspaceName}, che non ha i viaggi abilitati. Cambia il tuo spazio di lavoro predefinito per poter prenotare!`,
+            title: 'Viaggi non abilitato',
+            message: 'Per prenotare, abilita i viaggi sul tuo spazio di lavoro predefinito oppure imposta come predefinito uno spazio di lavoro con viaggi abilitati.',
         },
     },
     workspace: {
diff --git a/src/languages/ja.ts b/src/languages/ja.ts
index 81af3c1e..80281d8b 100644
--- a/src/languages/ja.ts
+++ b/src/languages/ja.ts
@@ -4435,8 +4435,7 @@ ${integrationName === CONST.ONBOARDING_ACCOUNTING_MAPPING.other ? 'あなたの'
         },
         defaultWorkspaceTravelDisabled: {
             title: '出張機能は有効になっていません',
-            message: ({workspaceName}: {workspaceName: string}) =>
-                `現在のデフォルトワークスペースは ${workspaceName} で、出張手配機能が有効になっていません。予約するには、デフォルトワークスペースを変更してください。`,
+            message: '予約するには、デフォルトのワークスペースで出張機能を有効にするか、出張機能が有効になっているワークスペースをデフォルトとして設定してください。',
         },
     },
     workspace: {
diff --git a/src/languages/nl.ts b/src/languages/nl.ts
index 2aeee23f..c06b53ff 100644
--- a/src/languages/nl.ts
+++ b/src/languages/nl.ts
@@ -4469,8 +4469,7 @@ ${amount} voor ${merchant} - ${date}`,
         },
         defaultWorkspaceTravelDisabled: {
             title: 'Reizen is niet ingeschakeld',
-            message: ({workspaceName}: {workspaceName: string}) =>
-                `Je huidige standaardwerkruimte is ${workspaceName} en heeft reizen niet ingeschakeld. Wijzig je standaardwerkruimte om te kunnen boeken!`,
+            message: 'Om te boeken, schakel reizen in op je standaardwerkruimte of stel een standaardwerkruimte in die reizen ondersteunt.',
         },
     },
     workspace: {
diff --git a/src/languages/pl.ts b/src/languages/pl.ts
index 1f5a1d3a..ddfd3097 100644
--- a/src/languages/pl.ts
+++ b/src/languages/pl.ts
@@ -4453,9 +4453,8 @@ ${amount} dla ${merchant} - ${date}`,
                 'Wiesz, że możesz rezerwować i zarządzać przejazdami pociągiem bezpośrednio w Expensify? I że paragony są automatycznie przesyłane za Ciebie? Następnym razem po prostu zarezerwuj przez <a href="https://travel.expensify.com">Expensify Travel</a>',
         },
         defaultWorkspaceTravelDisabled: {
-            title: 'Podróże nie są włączone',
-            message: ({workspaceName}: {workspaceName: string}) =>
-                `Twoje domyślne miejsce pracy to ${workspaceName}, w którym podróże nie są włączone. Zmień domyślne miejsce pracy, żeby móc rezerwować!`,
+            title: 'Podróże są wyłączone',
+            message: 'Aby dokonać rezerwacji, włącz podróże w swojej domyślnej przestrzeni roboczej lub ustaw jako domyślną inną przestrzeń roboczą, w której podróże są włączone.',
         },
     },
     workspace: {
diff --git a/src/languages/pt-BR.ts b/src/languages/pt-BR.ts
index 14b7dfa6..f1b87d45 100644
--- a/src/languages/pt-BR.ts
+++ b/src/languages/pt-BR.ts
@@ -4462,9 +4462,8 @@ ${amount} para ${merchant} - ${date}`,
                 'Você sabia que dá para reservar e gerenciar viagens de trem direto no Expensify? E que os recibos são enviados automaticamente para você? Da próxima vez, é só reservar pelo <a href="https://travel.expensify.com">Expensify Travel</a> 🚂',
         },
         defaultWorkspaceTravelDisabled: {
-            title: 'Viagens não estão ativadas',
-            message: ({workspaceName}: {workspaceName: string}) =>
-                `Seu espaço de trabalho padrão atual é ${workspaceName}, que não tem viagens ativadas. Altere seu espaço de trabalho padrão para poder reservar!`,
+            title: 'Viagem não está ativada',
+            message: 'Para fazer uma reserva, ative viagens no seu espaço de trabalho padrão ou altere seu espaço de trabalho padrão para um que tenha viagens ativadas.',
         },
     },
     workspace: {
diff --git a/src/languages/zh-hans.ts b/src/languages/zh-hans.ts
index 5d261980..46c3dcbf 100644
--- a/src/languages/zh-hans.ts
+++ b/src/languages/zh-hans.ts
@@ -4329,10 +4329,7 @@ ${amount},商户:${merchant} - 日期:${date}`,
             railManual: '你知道吗?你可以直接在 Expensify 中预订和管理火车行程!下次无需再手动创建报销,只需通过 <a href="https://travel.expensify.com">Expensify Travel</a> 预订即可 🚂',
             railCard: '你知道吗?你可以直接在 Expensify 中预订和管理火车行程,而且还会自动为你上传收据。下次只需通过 <a href="https://travel.expensify.com">Expensify Travel</a> 预订即可 🚂',
         },
-        defaultWorkspaceTravelDisabled: {
-            title: '差旅功能未启用',
-            message: ({workspaceName}: {workspaceName: string}) => `您当前的默认工作区是 ${workspaceName},尚未启用差旅功能。请更改默认工作区以预订差旅!`,
-        },
+        defaultWorkspaceTravelDisabled: {title: '差旅功能未启用', message: '如需预订,请在您的默认工作区中启用差旅功能,或将默认工作区切换为已启用差旅功能的工作区。'},
     },
     workspace: {
         common: {

Note

You can apply these changes to your branch by copying the patch to your clipboard, then running pbpaste | git apply 😉

View workflow run

@blimpich
blimpich marked this pull request as ready for review August 12, 2026 20:25
@blimpich
blimpich requested review from a team as code owners August 12, 2026 20:25
@melvin-bot
melvin-bot Bot requested review from joekaufmanexpensify and situchan and removed request for a team August 12, 2026 20:25
@melvin-bot

melvin-bot Bot commented Aug 12, 2026

Copy link
Copy Markdown

@situchan Please copy/paste the Reviewer Checklist from here into a new comment on this PR and complete it. If you have the K2 extension, you can simply click: [this button]

@melvin-bot
melvin-bot Bot requested review from a team and truph01 August 12, 2026 20:25
@melvin-bot

melvin-bot Bot commented Aug 12, 2026

Copy link
Copy Markdown

@truph01 Please copy/paste the Reviewer Checklist from here into a new comment on this PR and complete it. If you have the K2 extension, you can simply click: [this button]

@melvin-bot
melvin-bot Bot removed the request for review from a team August 12, 2026 20:25
@blimpich

Copy link
Copy Markdown
Contributor Author

Hmm not sure why two C+ got assigned to review. Going to unassign @truph01

@blimpich
blimpich removed the request for review from truph01 August 12, 2026 20:27
@situchan

Copy link
Copy Markdown
Contributor

Reviewer Checklist

  • I have verified the author checklist is complete (all boxes are checked off).
  • I verified the correct issue is linked in the ### Fixed Issues section above
  • I verified testing steps are clear and they cover the changes made in this PR
    • I verified the steps for local testing are in the Tests section
    • I verified the steps for Staging and/or Production testing are in the QA steps section
    • I verified the steps cover any possible failure scenarios (i.e. verify an input displays the correct error message if the entered data is not correct)
    • I turned off my network connection and tested it while offline to ensure it matches the expected behavior (i.e. verify the default avatar icon is displayed if app is offline)
  • I checked that screenshots or videos are included for tests on all platforms
  • I included screenshots or videos for tests on all platforms
  • I verified that the composer does not automatically focus or open the keyboard on mobile unless explicitly intended. This includes checking that returning the app from the background does not unexpectedly open the keyboard.
  • I verified tests pass on all platforms & I tested again on:
    • Android: HybridApp
    • Android: mWeb Chrome
    • iOS: HybridApp
    • iOS: mWeb Safari
    • MacOS: Chrome / Safari
  • If there are any errors in the console that are unrelated to this PR, I either fixed them (preferred) or linked to where I reported them in Slack
  • I verified proper code patterns were followed (see Reviewing the code)
    • I verified that comments were added to code that is not self explanatory
    • I verified that any new or modified comments were clear, correct English, and explained "why" the code was doing something instead of only explaining "what" the code was doing.
    • I verified any copy / text that was added to the app is grammatically correct in English. It adheres to proper capitalization guidelines (note: only the first word of header/labels should be capitalized), and is either coming verbatim from figma or has been approved by marketing (in order to get marketing approval, ask the Bug Zero team member to add the Waiting for copy label to the issue)
  • If a new code pattern is added I verified it was agreed to be used by multiple Expensify engineers
  • I verified that this PR follows the guidelines as stated in the Review Guidelines
  • I verified other components that can be impacted by these changes have been tested, and I retested again (i.e. if the PR modifies a shared library or component like Avatar, I verified the components using Avatar have been tested & I retested again)
  • If a new component is created I verified that:
    • A similar component doesn't exist in the codebase
    • All props are defined accurately
    • The component has a clear name that is non-ambiguous and the purpose of the component can be inferred from the name alone
    • The only data being stored in the state is data necessary for rendering and nothing else
    • The component has the minimum amount of code necessary for its purpose, and it is broken down into smaller components in order to separate concerns and functions
  • If a new CSS style is added I verified that:
    • A similar style doesn't already exist
    • The style can't be created with an existing StyleUtils function (i.e. StyleUtils.getBackgroundAndBorderStyle(theme.componentBG)
  • If the PR modifies code that runs when editing or sending messages, I tested and verified there is no unexpected behavior for all supported markdown - URLs, single line code, code blocks, quotes, headings, bold, strikethrough, and italic.
  • If the PR modifies a generic component, I tested and verified that those changes do not break usages of that component in the rest of the App (i.e. if a shared library or component like Avatar is modified, I verified that Avatar is working as expected in all cases)
  • If the PR modifies a component related to any of the existing Storybook stories, I tested and verified all stories for that component are still working as expected.
  • If the PR modifies a component or page that can be accessed by a direct deeplink, I verified that the code functions as expected when the deeplink is used - from a logged in and logged out account.
  • If the PR modifies the UI (e.g. new buttons, new UI components, changing the padding/spacing/sizing, moving components, etc) or modifies the form input styles:
    • I verified that all the inputs inside a form are aligned with each other.
    • I added Design label and/or tagged @Expensify/design so the design team can review the changes.
  • For any bug fix or new feature in this PR, I verified that sufficient unit tests are included to prevent regressions in this flow.
  • If the main branch was merged into this PR after a review, I tested again and verified the outcome was still expected according to the Test steps.
  • I have checked off every checkbox in the PR reviewer checklist, including those that don't apply to this PR.

Screenshots/Videos

Android: HybridApp
Android: mWeb Chrome
iOS: HybridApp
iOS: mWeb Safari
MacOS: Chrome / Safari

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: c931a41077

ℹ️ About Codex in GitHub

Codex has been enabled to automatically 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 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread src/libs/PolicyUtils.ts

/** Whether travel is enabled on a workspace, meaning it can be used to book a trip. */
function hasAcceptedTravelTerms(policy: OnyxEntry<Policy>, accountTravelSettings: TravelSettings | undefined): boolean {
return !!(policy?.travelSettings?.hasAcceptedTerms ?? (accountTravelSettings?.hasAcceptedTerms && isWorkspaceProvisionedForTravel(policy?.travelSettings)));

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Require isTravelEnabled before treating travel as enabled

When an admin toggles Travel off, enablePolicyTravel() only flips policy.isTravelEnabled and does not clear travelSettings.hasAcceptedTerms. This helper still returns true for that default workspace, so the new default-workspace guard in BookTravelButton allows openTravelDotLink() even though the default workspace has Travel disabled. Please include the policy feature flag in this enabled check, or check it separately at the guard.

Useful? React with 👍 / 👎.


return travelEnabledPolicy?.travelSettings?.hasAcceptedTerms ?? (travelSettings?.hasAcceptedTerms && isPolicyProvisioned);
}, [travelEnabledPolicy, isBlockedFromSpotnanaTravel, primaryContactMethod, travelSettings?.hasAcceptedTerms]);
return hasAcceptedTravelTerms(travelEnabledPolicy, travelSettings);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Include the default-workspace guard in the quick action

On wide Home pages, this quick-creation Travel button still computes readiness only from the first travel-enabled workspace. If the user's default workspace (NVP_ACTIVE_POLICY_ID) has Travel disabled but another workspace has accepted terms, this returns true, so pressing Travel goes straight to openTravelDotLink(travelEnabledPolicy?.id) and bypasses the new default-workspace error shown by BookTravelButton. Please apply the same default-policy check here before opening TravelDot.

Useful? React with 👍 / 👎.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants