Skip to content

feat: add order invoice component#282

Open
tomrndom wants to merge 3 commits into
mainfrom
feat/order-pdf
Open

feat: add order invoice component#282
tomrndom wants to merge 3 commits into
mainfrom
feat/order-pdf

Conversation

@tomrndom

@tomrndom tomrndom commented Jul 3, 2026

Copy link
Copy Markdown
Contributor

ref: https://app.clickup.com/t/9014802374/86baq9key

Signed-off-by: Tomás Castillo tcastilloboireau@gmail.com

Summary by CodeRabbit

  • New Features

    • Added PDF invoice generation for orders, including a printable order summary with line items, discounts, fees, payments, refunds, notes, and balance details.
    • Added quick actions to generate or preview the invoice as a PDF.
    • Included the new PDF workflow in the app’s build output.
  • Tests

    • Added coverage for invoice row formatting and PDF rendering behavior.

Signed-off-by: Tomás Castillo <tcastilloboireau@gmail.com>
@coderabbitai

coderabbitai Bot commented Jul 3, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@tomrndom, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 40 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: c7822614-a8b0-4f9b-9c7a-1e4bd755d37a

📥 Commits

Reviewing files that changed from the base of the PR and between 58c7524 and 27283ec.

📒 Files selected for processing (4)
  • src/components/index.js
  • src/components/order-invoice-pdf/__tests__/order-invoice-pdf.test.js
  • src/components/order-invoice-pdf/index.js
  • webpack.common.js
📝 Walkthrough

Walkthrough

Adds a new order-pdf component module implementing order invoice PDF generation and rendering with @react-pdf/renderer, exposes OrderPdf, buildOrderPdfRows, generateInvoicePDF, and previewPDF via the components index and a new webpack entry, adds test coverage, and widens the @react-pdf/renderer dependency ranges.

Changes

Order PDF feature

Layer / File(s) Summary
Dependency version updates
package.json
Widens @react-pdf/renderer devDependency to ^4.4.1 and peerDependency to ^3.1.11 || ^4.0.0.
Formatting helpers and icon utilities
src/components/order-pdf/index.js
Adds imports, formatDate/formatAddress/venue name helpers, icon path map, and PdfIcon SVG renderer.
Row construction and balance logic
src/components/order-pdf/index.js
Implements buildRows(order, summit) producing item/discount/fee/payment/refund/note rows with a running balanceCents, plus fmtBalance formatting.
PDF styles and row style mapping
src/components/order-pdf/index.js
Adds createStyles and createRowStyles defining page/table/typography and per-row-type style/badge configuration.
Table row and reconciliation subcomponents
src/components/order-pdf/index.js
Implements PdfTableRow, FieldRow, and ReconciliationBlock for rendering rows and reconciliation figures.
OrderPdf component and export utilities
src/components/order-pdf/index.js
Implements OrderPdf invoice layout with propTypes, generateInvoicePDF (blob download), and previewPDF (new-tab preview).
Module exports and build wiring
src/components/index.js, webpack.common.js
Re-exports the new PDF utilities from the components index and adds a components/order-pdf webpack entry.
Test coverage
src/components/__tests__/order-pdf-.test.js
Adds Jest tests covering buildOrderPdfRows row/balance logic and OrderPdf render output including reconciliation labels.

Estimated code review effort: 3 (Moderate) | ~30 minutes

Sequence Diagram(s)

sequenceDiagram
    participant Caller
    participant generateInvoicePDF
    participant OrderPdf
    participant ReactPDF as "`@react-pdf/renderer`"
    participant Browser

    Caller->>generateInvoicePDF: generateInvoicePDF(order, summit, opts)
    generateInvoicePDF->>OrderPdf: render(order, summit, logoSrc, fontFamily)
    generateInvoicePDF->>ReactPDF: pdf(OrderPdf).toBlob()
    ReactPDF-->>generateInvoicePDF: blob
    generateInvoicePDF->>Browser: createObjectURL(blob)
    generateInvoicePDF->>Browser: trigger anchor download
    generateInvoicePDF->>Browser: revokeObjectURL(url)
Loading

Suggested reviewers: smarcet

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: adding an order invoice component.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
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
  • Commit unit tests in branch feat/order-pdf

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

Choose a reason for hiding this comment

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

Actionable comments posted: 4

🤖 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/components/__tests__/order-pdf-.test.js`:
- Line 16: The test import is wrong in order-pdf-.test.js: it only brings in
buildOrderPdfRows and OrderPdf from the components barrel, but the suite
actually calls buildRows, and importing via the barrel also pulls in react-dnd
through SortableTable. Update the test to import the order-pdf module directly
and use the matching exported helper name (or import the alias used by the
tests) so buildRows is defined and the react-dnd dependency chain is avoided.

In `@src/components/order-pdf/index.js`:
- Around line 82-101: The item filtering in the order PDF is inconsistent with
the displayed default quantity: the current filter in the item iteration drops
rows when quantity is missing, but the row rendering in the same block (`qty` in
the `form.items` processing) treats missing quantity as 1. Update the
`form.items` filter logic so optional/missing quantities are still included when
they should render as default quantity 1, and keep the balance calculation and
PDF rows aligned with the `rows.push` item mapping.
- Around line 804-805: The previewPDF flow is creating a blob URL and opening it
in a new tab without releasing it afterward, which can leak memory on repeated
previews. Update the URL handling around the object URL creation in previewPDF
so the URL is revoked after the new window/tab has been opened, while keeping
the current open-in-new-tab behavior intact.
- Around line 32-33: The formatDate helper currently calls .format()
unconditionally on the result of epochToMomentTimeZone, which can be falsy for
missing or 0 epochs and crash invoice rendering. Update formatDate to guard the
value returned by epochToMomentTimeZone before formatting, and return a safe
fallback when no valid moment-like value exists. Keep the fix localized to
formatDate so callers of the order PDF rendering path continue working without
changes.
🪄 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: b0cf3870-2447-4bcc-95d2-77eafc9856b4

📥 Commits

Reviewing files that changed from the base of the PR and between d418849 and 58c7524.

📒 Files selected for processing (5)
  • package.json
  • src/components/__tests__/order-pdf-.test.js
  • src/components/index.js
  • src/components/order-pdf/index.js
  • webpack.common.js

Comment thread src/components/__tests__/order-pdf-.test.js Outdated
Comment thread src/components/order-pdf/index.js Outdated
Comment on lines +82 to +101
(form.items || [])
.filter((item) => item.quantity)
.forEach((item) => {
// Cancelled is per-item
const cancelled = !!item.canceled_by_id;
const cancelledBy = cancelled
? `Cancelled by ${item.canceled_by_full_name} on ${formatDate(item.canceled_at, summit.time_zone_id, "YYYY/MM/DD HH:mm")}`
: "";

// Cancelled items still accumulate
balanceCents += item.amount;

rows.push({
rowKey: `item-${item.line_id ?? item.id}`,
type: "item",
// Table shows form.code per item row (columnKey: "formCode", value: form.code)
code: String(form.code || ""),
description: String(item.type?.name || item.title || ""),
addon: String(form.add_on?.name || ""),
qty: String(item.quantity ?? 1),

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Keep the quantity filter consistent with the default quantity.

Line 83 drops items with missing quantity, but Line 101 renders missing quantity as 1. If quantity is optional, those invoice rows disappear from the balance and PDF.

Proposed fix
-      .filter((item) => item.quantity)
+      .filter((item) => (item.quantity ?? 1) > 0)
📝 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.

Suggested change
(form.items || [])
.filter((item) => item.quantity)
.forEach((item) => {
// Cancelled is per-item
const cancelled = !!item.canceled_by_id;
const cancelledBy = cancelled
? `Cancelled by ${item.canceled_by_full_name} on ${formatDate(item.canceled_at, summit.time_zone_id, "YYYY/MM/DD HH:mm")}`
: "";
// Cancelled items still accumulate
balanceCents += item.amount;
rows.push({
rowKey: `item-${item.line_id ?? item.id}`,
type: "item",
// Table shows form.code per item row (columnKey: "formCode", value: form.code)
code: String(form.code || ""),
description: String(item.type?.name || item.title || ""),
addon: String(form.add_on?.name || ""),
qty: String(item.quantity ?? 1),
(form.items || [])
.filter((item) => (item.quantity ?? 1) > 0)
.forEach((item) => {
// Cancelled is per-item
const cancelled = !!item.canceled_by_id;
const cancelledBy = cancelled
? `Cancelled by ${item.canceled_by_full_name} on ${formatDate(item.canceled_at, summit.time_zone_id, "YYYY/MM/DD HH:mm")}`
: "";
// Cancelled items still accumulate
balanceCents += item.amount;
rows.push({
rowKey: `item-${item.line_id ?? item.id}`,
type: "item",
// Table shows form.code per item row (columnKey: "formCode", value: form.code)
code: String(form.code || ""),
description: String(item.type?.name || item.title || ""),
addon: String(form.add_on?.name || ""),
qty: String(item.quantity ?? 1),
🤖 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/components/order-pdf/index.js` around lines 82 - 101, The item filtering
in the order PDF is inconsistent with the displayed default quantity: the
current filter in the item iteration drops rows when quantity is missing, but
the row rendering in the same block (`qty` in the `form.items` processing)
treats missing quantity as 1. Update the `form.items` filter logic so
optional/missing quantities are still included when they should render as
default quantity 1, and keep the balance calculation and PDF rows aligned with
the `rows.push` item mapping.

Comment on lines +804 to +805
const url = URL.createObjectURL(blob);
window.open(url, "_blank");

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

Revoke preview blob URLs after opening.

previewPDF creates an object URL but never releases it, so repeated previews can leak browser memory.

Proposed fix
   const url = URL.createObjectURL(blob);
-  window.open(url, "_blank");
+  window.open(url, "_blank");
+  setTimeout(() => URL.revokeObjectURL(url), 0);
 };
📝 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.

Suggested change
const url = URL.createObjectURL(blob);
window.open(url, "_blank");
const url = URL.createObjectURL(blob);
window.open(url, "_blank");
setTimeout(() => URL.revokeObjectURL(url), 0);
🤖 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/components/order-pdf/index.js` around lines 804 - 805, The previewPDF
flow is creating a blob URL and opening it in a new tab without releasing it
afterward, which can leak memory on repeated previews. Update the URL handling
around the object URL creation in previewPDF so the URL is revoked after the new
window/tab has been opened, while keeping the current open-in-new-tab behavior
intact.

tomrndom added 2 commits July 3, 2026 18:30
Signed-off-by: Tomás Castillo <tcastilloboireau@gmail.com>
Signed-off-by: Tomás Castillo <tcastilloboireau@gmail.com>

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Adds a reusable “order invoice PDF” component to uicore, enabling apps that consume this library to generate and preview an order invoice PDF (with line items, fees, discounts, payments/refunds, notes, reconciliation, and amount due).

Changes:

  • Introduces OrderPdf + helpers (buildRows, generateInvoicePDF, previewPDF) under src/components/order-invoice-pdf/.
  • Exposes the new component/helpers via the central src/components/index.js export surface and adds a webpack entrypoint.
  • Updates @react-pdf/renderer to v4.x and adds unit tests for row-building and rendering.

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
webpack.common.js Adds a new webpack entry for the invoice PDF component bundle.
src/components/order-invoice-pdf/index.js Implements PDF layout, row building, and generate/preview helpers.
src/components/order-invoice-pdf/__tests__/order-invoice-pdf.test.js Adds tests for row formatting and basic render behavior.
src/components/index.js Exports the new invoice PDF APIs from the library’s main component index.
package.json Bumps @react-pdf/renderer devDependency and broadens peer dependency to allow v4.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +51 to +58
return [
address.address_1,
address.address_2,
address.city,
address.state,
address.zip_code,
address.country
]
Comment on lines +809 to +820
export const previewPDF = async (order, summit, { logoSrc, fontFamily } = {}) => {
const blob = await pdf(
<OrderPdf
order={order}
summit={summit}
logoSrc={logoSrc}
fontFamily={fontFamily}
/>
).toBlob();
const url = URL.createObjectURL(blob);
window.open(url, "_blank");
};
Comment on lines +309 to +314
const makeRenderOrder = (overrides = {}) => ({
number: "ORD-2026-001",
total: 0,
purchased_date: "2026-01-15T10:00:00Z",
purchased_by_full_name: "Jane Doe",
client: { contact_name: "Jane Doe", company_name: "Acme Corp" },
Comment on lines +352 to +356
expect(text).toContain("Jane Doe");
expect(text).toContain("OpenStack Summit 2026");
expect(text).toContain("Main Hall");
expect(text).toContain("123 Expo Blvd");
});
Comment thread src/components/index.js
export {default as Panel} from './sections/panel'
export {default as SimpleLinkList} from './simple-link-list'
export {default as SummitDropdown} from './summit-dropdown'
export {OrderPdf, buildRows as buildOrderPdfRows, generateInvoicePDF, previewPDF} from './order-invoice-pdf'

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

this is wrong please do check

// export {default as TextEditorV3} from './inputs/editor-input-v3'

this export force to incluide a 3rd party deps even when is not used

Comment thread package.json
"@mui/material": "^6.4.3",
"@mui/x-date-pickers": "^7.26.0",
"@react-pdf/renderer": "^3.1.11",
"@react-pdf/renderer": "^3.1.11 || ^4.0.0",

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

@tomrndom this is wrong u should use the same version on both places
"@react-pdf/renderer": "^4.4.1",

@smarcet smarcet left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

@tomrndom please review

}
});

const PdfTableRow = ({ row, styles, rowStyles }) => {

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.

can you put each of the components and svgs in this file in its own file ? is very hard to read a 1000 line file

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.

4 participants