Skip to content

Enhance invoice template and shipping data#340

Merged
rafiqul4 merged 3 commits intomainfrom
invoicefix
Mar 12, 2026
Merged

Enhance invoice template and shipping data#340
rafiqul4 merged 3 commits intomainfrom
invoicefix

Conversation

@rafiqul4
Copy link
Collaborator

Revamp the invoice PDF template with improved styling and richer shipment/payment metadata. Changes include redesigned styles (header, metadata, badges, table, summary, shipment label, tracking grid, note box, footer), better address parsing/formatting, Ship To fallback to billing, discount chip, and clearer tracking + customer note sections. To support these display changes the invoice data shape and service mapping were extended: address now includes city/state/postalCode/country/website, and InvoiceData/OrderService now expose discountCode, shippingMethod, estimatedDelivery, shippingStatus, and customerNote (plus small null/coalescing fixes). These updates make invoices more informative and production-ready.

Revamp the invoice PDF template with improved styling and richer shipment/payment metadata. Changes include redesigned styles (header, metadata, badges, table, summary, shipment label, tracking grid, note box, footer), better address parsing/formatting, Ship To fallback to billing, discount chip, and clearer tracking + customer note sections. To support these display changes the invoice data shape and service mapping were extended: address now includes city/state/postalCode/country/website, and InvoiceData/OrderService now expose discountCode, shippingMethod, estimatedDelivery, shippingStatus, and customerNote (plus small null/coalescing fixes). These updates make invoices more informative and production-ready.
Copilot AI review requested due to automatic review settings March 11, 2026 10:53
@github-project-automation github-project-automation bot moved this to Backlog in StormCom Mar 11, 2026
@vercel
Copy link

vercel bot commented Mar 11, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
stormcomui Ready Ready Preview, Comment Mar 12, 2026 4:25am

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Updates the invoice PDF generation to include richer store/shipping/payment metadata and a redesigned invoice layout, backed by expanded InvoiceData typing and updated order-to-invoice mapping.

Changes:

  • Expanded InvoiceData (store address fields + discount/shipping/customer note metadata).
  • Extended OrderService.getInvoiceData() Prisma selection and mapping for the new invoice fields.
  • Revamped the React-PDF invoice template styling/layout, including shipment label, tracking grid, discount chip, and customer note section.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 6 comments.

File Description
src/lib/types/invoice.ts Extends invoice data contract with store address fields and new shipment/discount/note metadata.
src/lib/services/order.service.ts Adds store fields to invoice query and maps new discount/shipping/note fields into InvoiceData.
src/components/invoices/invoice-template.tsx Redesigns PDF template layout/styles and renders the new metadata (ship-to fallback, discount code chip, tracking grid, customer note).

Comment on lines +401 to +407
const parseAddress = (addr: Record<string, unknown> | null): ParsedAddress | null => {
if (!addr) return null;
// Handle case where the JSON was double-encoded (stored as a string)
if (typeof addr === 'string') {
try { return JSON.parse(addr as string) as ParsedAddress; } catch { return null; }
}
return addr as ParsedAddress;
Copy link

Copilot AI Mar 11, 2026

Choose a reason for hiding this comment

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

parseAddress is typed as (addr: Record<string, unknown> | null), but it contains a typeof addr === 'string' branch. With the current InvoiceData type (billingAddress/shippingAddress are Record<string, unknown> | null), this condition is statically impossible and should fail TypeScript (and also hides that Order.billingAddress/shippingAddress are stored as strings in Prisma). Update the types to include string (or, preferably, JSON.parse the address strings in OrderService.getInvoiceData and keep the template working with a typed object).

Copilot uses AI. Check for mistakes.
Comment on lines +465 to +469
const shipToName = effectiveShipToAddr
? [effectiveShipToAddr.firstName, effectiveShipToAddr.lastName].filter(Boolean).join(' ')
: data.customer
? `${data.customer.firstName} ${data.customer.lastName}`
: 'N/A';
Copy link

Copilot AI Mar 11, 2026

Choose a reason for hiding this comment

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

shipToName can become an empty string when an address object exists but has no firstName/lastName (e.g. { address: "..." }). Because the ternary only checks effectiveShipToAddr truthiness, the customer fallback never runs and the recipient name renders blank. Consider falling back when the computed name is empty (e.g., compute the name string first and then || to customer name / "N/A").

Suggested change
const shipToName = effectiveShipToAddr
? [effectiveShipToAddr.firstName, effectiveShipToAddr.lastName].filter(Boolean).join(' ')
: data.customer
? `${data.customer.firstName} ${data.customer.lastName}`
: 'N/A';
const shipToAddrName = effectiveShipToAddr
? [effectiveShipToAddr.firstName, effectiveShipToAddr.lastName].filter(Boolean).join(' ')
: '';
const customerName = data.customer
? [data.customer.firstName, data.customer.lastName].filter(Boolean).join(' ')
: '';
const shipToName = shipToAddrName || customerName || 'N/A';

Copilot uses AI. Check for mistakes.
Comment on lines +540 to +542
{(shipToLines.length > 0 ? shipToLines : billToLines).slice(2).map((line, i) => (
<Text key={i} style={styles.shipmentAddress}>{line}</Text>
))}
Copy link

Copilot AI Mar 11, 2026

Choose a reason for hiding this comment

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

In the shipment label "Ship To" address, using .slice(2) on the chosen address lines can drop the actual street address/city lines when the parsed address doesn't include both a name and phone/email. This can result in an empty/partial address on the label. Instead of slicing by index, skip specific fields (name/phone/email) when building the lines for this label, or build a dedicated formatShippingLabelLines() that only returns address lines.

Suggested change
{(shipToLines.length > 0 ? shipToLines : billToLines).slice(2).map((line, i) => (
<Text key={i} style={styles.shipmentAddress}>{line}</Text>
))}
{(shipToLines.length > 0 ? shipToLines : billToLines)
.filter((line) => {
const trimmed = line.trim();
const lower = trimmed.toLowerCase();
const phone = shipToPhone?.trim();
return (
trimmed.length > 0 &&
trimmed !== shipToName?.trim() &&
(!phone || trimmed !== phone) &&
!lower.startsWith('tel:') &&
!lower.startsWith('phone:')
);
})
.map((line, i) => (
<Text key={i} style={styles.shipmentAddress}>{line}</Text>
))}

Copilot uses AI. Check for mistakes.
Comment on lines +583 to +593
{data.customer && (
<Text style={styles.addressName}>
{data.customer.firstName} {data.customer.lastName}
</Text>
)}
{billToLines.length > 0 ? (
<>
{billToLines.map((line, i) => (
<Text key={i} style={i < 2 ? styles.addressHighlight : styles.addressText}>
{line}
</Text>
Copy link

Copilot AI Mar 11, 2026

Choose a reason for hiding this comment

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

The Bill To block prints the customer name (data.customer.firstName/lastName) and then renders billToLines, which may also include the parsed firstName/lastName as its first line. This can duplicate the recipient name in the PDF. Consider either omitting the name line from formatAddressLines when you already render addressName, or only render addressName when the first formatted line is not a name.

Copilot uses AI. Check for mistakes.
Comment on lines +505 to 514
<Text style={styles.label}>Order Status</Text>
<View style={[styles.badge, orderBadgeStyle()]}>
<Text>{data.status}</Text>
</View>
</View>
<View style={styles.metadataBlock}>
<Text style={styles.label}>Payment Status</Text>
<View
style={[
styles.badge,
data.paymentStatus === 'PAID' ? styles.badgePaid : styles.badgePending,
]}
>
<View style={[styles.badge, paymentBadgeStyle()]}>
<Text>{data.paymentStatus}</Text>
</View>
Copy link

Copilot AI Mar 11, 2026

Choose a reason for hiding this comment

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

Status badges apply fontSize, fontFamily, textAlign, and color via styles.badge/styles.badge* on a <View>, while the nested <Text> has no style. In @react-pdf/renderer these text-related styles generally need to be on the <Text> node (and color won't reliably affect the child text when set on a View), so the badge text may render with the wrong size/weight/color. Move typography/color to a dedicated badgeText style applied to the <Text>, and keep only layout/background/padding on the <View>.

Copilot uses AI. Check for mistakes.
Comment on lines +165 to +179
// ── Shipping info strip ────────────────────────────────────────────────────
shippingStrip: {
flexDirection: 'row',
gap: 12,
marginBottom: 18,
padding: 10,
backgroundColor: '#eff6ff',
borderRadius: 4,
borderWidth: 1,
borderColor: '#bfdbfe',
},
shippingStripBlock: {
flex: 1,
},

Copy link

Copilot AI Mar 11, 2026

Choose a reason for hiding this comment

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

shippingStrip / shippingStripBlock styles are defined but never used in the component. Consider removing them (or wiring them up) to avoid accumulating unused style code in the PDF template.

Suggested change
// ── Shipping info strip ────────────────────────────────────────────────────
shippingStrip: {
flexDirection: 'row',
gap: 12,
marginBottom: 18,
padding: 10,
backgroundColor: '#eff6ff',
borderRadius: 4,
borderWidth: 1,
borderColor: '#bfdbfe',
},
shippingStripBlock: {
flex: 1,
},

Copilot uses AI. Check for mistakes.
@rafiqul4 rafiqul4 merged commit 021808a into main Mar 12, 2026
5 checks passed
@rafiqul4 rafiqul4 deleted the invoicefix branch March 12, 2026 04:25
@github-project-automation github-project-automation bot moved this from Backlog to Done in StormCom Mar 12, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

2 participants