feat: Hook up customer/company address to invoice preview of payment page

This commit is contained in:
Ahmed Bouhuolia
2024-10-01 09:47:58 +02:00
parent 7ceb785c1b
commit ace75f2dfa
7 changed files with 63 additions and 27 deletions

View File

@@ -3,6 +3,7 @@ import { ItemEntryTransformer } from './ItemEntryTransformer';
import { SaleInvoiceTaxEntryTransformer } from './SaleInvoiceTaxEntryTransformer'; import { SaleInvoiceTaxEntryTransformer } from './SaleInvoiceTaxEntryTransformer';
import { SaleInvoiceTransformer } from './SaleInvoiceTransformer'; import { SaleInvoiceTransformer } from './SaleInvoiceTransformer';
import { Transformer } from '@/lib/Transformer/Transformer'; import { Transformer } from '@/lib/Transformer/Transformer';
import { contactAddressTextFormat } from '@/utils/address-text-format';
export class GetInvoicePaymentLinkMetaTransformer extends SaleInvoiceTransformer { export class GetInvoicePaymentLinkMetaTransformer extends SaleInvoiceTransformer {
/** /**
@@ -43,6 +44,7 @@ export class GetInvoicePaymentLinkMetaTransformer extends SaleInvoiceTransformer
'organization', 'organization',
'isReceivable', 'isReceivable',
'hasStripePaymentMethod', 'hasStripePaymentMethod',
'formattedCustomerAddress',
]; ];
}; };
@@ -101,6 +103,14 @@ export class GetInvoicePaymentLinkMetaTransformer extends SaleInvoiceTransformer
(paymentMethod) => paymentMethod.paymentIntegration.service === 'Stripe' (paymentMethod) => paymentMethod.paymentIntegration.service === 'Stripe'
); );
} }
protected formattedCustomerAddress(invoice) {
return contactAddressTextFormat(invoice.customer, `{ADDRESS_1}
{ADDRESS_2}
{CITY}, {STATE} {POSTAL_CODE}
{COUNTRY}
{PHONE}`);
}
} }
class GetPaymentLinkOrganizationMetaTransformer extends Transformer { class GetPaymentLinkOrganizationMetaTransformer extends Transformer {

View File

@@ -68,11 +68,11 @@ export default class TenantMetadata extends BaseModel {
*/ */
public get addressTextFormatted() { public get addressTextFormatted() {
const defaultMessage = `<strong>{ORGANIZATION_NAME}</strong> const defaultMessage = `<strong>{ORGANIZATION_NAME}</strong>
{ADDRESS_1}, {ADDRESS_1}
{ADDRESS_2}, {ADDRESS_2}
{CITY} {STATE}, {CITY}, {STATE} {POSTAL_CODE}
{POSTAL_CODE},
{COUNTRY} {COUNTRY}
{PHONE}
`; `;
return organizationAddressTextFormat(defaultMessage, { return organizationAddressTextFormat(defaultMessage, {
organizationName: this.name, organizationName: this.name,
@@ -81,6 +81,7 @@ export default class TenantMetadata extends BaseModel {
state: this.address?.stateProvince, state: this.address?.stateProvince,
city: this.address?.city, city: this.address?.city,
postalCode: this.address?.postalCode, postalCode: this.address?.postalCode,
phone: this.address?.phone,
country: 'United State', country: 'United State',
}); });
} }

View File

@@ -8,17 +8,27 @@ interface OrganizationAddressFormatArgs {
city?: string; city?: string;
country?: string; country?: string;
postalCode?: string; postalCode?: string;
phone?: string;
} }
const defaultMessage = ` const defaultMessage = `
<strong>{ORGANIZATION_NAME}</strong> <strong>{ORGANIZATION_NAME}</strong>
{ADDRESS_1}, {ADDRESS_1}
{ADDRESS_2}, {ADDRESS_2}
{CITY} {STATE}, {CITY}, {STATE} {POSTAL_CODE}
{POSTAL_CODE},
{COUNTRY} {COUNTRY}
{PHONE}
`; `;
/**
* Formats the address text based on the provided message and arguments.
* This function replaces placeholders in the message with actual values
* from the OrganizationAddressFormatArgs. It ensures that the final
* formatted message is clean and free of excessive newlines.
*
* @param {string} message - The message template containing placeholders.
* @param {Record<string, string>} args - The arguments containing the values to replace in the message.
* @returns {string} - The formatted address text.
*/
const formatText = (message: string, replacements: Record<string, string>) => { const formatText = (message: string, replacements: Record<string, string>) => {
let formattedMessage = Object.entries(replacements).reduce( let formattedMessage = Object.entries(replacements).reduce(
(msg, [key, value]) => { (msg, [key, value]) => {
@@ -26,9 +36,11 @@ const formatText = (message: string, replacements: Record<string, string>) => {
}, },
message message
); );
formattedMessage = formattedMessage.replace(/\n{2,}/g, '\n').trim();
formattedMessage = formattedMessage.replace(/\n/g, '<br />'); formattedMessage = formattedMessage.replace(/\n/g, '<br />');
formattedMessage = formattedMessage.trim();
return formattedMessage.trim(); return formattedMessage;
}; };
export const organizationAddressTextFormat = ( export const organizationAddressTextFormat = (
@@ -43,6 +55,7 @@ export const organizationAddressTextFormat = (
STATE: args.state || '', STATE: args.state || '',
POSTAL_CODE: args.postalCode || '', POSTAL_CODE: args.postalCode || '',
COUNTRY: args.country || '', COUNTRY: args.country || '',
PHONE: args.phone || '',
}; };
return formatText(message, replacements); return formatText(message, replacements);
}; };
@@ -56,15 +69,15 @@ interface ContactAddressTextFormatArgs {
city?: string; city?: string;
address2?: string; address2?: string;
address1?: string; address1?: string;
phone?: string;
} }
const contactFormatMessage = `{CONTACT_NAME} const contactFormatMessage = `{CONTACT_NAME}
{ADDRESS_1} {ADDRESS_1}
{ADDRESS_2} {ADDRESS_2}
{CITY} {STATE} {CITY}, {STATE} {POSTAL_CODE}
{POSTAL_CODE}
{COUNTRY} {COUNTRY}
{EMAIL} {PHONE}
`; `;
export const contactAddressTextFormat = ( export const contactAddressTextFormat = (
@@ -80,6 +93,7 @@ export const contactAddressTextFormat = (
postalCode: contact?.billingAddressPostcode, postalCode: contact?.billingAddressPostcode,
city: contact?.billingAddressCity, city: contact?.billingAddressCity,
email: contact?.email, email: contact?.email,
phone: contact?.billingAddressPhone,
} as ContactAddressTextFormatArgs; } as ContactAddressTextFormatArgs;
const replacements: Record<string, string> = { const replacements: Record<string, string> = {
@@ -91,6 +105,7 @@ export const contactAddressTextFormat = (
POSTAL_CODE: args.postalCode || '', POSTAL_CODE: args.postalCode || '',
COUNTRY: args.country || '', COUNTRY: args.country || '',
EMAIL: args?.email || '', EMAIL: args?.email || '',
PHONE: args?.phone || '',
}; };
return formatText(message, replacements); return formatText(message, replacements);
}; };

View File

@@ -10,11 +10,11 @@ export const DefaultPdfTemplateItemDescription =
'Website development with content and SEO optimization'; 'Website development with content and SEO optimization';
export const DefaultPdfTemplateAddressBilledTo = `Bigcapital Technology, Inc.<br /> export const DefaultPdfTemplateAddressBilledTo = `Bigcapital Technology, Inc.<br />
131 Continental Dr Suite 305 Newark, <br /> 131 Continental Dr, <br />
Delaware 19713, <br /> Suite 305, <br />
United States, <br /> Newark, Delaware 19713, <br />
+1 762-339-5634, <br /> United States,<br />
ahmed@bigcapital.app +1 762-339-5634
`; `;
export const DefaultPdfTemplateAddressBilledFrom = `131 Continental Dr Suite 305 Newark, <br /> export const DefaultPdfTemplateAddressBilledFrom = `131 Continental Dr Suite 305 Newark, <br />

View File

@@ -1,4 +1,4 @@
import { Text, Classes, Button, Intent } from '@blueprintjs/core'; import { Text, Classes, Button, Intent, Tag } from '@blueprintjs/core';
import clsx from 'classnames'; import clsx from 'classnames';
import { AppToaster, Box, Group, Stack } from '@/components'; import { AppToaster, Box, Group, Stack } from '@/components';
import { usePaymentPortalBoot } from './PaymentPortalBoot'; import { usePaymentPortalBoot } from './PaymentPortalBoot';
@@ -54,20 +54,25 @@ export function PaymentPortal() {
{sharableLinkMeta?.organization?.name} Sent an Invoice for{' '} {sharableLinkMeta?.organization?.name} Sent an Invoice for{' '}
{sharableLinkMeta?.totalFormatted} {sharableLinkMeta?.totalFormatted}
</h1> </h1>
<Text className={clsx(Classes.TEXT_MUTED, styles.invoiceDueDate)}> <Group spacing={10}>
Invoice due {sharableLinkMeta?.dueDateFormatted} <Text className={clsx(Classes.TEXT_MUTED, styles.invoiceDueDate)}>
</Text> Invoice due {sharableLinkMeta?.dueDateFormatted}{' '}
</Text>
</Group>
</Stack> </Stack>
<Stack className={styles.address} spacing={2}> <Stack className={styles.address} spacing={2}>
<Box className={styles.customerName}> <Box className={styles.customerName}>
{sharableLinkMeta?.customerName} {sharableLinkMeta?.customerName}
</Box> </Box>
<Box>Bigcapital Technology, Inc.</Box>
<Box>131 Continental Dr Suite 305 Newark,</Box> {sharableLinkMeta?.formattedCustomerAddress && (
<Box>Delaware 19713</Box> <Box
<Box>United States</Box> dangerouslySetInnerHTML={{
<Box>ahmed@bigcapital.app</Box> __html: sharableLinkMeta?.formattedCustomerAddress,
}}
/>
)}
</Stack> </Stack>
<h2 className={styles.invoiceNumber}> <h2 className={styles.invoiceNumber}>

View File

@@ -34,6 +34,10 @@ export function PaymentInvoicePreviewContent() {
label: tax.name, label: tax.name,
amount: tax.taxRateAmountFormatted, amount: tax.taxRateAmountFormatted,
}))} }))}
companyAddress={
sharableLinkMeta?.organization?.addressTextFormatted
}
customerAddress={sharableLinkMeta?.formattedCustomerAddress}
/> />
</Box> </Box>
</DrawerBody> </DrawerBody>

View File

@@ -108,6 +108,7 @@ export interface GetInvoicePaymentLinkResponse {
organization: GetInvoicePaymentLinkOrganizationRes; organization: GetInvoicePaymentLinkOrganizationRes;
hasStripePaymentMethod: boolean; hasStripePaymentMethod: boolean;
isReceivable: boolean; isReceivable: boolean;
formattedCustomerAddress: string;
} }
/** /**