Skip to content

[CSM Portal] add CSV export of the filtered result set to the listing pages - #1300

Merged
Rashmika998 merged 2 commits into
wso2-open-operations:mainfrom
rksk:csm-csv-export
Jul 31, 2026
Merged

Rashmika998 merged 2 commits into
wso2-open-operations:mainfrom
rksk:csm-csv-export

Conversation

@rksk

@rksk rksk commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

What

Adds an Export CSV action to the CSM portal's listing pages: cases and service requests (plus engagements, security reports and the project-scoped issues tab, which share the same list component), incidents, and change requests.

The export covers the entire currently-filtered, currently-sorted result set — not just the page on screen — by paging the existing search endpoints client side. No new backend endpoint.

Why

CS engineers need the filtered list out of the portal for periodic reporting (for example, every incident created in a given month for one product). Today the only options are paging through the UI and copying by hand, or asking someone to pull the data from the backing data source.

How, and the part worth reviewing closely

The risk with a client-side export is a file that looks complete and is not. Guards against that:

  • The pager loops until the server's reported total is exhausted.
  • There is a safety cap (default 5,000 rows, overridable per call site). Past it the file still downloads, but it is named *-partial.csv and a banner tells the user the export was truncated and to narrow the filter. It never silently stops short.
  • A mid-export failure is surfaced, not handed over as a partial file pretending to be whole.
  • Progress shows as a live "N of Total" count, because paging a month of records is not instant and a button that appears to do nothing reads as a bug.

To stop the export drifting from what the screen shows, the filter-payload builder and row mapper were extracted out of the list hook into a shared module and used by both, rather than duplicated. timeCardCsvExport was also refactored onto the shared CSV helpers instead of its own private copy (behaviour and tests unchanged).

Known limitation, deliberately not worked around

The incident listing currently supports only free-text search, priority and parent as filters — there is no created-date range and no product filter yet. So the export faithfully exports what the filter bar can express today, which is less than the reporting use case needs. Adding those filters is tracked separately; this change does not paper over the gap.

Client-side paging is also inherently chatty: page size below is capped at 50, so a few hundred rows means several sequential round trips before the download begins. That trade was chosen deliberately over adding a server-side export endpoint.

Verification

  • npx tsc -b — clean
  • npx eslint . — 0 errors; 1 pre-existing warning in CsmCaseDetailPage.tsx, confirmed present on main
  • npx vitest run — 551 passed, 9 failed; the 9 are pre-existing failures on main in three files this branch does not touch
  • 22 new tests: pager exhaustion, truncation, mid-export failure, hook progress and filename tagging, button busy state and banners

Not exercised against a running backend.

Summary by CodeRabbit

  • New Features
    • Added CSV export for filtered cases, incidents, and change requests across all result pages.
    • Added export progress indicators, busy states, success confirmations, and clear failure or truncation notices.
    • Added a 5,000-row safety limit for exports.
  • Bug Fixes
    • Improved case filtering and result mapping, including assignee resolution and empty-result handling.
    • Standardized CSV formatting and downloading for timecard exports.
  • Tests
    • Added coverage for pagination, progress, failures, truncation, and CSV formatting.

Adds an Export CSV action to the cases/service-requests, incidents, and
change-requests listings. Client-side over paged fetches of the existing
list-search endpoints (no new backend endpoint): it pages the currently
applied filters and sort to exhaustion, mapping the same columns/display
values the table already shows, and downloads one CSV. A safety row cap
stops a runaway export, but the result is never silently short — the
filename and an on-screen notice both flag a truncated file and tell the
engineer to narrow their filters.

Extracts the cases search filter-building, assignee-id resolution, and
row mapping out of useGetCsmCases into a shared module so the export
pages with the exact same query the listing itself sends, instead of a
second, driftable copy of it. Refactors the time-card CSV export to
share the new csvField/rowsToCsvText helpers instead of duplicating them.
@coderabbitai

coderabbitai Bot commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 5e175bd9-18bf-40a8-8a14-4611863f77cf

📥 Commits

Reviewing files that changed from the base of the PR and between 9f45f2b and f737da8.

📒 Files selected for processing (2)
  • apps/csm-portal/webapp/src/features/csm-operations/components/ChangeRequestsTab.tsx
  • apps/csm-portal/webapp/src/features/csm-operations/components/IncidentsTab.tsx
🚧 Files skipped from review as they are similar to previous changes (2)
  • apps/csm-portal/webapp/src/features/csm-operations/components/IncidentsTab.tsx
  • apps/csm-portal/webapp/src/features/csm-operations/components/ChangeRequestsTab.tsx

📝 Walkthrough

Walkthrough

Adds a shared paginated CSV export flow with progress, truncation handling, and download utilities. Integrates export controls into cases, change requests, and incidents, centralizes case-search mapping, and updates timecard CSV generation.

Changes

CSV export flow

Layer / File(s) Summary
CSV formatting and paginated collection
apps/csm-portal/webapp/src/utils/csvExport.ts, apps/csm-portal/webapp/src/utils/__tests__/csvExport.test.ts, apps/csm-portal/webapp/src/features/csm-timecards/utils/timeCardCsvExport.ts
Adds shared CSV escaping, CRLF serialization, browser downloads, paginated fetching with progress and row caps, tests, and timecard integration.
Export hook and button behavior
apps/csm-portal/webapp/src/hooks/useFilteredCsvExport.ts, apps/csm-portal/webapp/src/components/FilteredCsvExportButton.tsx, apps/csm-portal/webapp/src/hooks/__tests__/useFilteredCsvExport.test.ts, apps/csm-portal/webapp/src/components/FilteredCsvExportButton.test.tsx
Adds export state management, page retrieval, filename handling, progress UI, banner feedback, and tests for success, truncation, failure, and busy states.
Case-search payload and row mapping
apps/csm-portal/webapp/src/features/csm-cases/utils/caseSearchPayload.ts, apps/csm-portal/webapp/src/features/csm-cases/api/useGetCsmCases.ts
Centralizes case filter construction, assignee resolution, empty-result handling, and backend case-row mapping.
Listing export integrations
apps/csm-portal/webapp/src/features/csm-cases/components/CsmIssuesView.tsx, apps/csm-portal/webapp/src/features/csm-operations/components/ChangeRequestsTab.tsx, apps/csm-portal/webapp/src/features/csm-operations/components/IncidentsTab.tsx
Adds server-paged CSV export controls and row mappers for cases, change requests, and incidents.

Estimated code review effort: 4 (Complex) | ~45 minutes

Possibly related PRs

Suggested labels: Type/New Feature

Suggested reviewers: cloby99

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Description check ⚠️ Warning The description explains the purpose, implementation, limitations, and verification, but it omits many required template sections and issue links. Add the missing template sections, including release note, documentation, training, certification, marketing, security checks, test environment, migrations, and related issue or PR links.
Docstring Coverage ⚠️ Warning Docstring coverage is 75.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly describes the primary change: adding filtered-result CSV export actions to CSM Portal listing pages.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
apps/csm-portal/webapp/src/utils/csvExport.ts (1)

38-42: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Consider prepending a UTF-8 BOM for Excel compatibility.

downloadCsv writes plain UTF-8 without a byte-order mark. This is the shared download path for every CSV export on the portal (timecards today, and cases/incidents/change-requests once this stack lands), so it's worth fixing once here. Excel on Windows guesses the legacy code page when no BOM is present, and any non-ASCII characters in exported data (accented customer/engineer names, etc.) will render as mojibake on double-click-open, even though the underlying bytes are valid UTF-8.

♻️ Proposed fix
 export function downloadCsv(header: string[], rows: string[][], filename: string): void {
   const csv = rowsToCsvText(header, rows);
-  saveBlob(new Blob([csv], { type: "text/csv;charset=utf-8;" }), filename);
+  saveBlob(new Blob(["\uFEFF" + csv], { type: "text/csv;charset=utf-8;" }), filename);
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/csm-portal/webapp/src/utils/csvExport.ts` around lines 38 - 42, Update
downloadCsv to prepend a UTF-8 BOM to the generated CSV content before creating
the Blob, while preserving the existing rowsToCsvText conversion, text/csv
charset, and saveBlob download flow.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@apps/csm-portal/webapp/src/features/csm-operations/components/ChangeRequestsTab.tsx`:
- Around line 136-148: Guard both fetchChangeRequestsPage in
ChangeRequestsTab.tsx (lines 136-148) and fetchIncidentsPage in IncidentsTab.tsx
(lines 118-131) against missing search-response fields by defaulting the
returned items arrays to [] and totals to 0. Update each return value
consistently so useFilteredCsvExport always receives valid pagination data.

---

Nitpick comments:
In `@apps/csm-portal/webapp/src/utils/csvExport.ts`:
- Around line 38-42: Update downloadCsv to prepend a UTF-8 BOM to the generated
CSV content before creating the Blob, while preserving the existing
rowsToCsvText conversion, text/csv charset, and saveBlob download flow.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 8ae19bd1-669d-42f9-a725-084bc56a4faa

📥 Commits

Reviewing files that changed from the base of the PR and between 95f7733 and 9f45f2b.

📒 Files selected for processing (12)
  • apps/csm-portal/webapp/src/components/FilteredCsvExportButton.test.tsx
  • apps/csm-portal/webapp/src/components/FilteredCsvExportButton.tsx
  • apps/csm-portal/webapp/src/features/csm-cases/api/useGetCsmCases.ts
  • apps/csm-portal/webapp/src/features/csm-cases/components/CsmIssuesView.tsx
  • apps/csm-portal/webapp/src/features/csm-cases/utils/caseSearchPayload.ts
  • apps/csm-portal/webapp/src/features/csm-operations/components/ChangeRequestsTab.tsx
  • apps/csm-portal/webapp/src/features/csm-operations/components/IncidentsTab.tsx
  • apps/csm-portal/webapp/src/features/csm-timecards/utils/timeCardCsvExport.ts
  • apps/csm-portal/webapp/src/hooks/__tests__/useFilteredCsvExport.test.ts
  • apps/csm-portal/webapp/src/hooks/useFilteredCsvExport.ts
  • apps/csm-portal/webapp/src/utils/__tests__/csvExport.test.ts
  • apps/csm-portal/webapp/src/utils/csvExport.ts

…y or total

The change-request and incident export page fetchers returned the search
response's array and total straight through, unlike the rest of the export code
and the case list hook, which both fall back with `?? []` / `?? 0` on the same
class of response.

`fetchAllPages` reads `page.items.length` to advance its offset, so an omitted
array would throw mid-export instead of terminating the loop cleanly.
@rksk

rksk commented Jul 31, 2026

Copy link
Copy Markdown
Contributor Author

@coderabbitai resume

@coderabbitai

coderabbitai Bot commented Jul 31, 2026

Copy link
Copy Markdown
Contributor
✅ Action performed

Reviews resumed.

@Rashmika998
Rashmika998 merged commit e82aae4 into wso2-open-operations:main Jul 31, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants