From fa2f702b48754ec4efa8741519feeafd2381c39c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Castillo?= Date: Wed, 26 Aug 2026 14:33:10 -0300 Subject: [PATCH 1/5] fix: add partial cancellations into pdf invoice MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Tomás Castillo --- .../__tests__/fixtures/purchase-v2.json | 45 +++++++++++++ .../__tests__/order-invoice-pdf.test.js | 67 ++++++++++++++++--- .../components/pdf-table-row.js | 11 ++- src/components/order-invoice-pdf/helpers.js | 37 ++++++---- 4 files changed, 135 insertions(+), 25 deletions(-) diff --git a/src/components/order-invoice-pdf/__tests__/fixtures/purchase-v2.json b/src/components/order-invoice-pdf/__tests__/fixtures/purchase-v2.json index b64c1a08..ba743206 100644 --- a/src/components/order-invoice-pdf/__tests__/fixtures/purchase-v2.json +++ b/src/components/order-invoice-pdf/__tests__/fixtures/purchase-v2.json @@ -86,6 +86,51 @@ "canceled_by_email": "admin@example.com", "canceled_by_full_name": "Admin User", "cancel_reason": "Sponsor downgraded package", + "canceled_quantity": 1, + "cancellations": [ + { + "id": 501, + "quantity": 1, + "amount": 15000, + "reason": "Sponsor downgraded package", + "canceled_by_id": 7, + "canceled_by_email": "admin@example.com", + "canceled_by_full_name": "Admin User", + "created": 1699900000 + } + ], + "meta_fields": [] + }, + { + "line_id": 9003, + "position": 3, + "code": "ITEM-C", + "title": "Extra Badges", + "type": { "id": 213, "code": "ITEM-C", "name": "Extra Badges" }, + "notes": null, + "quantity": 5, + "current_rate_type": "Early", + "current_rate": 10000, + "custom_rate": null, + "amount": 50000, + "canceled_at": null, + "canceled_by_id": null, + "canceled_by_email": null, + "canceled_by_full_name": null, + "cancel_reason": null, + "canceled_quantity": 2, + "cancellations": [ + { + "id": 502, + "quantity": 2, + "amount": 20000, + "reason": "Partial downgrade", + "canceled_by_id": 7, + "canceled_by_email": "admin@example.com", + "canceled_by_full_name": "Admin User", + "created": 1699950000 + } + ], "meta_fields": [] } ] diff --git a/src/components/order-invoice-pdf/__tests__/order-invoice-pdf.test.js b/src/components/order-invoice-pdf/__tests__/order-invoice-pdf.test.js index 3337829c..904f4f51 100644 --- a/src/components/order-invoice-pdf/__tests__/order-invoice-pdf.test.js +++ b/src/components/order-invoice-pdf/__tests__/order-invoice-pdf.test.js @@ -42,7 +42,7 @@ const TRANSLATIONS = { "sponsor_order_grid.refunded": "Refunded", "sponsor_order_grid.retained": "Retained as cancellation fee", "sponsor_order_grid.credited": "Credited to Payment Method", - "sponsor_order_grid.cancelled_by": "Cancelled {date} by {user}", + "sponsor_order_grid.cancelled_by": "Cancelled ({x} of {y}) - {date} - {user}", "mui_table.payment": "Payment", "mui_table.discount": "Discount", "mui_table.refund": "Refund", @@ -111,7 +111,8 @@ const MOCK_SUMMIT = { time_zone_id: "UTC" }; const baseForm = purchaseV2Fixture.forms[0]; const baseItem = baseForm.items[0]; // not cancelled -const baseCancelledItem = baseForm.items[1]; // cancelled +const baseCancelledItem = baseForm.items[1]; // fully cancelled +const basePartiallyCancelledItem = baseForm.items[2]; // partially cancelled const baseFee = purchaseV2Fixture.fees[0]; const basePayment = purchaseV2Fixture.payments[0]; const baseRefund = purchaseV2Fixture.refunds[0]; @@ -123,6 +124,10 @@ const makeCancelledItem = (overrides = {}) => ({ ...baseCancelledItem, ...overrides }); +const makePartiallyCancelledItem = (overrides = {}) => ({ + ...basePartiallyCancelledItem, + ...overrides +}); const makeFee = (overrides = {}) => ({ ...baseFee, ...overrides }); const makePayment = (overrides = {}) => ({ ...basePayment, ...overrides }); const makeRefund = (overrides = {}) => ({ ...baseRefund, ...overrides }); @@ -224,34 +229,51 @@ describe("buildRows — item rows", () => { // ─── Cancelled items (per-item, not per-form) ───────────────────────────────── describe("buildRows — cancelled items", () => { - it("sets cancelled: true and populates cancelledBy when item.canceled_by_id is set", () => { + it("sets cancelled: true and populates cancellations when canceled_quantity equals quantity", () => { const rows = buildRows( { forms: [makeForm({ items: [makeCancelledItem()] })] }, MOCK_SUMMIT ); expect(rows[0].cancelled).toBe(true); - expect(rows[0].cancelledBy).toMatch(/Admin User/); + expect(rows[0].partiallyCancelled).toBe(false); + expect(rows[0].cancellations).toHaveLength(1); + expect(rows[0].cancellations[0].label).toMatch(/Admin User/); + expect(rows[0].qty).toBe("0"); // quantity(1) - canceled_quantity(1) }); - it("sets cancelled: false and empty cancelledBy when canceled_by_id is absent or null", () => { - const withNull = makeForm({ + it("sets cancelled: false, partiallyCancelled: false and no cancellations when canceled_quantity is absent or 0", () => { + const withZero = makeForm({ id: 1, discount_in_cents: 0, - items: [makeItem({ canceled_by_id: null })] + items: [makeItem({ canceled_quantity: 0 })] }); const withAbsent = makeForm({ id: 2, discount_in_cents: 0, items: [makeItem()] }); - const rows = buildRows({ forms: [withNull, withAbsent] }, MOCK_SUMMIT); + const rows = buildRows({ forms: [withZero, withAbsent] }, MOCK_SUMMIT); rows.forEach((r) => { expect(r.cancelled).toBe(false); - expect(r.cancelledBy).toBe(""); + expect(r.partiallyCancelled).toBe(false); + expect(r.cancellations).toEqual([]); }); }); - it("cancelled items still accumulate into the running balance", () => { + it("sets partiallyCancelled: true (and cancelled: false) when canceled_quantity is between 0 and quantity", () => { + const rows = buildRows( + { forms: [makeForm({ items: [makePartiallyCancelledItem()] })] }, + MOCK_SUMMIT + ); + expect(rows[0].cancelled).toBe(false); + expect(rows[0].partiallyCancelled).toBe(true); + // quantity(5) - canceled_quantity(2) = 3 remaining + expect(rows[0].qty).toBe("3"); + expect(rows[0].cancellations).toHaveLength(1); + expect(rows[0].cancellations[0].label).toMatch(/Admin User/); + }); + + it("fully cancelled items still accumulate their full amount into the running balance", () => { const normalItem = makeItem({ amount: 8000 }); const cancelledItem = makeCancelledItem({ amount: 10000 }); const rows = buildRows( @@ -269,6 +291,15 @@ describe("buildRows — cancelled items", () => { expect(cancelled.balanceCents).toBe(18000); // 8000 + 10000 }); + it("partially cancelled items still accumulate their full amount into the running balance (matches SponsorOrderGrid; cancellation only nets out via reconciliation)", () => { + const partialItem = makePartiallyCancelledItem({ amount: 50000 }); + const rows = buildRows( + { forms: [makeForm({ discount_in_cents: 0, items: [partialItem] })] }, + MOCK_SUMMIT + ); + expect(rows[0].balanceCents).toBe(50000); + }); + it("a form-level canceled_by_id does not mark items as cancelled", () => { const form = makeForm({ canceled_by_id: 99, items: [makeItem()] }); const rows = buildRows({ forms: [form] }, MOCK_SUMMIT); @@ -607,6 +638,22 @@ describe("OrderPdf — reconciliation block", () => { }); }); +// ─── Partial cancellation (render-level) ────────────────────────────────── + +describe("OrderPdf — partial cancellation", () => { + it("shows a quantity split for a partially-cancelled line instead of a full strikethrough row", () => { + const { container } = render( + + ); + const text = container.textContent; + // basePartiallyCancelledItem: quantity 5, canceled_quantity 2 -> 3 remaining + expect(text).toContain("Extra Badges - Total: 3"); + // Cancellation event line uses the (x of y) translation tokens + expect(text).toContain("Cancelled (2 of 5) - "); + expect(text).toContain("Admin User"); + }); +}); + // ─── generateInvoicePDF ──────────────────────────────────────────────────── describe("generateInvoicePDF", () => { diff --git a/src/components/order-invoice-pdf/components/pdf-table-row.js b/src/components/order-invoice-pdf/components/pdf-table-row.js index 173197e6..8e0db9d6 100644 --- a/src/components/order-invoice-pdf/components/pdf-table-row.js +++ b/src/components/order-invoice-pdf/components/pdf-table-row.js @@ -52,9 +52,14 @@ export const PdfTableRow = ({ row, styles, rowStyles }) => { ? `${row.description} - ${T.translate("mui_table.total")}: ${row.qty}` : row.description} - {row.cancelledBy && ( - {row.cancelledBy} - )} + {(row.cancellations || []).map((cancellation) => ( + + {cancellation.label} + {cancellation.reason && ( + {cancellation.reason} + )} + + ))} {row.subDescription && ( {row.subDescription} )} diff --git a/src/components/order-invoice-pdf/helpers.js b/src/components/order-invoice-pdf/helpers.js index 36e0ee62..a2e57c79 100644 --- a/src/components/order-invoice-pdf/helpers.js +++ b/src/components/order-invoice-pdf/helpers.js @@ -77,16 +77,19 @@ export const buildRows = (order) => { (form.items || []) .filter((item) => (item.quantity ?? 1) > 0) .forEach((item) => { - // Cancelled is per-item - const cancelled = !!item.canceled_by_id; - const cancelledBy = cancelled - ? T.translate("sponsor_order_grid.cancelled_by", { - user: item.canceled_by_full_name, - date: formatDate(item.canceled_at, "LOC", "YYYY/MM/DD HH:mm") - }) - : ""; - - // Cancelled items still accumulate + // Cancellation is per-item and quantity-scoped: canceled_quantity may + // be anywhere from 0 (not cancelled) up to quantity (fully cancelled), + // with the individual cancellation events (and their frozen per-event + // amounts) listed in cancellations. Mirrors SponsorOrderGrid's contract. + const quantity = item.quantity ?? 1; + const canceledQuantity = item.canceled_quantity ?? 0; + const cancellations = item.cancellations ?? []; + const cancelled = canceledQuantity > 0 && canceledQuantity === quantity; + const partiallyCancelled = canceledQuantity > 0 && canceledQuantity < quantity; + + // Matches SponsorOrderGrid: a charge stays in the ledger in full + // whether it's cancelled or not, partially or fully -- cancellation + // only nets out via the reconciliation block below. balanceCents += item.amount; rows.push({ @@ -96,11 +99,21 @@ export const buildRows = (order) => { code: String(form.code || ""), description: String(item.type?.name || item.title || ""), addon: String(form.add_on?.name || ""), - qty: String(item.quantity ?? 1), + qty: String(quantity - canceledQuantity), price: currencyAmountFromCents(item.amount), balanceCents, cancelled, - cancelledBy + partiallyCancelled, + cancellations: cancellations.map((c) => ({ + id: c.id, + label: T.translate("sponsor_order_grid.cancelled_by", { + x: c.quantity, + y: quantity, + user: c.canceled_by_full_name, + date: formatDate(c.created, "LOC", "YYYY/MM/DD HH:mm") + }), + reason: c.reason ? String(c.reason) : "" + })) }); }); From 19a45ede6414e8b9995457d6bfe1afef5c962620 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Castillo?= Date: Wed, 26 Aug 2026 14:35:34 -0300 Subject: [PATCH 2/5] fix: publish 5.0.54-beta.0 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Tomás Castillo --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 90e38915..5ddb46c2 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "openstack-uicore-foundation", - "version": "5.0.53", + "version": "5.0.54-beta.0", "description": "ui reactjs components for openstack marketing site", "main": "lib/openstack-uicore-foundation.js", "scripts": { From dac68e73004833f8f40d9cb40122df6c003b97d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Castillo?= Date: Wed, 26 Aug 2026 17:11:37 -0300 Subject: [PATCH 3/5] fix: adjust cancellation date format, add cancellation header, apply styles MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Tomás Castillo --- .../__tests__/order-invoice-pdf.test.js | 40 +++++++++++++++++ .../components/cancelled-items-summary.js | 45 +++++++++++++++++++ .../components/pdf-table-row.js | 4 +- src/components/order-invoice-pdf/helpers.js | 8 +++- src/components/order-invoice-pdf/index.js | 6 +++ src/components/order-invoice-pdf/styles.js | 22 +++++++++ 6 files changed, 123 insertions(+), 2 deletions(-) create mode 100644 src/components/order-invoice-pdf/components/cancelled-items-summary.js diff --git a/src/components/order-invoice-pdf/__tests__/order-invoice-pdf.test.js b/src/components/order-invoice-pdf/__tests__/order-invoice-pdf.test.js index 904f4f51..d6c2e4c7 100644 --- a/src/components/order-invoice-pdf/__tests__/order-invoice-pdf.test.js +++ b/src/components/order-invoice-pdf/__tests__/order-invoice-pdf.test.js @@ -43,6 +43,7 @@ const TRANSLATIONS = { "sponsor_order_grid.retained": "Retained as cancellation fee", "sponsor_order_grid.credited": "Credited to Payment Method", "sponsor_order_grid.cancelled_by": "Cancelled ({x} of {y}) - {date} - {user}", + "sponsor_order_grid.cancelled_items": "Cancelled items:", "mui_table.payment": "Payment", "mui_table.discount": "Discount", "mui_table.refund": "Refund", @@ -651,6 +652,45 @@ describe("OrderPdf — partial cancellation", () => { // Cancellation event line uses the (x of y) translation tokens expect(text).toContain("Cancelled (2 of 5) - "); expect(text).toContain("Admin User"); + expect(text).toContain( + formatDate( + basePartiallyCancelledItem.cancellations[0].created, + "LOC", + "M/D/YY [@] h:mm A" + ) + ); + }); +}); + +// ─── Cancelled items summary (render-level) ─────────────────────────────── +// +// Mirrors SponsorOrderGrid's CancelledItems: an at-a-glance list of every +// (partially or fully) cancelled line, so a reader doesn't have to scan the +// whole table to find them. + +describe("OrderPdf — cancelled items summary", () => { + it("lists every cancelled line as 'formCode - itemCode (x/y)'", () => { + const { container } = render( + + ); + const text = container.textContent; + expect(text).toContain("Cancelled items:"); + // baseCancelledItem: form GOLD-1, ITEM-B, fully cancelled (1/1) + expect(text).toContain("GOLD-1 - ITEM-B (1/1)"); + // basePartiallyCancelledItem: form GOLD-1, ITEM-C, partially cancelled (2/5) + expect(text).toContain("GOLD-1 - ITEM-C (2/5)"); + }); + + it("omits the summary entirely when no line is cancelled", () => { + const { container } = render( + + ); + expect(container.textContent).not.toContain("Cancelled items:"); }); }); diff --git a/src/components/order-invoice-pdf/components/cancelled-items-summary.js b/src/components/order-invoice-pdf/components/cancelled-items-summary.js new file mode 100644 index 00000000..cab0aecd --- /dev/null +++ b/src/components/order-invoice-pdf/components/cancelled-items-summary.js @@ -0,0 +1,45 @@ +/** + * Copyright 2026 OpenStack Foundation + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * */ + +import React from "react"; +import T from "i18n-react/dist/i18n-react"; +import { View, Text } from "@react-pdf/renderer"; +import { PdfIcon } from "./pdf-icon"; + +// Mirrors SponsorOrderGrid's CancelledItems: an at-a-glance list of every +// (partially or fully) cancelled line, so a reader doesn't have to scan the +// whole table to find them. `items` are order-invoice-pdf rows (from +// buildRows), not raw order items. +export const CancelledItemsSummary = ({ items, styles }) => { + if (!items || items.length === 0) return null; + + return ( + + + + {T.translate("sponsor_order_grid.cancelled_items")} + + {items.map((item) => ( + + {item.code} - {item.itemCode} ({item.canceledQuantity}/{item.quantity}) + + ))} + + ); +}; diff --git a/src/components/order-invoice-pdf/components/pdf-table-row.js b/src/components/order-invoice-pdf/components/pdf-table-row.js index 8e0db9d6..2dc52777 100644 --- a/src/components/order-invoice-pdf/components/pdf-table-row.js +++ b/src/components/order-invoice-pdf/components/pdf-table-row.js @@ -56,7 +56,9 @@ export const PdfTableRow = ({ row, styles, rowStyles }) => { {cancellation.label} {cancellation.reason && ( - {cancellation.reason} + + - {cancellation.reason} + )} ))} diff --git a/src/components/order-invoice-pdf/helpers.js b/src/components/order-invoice-pdf/helpers.js index a2e57c79..63d043b6 100644 --- a/src/components/order-invoice-pdf/helpers.js +++ b/src/components/order-invoice-pdf/helpers.js @@ -97,9 +97,15 @@ export const buildRows = (order) => { type: "item", // Table shows form.code per item row (columnKey: "formCode", value: form.code) code: String(form.code || ""), + // Distinct from `code` (the form's code) -- this is the item's own + // code, used by the "Cancelled items:" summary to mirror + // SponsorOrderGrid's "formCode - itemCode (x/y)" links. + itemCode: String(item.type?.code || item.code || ""), description: String(item.type?.name || item.title || ""), addon: String(form.add_on?.name || ""), qty: String(quantity - canceledQuantity), + quantity, + canceledQuantity, price: currencyAmountFromCents(item.amount), balanceCents, cancelled, @@ -110,7 +116,7 @@ export const buildRows = (order) => { x: c.quantity, y: quantity, user: c.canceled_by_full_name, - date: formatDate(c.created, "LOC", "YYYY/MM/DD HH:mm") + date: formatDate(c.created, "LOC", "M/D/YY [@] h:mm A") }), reason: c.reason ? String(c.reason) : "" })) diff --git a/src/components/order-invoice-pdf/index.js b/src/components/order-invoice-pdf/index.js index 32996024..4bca0a0b 100644 --- a/src/components/order-invoice-pdf/index.js +++ b/src/components/order-invoice-pdf/index.js @@ -26,6 +26,7 @@ import { import { FieldRow } from "./components/field-row"; import { PdfTableRow } from "./components/pdf-table-row"; import { ReconciliationBlock } from "./components/reconciliation-block"; +import { CancelledItemsSummary } from "./components/cancelled-items-summary"; import { formatBalance, getOrderTotal } from "../../utils/money"; export { buildRows }; @@ -45,6 +46,9 @@ export const OrderPdf = ({ order, summit, logoSrc, theme }) => { const styles = createStyles(fontFamily); const rowStyles = createRowStyles(styles); const rows = buildRows(order, summit); + const cancelledItems = rows.filter( + (row) => row.type === "item" && row.cancellations?.length > 0 + ); const mainLocation = summit.main_locations?.[0] ?? summit.locations?.find((location) => location.is_main); @@ -147,6 +151,8 @@ export const OrderPdf = ({ order, summit, logoSrc, theme }) => { + + {/* Table */} diff --git a/src/components/order-invoice-pdf/styles.js b/src/components/order-invoice-pdf/styles.js index fcff0ba3..2ce76b6f 100644 --- a/src/components/order-invoice-pdf/styles.js +++ b/src/components/order-invoice-pdf/styles.js @@ -86,6 +86,27 @@ export const createStyles = (fontFamily) => StyleSheet.create({ fieldValue: { flex: 9 }, + cancelledItemsWrapper: { + flexDirection: "row", + flexWrap: "wrap", + alignItems: "center", + justifyContent: "flex-end", + marginTop: 8 + }, + cancelledItemsLabel: { + fontFamily, + fontSize: 8, + color: "#212529", + marginRight: 6 + }, + cancelledItemLink: { + fontSize: 8, + color: "#6C757D", + marginRight: 8 + }, + cancelledItemLinkBold: { + fontWeight: "bold" + }, tableWrapper: { marginTop: 8, borderRadius: 8, @@ -212,6 +233,7 @@ export const createStyles = (fontFamily) => StyleSheet.create({ }, paymentDesc: { fontFamily, fontSize: 8, fontWeight: "bold" }, muted: { color: "#6C757D", fontSize: 8 }, + cancellationReason: { color: "#6C757D", fontSize: 8, marginLeft: 8, fontStyle: "italic" }, typeCell: { width: "15%", flexDirection: "row", alignItems: "center" }, typeBadgeLabel: { fontSize: 8 }, cancelledText: { color: "#9ca3af", textDecoration: "line-through" }, From 43d5288f667e507a9ed1742f93b4d5e2d3aec317 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Castillo?= Date: Wed, 26 Aug 2026 17:19:24 -0300 Subject: [PATCH 4/5] fix: add svg arrow icon to avoid issues with font MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Tomás Castillo --- src/components/order-invoice-pdf/components/pdf-icon.js | 4 +++- .../order-invoice-pdf/components/pdf-table-row.js | 9 ++++++--- src/components/order-invoice-pdf/styles.js | 7 ++++++- 3 files changed, 15 insertions(+), 5 deletions(-) diff --git a/src/components/order-invoice-pdf/components/pdf-icon.js b/src/components/order-invoice-pdf/components/pdf-icon.js index 23033842..64780092 100644 --- a/src/components/order-invoice-pdf/components/pdf-icon.js +++ b/src/components/order-invoice-pdf/components/pdf-icon.js @@ -21,7 +21,9 @@ const MUI_ICON_PATHS = { Refresh: "M17.65 6.35C16.2 4.9 14.21 4 12 4c-4.42 0-7.99 3.58-7.99 8s3.57 8 7.99 8c3.73 0 6.84-2.55 7.73-6h-2.08c-.82 2.33-3.04 4-5.65 4-3.31 0-6-2.69-6-6s2.69-6 6-6c1.66 0 3.14.69 4.22 1.78L13 11h7V4l-2.35 2.35z", DoNotDisturb: - "M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.42 0-8-3.58-8-8 0-1.85.63-3.55 1.69-4.9L16.9 18.31C15.55 19.37 13.85 20 12 20zm6.31-3.1L7.1 5.69C8.45 4.63 10.15 4 12 4c4.42 0 8 3.58 8 8 0 1.85-.63 3.55-1.69 4.9z" + "M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.42 0-8-3.58-8-8 0-1.85.63-3.55 1.69-4.9L16.9 18.31C15.55 19.37 13.85 20 12 20zm6.31-3.1L7.1 5.69C8.45 4.63 10.15 4 12 4c4.42 0 8 3.58 8 8 0 1.85-.63 3.55-1.69 4.9z", + SubdirectoryArrowRight: + "m19 15-6 6-1.42-1.42L15.17 16H4V4h2v10h9.17l-3.59-3.58L13 9z" }; const PDF_ICON_SIZE = 8; diff --git a/src/components/order-invoice-pdf/components/pdf-table-row.js b/src/components/order-invoice-pdf/components/pdf-table-row.js index 2dc52777..86ecec8e 100644 --- a/src/components/order-invoice-pdf/components/pdf-table-row.js +++ b/src/components/order-invoice-pdf/components/pdf-table-row.js @@ -56,9 +56,12 @@ export const PdfTableRow = ({ row, styles, rowStyles }) => { {cancellation.label} {cancellation.reason && ( - - - {cancellation.reason} - + + + + {cancellation.reason} + + )} ))} diff --git a/src/components/order-invoice-pdf/styles.js b/src/components/order-invoice-pdf/styles.js index 2ce76b6f..5681a500 100644 --- a/src/components/order-invoice-pdf/styles.js +++ b/src/components/order-invoice-pdf/styles.js @@ -233,7 +233,12 @@ export const createStyles = (fontFamily) => StyleSheet.create({ }, paymentDesc: { fontFamily, fontSize: 8, fontWeight: "bold" }, muted: { color: "#6C757D", fontSize: 8 }, - cancellationReason: { color: "#6C757D", fontSize: 8, marginLeft: 8, fontStyle: "italic" }, + cancellationReasonRow: { + flexDirection: "row", + alignItems: "center", + marginLeft: 8 + }, + cancellationReason: { color: "#6C757D", fontSize: 8, fontStyle: "italic" }, typeCell: { width: "15%", flexDirection: "row", alignItems: "center" }, typeBadgeLabel: { fontSize: 8 }, cancelledText: { color: "#9ca3af", textDecoration: "line-through" }, From f995232409c03d6a569c22daa801fb7c4d4fdc47 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Castillo?= Date: Wed, 26 Aug 2026 17:32:08 -0300 Subject: [PATCH 5/5] fix: update version to publish beta MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Tomás Castillo --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 5ddb46c2..4c4d582f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "openstack-uicore-foundation", - "version": "5.0.54-beta.0", + "version": "5.0.54-beta.1", "description": "ui reactjs components for openstack marketing site", "main": "lib/openstack-uicore-foundation.js", "scripts": {