#737 Add Import Report Download and Error Drilldown UX FIXED - #830
Open
Kappa16 wants to merge 3 commits into
Open
#737 Add Import Report Download and Error Drilldown UX FIXED#830Kappa16 wants to merge 3 commits into
Kappa16 wants to merge 3 commits into
Conversation
|
@Kappa16 Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits. You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀 |
Author
|
im on it |
…rror-Drilldown-UX-FIX
Contributor
Author
|
IM ON IT @Cedarich |
…ad-and-Error-Drilldown-UX-FIX
Cedarich
requested changes
Jul 28, 2026
Cedarich
left a comment
Contributor
There was a problem hiding this comment.
Kindly fix failing workflow
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
CLOSE #737
🔍 FINDINGS (what was wrong in the codebase)
Finding 1 — No backend-generated report existed anywhere
ImportRecipientsWizard.tsx) only calledbuildValidationReport(validationResult)— a purely client-side CSV assembled from the browser's own validation state./recipients/import/reportendpoint in the NestJS backend — in fact, the backend had no recipients module at all (app/backend/src/contained no recipient/import code), and the mock API layer only handled/recipients/import/validateand/confirm.Finding 2 — The report was flat and unstructured
rowNumber, status, messages(all messages joined with" | "),fields(joined the same way).Finding 3 — Row-level failures were not searchable or filterable
Step3Validation.tsxrendered a static flat list of row cards.app/mobilewith Expo (React Native), Health Screen + Module Docs 📱 #4,812 or find every "wallet" failure), no status filter (couldn't isolate errors from warnings), no match counts, no empty-state handling.Finding 4 — Large reports were handled by hiding data, not by scaling
capValidationErrors()hard-capped the display at 50 rows (MAX_DISPLAY_ERRORS) and told users "Download the full report to see all results" — i.e., inspection of rows 51+ in the UI was impossible.Finding 5 — Minor defects discovered along the way
ref={liveRegionRef}in the wizard — two elements shared one ref; the first silently lost the reference, and auseEffectmanually mutatedtextContentof a React-managed node (anti-pattern risking hydration/re-render conflicts).🛠️ FIX FEATURES (what the fix delivers)
Feature 1 — True backend-generated report endpoint (AC #1)
app/backend/src/recipients/(service + controller + module), registered inapp.module.ts, role-guarded consistently with the campaigns module:POST /recipients/import/report— validates the uploaded CSV server-side and streams back a structured CSV attachment with proper headers:Content-Type: text/csv,Content-Disposition: attachment; filename="recipient-import-report-<campaignId>.csv",X-Report-Id,X-Report-Generated-At.POST /recipients/import/validateandPOST /recipients/import/confirmadded for full endpoint parity with the frontend contract./recipients/import/reporthandler added tohandlers.ts(sharing a refactored validation helper with the validate handler), so demo/mock mode exercises the exact same contract.Feature 2 — Structured, spreadsheet-ready report format
Each report now contains:
# reportId,# campaignId,# generatedAt,# source: backend|local,# totalRows/validRows/warningRows/errorRows);rowNumber, status, severity, field, message, name, wallet, phone;\nline endings.Feature 3 — Backend-first download with graceful fallback
downloadImportReport(campaignId, file, fallback)incsv-validation.ts:X-Report-Id,X-Report-Generated-At);{ blob, filename, meta }includingmeta.source: 'backend' | 'local'so the UI can state exactly where the report came from.Feature 4 — Searchable & filterable row failures (AC #2)
New
ValidationReportPanelcomponent + pure, fully unit-testedfilterValidationRows():All / Errors / Warnings / Valid) as accessible toggle buttons (aria-pressed) with live counts per status;aria-liveannouncements ("2 matching rows — showing 2"), a one-click Clear search, and a friendly empty state when nothing matches;useMemo-cached and the visible window auto-resets when the query/filter changes (React's sanctioned render-time state-adjustment pattern — no effect churn).Feature 5 — Large reports stay smooth (AC #3)
INITIAL_REPORT_PAGE_SIZE), +50 rows per increment (REPORT_PAGE_INCREMENT), auto-triggered by anIntersectionObserversentinel (240 px pre-fetch margin) plus an explicit "Show more rows (N remaining)" button for environments without IntersectionObserver;REPORT_SERIALIZATION_BATCH_SIZE = 5,000) so generating a 50k-row report yields control between chunks instead of stalling the main thread;capValidationErrorsremains exported for backward compatibility (its existing tests still pass).Feature 6 — Accessibility & robustness cleanups
aria-liveregion and the manualtextContentmutation; announcements now flow through a single React-rendered live region;aria-label="Search validation rows", labelled filter group, focus-visible styles consistent with the existing design system);lastReportSource,isDownloadingReport) on file change and "Start over".Feature 7 — Comprehensive regression-proof test coverage (26 new tests, all passing)
csv-validation.test.ts(extended)filterValidationRows(7), structured report format + escaping (2),downloadImportReportbackend/fallback/empty-body paths (4), windowing constants (1) — on top of the 39 total greenValidationReportPanel.test.tsx(new)recipients-import-report.test.ts(new)downloadImportReport→ realfetchClient→ mock backend resolvingsource: 'backend'recipients.service.spec.ts(new, backend)✅ Verdict per Acceptance Criterion
POST /recipients/import/report(NestJS + mock), backend-first client flow,source: backendverified end-to-end by testNo conflicts introduced: backend
nest buildexits 0 with 565/565 tests green; frontend lint 0 problems,tscdiff vs. pristine is empty, and the 13 failing frontend tests + thenext buildaxios failure were proven pre-existing and identical on pristine code via A/Bgit stashruns.