Surface "Import workflows" on the Workflows page - #94694
Conversation
Co-authored-by: Jayesh Mangwani <jayeshmangwani@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/fr.ts b/src/languages/fr.ts
index 5163dd5e921..b951eace227 100644
--- a/src/languages/fr.ts
+++ b/src/languages/fr.ts
@@ -1144,7 +1144,7 @@ const translations: TranslationDeepObject<typeof en> = {
'Le fichier que vous avez téléchargé est soit vide, soit contient des données non valides. Veuillez vous assurer que le fichier est correctement formaté et contient les informations nécessaires avant de le télécharger à nouveau.',
importSpreadsheetLibraryError: 'Échec du chargement du module de feuille de calcul. Veuillez vérifier votre connexion Internet et réessayer.',
importSpreadsheet: 'Importer un tableau',
- importWorkflows: 'Importer les workflows',
+ importWorkflows: 'Importer des workflows',
downloadCSV: 'Télécharger le CSV',
importMemberConfirmation: () => ({
one: `Veuillez confirmer les détails ci-dessous pour un nouveau membre de l’espace de travail qui sera ajouté dans le cadre de ce téléversement. Les membres existants ne recevront aucune mise à jour de rôle ni message d’invitation.`,
diff --git a/src/languages/it.ts b/src/languages/it.ts
index 70036f2ef86..6a19b96db81 100644
--- a/src/languages/it.ts
+++ b/src/languages/it.ts
@@ -1141,7 +1141,7 @@ const translations: TranslationDeepObject<typeof en> = {
'Il file che hai caricato è vuoto oppure contiene dati non validi. Assicurati che il file sia formattato correttamente e contenga le informazioni necessarie prima di caricarlo di nuovo.',
importSpreadsheetLibraryError: 'Caricamento del modulo foglio di calcolo non riuscito. Controlla la connessione a Internet e riprova.',
importSpreadsheet: 'Importa foglio di calcolo',
- importWorkflows: 'Importa flussi di lavoro',
+ importWorkflows: 'Importare flussi di lavoro',
downloadCSV: 'Scarica CSV',
importMemberConfirmation: () => ({
one: `Conferma i dettagli seguenti per il nuovo membro dello spazio di lavoro che verrà aggiunto come parte di questo caricamento. I membri esistenti non riceveranno aggiornamenti di ruolo né messaggi di invito.`,
diff --git a/src/languages/ja.ts b/src/languages/ja.ts
index c8c36c46fb6..34533e49511 100644
--- a/src/languages/ja.ts
+++ b/src/languages/ja.ts
@@ -1127,7 +1127,7 @@ const translations: TranslationDeepObject<typeof en> = {
'アップロードしたファイルは空であるか、無効なデータが含まれています。再度アップロードする前に、ファイルの形式が正しく、必要な情報が含まれていることを確認してください。',
importSpreadsheetLibraryError: 'スプレッドシートモジュールの読み込みに失敗しました。インターネット接続を確認して、もう一度お試しください。',
importSpreadsheet: 'スプレッドシートをインポート',
- importWorkflows: 'ワークフローをインポート',
+ importWorkflows: 'ワークフローのインポート',
downloadCSV: 'CSV をダウンロード',
importMemberConfirmation: () => ({
one: `このアップロードの一部として追加される新しいワークスペースメンバーの詳細を、以下で確認してください。既存のメンバーにはロールの更新や招待メッセージは送信されません。`,
Note You can apply these changes to your branch by copying the patch to your clipboard, then running |
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.
|
|
@MelvinBot , the placement of the Import Workflows button is incorrect. It should be positioned in the top-right corner of the page, like in this screenshot. Also, why are we seeing the "Add approval workflow" option here? |
|
@MelvinBot merge main, resolve conflicts and check this comment: #94694 (comment) |
…sOnWorkflowsPage Co-authored-by: Jayesh Mangwani <jayeshmangwani@users.noreply.github.com> # Conflicts: # src/pages/workspace/workflows/WorkspaceWorkflowsPage.tsx
|
🤖 Done with ✅ Merged
|
| Members page | → Workflows page |
|---|---|
Invite member (primary button) |
Add workflow (primary button) |
More ▾ → Import spreadsheet |
More ▾ → Import workflows |
So my plan is to move these to the page's top-right header via WorkspacePageWithSections' headerContent, promoting Add workflow to the primary button (that's the natural home for it — which also resolves your "why is it in the dropdown" point) and leaving Import workflows in More. I'll preserve the current visibility rule: the actions only appear when the Approvals toggle is enabled and the user can edit approvals, so they won't show on a workspace with approvals off.
One thing to confirm before I restructure the UI: do you want to keep Add workflow as the primary button (Members-style, per the table above), or surface only Import workflows in the top-right and drop "Add workflow" from this surface entirely? Your "why are we seeing Add approval workflow here?" could mean either, and it changes the header, so I'd rather get it right in one pass.
Reply with your pick (or @MelvinBot go with the primary Add workflow button) and I'll implement + push.
|
@MelvinBot , these are the changes we've made so far:
However, here's what we actually need to do:
|
Co-authored-by: Jayesh Mangwani <jayeshmangwani@users.noreply.github.com>
|
🤖 Restructured the UI per your feedback and pushed. Two changes:
The header Checks run locally
Please run through the manual test steps and confirm the placement matches the design before marking Ready for review. |
|
I looked into the failing checks — no code fix is needed, and I haven't pushed anything. The only failing check is ➡️ To clear it: Every functional check is green: The two Reassure performance jobs ( Checklist failure logFrom the The 11 newly-added items are the standard React-component review boxes (naming, prop comments, component state, selectors, etc.) that got added because this PR introduces a new component. |
|
@MelvinBot, please make sure to check every box in the checklist. I'll add the Tests section latest, and the attachments to the reviewer checklist. Also, please fix the PR description, it looks a bit disorganized at the moment. |
|
Done, ✅ Checked every box in the PR Author Checklist — all 41 boxes (top-level + nested) are now checked. 🧹 Reorganized the PR description. What was disorganized and what I fixed:
Left for you (as you noted):
|
|
👋 I looked at the failing checks — there's nothing here I can fix by pushing code. The only failing check is
New dynamic checks were added to the checklist (component-related items triggered by your changes), and those boxes are unchecked. This is a manual author checklist — it needs you to review and tick each box in the PR description. Once all boxes are checked, the check will pass. All other checks (tests, lint, typecheck, build, etc.) are passing ✅, so no code fix is needed. |
|
@MelvinBot Please use this https://github.com/Expensify/App/blob/main/.github/PULL_REQUEST_TEMPLATE.md checklist, check all the boxes, and include all the relevant details from the current PR description. I'll test the PR and add the videos to the reviewer checklist. |
|
I re-checked — all automated checks are passing ✅. The only red check is Because this PR touches a component, the workflow added 10 new component-review boxes to the PR description that are still unchecked:
Please review and check these 10 boxes in the PR description (the "If you are a member of the App team and this PR was opened by a Contributor" component section):
Once all boxes are checked, the check re-runs and passes automatically. |
|
Done, Rebuilt the PR description strictly against the canonical PULL_REQUEST_TEMPLATE.md, kept all the real details from the prior description, and checked every box. The I also tracked down why the description kept getting mangled with duplicated checklist items and a stray Root cause of the earlier manglingThe Each time the body was saved without those 10 items, the action appended them (as unchecked) and rewrote the body — and because the reconstruct step concatenates The fix was to include all 10 NEW_COMPONENT items already checked in the body. The action now finds every dynamic item present and checked, so it no longer rewrites/appends anything, the body stays clean, and the check passes. Left for you, as you asked:
One heads-up: since you asked to check all the boxes, the platform-test and screenshot attestation rows are checked even though the recordings aren't attached yet — uncheck any that don't apply once you finish testing and add the videos. |
|
Bug: When we go to More → Import workflows, we're navigated to the |
…creen Add dedicated WORKSPACE_WORKFLOWS_IMPORTED and WORKSPACE_WORKFLOWS_IMPORTED_CONFIRMATION screens that reuse the Members import mapping/confirmation pages, branching the title and back/return/confirmation navigation on the route so importing from Workflows keeps the user in the Workflows context instead of landing on members/imported. Co-authored-by: Jayesh Mangwani <jayeshmangwani@users.noreply.github.com>
|
Fixed in Root cause: Fix: Added a dedicated What changed
Checks: One remaining edge (let me know if you want it covered): during a Workflows import where roles are missing, the confirmation screen's role menu still opens the shared Please pull the branch and run through the Import workflows flow to confirm the mapping screen and return navigation now stay in the Workflows context. |
|
@MelvinBot Bug: We can import the spreadsheet and the success modal is displayed, but the imported approval workflow is not reflected in the list. The newly added |
AnalysisRoot cause: The imported approvals never make it into Onyx on the client, and the Workflows page never re-fetches after the import — so the list keeps rendering stale data. The columns are mapped and sent correctly —
Net effect: unless the Recommended fix: Refresh the Workflows page's data when the user returns from the import. The simplest, contained option is to move the fetch off a mount-only effect so it re-runs on focus: // WorkspaceWorkflowsPage.tsx — replace the mount-only useEffect(fetchData, [])
useFocusEffect(
useCallback(() => {
fetchData();
}, [fetchData]),
);This re-runs Verification / open questionConfidence: medium-high on the App-side gaps above (mount-only fetch + no optimistic Also worth a quick check: on a Control policy the Next Steps: Reply with |
|
@MelvinBot merge main and resolve conflicts |
…sOnWorkflowsPage Co-authored-by: Jayesh Mangwani <jayeshmangwani@users.noreply.github.com> # Conflicts: # src/pages/workspace/members/ImportedMembersConfirmationPage.tsx # src/pages/workspace/members/ImportedMembersPage.tsx # src/pages/workspace/workflows/WorkspaceWorkflowsPage.tsx
|
🤖 Merged All three conflicts were the same shape: Conflict resolutions
Verified no conflict markers remain and each affected import appears exactly once. I was unable to run the typechecker in this environment, so please rely on CI to confirm the type/lint checks pass. |
|
MelvinBot has made 5 consecutive commits trying to fix CI failures. Automatic CI fix dispatches are now paused for this PR. Next steps: A human should review the recent commits and resolve the conflicting CI errors manually. |
|
@MelvinBot I can't add a commit directly because I don't have access. However, if I provide the diff below, could you make that change? diff --git a/src/pages/workspace/workflows/ImportWorkflowsPage.tsx b/src/pages/workspace/workflows/ImportWorkflowsPage.tsx
index 7ef115a2447..24214271f20 100644
--- a/src/pages/workspace/workflows/ImportWorkflowsPage.tsx
+++ b/src/pages/workspace/workflows/ImportWorkflowsPage.tsx
@@ -1,15 +1,20 @@
-import React from 'react';
import ImportSpreadsheet from '@components/ImportSpreadsheet';
+
import usePolicy from '@hooks/usePolicy';
+
import type {PlatformStackScreenProps} from '@libs/Navigation/PlatformStackNavigation/types';
import type {SettingsNavigatorParamList} from '@libs/Navigation/types';
import {goBackFromInvalidPolicy} from '@libs/PolicyUtils';
+
import AccessOrNotFoundWrapper from '@pages/workspace/AccessOrNotFoundWrapper';
+
import CONST from '@src/CONST';
import ROUTES from '@src/ROUTES';
import type SCREENS from '@src/SCREENS';
import {isEmptyObject} from '@src/types/utils/EmptyObject';
+import React from 'react';
+
type ImportWorkflowsPageProps = PlatformStackScreenProps<SettingsNavigatorParamList, typeof SCREENS.WORKSPACE.WORKFLOWS_IMPORT>;
|
|
Looovely! Thanks for that @jayeshmangwani |
@dubielzyk-expensify Do we want to make any changes for this, or are we good to move forward with this PR? |
|
We're good 👍 |
jayeshmangwani
left a comment
There was a problem hiding this comment.
We're good to move forward with this PR now 🚀 .
|
@deetergp , we're good to move forward with this PR. We've updated the requested changes #94694 (comment). |
|
@jayeshmangwani My apologies, I was OOO most of last week. Code looks good but you've got merge conflicts. |
|
@MelvinBot merge main and resolve conflicts. |
…sOnWorkflowsPage Co-authored-by: Jayesh Mangwani <jayeshmangwani@users.noreply.github.com> # Conflicts: # src/pages/workspace/members/ImportedMembersConfirmationPage.tsx
|
🤖 Merged One conflict, in
I combined the two: adopted <Button.Text>{isWorkflowsImport ? translate('common.invite') : translate('common.import')}</Button.Text>All other incoming changes (routes, screens, language files, navigators) auto-merged cleanly.
|
|
@jayeshmangwani Still got conflicts |
|
@MelvinBot merge main and resolve conflicts. |
1 similar comment
|
@MelvinBot merge main and resolve conflicts. |
|
Great… I resolved the merge commit in the Github UI and now I can't merge it 😅 Time to snag another internal reviewer. |
|
✋ This PR was not deployed to staging yet because QA is ongoing. It will be automatically deployed to staging after the next production release. |
|
🚧 deetergp 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! 🧪🧪
|




Explanation of Change
Approval workflows can already be bulk-configured via CSV, but the importer is only reachable from Members → More → Import spreadsheet. Admins who manage approvals naturally go to the Workflows page, where there was no import entry point.
This PR surfaces the importer on the Workflows page, matching the approved design (the Rules-page
Moredropdown pattern) and the Import workflows label agreed in the issue:Moredropdown at the top of the Approvals card (ButtonWithDropdownMenu) with a single Import workflows action. The dropdown reuses the same!shouldBlockApprovalWorkflowEditing && canWriteApprovalsguard that gates the existing approval actions, plus the Members importer's locked-account / offline handling. The existing Add approval workflow button is unchanged.WORKSPACE_WORKFLOWS_IMPORTroute and a thinImportWorkflowsPagewrapper that renders<ImportSpreadsheet backTo={WORKSPACE_WORKFLOWS} goTo={WORKSPACE_MEMBERS_IMPORTED} />. Reusing the existing mapping screen means thesubmitsTo/approvesTocolumn mapping, theimportPolicyMemberscall, and the plan-upgrade prompt for non-Control workspaces (the edge case the issue asked us to confirm — handled atImportedMembersPageviaisRequiredControlPolicy) all come for free.spreadsheet.importWorkflowstranslation ("Import workflows") across all locale files.This keeps the change additive: no parser or backend changes, and the existing Members import flow is untouched.
Known v1 limitation / follow-up (per the proposal's open question): because the import reuses the Members mapping screen (
WORKSPACE_MEMBERS_IMPORTED) as‑is, after completing an import started from Workflows the user is returned to the Members page, and that screen's title/back still reference members. The proposal recommended reusing the screen as‑is for v1; a dedicated workflows mapping screen (Workflows-context title + return navigation) can be a fast follow-up.Fixed Issues
$ #93574
PROPOSAL: #93574 (comment)
Tests
Prerequisites
Collect Workspace
email,submitTo,approveTo, androlecolumns.Control Workspace
email,submitTo,approveTo, androlecolumns.Note: For testing, you can use this spreadsheet as well: sample.xlsx
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./** comment above it */thisproperly so there are no scoping issues (i.e. foronClick={this.submit}the methodthis.submitshould be bound tothisin the constructor)thisare necessary to be bound (i.e. avoidthis.submit = this.submit.bind(this);ifthis.submitis never passed to a component event handler likeonClick)Screenshots/Videos
Android: Native
Android: mWeb Chrome
iOS: Native
iOS: mWeb Safari
MacOS: Chrome / Safari