[Bulk workspace edits] Release 2: remove Control-only restriction + Upgrade page#94060
Conversation
…iction Release 2 lifts the R1 restriction that only allowed Corporate targets when copying from a Corporate (Control) workspace. Every admin-accessible, non-personal workspace is now an eligible copy target regardless of plan, at both gating points: the "Copy settings" entry action in the workspaces list and the Select Workspaces step. Copying Control-only settings onto a Collect target is handled by the upgrade step added separately.
Add bulkUpgradeToCorporate, which upgrades several workspaces to Corporate (Control) in a single UpgradeToCorporate call by passing policyIDList instead of policyID, with optimistic/success/failure Onyx updates per policy. Extend UpgradeToCorporateParams to accept either policyID or policyIDList.
…Intro Pull the upgrade card (icon + "Upgrade to unlock" badge, title/description, optional plan-availability copy, button, and plans & pricing note) out of UpgradeIntro into a presentational UpgradeIntroView that takes its content via props, and refactor UpgradeIntro to render it. The note's subscription link is now computed inside the view so callers don't have to. Also allow UpgradeConfirmation to override its button label so other flows can show "Continue" instead of "Got it, thanks". Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Insert an Upgrade step between Select Features and Confirm, shown only when at least one selected feature is Control-only (per canPolicyAccessFeature) and at least one target is on the Collect plan; otherwise the flow goes straight to Confirm. The new CopyPolicySettingsUpgradePage reuses UpgradeIntroView, calls bulkUpgradeToCorporate for all selected Collect targets, and shows the shared UpgradeConfirmation success view (with a Continue button) only once the upgrade settles successfully - staying on the intro with a loading state while pending and reverting on failure. Registers the new screen/route in the modal stack, linking config, and WORKSPACES_LIST_TO_RHP, adds the upgrade-detection helpers and English copy, and covers the pending/success/failure states with tests.
|
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 |
…ream merge
Upstream moved copy-settings target eligibility into a shared selector returning
{adminNonPersonal, corporateOnly} consumed by the new WorkspaceRowThreeDotsMenu.
Reconcile that with the R2 change (no plan restriction): the selector now returns
a flat list of every admin-accessible, non-personal workspace ID, the page derives
a per-row isEligibleToCopy boolean from it, and the row menu reads that boolean
instead of receiving the targets list. Drops the now-unused corporateOnly field
and updates PolicySelectorTest accordingly.
…pgrade copy Generated the non-English translations for the copy-settings upgrade step copy (workspace.copyPolicySettings.upgrade) across all supported locales.
Codecov Report❌ Looks like you've decreased code coverage for some files. Please write tests to increase, or at least maintain, the existing level of code coverage. See our documentation here for how to interpret this table.
|
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 281d4e11d0
ℹ️ 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".
…pers Codecov flagged the new UpgradeIntroView (0% - it was mocked everywhere) and the new copy-settings upgrade helpers as uncovered. Add a render test for UpgradeIntroView (content, button press, disabled state, illustration variant) and unit tests for getControlOnlySelectedParts, getCollectTargetsToUpgrade, and shouldShowCopyPolicySettingsUpgradeStep.
…ettings targets Removing the Control-only restriction let any non-personal workspace be a copy target, including Submit workspaces. Submit policies fail canPolicyAccessFeature (they aren't paid group policies), so the upgrade gate - which only inspects Collect targets - never flagged them and the flow went straight to Confirm, optimistically writing Control-only settings onto a Submit workspace. Limit eligible targets to paid group workspaces (Collect/Control) in both the workspaces-list selector and the Select Workspaces step, so the upgrade step (Collect -> Control) fully covers every eligible target.
This commit introduces the phrase "unlock features" in various languages, enhancing the user interface for feature unlocking across the application. The new translations have been added to the respective language files, ensuring consistency and clarity for users in German, English, Spanish, French, Italian, Japanese, Dutch, Polish, Portuguese, and Simplified Chinese.
…POLICY_FIELDS Move the copy-settings part -> PolicyFeatureName map into CopyPolicySettings.ts next to PARTS_TO_POLICY_FIELDS (both relate a Part to Policy vocabulary) and export it, so CopyPolicySettingsUtils imports it instead of defining its own. Typed as Partial<Record<Part, PolicyFeatureName>> so callers index by any Part without a cast. A doc comment explains why it stays separate from PARTS_TO_POLICY_FIELDS (the feature toggle isn't reliably derivable from the copied-fields list).
|
@shubham1206agra 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] |
…ures The upgrade step listed Control-only features using a `'workspace.common.rules'` fallback when a part had no FEATURE_ROWS entry, which would mislabel any such part as "Rules". Translate each part's real label and filter out any without one instead, so the listed feature names are always accurate and there's no magic key.
|
@fedirjh Please fix the knip check |
|
@shubham1206agra Fixed |
|
@fedirjh conflicts |
Copy-settings targets are intentionally billing/paid-only (Collect/Control), so isPaidGroupPolicy is the correct gate. Disable no-restricted-imports on the line with a reason.
|
@yuwenmemon It's ready |
|
🚧 yuwenmemon has triggered a test Expensify/App build. You can view the workflow run here. |
|
✋ This PR was not deployed to staging yet because QA is ongoing. It will be automatically deployed to staging after the next production release. |
|
🧪🧪 Use the links below to test this adhoc build on Android, iOS, and Web. Happy testing! 🧪🧪
|
|
🚀 Deployed to staging by https://github.com/yuwenmemon in version: 9.4.32-0 🚀
|
|
Deploy Blocker #95690 was identified to be related to this PR. |
|
Deploy Blocker #95691 was identified to be related to this PR. |
|
Deploy Blocker #95722 was identified to be related to this PR. |
|
Deploy Blocker #95723 was identified to be related to this PR. |
|
🚀 Deployed to production by https://github.com/grgia in version: 9.4.32-3 🚀
Bundle Size Analysis (Sentry): |
Explanation of Change
Release 2 of the Add Bulk Workspace Edits project: the upgrade-flow work on the App side for copying workspace settings.
Removed the Control-only target restriction. Copying from a Corporate (Control) workspace no longer limits targets to Corporate workspaces. Every admin-accessible, non-personal workspace is now an eligible target at both gating points — the "Copy settings" action in the workspaces list and the Select Workspaces step.
New Upgrade RHP step inserted between Select Features and Confirm. It is shown only when both:
canPolicyAccessFeature()), andOtherwise the flow goes straight to Confirm.
New screen/route/page —
POLICY_COPY_SETTINGS.UPGRADE→policy/:policyID/copy-settings/upgrade→CopyPolicySettingsUpgradePage, registered in the modal stack, linking config, andWORKSPACES_LIST_TO_RHP.Confirmation action — the page's primary button calls
UpgradeToCorporatewith the IDs of all selected Collect workspaces, passingpolicyIDList(bulk) instead ofpolicyID. The sharedUpgradeConfirmationsuccess view is shown only once the upgrade settles successfully; while pending the intro stays with a loading state, and on failure it reverts for a retry.To keep things DRY, the upgrade card was extracted from
UpgradeIntrointo a reusableUpgradeIntroView.Fixed Issues
$ #88674
PROPOSAL: N/A (internal task)
Tests
Offline tests
QA Steps
Same as tests.
PR Author Checklist
### Fixed Issuessection aboveTestssectionOffline stepssectionQA stepssectiontoggleReportand notonIconClick)Avatar, 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.mainbranch was merged into this PR after a review, I tested again and verified the outcome was still expected according to theTeststeps.Screenshots/Videos
CleanShot.2026-06-19.at.19.49.57.mp4
Android: Native
Android: mWeb Chrome
iOS: Native
iOS: mWeb Safari
MacOS: Chrome / Safari