Skip to content

[Payment due @ChavdaSachin] Add bulk actions for company cards - #94678

Merged
JS00001 merged 31 commits into
Expensify:mainfrom
KJ21-ENG:KJ21-ENG/93334-bulk-actions-company-cards
Jul 29, 2026
Merged

JS00001 merged 31 commits into
Expensify:mainfrom
KJ21-ENG:KJ21-ENG/93334-bulk-actions-company-cards

Conversation

@KJ21-ENG

@KJ21-ENG KJ21-ENG commented Jun 26, 2026 •

Copy link
Copy Markdown
Contributor

Explanation of Change

Adds multi-select bulk actions to the BYOC company cards table. The table now uses stable card keys for selection, disables pending-delete rows from selection, and derives a selected-row action menu for unassigning assigned cards, viewing selected card transactions, and exporting selected rows as CSV.

This also updates the BYOC header to match the settled layout: feed selector plus standalone Settings, search with "Find company card", and a selected-count dropdown when rows are selected.

As part of this (tracked in #96047), a shared Display > Sort by control (TableSettingsTrigger) is now rendered automatically by Table.FilterBar on every table with sortable columns (members, categories, tags, rooms, etc.). On narrow layouts it renders as a gear icon button and is hidden for tables that lock their sorting via narrowLayoutSortColumn, since user sorting is ignored there. Additionally, table selection is now cleared when the search string changes (previously only when filters changed) — this applies to every selectable table, not just company cards.

Fixed Issues

$ #93334
$ #96047

PROPOSAL: #93334 (comment)

Tests

  1. Verify that no errors appear in the JS console.
  2. Sign in as a workspace admin with a BYOC company card feed that has assigned and unassigned cards.
  3. Go to Workspace settings > Company cards.
  4. Verify the header shows the feed selector and a standalone Settings button.
  5. Verify the controls row shows the Find company card search field, a Filters button, and a Display button (on narrow layouts the last two render as filter and gear icon buttons).
  6. Open Filters and verify the existing status filter options are available.
  7. Open Display and verify it only exposes Sort by, with sortable columns and sort order.
  8. Select multiple assigned cards and verify the selected-count dropdown shows Unassign cards, View transactions, and Export as CSV.
  9. Click View transactions and verify Search opens filtered to the selected assigned card IDs.
  10. Select only unassigned cards, then a mixed assigned/unassigned set, and verify the selected-count dropdown only shows Export as CSV.
  11. Export selected cards as CSV and verify the file includes Email, Name, Card number, Transaction start date, Last updated, and Assigned columns.
  12. Select multiple assigned cards, choose Unassign cards, confirm the modal, and verify the selected cards are unassigned.
  13. Select ALL assigned cards on a feed with no unassigned cards and confirm Unassign cards. Verify a loading indicator is shown (not the "No cards in this feed" empty state) while the requests are in flight, and the table settles correctly afterwards.
  14. Type a search that narrows the table, select a filtered row, and verify the selection sticks. Clear the selection and verify the filter bar returns with the search text still applied.
  15. With cards selected, switch feeds via the feed selector and verify the selection is cleared.
  16. Go to the workspace Members page, verify the Display button appears there too, and that Sort by works. Select several members, type into the search field, and verify the selection is cleared.
  • Verify that no errors appear in the JS console

Offline tests

  1. Go offline on the company cards page.
  2. Select company card rows and verify the selected-count dropdown updates correctly.
  3. Export selected rows as CSV and verify the file is downloaded locally.
  4. Verify that no errors appear in the JS console.

QA Steps

  1. Sign in as a workspace admin with a BYOC company card feed that has assigned and unassigned cards.
  2. Go to Workspace settings > Company cards.
  3. Verify the feed selector, standalone Settings button, Find company card search field, Filters button, and Display button are shown (on narrow layouts Filters and Display render as icon buttons).
  4. Select multiple assigned cards and verify Unassign cards, View transactions, and Export as CSV are available.
  5. Select only unassigned cards, then mixed assigned/unassigned cards, and verify only Export as CSV is available.
  6. Verify View transactions opens Search filtered to the selected assigned cards.
  7. Verify Export as CSV downloads a CSV with the selected rows.
  8. Verify Unassign cards shows one confirmation modal and unassigns the selected assigned cards after confirmation.
  9. Search to narrow the table, select a filtered row, and verify the selection sticks and the search survives clearing the selection.
  10. On the workspace Members page, verify the Display > Sort by button appears and works, and that typing in the search field clears an existing selection.
  • Verify that no errors appear in the JS console

PR Author Checklist

  • I linked the correct issue in the ### Fixed Issues section above
  • I wrote clear testing steps that cover the changes made in this PR
    • I added steps for local testing in the Tests section
    • I added steps for the expected offline behavior in the Offline steps section
    • I added steps for Staging and/or Production testing in the QA steps section
    • I added steps to cover 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 tested this PR with a High Traffic account against the staging or production API to ensure there are no regressions (e.g. long loading states that impact usability).
  • I included screenshots or videos for tests on all platforms
  • I ran the tests on all platforms & verified they passed on:
    • Android: Native
    • Android: mWeb Chrome
    • iOS: Native
    • iOS: mWeb Safari
    • MacOS: Chrome / Safari
  • I verified there are no console errors (if there's a console error not related to the PR, report it or open an issue for it to be fixed)
  • I followed proper code patterns (see Reviewing the code)
    • I verified that any callback methods that were added or modified are named for what the method does and never what callback they handle (i.e. toggleReport and not onIconClick)
    • 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 followed the guidelines as stated in the Review Guidelines
  • I tested other components that can be impacted by my changes (i.e. if the PR modifies a shared library or component like Avatar, I verified the components using Avatar are working as expected)
  • If any new file was added I verified that:
    • The file has a description of what it does and/or why is needed at the top of the file if the code is not self explanatory
  • 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 new assets were added or existing ones were modified, I verified that:
    • The assets are optimized and compressed (for SVG files, run npm run compress-svg)
    • The assets load correctly across all supported platforms.
  • 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.
  • I added unit tests for any new feature or bug fix in this PR to help automatically prevent regressions in this user 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.

Screenshots/Videos

Android: Native
Record_2026-06-27-17-53-44_40d2c9e2d8fb5b5aba3904834590021e.mp4
Android: mWeb Chrome
Record_2026-06-27-17-31-10.mp4
iOS: Native
Simulator.Screen.Recording.-.iPhone.17.-.2026-06-27.at.18.03.10.mov
iOS: mWeb Safari
Simulator.Screen.Recording.-.iPhone.17.-.2026-06-27.at.17.33.42.mov
MacOS: Chrome / Safari
ezyZip.60.mp4

@melvin-bot

melvin-bot Bot commented Jun 26, 2026

Copy link
Copy Markdown

Hey, I noticed you changed src/languages/en.ts in a PR from a fork. For security reasons, translations are not generated automatically for PRs from forks.

If you want to automatically generate translations for other locales, an Expensify employee will have to:

  1. Look at the code and make sure there are no malicious changes.
  2. Run the Generate static translations GitHub workflow. If you have write access and the K2 extension, you can simply click: [this button]

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 --help

Typically, you'd want to translate only what you changed by running npx ts-node ./scripts/generateTranslations.ts --compare-ref main

@codecov

codecov Bot commented Jun 26, 2026 •

Copy link
Copy Markdown

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.

Files with missing lines Coverage Δ
src/CONST/index.ts 94.81% <ø> (ø)
...h/SearchPageHeader/SearchDisplayDropdownButton.tsx 50.00% <100.00%> (-2.95%) ⬇️
src/components/Table/TableContext.tsx 83.33% <ø> (ø)
src/components/Table/TableFilterBar/index.tsx 100.00% <100.00%> (ø)
src/components/Table/middlewares/selection.ts 87.03% <100.00%> (+27.31%) ⬆️
src/styles/index.ts 62.62% <100.00%> (-0.78%) ⬇️
src/components/Table/Table.tsx 95.55% <50.00%> (+0.20%) ⬆️
...ble/TableFilterBar/TableDisplaySettingsTrigger.tsx 95.00% <95.00%> (ø)
...onents/Tables/WorkspaceCompanyCardsTable/index.tsx 66.19% <93.75%> (+2.91%) ⬆️
...sTable/WorkspaceCompanyCardsTableHeaderButtons.tsx 0.00% <0.00%> (ø)
... and 3 more
... and 1143 files with indirect coverage changes

@KJ21-ENG
KJ21-ENG marked this pull request as ready for review June 27, 2026 12:39
@KJ21-ENG
KJ21-ENG requested review from a team as code owners June 27, 2026 12:39
@melvin-bot
melvin-bot Bot requested review from ChavdaSachin and JmillsExpensify and removed request for a team June 27, 2026 12:39
@melvin-bot

melvin-bot Bot commented Jun 27, 2026

Copy link
Copy Markdown

@ChavdaSachin 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]

Comment thread src/components/Tables/WorkspaceCompanyCardsTable/index.tsx Outdated
Comment thread src/components/Tables/WorkspaceCompanyCardsTable/index.tsx Outdated
@KJ21-ENG

Copy link
Copy Markdown
Contributor Author

FYI :- Failing TypeScript Checks are unrelated to thi PR

@JmillsExpensify
JmillsExpensify requested review from joekaufmanexpensify and removed request for JmillsExpensify June 28, 2026 20:47
@JmillsExpensify

Copy link
Copy Markdown
Contributor

Adding @joekaufmanexpensify to this one.

…-actions-company-cards

# Conflicts:
#	src/components/Tables/WorkspaceCompanyCardsTable/WorkspaceCompanyCardsTableHeaderButtons.tsx
#	src/components/Tables/WorkspaceCompanyCardsTable/index.tsx
@KJ21-ENG

KJ21-ENG commented Jul 6, 2026

Copy link
Copy Markdown
Contributor Author

@ChavdaSachin @joekaufmanexpensify
Kind ping here, Thanks!

@joekaufmanexpensify

Copy link
Copy Markdown
Contributor

I was OOO. Back today and will review 👍

@KJ21-ENG

KJ21-ENG commented Jul 29, 2026 •

Copy link
Copy Markdown
Contributor Author

@shawnborton

Copy link
Copy Markdown
Contributor

Looks good 👍

@JS00001
JS00001 merged commit a0d8671 into Expensify:main Jul 29, 2026
34 checks passed
@github-actions

Copy link
Copy Markdown
Contributor

🚧 JS00001 has triggered a test Expensify/App build. You can view the workflow run here.

@OSBotify

Copy link
Copy Markdown
Contributor

✋ This PR was not deployed to staging yet because QA is ongoing. It will be automatically deployed to staging after the next production release.

@OSBotify

Copy link
Copy Markdown
Contributor

🚀 Deployed to staging by https://github.com/JS00001 in version: 9.4.46-0 🚀

platform result
🕸 web 🕸 success ✅
🤖 android 🤖 success ✅
🍎 iOS 🍎 success ✅

@MelvinBot

Copy link
Copy Markdown
Contributor

🤖 Help site review — changes required

Yes, this PR introduces net-new, user-facing company-card management capabilities that aren't documented anywhere in docs/articles. I've created a draft help article documenting them.

Draft PR: #97387 — Docs updates for E/A#94678 Add bulk actions company cards help article (labeled HelpDot)

What's now documented (new article Manage-Multiple-Company-Cards.md):

  • Selecting multiple cards on the Company cards page to reveal the selected-count dropdown
  • Unassign cards (bulk) — assigned-card-only action, with the confirmation step
  • View transactions — assigned-card-only action that opens Search filtered to the selected cards
  • Export as CSV — available for any selection, with the exact column set (Email, Name, Card number, Transaction start date, Last updated, Assigned)
  • Why only Export as CSV appears for unassigned/mixed selections, and that selection clears on search/feed change

All feature names, button labels, and CSV column headers were verified against the exact strings in src/languages/en.ts on this PR.

Why a new article rather than editing an existing one

The bulk-management workflow is distinct from the existing single-card assignment flow in Assign-Company-Cards.md, and the HelpDot authoring guidelines require one primary workflow per article. Unassigning, viewing transactions, and exporting share a single selection step, so they belong together in their own article.

The Display > Sort by control and the Find company card search are generic table affordances rather than a documentable workflow on their own, so they're referenced only as part of the selection steps and don't need standalone coverage.

⚠️ Note on assignment: I couldn't assign the draft PR to KJ21-ENG — the account doesn't have a high enough access level on Expensify/App to be a valid GitHub assignee, so the API silently dropped it. You're @-mentioned on the draft PR instead. If you'd like to be set as assignee, someone with write access can add you.

@KJ21-ENG, please review the linked help site PR and confirm it reflects the current behavior. Then mark the linked help site PR Ready for review

@lanitochka17

Copy link
Copy Markdown

Deploy Blocker #97393 was identified to be related to this PR.

@mitarachim

Copy link
Copy Markdown

Deploy Blocker #97394 was identified to be related to this PR.

@mitarachim

Copy link
Copy Markdown

Deploy Blocker #97406 was identified to be related to this PR.

@mitarachim

Copy link
Copy Markdown

Deploy Blocker #97411 was identified to be related to this PR.

@mitarachim

Copy link
Copy Markdown

Deploy Blocker #97412 was identified to be related to this PR.

@lanitochka17

Copy link
Copy Markdown

Deploy Blocker #97494 was identified to be related to this PR.

@OSBotify

Copy link
Copy Markdown
Contributor

🚀 Deployed to production by https://github.com/marcaaron in version: 9.4.46-10 🚀

platform result
🕸 web 🕸 success ✅
🤖 android 🤖 success ✅
🍎 iOS 🍎 success ✅

Bundle Size Analysis (Sentry):

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.