feat: add invoice pdf download on purchase lists#1021
Conversation
Signed-off-by: Tomás Castillo <tcastilloboireau@gmail.com>
📝 WalkthroughWalkthroughSponsor purchase list views now download invoice PDFs by fetching sponsor orders, generating PDFs with summit context, preventing concurrent downloads, and showing translated errors. Sponsor order retrieval accepts a fallback sponsor ID, and the UI foundation dependency is updated. ChangesSponsor invoice downloads
Estimated code review effort: 3 (Moderate) | ~20 minutes Sequence Diagram(s)sequenceDiagram
participant PurchaseView
participant getSponsorOrder
participant SponsorOrdersAPI
participant generateInvoicePDF
participant Snackbar
PurchaseView->>getSponsorOrder: fetch selected purchase order
getSponsorOrder->>SponsorOrdersAPI: request order with computed sponsor_id
SponsorOrdersAPI-->>getSponsorOrder: return sponsor order
getSponsorOrder-->>PurchaseView: provide order data
PurchaseView->>generateInvoicePDF: generate invoice PDF
generateInvoicePDF-->>PurchaseView: complete or fail
PurchaseView->>Snackbar: show translated error on failure
Possibly related PRs
Suggested reviewers: 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Warning There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure. 🔧 ESLint
package.jsonParsing error: Missing semicolon. (2:8) Comment |
There was a problem hiding this comment.
Actionable comments posted: 3
🤖 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 `@src/actions/sponsor-purchases-actions.js`:
- Around line 303-308: Update the sponsor ID selection in the purchase action
around getAccessTokenSafely so the explicit sponsorId argument is preferred,
falling back to sponsor.id only when it is absent. Preserve the existing
currentSponsorState lookup for that fallback and use the selected ID for
subsequent sponsor-specific requests.
In `@src/pages/sponsors/show-purchase-list-page/index.js`:
- Around line 202-208: Add the same localized invoice-download aria-label to
both IconButton controls: src/pages/sponsors/show-purchase-list-page/index.js
lines 202-208 and
src/pages/sponsors/sponsor-page/tabs/sponsor-purchases-tab/index.js lines
185-191. Reuse the existing localization mechanism and keep the button behavior
unchanged.
- Around line 99-109: Update the failure handler in handleInvoiceDownload to use
the imported translation instance instead of the undefined t reference, while
preserving the existing invoice_generation error message and loading-state
cleanup.
🪄 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: defaults
Review profile: CHILL
Plan: Pro
Run ID: 9ed2e073-a69c-4222-8d01-dcd070c01dd6
⛔ Files ignored due to path filters (2)
src/assets/fn-invoice-header.pngis excluded by!**/*.pngyarn.lockis excluded by!**/yarn.lock,!**/*.lock
📒 Files selected for processing (5)
package.jsonsrc/actions/sponsor-purchases-actions.jssrc/i18n/en.jsonsrc/pages/sponsors/show-purchase-list-page/index.jssrc/pages/sponsors/sponsor-page/tabs/sponsor-purchases-tab/index.js
| const { currentSummitState, currentSponsorState } = getState(); | ||
| const { currentSummit } = currentSummitState; | ||
| const { entity: sponsor } = currentSponsorState; | ||
| const accessToken = await getAccessTokenSafely(); | ||
|
|
||
| return getRequest( | ||
| null, | ||
| createAction(RECEIVE_SPONSOR_ORDER), | ||
| `${window.PURCHASES_API_URL}/api/v2/summits/${currentSummit.id}/sponsors/${sponsor.id}/purchases/${orderId}`, | ||
| authErrorHandler | ||
| )(params)(dispatch).finally(() => { | ||
| dispatch(stopLoading()); | ||
| }); | ||
| }; | ||
| const sponsor_id = sponsor.id || sponsorId; |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
Prioritize the explicit purchase sponsor ID.
The global purchase list passes the row’s purchaseOrder.sponsor_id, but Line 308 ignores it whenever Redux still holds a previously viewed sponsor. That can fetch the order through the wrong sponsor URL. Prefer the explicit argument, with Redux only as the fallback.
Proposed fix
- const sponsor_id = sponsor.id || sponsorId;
+ const sponsor_id = sponsorId || (sponsor && sponsor.id);📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| const { currentSummitState, currentSponsorState } = getState(); | |
| const { currentSummit } = currentSummitState; | |
| const { entity: sponsor } = currentSponsorState; | |
| const accessToken = await getAccessTokenSafely(); | |
| return getRequest( | |
| null, | |
| createAction(RECEIVE_SPONSOR_ORDER), | |
| `${window.PURCHASES_API_URL}/api/v2/summits/${currentSummit.id}/sponsors/${sponsor.id}/purchases/${orderId}`, | |
| authErrorHandler | |
| )(params)(dispatch).finally(() => { | |
| dispatch(stopLoading()); | |
| }); | |
| }; | |
| const sponsor_id = sponsor.id || sponsorId; | |
| const { currentSummitState, currentSponsorState } = getState(); | |
| const { currentSummit } = currentSummitState; | |
| const { entity: sponsor } = currentSponsorState; | |
| const accessToken = await getAccessTokenSafely(); | |
| const sponsor_id = sponsorId || (sponsor && sponsor.id); |
🤖 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 `@src/actions/sponsor-purchases-actions.js` around lines 303 - 308, Update the
sponsor ID selection in the purchase action around getAccessTokenSafely so the
explicit sponsorId argument is preferred, falling back to sponsor.id only when
it is absent. Preserve the existing currentSponsorState lookup for that fallback
and use the selected ID for subsequent sponsor-specific requests.
| const handleInvoiceDownload = (purchaseOrder) => { | ||
| if (loadingPDF) return; | ||
| setLoadingPDF(true); | ||
| getSponsorOrder(purchaseOrder.id, purchaseOrder.sponsor_id) | ||
| .then(({ response: fetchedOrder }) => | ||
| generateInvoicePDF(fetchedOrder, currentSummit, { | ||
| logoSrc: logoInvoice | ||
| }) | ||
| ) | ||
| .catch(() => errorMessage(t("errors.invoice_generation"))) | ||
| .finally(() => setLoadingPDF(false)); |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
Use the imported translation instance in the failure handler.
Line 108 references undefined t; a failed fetch/PDF generation throws instead of showing the snackbar.
- .catch(() => errorMessage(t("errors.invoice_generation")))
+ .catch(() => errorMessage(T.translate("errors.invoice_generation")))📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| const handleInvoiceDownload = (purchaseOrder) => { | |
| if (loadingPDF) return; | |
| setLoadingPDF(true); | |
| getSponsorOrder(purchaseOrder.id, purchaseOrder.sponsor_id) | |
| .then(({ response: fetchedOrder }) => | |
| generateInvoicePDF(fetchedOrder, currentSummit, { | |
| logoSrc: logoInvoice | |
| }) | |
| ) | |
| .catch(() => errorMessage(t("errors.invoice_generation"))) | |
| .finally(() => setLoadingPDF(false)); | |
| const handleInvoiceDownload = (purchaseOrder) => { | |
| if (loadingPDF) return; | |
| setLoadingPDF(true); | |
| getSponsorOrder(purchaseOrder.id, purchaseOrder.sponsor_id) | |
| .then(({ response: fetchedOrder }) => | |
| generateInvoicePDF(fetchedOrder, currentSummit, { | |
| logoSrc: logoInvoice | |
| }) | |
| ) | |
| .catch(() => errorMessage(T.translate("errors.invoice_generation"))) | |
| .finally(() => setLoadingPDF(false)); |
🤖 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 `@src/pages/sponsors/show-purchase-list-page/index.js` around lines 99 - 109,
Update the failure handler in handleInvoiceDownload to use the imported
translation instance instead of the undefined t reference, while preserving the
existing invoice_generation error message and loading-state cleanup.
| <IconButton | ||
| size="large" | ||
| sx={{ color: "primary.main" }} | ||
| onClick={() => handleMenu(row)} | ||
| onClick={() => handleInvoiceDownload(row)} | ||
| > | ||
| <DownloadIcon fontSize="large" /> | ||
| </IconButton> |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
Add an accessible name to both invoice-download buttons.
Screen-reader users cannot identify icon-only controls without an aria-label.
src/pages/sponsors/show-purchase-list-page/index.js#L202-L208: add a localized invoice-downloadaria-label.src/pages/sponsors/sponsor-page/tabs/sponsor-purchases-tab/index.js#L185-L191: add the same localizedaria-label.
📍 Affects 2 files
src/pages/sponsors/show-purchase-list-page/index.js#L202-L208(this comment)src/pages/sponsors/sponsor-page/tabs/sponsor-purchases-tab/index.js#L185-L191
🤖 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 `@src/pages/sponsors/show-purchase-list-page/index.js` around lines 202 - 208,
Add the same localized invoice-download aria-label to both IconButton controls:
src/pages/sponsors/show-purchase-list-page/index.js lines 202-208 and
src/pages/sponsors/sponsor-page/tabs/sponsor-purchases-tab/index.js lines
185-191. Reuse the existing localization mechanism and keep the button behavior
unchanged.
ref: https://app.clickup.com/t/9014802374/86bb31qvd
Signed-off-by: Tomás Castillo tcastilloboireau@gmail.com
Summary by CodeRabbit
New Features
Bug Fixes
Improvements