mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-20 14:50:32 +00:00
feat: Invoice mail receipt preview
This commit is contained in:
@@ -7,13 +7,14 @@ export function ElementCustomizePreviewContent() {
|
|||||||
return (
|
return (
|
||||||
<Box
|
<Box
|
||||||
style={{
|
style={{
|
||||||
padding: '28px 24px 40px',
|
// padding: '28px 24px 40px',
|
||||||
backgroundColor: '#F5F5F5',
|
// backgroundColor: '#F5F5F5',
|
||||||
overflow: 'auto',
|
// overflow: 'auto',
|
||||||
flex: '1',
|
// flex: '1',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{PaperTemplate}
|
{PaperTemplate}
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,23 @@
|
|||||||
|
import { InvoicePaymentPage, PaymentPageProps } from './PaymentPage';
|
||||||
|
|
||||||
|
interface InvoicePaymentPagePreviewProps extends Partial<PaymentPageProps> { }
|
||||||
|
|
||||||
|
export function InvoicePaymentPagePreview(
|
||||||
|
props: InvoicePaymentPagePreviewProps,
|
||||||
|
) {
|
||||||
|
return (
|
||||||
|
<InvoicePaymentPage
|
||||||
|
paidAmount={'$1,000.00'}
|
||||||
|
dueDate={'20 Sep 2024'}
|
||||||
|
total={'$1,000.00'}
|
||||||
|
subtotal={'$1,000.00'}
|
||||||
|
dueAmount={'$1,000.00'}
|
||||||
|
customerName={'Ahmed Bouhuolia'}
|
||||||
|
organizationName={'Bigcapital Technology, Inc.'}
|
||||||
|
invoiceNumber={'INV-000001'}
|
||||||
|
companyLogoUri={' '}
|
||||||
|
organizationAddress={' '}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
181
packages/webapp/src/containers/PaymentPortal/PaymentPage.tsx
Normal file
181
packages/webapp/src/containers/PaymentPortal/PaymentPage.tsx
Normal file
@@ -0,0 +1,181 @@
|
|||||||
|
import { Text, Classes, Button, Intent, ButtonProps } from '@blueprintjs/core';
|
||||||
|
import clsx from 'classnames';
|
||||||
|
import { Box, Group, Stack } from '@/components';
|
||||||
|
import styles from './PaymentPortal.module.scss';
|
||||||
|
|
||||||
|
export interface PaymentPageProps {
|
||||||
|
companyLogoUri: string;
|
||||||
|
organizationName: string;
|
||||||
|
customerName: string;
|
||||||
|
subtotal: string;
|
||||||
|
total: string;
|
||||||
|
dueDate: string;
|
||||||
|
viewInvoiceLabel?: string;
|
||||||
|
invoiceNumber: string;
|
||||||
|
totalLabel?: string;
|
||||||
|
subtotalLabel?: string;
|
||||||
|
customerAddress?: string;
|
||||||
|
downloadInvoiceBtnLabel?: string;
|
||||||
|
showPayButton?: boolean;
|
||||||
|
paidAmount: string;
|
||||||
|
paidAmountLabel?: string;
|
||||||
|
organizationAddress: string;
|
||||||
|
dueAmount: string;
|
||||||
|
dueAmountLabel?: string;
|
||||||
|
downloadInvoiceButtonProps?: Partial<ButtonProps>;
|
||||||
|
payInvoiceButtonProps?: Partial<ButtonProps>;
|
||||||
|
viewInvoiceButtonProps?: Partial<ButtonProps>;
|
||||||
|
invoiceNumberLabel?: string;
|
||||||
|
buyNote?: string;
|
||||||
|
copyrightText?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function InvoicePaymentPage({
|
||||||
|
companyLogoUri,
|
||||||
|
organizationName,
|
||||||
|
customerName,
|
||||||
|
subtotal,
|
||||||
|
total,
|
||||||
|
dueDate,
|
||||||
|
paidAmount,
|
||||||
|
paidAmountLabel = 'Paid Amount (-)',
|
||||||
|
invoiceNumber,
|
||||||
|
customerAddress,
|
||||||
|
totalLabel = 'Total',
|
||||||
|
subtotalLabel = 'Subtotal',
|
||||||
|
viewInvoiceLabel = 'View Invoice',
|
||||||
|
downloadInvoiceBtnLabel = 'Download Invoice',
|
||||||
|
showPayButton = true,
|
||||||
|
organizationAddress,
|
||||||
|
dueAmount,
|
||||||
|
dueAmountLabel = 'Due Amount',
|
||||||
|
downloadInvoiceButtonProps,
|
||||||
|
payInvoiceButtonProps,
|
||||||
|
viewInvoiceButtonProps,
|
||||||
|
invoiceNumberLabel = 'Invoice #',
|
||||||
|
buyNote = 'By confirming your payment, you allow Bigcapital Technology, Inc. to charge you for this payment and save your payment information in accordance with their terms.',
|
||||||
|
copyrightText = `© 2024 Bigcapital Technology, Inc. <br /> All rights reserved.`,
|
||||||
|
}: PaymentPageProps) {
|
||||||
|
return (
|
||||||
|
<Box className={styles.root}>
|
||||||
|
<Stack spacing={0} className={styles.body}>
|
||||||
|
<Stack>
|
||||||
|
<Group spacing={10}>
|
||||||
|
{companyLogoUri && (
|
||||||
|
<Box
|
||||||
|
className={styles.companyLogoWrap}
|
||||||
|
style={{
|
||||||
|
backgroundImage: `url(${companyLogoUri})`,
|
||||||
|
}}
|
||||||
|
></Box>
|
||||||
|
)}
|
||||||
|
<Text>{organizationName}</Text>
|
||||||
|
</Group>
|
||||||
|
|
||||||
|
<Stack spacing={6}>
|
||||||
|
<h1 className={styles.bigTitle}>
|
||||||
|
{organizationName} Sent an Invoice for {total}
|
||||||
|
</h1>
|
||||||
|
<Group spacing={10}>
|
||||||
|
<Text className={clsx(Classes.TEXT_MUTED, styles.invoiceDueDate)}>
|
||||||
|
Invoice due {dueDate}{' '}
|
||||||
|
</Text>
|
||||||
|
</Group>
|
||||||
|
</Stack>
|
||||||
|
|
||||||
|
<Stack className={styles.address} spacing={2}>
|
||||||
|
<Box className={styles.customerName}>{customerName}</Box>
|
||||||
|
|
||||||
|
{customerAddress && (
|
||||||
|
<Box dangerouslySetInnerHTML={{ __html: customerAddress }} />
|
||||||
|
)}
|
||||||
|
</Stack>
|
||||||
|
|
||||||
|
<h2 className={styles.invoiceNumber}>Invoice {invoiceNumber}</h2>
|
||||||
|
|
||||||
|
<Stack spacing={0} className={styles.totals}>
|
||||||
|
<Group
|
||||||
|
position={'apart'}
|
||||||
|
className={clsx(styles.totalItem, styles.borderBottomGray)}
|
||||||
|
>
|
||||||
|
<Text>{subtotalLabel}</Text>
|
||||||
|
<Text>{subtotal}</Text>
|
||||||
|
</Group>
|
||||||
|
|
||||||
|
<Group position={'apart'} className={styles.totalItem}>
|
||||||
|
<Text>{totalLabel}</Text>
|
||||||
|
<Text style={{ fontWeight: 500 }}>{total}</Text>
|
||||||
|
</Group>
|
||||||
|
{/*
|
||||||
|
{sharableLinkMeta?.taxes?.map((tax, key) => (
|
||||||
|
<Group key={key} position={'apart'} className={styles.totalItem}>
|
||||||
|
<Text>{tax?.name}</Text>
|
||||||
|
<Text>{tax?.taxRateAmountFormatted}</Text>
|
||||||
|
</Group>
|
||||||
|
))} */}
|
||||||
|
<Group
|
||||||
|
position={'apart'}
|
||||||
|
className={clsx(styles.totalItem, styles.borderBottomGray)}
|
||||||
|
>
|
||||||
|
<Text>{paidAmountLabel}</Text>
|
||||||
|
<Text>{paidAmount}</Text>
|
||||||
|
</Group>
|
||||||
|
|
||||||
|
<Group
|
||||||
|
position={'apart'}
|
||||||
|
className={clsx(styles.totalItem, styles.borderBottomDark)}
|
||||||
|
>
|
||||||
|
<Text>Due Amount</Text>
|
||||||
|
<Text style={{ fontWeight: 500 }}>{dueAmount}</Text>
|
||||||
|
</Group>
|
||||||
|
</Stack>
|
||||||
|
</Stack>
|
||||||
|
|
||||||
|
<Stack spacing={8} className={styles.footerButtons}>
|
||||||
|
<Button
|
||||||
|
minimal
|
||||||
|
className={clsx(styles.footerButton, styles.downloadInvoiceButton)}
|
||||||
|
{...downloadInvoiceButtonProps}
|
||||||
|
>
|
||||||
|
{downloadInvoiceBtnLabel}
|
||||||
|
</Button>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
className={clsx(styles.footerButton, styles.viewInvoiceButton)}
|
||||||
|
{...viewInvoiceButtonProps}
|
||||||
|
>
|
||||||
|
{viewInvoiceLabel}
|
||||||
|
</Button>
|
||||||
|
|
||||||
|
{showPayButton && (
|
||||||
|
<Button
|
||||||
|
intent={Intent.PRIMARY}
|
||||||
|
className={clsx(styles.footerButton, styles.buyButton)}
|
||||||
|
{...payInvoiceButtonProps}
|
||||||
|
>
|
||||||
|
Pay {total}
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
</Stack>
|
||||||
|
|
||||||
|
{buyNote && (
|
||||||
|
<Text className={clsx(Classes.TEXT_MUTED, styles.buyNote)}>
|
||||||
|
{buyNote}
|
||||||
|
</Text>
|
||||||
|
)}
|
||||||
|
</Stack>
|
||||||
|
|
||||||
|
<Stack spacing={18} className={styles.footer}>
|
||||||
|
<Box dangerouslySetInnerHTML={{ __html: organizationAddress }}></Box>
|
||||||
|
|
||||||
|
{copyrightText && (
|
||||||
|
<Stack
|
||||||
|
spacing={0}
|
||||||
|
className={styles.footerText}
|
||||||
|
dangerouslySetInnerHTML={{ __html: copyrightText }}
|
||||||
|
></Stack>
|
||||||
|
)}
|
||||||
|
</Stack>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
import React from 'react';
|
import React, { lazy, Suspense } from 'react';
|
||||||
import * as R from 'ramda';
|
import * as R from 'ramda';
|
||||||
import { useFormikContext } from 'formik';
|
import { useFormikContext } from 'formik';
|
||||||
|
import { Spinner, Tab } from '@blueprintjs/core';
|
||||||
import {
|
import {
|
||||||
InvoicePaperTemplate,
|
InvoicePaperTemplate,
|
||||||
InvoicePaperTemplateProps,
|
InvoicePaperTemplateProps,
|
||||||
@@ -19,6 +20,19 @@ import { BrandingTemplateForm } from '@/containers/BrandingTemplates/BrandingTem
|
|||||||
import { useElementCustomizeContext } from '@/containers/ElementCustomize/ElementCustomizeProvider';
|
import { useElementCustomizeContext } from '@/containers/ElementCustomize/ElementCustomizeProvider';
|
||||||
import { initialValues } from './constants';
|
import { initialValues } from './constants';
|
||||||
import { useIsTemplateNamedFilled } from '@/containers/BrandingTemplates/utils';
|
import { useIsTemplateNamedFilled } from '@/containers/BrandingTemplates/utils';
|
||||||
|
import { InvoiceCustomizeTabs } from './InvoiceCustomizeTabs';
|
||||||
|
|
||||||
|
const InvoicePaymentPagePreview = lazy(() =>
|
||||||
|
import('@/containers/PaymentPortal/InvoicePaymentPagePreview').then(
|
||||||
|
(module) => ({ default: module.InvoicePaymentPagePreview }),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
const InvoiceMailReceiptPreview = lazy(() =>
|
||||||
|
import(
|
||||||
|
'@/containers/Sales/Invoices/InvoiceCustomize/InvoiceMailReceiptPreview'
|
||||||
|
).then((module) => ({ default: module.InvoiceMailReceiptPreview })),
|
||||||
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Invoice branding template customize.
|
* Invoice branding template customize.
|
||||||
@@ -56,7 +70,39 @@ function InvoiceCustomizeFormContent() {
|
|||||||
return (
|
return (
|
||||||
<ElementCustomizeContent>
|
<ElementCustomizeContent>
|
||||||
<ElementCustomize.PaperTemplate>
|
<ElementCustomize.PaperTemplate>
|
||||||
<InvoicePaperTemplateFormConnected />
|
<InvoiceCustomizeTabs
|
||||||
|
defaultSelectedTabId={'pdf-document'}
|
||||||
|
id={'customize-preview-tabs'}
|
||||||
|
renderActiveTabPanelOnly
|
||||||
|
>
|
||||||
|
<Tab
|
||||||
|
id="pdf-document"
|
||||||
|
title={'PDF document'}
|
||||||
|
panel={
|
||||||
|
<Suspense fallback={<Spinner />}>
|
||||||
|
<InvoicePaperTemplateFormConnected />
|
||||||
|
</Suspense>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<Tab
|
||||||
|
id={'payment-page'}
|
||||||
|
title={'Payment page'}
|
||||||
|
panel={
|
||||||
|
<Suspense fallback={<Spinner />}>
|
||||||
|
<InvoicePaymentPagePreview />
|
||||||
|
</Suspense>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<Tab
|
||||||
|
id={'email-receipt'}
|
||||||
|
title={'Email receipt'}
|
||||||
|
panel={
|
||||||
|
<Suspense fallback={<Spinner />}>
|
||||||
|
<InvoiceMailReceiptPreview mx={'auto'} />
|
||||||
|
</Suspense>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</InvoiceCustomizeTabs>
|
||||||
</ElementCustomize.PaperTemplate>
|
</ElementCustomize.PaperTemplate>
|
||||||
|
|
||||||
<ElementCustomize.FieldsTab id={'general'} label={'General'}>
|
<ElementCustomize.FieldsTab id={'general'} label={'General'}>
|
||||||
@@ -90,7 +136,6 @@ const withInvoicePreviewTemplateProps = <P extends object>(
|
|||||||
...brandingState,
|
...brandingState,
|
||||||
...values,
|
...values,
|
||||||
};
|
};
|
||||||
|
|
||||||
return <Component {...(props as P)} {...mergedProps} />;
|
return <Component {...(props as P)} {...mergedProps} />;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -0,0 +1,28 @@
|
|||||||
|
import { css } from '@emotion/css';
|
||||||
|
import { Tabs, TabsProps } from '@blueprintjs/core';
|
||||||
|
|
||||||
|
interface InvoiceCustomizeTabsProps extends TabsProps { }
|
||||||
|
|
||||||
|
export function InvoiceCustomizeTabs(props: InvoiceCustomizeTabsProps) {
|
||||||
|
return (
|
||||||
|
<Tabs
|
||||||
|
className={css`
|
||||||
|
.bp4-tab-list {
|
||||||
|
padding: 0 20px;
|
||||||
|
background: #fff;
|
||||||
|
border-bottom: 1px solid #dcdcdd;
|
||||||
|
}
|
||||||
|
.bp4-tab {
|
||||||
|
line-height: 40px;
|
||||||
|
}
|
||||||
|
.bp4-tab:not([aria-selected='true']) {
|
||||||
|
color: #5f6b7c;
|
||||||
|
}
|
||||||
|
.bp4-tab-indicator-wrapper .bp4-tab-indicator {
|
||||||
|
height: 2px;
|
||||||
|
}
|
||||||
|
`}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,137 @@
|
|||||||
|
import { Button, Intent } from '@blueprintjs/core';
|
||||||
|
import { css } from '@emotion/css';
|
||||||
|
import { x } from '@xstyled/emotion';
|
||||||
|
import { Group, Stack, StackProps } from '@/components';
|
||||||
|
|
||||||
|
export interface InvoiceMailReceiptProps extends StackProps {
|
||||||
|
companyLogoUri?: string;
|
||||||
|
message: string;
|
||||||
|
companyName: string;
|
||||||
|
invoiceNumber: string;
|
||||||
|
dueDate: string;
|
||||||
|
items?: Array<{ label: string; total: string; quantity: string | number }>;
|
||||||
|
total: string;
|
||||||
|
dueAmount: string;
|
||||||
|
totalLabel?: string;
|
||||||
|
dueAmountLabel?: string;
|
||||||
|
viewInvoiceButtonLabel?: string;
|
||||||
|
viewInvoiceButtonOnClick?: () => void;
|
||||||
|
invoiceNumberLabel?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function InvoiceMailReceipt({
|
||||||
|
companyLogoUri,
|
||||||
|
message,
|
||||||
|
companyName,
|
||||||
|
total,
|
||||||
|
invoiceNumber,
|
||||||
|
dueDate,
|
||||||
|
dueAmount,
|
||||||
|
items,
|
||||||
|
viewInvoiceButtonLabel = 'View Invoice',
|
||||||
|
viewInvoiceButtonOnClick,
|
||||||
|
totalLabel = 'Total',
|
||||||
|
dueAmountLabel = 'Due Amount',
|
||||||
|
invoiceNumberLabel = 'Invoice #',
|
||||||
|
...restProps
|
||||||
|
}: InvoiceMailReceiptProps) {
|
||||||
|
return (
|
||||||
|
<Stack
|
||||||
|
bg="white"
|
||||||
|
w={'100%'}
|
||||||
|
maxWidth={'500px'}
|
||||||
|
p={'35px 25px'}
|
||||||
|
borderRadius={'5px'}
|
||||||
|
boxShadow={'0 10px 15px rgba(0, 0, 0, 0.05)'}
|
||||||
|
color={'black'}
|
||||||
|
{...restProps}
|
||||||
|
>
|
||||||
|
<Stack spacing={16} textAlign={'center'}>
|
||||||
|
{companyLogoUri && (
|
||||||
|
<x.div h={'90px'} w={'90px'} bg="#F2F2F2" mx="auto"></x.div>
|
||||||
|
)}
|
||||||
|
<Stack spacing={8}>
|
||||||
|
<x.h1 m={0} fontSize={'18px'} fontWeight={500} color="#404854">
|
||||||
|
{companyName}
|
||||||
|
</x.h1>
|
||||||
|
|
||||||
|
<x.h3 color="#383E47" fontWeight={500}>
|
||||||
|
{total}
|
||||||
|
</x.h3>
|
||||||
|
|
||||||
|
<x.span fontSize={'13px'} color="#404854">
|
||||||
|
{invoiceNumberLabel} {invoiceNumber}
|
||||||
|
</x.span>
|
||||||
|
|
||||||
|
<x.span fontSize={'13px'} color="#404854">
|
||||||
|
Due {dueDate}
|
||||||
|
</x.span>
|
||||||
|
</Stack>
|
||||||
|
</Stack>
|
||||||
|
|
||||||
|
<x.p m={0} whiteSpace={'pre-line'} color="#252A31">
|
||||||
|
{message}
|
||||||
|
</x.p>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
large
|
||||||
|
intent={Intent.PRIMARY}
|
||||||
|
className={css`
|
||||||
|
&.bp4-large {
|
||||||
|
min-height: 38px;
|
||||||
|
}
|
||||||
|
`}
|
||||||
|
onClick={viewInvoiceButtonOnClick}
|
||||||
|
>
|
||||||
|
{viewInvoiceButtonLabel}
|
||||||
|
</Button>
|
||||||
|
|
||||||
|
<Stack spacing={0}>
|
||||||
|
{items?.map((item, key) => (
|
||||||
|
<Group
|
||||||
|
key={key}
|
||||||
|
h={'40px'}
|
||||||
|
position={'apart'}
|
||||||
|
borderBottomStyle="solid"
|
||||||
|
borderBottomWidth={'1px'}
|
||||||
|
borderBottomColor={'#D9D9D9'}
|
||||||
|
borderTopStyle="solid"
|
||||||
|
borderTopColor={'#D9D9D9'}
|
||||||
|
borderTopWidth={'1px'}
|
||||||
|
>
|
||||||
|
<x.span>{item.label}</x.span>
|
||||||
|
<x.span>
|
||||||
|
{item.quantity} x {item.total}
|
||||||
|
</x.span>
|
||||||
|
</Group>
|
||||||
|
))}
|
||||||
|
|
||||||
|
<Group
|
||||||
|
h={'40px'}
|
||||||
|
position={'apart'}
|
||||||
|
borderBottomStyle="solid"
|
||||||
|
borderBottomWidth={'1px'}
|
||||||
|
borderColor={'#000'}
|
||||||
|
>
|
||||||
|
<x.span fontWeight={500}>{totalLabel}</x.span>
|
||||||
|
<x.span fontWeight={600} fontSize={15}>
|
||||||
|
{total}
|
||||||
|
</x.span>
|
||||||
|
</Group>
|
||||||
|
|
||||||
|
<Group
|
||||||
|
h={'40px'}
|
||||||
|
position={'apart'}
|
||||||
|
borderBottomStyle="solid"
|
||||||
|
borderBottomWidth={'1px'}
|
||||||
|
borderBottomColor={'#000'}
|
||||||
|
>
|
||||||
|
<x.span fontWeight={500}>{dueAmountLabel}</x.span>
|
||||||
|
<x.span fontWeight={600} fontSize={15}>
|
||||||
|
{dueAmount}
|
||||||
|
</x.span>
|
||||||
|
</Group>
|
||||||
|
</Stack>
|
||||||
|
</Stack>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
import {
|
||||||
|
InvoiceMailReceipt,
|
||||||
|
InvoiceMailReceiptProps,
|
||||||
|
} from './InvoiceMailReceipt';
|
||||||
|
|
||||||
|
export interface InvoiceMailReceiptPreviewProps
|
||||||
|
extends Partial<InvoiceMailReceiptProps> { }
|
||||||
|
|
||||||
|
const receiptMessage = `Hi Ahmed,
|
||||||
|
|
||||||
|
Here’s invoice INV-0002 for AED 0.00
|
||||||
|
|
||||||
|
The amount outstanding of AED $100,00 is due on 2 October 2024
|
||||||
|
|
||||||
|
View your bill online From your online you can print a PDF or pay your outstanding bills,
|
||||||
|
|
||||||
|
If you have any questions, please let us know,
|
||||||
|
|
||||||
|
Thanks,
|
||||||
|
Mohamed
|
||||||
|
`;
|
||||||
|
|
||||||
|
export function InvoiceMailReceiptPreview(
|
||||||
|
props: InvoiceMailReceiptPreviewProps,
|
||||||
|
) {
|
||||||
|
const propsWithDefaults = {
|
||||||
|
message: receiptMessage,
|
||||||
|
companyName: 'Bigcapital Technology, Inc.',
|
||||||
|
total: '$1,000.00',
|
||||||
|
invoiceNumber: 'INV-0001',
|
||||||
|
dueDate: '2 Oct 2024',
|
||||||
|
dueAmount: '$1,000.00',
|
||||||
|
items: [{ label: 'Line Item #1', total: '$1000.00', quantity: 1 }],
|
||||||
|
companyLogoUri: ' ',
|
||||||
|
...props,
|
||||||
|
};
|
||||||
|
return <InvoiceMailReceipt {...propsWithDefaults} />;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user