mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-20 14:50:32 +00:00
feat: receipt send mail preview
This commit is contained in:
@@ -1,6 +1,9 @@
|
|||||||
import { x } from '@xstyled/emotion';
|
import { x } from '@xstyled/emotion';
|
||||||
import { Group, Stack } from '@/components';
|
import { Group, Stack } from '@/components';
|
||||||
import { SendMailReceipt, SendMailReceiptProps } from '../SendMailViewDrawer/SendMailViewReceiptPreview';
|
import {
|
||||||
|
SendMailReceipt,
|
||||||
|
SendMailReceiptProps,
|
||||||
|
} from '../SendMailViewDrawer/SendMailViewReceiptPreview';
|
||||||
|
|
||||||
interface EstimateSendMailReceiptProps extends SendMailReceiptProps {
|
interface EstimateSendMailReceiptProps extends SendMailReceiptProps {
|
||||||
// # Company name.
|
// # Company name.
|
||||||
|
|||||||
@@ -3,19 +3,17 @@ import { EstimateSendMailReceipt } from './EstimateSendMailReceipt';
|
|||||||
import { EstimateSendMailPreviewHeader } from './EstimateSendMailPreviewHeader';
|
import { EstimateSendMailPreviewHeader } from './EstimateSendMailPreviewHeader';
|
||||||
import { Stack } from '@/components';
|
import { Stack } from '@/components';
|
||||||
|
|
||||||
export const EstimateSendMailReceiptPreview = () => {
|
const defaultEstimateMailReceiptProps = {
|
||||||
const props = {
|
companyName: 'Bigcapital Technology, Inc.',
|
||||||
companyName: 'Bigcapital Technology, Inc.',
|
companyLogoUri: ' ',
|
||||||
companyLogoUri: ' ',
|
|
||||||
|
|
||||||
message: '',
|
total: '$1,000.00',
|
||||||
total: '$1,000.00',
|
subtotal: '$1,000.00',
|
||||||
subtotal: '$1,000.00',
|
estimateNumber: 'INV-0001',
|
||||||
estimateNumber: 'INV-0001',
|
expirationDate: '2 Oct 2024',
|
||||||
expirationDate: '2 Oct 2024',
|
dueAmount: '$1,000.00',
|
||||||
dueAmount: '$1,000.00',
|
items: [{ label: 'Web development', total: '$1000.00', quantity: 1 }],
|
||||||
items: [{ label: 'Web development', total: '$1000.00', quantity: 1 }],
|
message: `Hi Ahmed Bouhuolia,
|
||||||
message: `Hi Ahmed Bouhuolia,
|
|
||||||
|
|
||||||
Here's invoice # INV-00002 for $738.30
|
Here's invoice # INV-00002 for $738.30
|
||||||
|
|
||||||
@@ -27,15 +25,15 @@ If you have any questions, please let us know.
|
|||||||
|
|
||||||
Thanks,
|
Thanks,
|
||||||
Bigcapital`,
|
Bigcapital`,
|
||||||
};
|
};
|
||||||
|
export const EstimateSendMailReceiptPreview = () => {
|
||||||
return (
|
return (
|
||||||
<Stack>
|
<Stack>
|
||||||
<EstimateSendMailPreviewHeader />
|
<EstimateSendMailPreviewHeader />
|
||||||
|
|
||||||
<Stack px={4} py={6}>
|
<Stack px={4} py={6}>
|
||||||
<EstimateSendMailReceipt
|
<EstimateSendMailReceipt
|
||||||
{...props}
|
{...defaultEstimateMailReceiptProps}
|
||||||
className={css`
|
className={css`
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
border-radius: 5px !important;
|
border-radius: 5px !important;
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import { Spinner } from '@blueprintjs/core';
|
import { Spinner } from '@blueprintjs/core';
|
||||||
import { css } from '@emotion/css';
|
|
||||||
import { Stack } from '@/components';
|
import { Stack } from '@/components';
|
||||||
import { InvoiceSendMailPreviewWithHeader } from './InvoiceSendMailHeaderPreview';
|
import { InvoiceSendMailPreviewWithHeader } from './InvoiceSendMailHeaderPreview';
|
||||||
import { useInvoiceHtml } from '@/hooks/query';
|
import { useInvoiceHtml } from '@/hooks/query';
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
import React, { createContext, useContext } from 'react';
|
import React, { createContext, useContext } from 'react';
|
||||||
import { Spinner } from '@blueprintjs/core';
|
import { Spinner } from '@blueprintjs/core';
|
||||||
import { useDrawerContext } from '@/components/Drawer/DrawerProvider';
|
import { useDrawerContext } from '@/components/Drawer/DrawerProvider';
|
||||||
import { useSaleReceiptMailState } from '@/hooks/query';
|
import { useSaleInvoiceMailState } from '@/hooks/query';
|
||||||
|
|
||||||
interface ReceiptSendMailBootValues {
|
interface ReceiptSendMailBootValues {
|
||||||
receiptId: number;
|
receiptId: number;
|
||||||
@@ -17,22 +17,20 @@ interface ReceiptSendMailBootProps {
|
|||||||
const ReceiptSendMailContentBootContext =
|
const ReceiptSendMailContentBootContext =
|
||||||
createContext<ReceiptSendMailBootValues>({} as ReceiptSendMailBootValues);
|
createContext<ReceiptSendMailBootValues>({} as ReceiptSendMailBootValues);
|
||||||
|
|
||||||
export const ReceiptSendMailBoot = ({
|
export const ReceiptSendMailBoot = ({ children }: ReceiptSendMailBootProps) => {
|
||||||
children,
|
|
||||||
}: ReceiptSendMailBootProps) => {
|
|
||||||
const {
|
const {
|
||||||
payload: { receiptId },
|
payload: { receiptId },
|
||||||
} = useDrawerContext();
|
} = useDrawerContext();
|
||||||
|
|
||||||
// Receipt mail options.
|
// Receipt mail options.
|
||||||
const { data: receiptMailState, isLoading: isReceiptMailState } =
|
const { data: receiptMailState, isLoading: isReceiptMailState } =
|
||||||
useSaleReceiptMailState(receiptId);
|
useSaleInvoiceMailState(receiptId);
|
||||||
|
|
||||||
const isLoading = isReceiptMailState;
|
const isLoading = isReceiptMailState;
|
||||||
|
|
||||||
if (isLoading) {
|
// if (isLoading) {
|
||||||
return <Spinner size={20} />;
|
// return <Spinner size={20} />;
|
||||||
}
|
// }
|
||||||
const value = {
|
const value = {
|
||||||
receiptId,
|
receiptId,
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,73 @@
|
|||||||
|
import { FCheckbox, FFormGroup, FInputGroup, Group, Stack } from "@/components";
|
||||||
|
import { SendMailViewToAddressField } from "../../Estimates/SendMailViewDrawer/SendMailViewToAddressField";
|
||||||
|
import { SendMailViewMessageField } from "../../Estimates/SendMailViewDrawer/SendMailViewMessageField";
|
||||||
|
import { Button, Intent } from "@blueprintjs/core";
|
||||||
|
import { useDrawerActions } from "@/hooks/state";
|
||||||
|
import { useDrawerContext } from "@/components/Drawer/DrawerProvider";
|
||||||
|
import { useFormikContext } from "formik";
|
||||||
|
|
||||||
|
const items: Array<any> = [];
|
||||||
|
const argsOptions: Array<any> = [];
|
||||||
|
|
||||||
export function ReceiptSendMailFormFields() {
|
export function ReceiptSendMailFormFields() {
|
||||||
return null;
|
return (
|
||||||
|
<Stack>
|
||||||
|
<Stack spacing={0} overflow="auto" flex="1" p={'30px'}>
|
||||||
|
<SendMailViewToAddressField
|
||||||
|
toMultiSelectProps={{ items }}
|
||||||
|
ccMultiSelectProps={{ items }}
|
||||||
|
bccMultiSelectProps={{ items }}
|
||||||
|
/>
|
||||||
|
<FFormGroup label={'Submit'} name={'subject'}>
|
||||||
|
<FInputGroup name={'subject'} large fastField />
|
||||||
|
</FFormGroup>
|
||||||
|
|
||||||
|
<SendMailViewMessageField argsOptions={argsOptions} />
|
||||||
|
|
||||||
|
<Group>
|
||||||
|
<FCheckbox name={'attachPdf'} label={'Attach PDF'} />
|
||||||
|
</Group>
|
||||||
|
</Stack>
|
||||||
|
|
||||||
|
<ReceiptSendMailFooter />
|
||||||
|
</Stack>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function ReceiptSendMailFooter() {
|
||||||
|
const { isSubmitting } = useFormikContext();
|
||||||
|
const { name } = useDrawerContext();
|
||||||
|
const { closeDrawer } = useDrawerActions();
|
||||||
|
|
||||||
|
const handleClose = () => {
|
||||||
|
closeDrawer(name);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Group
|
||||||
|
py={'12px'}
|
||||||
|
px={'16px'}
|
||||||
|
borderTop="1px solid #d8d8d9"
|
||||||
|
position={'apart'}
|
||||||
|
>
|
||||||
|
<Group spacing={10} ml={'auto'}>
|
||||||
|
<Button
|
||||||
|
disabled={isSubmitting}
|
||||||
|
onClick={handleClose}
|
||||||
|
style={{ minWidth: '65px' }}
|
||||||
|
>
|
||||||
|
Close
|
||||||
|
</Button>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
intent={Intent.PRIMARY}
|
||||||
|
loading={isSubmitting}
|
||||||
|
style={{ minWidth: '85px' }}
|
||||||
|
type="submit"
|
||||||
|
>
|
||||||
|
Send Mail
|
||||||
|
</Button>
|
||||||
|
</Group>
|
||||||
|
</Group>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
import { Stack } from '@/components';
|
||||||
|
import { ReceiptSendMailPreviewHeader } from './ReceiptSendMailPreviewHeader';
|
||||||
|
import { useDrawerContext } from '@/components/Drawer/DrawerProvider';
|
||||||
|
import { useGetSaleReceiptHtml } from '@/hooks/query';
|
||||||
|
import { Spinner } from '@blueprintjs/core';
|
||||||
|
import { SendMailViewPreviewPdfIframe } from '../../Estimates/SendMailViewDrawer/SendMailViewPreviewPdfIframe';
|
||||||
|
|
||||||
|
export function ReceiptSendMailPdfPreview() {
|
||||||
|
return (
|
||||||
|
<Stack>
|
||||||
|
<ReceiptSendMailPreviewHeader />
|
||||||
|
|
||||||
|
<Stack px={4} py={6}>
|
||||||
|
<ReceiptSendPdfPreviewIframe />
|
||||||
|
</Stack>
|
||||||
|
</Stack>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function ReceiptSendPdfPreviewIframe() {
|
||||||
|
const { payload } = useDrawerContext();
|
||||||
|
const { data, isLoading } = useGetSaleReceiptHtml(payload?.receiptId);
|
||||||
|
|
||||||
|
if (isLoading && data) {
|
||||||
|
return <Spinner size={20} />;
|
||||||
|
}
|
||||||
|
const iframeSrcDoc = data?.htmlContent;
|
||||||
|
|
||||||
|
console.log(data, 'data');
|
||||||
|
|
||||||
|
return <SendMailViewPreviewPdfIframe srcDoc={iframeSrcDoc} />;
|
||||||
|
}
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
import { Stack } from '@/components';
|
||||||
|
import { ReceiptSendMailPreviewHeader } from './ReceiptSendMailPreviewHeader';
|
||||||
|
import { ReceiptSendMailReceipt } from './ReceiptSendMailReceipt';
|
||||||
|
import { css } from '@emotion/css';
|
||||||
|
|
||||||
|
const defaultReceiptMailProps = {
|
||||||
|
companyLogoUri: 'https://via.placeholder.com/150',
|
||||||
|
companyName: 'Company Name',
|
||||||
|
receiptNumber: '1234',
|
||||||
|
total: '1000',
|
||||||
|
message: 'Thank you for your business!',
|
||||||
|
items: [
|
||||||
|
{ label: 'Item 1', quantity: 1, total: '500' },
|
||||||
|
{ label: 'Item 2', quantity: 2, total: '500' },
|
||||||
|
],
|
||||||
|
subtotal: '1000',
|
||||||
|
showViewReceiptButton: true,
|
||||||
|
viewReceiptButtonLabel: 'View Receipt',
|
||||||
|
};
|
||||||
|
|
||||||
|
export function ReceiptSendMailPreview() {
|
||||||
|
return (
|
||||||
|
<Stack>
|
||||||
|
<ReceiptSendMailPreviewHeader />
|
||||||
|
|
||||||
|
<Stack px={4} py={6}>
|
||||||
|
<ReceiptSendMailReceipt
|
||||||
|
{...defaultReceiptMailProps}
|
||||||
|
className={css`
|
||||||
|
margin: 0 auto;
|
||||||
|
border-radius: 5px !important;
|
||||||
|
transform: scale(0.9);
|
||||||
|
transform-origin: top;
|
||||||
|
box-shadow: 0 10px 15px rgba(0, 0, 0, 0.05) !important;
|
||||||
|
`}
|
||||||
|
/>
|
||||||
|
</Stack>
|
||||||
|
</Stack>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
import { SendViewPreviewHeader } from '../../Estimates/SendMailViewDrawer/SendMailViewPreviewHeader';
|
||||||
|
|
||||||
|
export function ReceiptSendMailPreviewHeader() {
|
||||||
|
return (
|
||||||
|
<SendViewPreviewHeader
|
||||||
|
companyName="A"
|
||||||
|
customerName="A"
|
||||||
|
subject={'adsfsdf'}
|
||||||
|
from={['a.bouhuolia@gmail.com']}
|
||||||
|
to={['a.bouhuolia@gmail.com']}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -1,3 +1,30 @@
|
|||||||
|
import { Suspense } from 'react';
|
||||||
|
import { Tab } from '@blueprintjs/core';
|
||||||
|
import { SendMailViewPreviewTabs } from '../../Estimates/SendMailViewDrawer/SendMailViewPreviewTabs';
|
||||||
|
import { ReceiptSendMailPreview } from './ReceiptSendMailPreview';
|
||||||
|
import { ReceiptSendMailPdfPreview } from './ReceiptSendMailPdfPreview';
|
||||||
|
|
||||||
export function ReceiptSendMailPreviewTabs() {
|
export function ReceiptSendMailPreviewTabs() {
|
||||||
return null;
|
return (
|
||||||
|
<SendMailViewPreviewTabs>
|
||||||
|
<Tab
|
||||||
|
id={'payment-page'}
|
||||||
|
title={'Payment page'}
|
||||||
|
panel={
|
||||||
|
<Suspense>
|
||||||
|
<ReceiptSendMailPreview />
|
||||||
|
</Suspense>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<Tab
|
||||||
|
id="pdf-document"
|
||||||
|
title={'PDF document'}
|
||||||
|
panel={
|
||||||
|
<Suspense>
|
||||||
|
<ReceiptSendMailPdfPreview />
|
||||||
|
</Suspense>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</SendMailViewPreviewTabs>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,142 @@
|
|||||||
|
import { x } from '@xstyled/emotion';
|
||||||
|
import { Group, Stack } from '@/components';
|
||||||
|
import {
|
||||||
|
SendMailReceipt,
|
||||||
|
SendMailReceiptProps,
|
||||||
|
} from '../../Estimates/SendMailViewDrawer/SendMailViewReceiptPreview';
|
||||||
|
|
||||||
|
interface ReceiptSendMailReceiptProps extends SendMailReceiptProps {
|
||||||
|
// # Company name.
|
||||||
|
companyLogoUri?: string;
|
||||||
|
companyName: string;
|
||||||
|
|
||||||
|
// # Receipt number.
|
||||||
|
receiptNumberLabel?: string;
|
||||||
|
receiptNumber: string;
|
||||||
|
|
||||||
|
// # Total.
|
||||||
|
total: string;
|
||||||
|
totalLabel?: string;
|
||||||
|
|
||||||
|
// # Message
|
||||||
|
message: string;
|
||||||
|
|
||||||
|
// # Receipt items.
|
||||||
|
items?: Array<{ label: string; total: string; quantity: string | number }>;
|
||||||
|
|
||||||
|
// # Subtotal
|
||||||
|
subtotal: string;
|
||||||
|
subtotalLabel?: string;
|
||||||
|
|
||||||
|
// # View receipt button
|
||||||
|
showViewReceiptButton?: boolean;
|
||||||
|
viewReceiptButtonLabel?: string;
|
||||||
|
viewReceiptButtonOnClick?: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function ReceiptSendMailReceipt({
|
||||||
|
// # Company name.
|
||||||
|
companyLogoUri,
|
||||||
|
companyName,
|
||||||
|
|
||||||
|
// # Receipt number.
|
||||||
|
receiptNumberLabel = 'Receipt #',
|
||||||
|
receiptNumber,
|
||||||
|
|
||||||
|
// # Total.
|
||||||
|
total,
|
||||||
|
totalLabel = 'Total',
|
||||||
|
|
||||||
|
// # Message
|
||||||
|
message,
|
||||||
|
|
||||||
|
// # Items
|
||||||
|
items,
|
||||||
|
subtotal,
|
||||||
|
subtotalLabel = 'Subtotal',
|
||||||
|
|
||||||
|
// # View receipt button
|
||||||
|
showViewReceiptButton,
|
||||||
|
viewReceiptButtonLabel,
|
||||||
|
viewReceiptButtonOnClick,
|
||||||
|
|
||||||
|
...rest
|
||||||
|
}: ReceiptSendMailReceiptProps) {
|
||||||
|
return (
|
||||||
|
<SendMailReceipt {...rest}>
|
||||||
|
<Stack spacing={16} textAlign={'center'}>
|
||||||
|
{companyLogoUri && <SendMailReceipt.CompanyLogo src={companyLogoUri} />}
|
||||||
|
|
||||||
|
<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">
|
||||||
|
{receiptNumberLabel} {receiptNumber}
|
||||||
|
</x.span>
|
||||||
|
</Stack>
|
||||||
|
|
||||||
|
{showViewReceiptButton && (
|
||||||
|
<SendMailReceipt.PrimaryButton
|
||||||
|
primaryColor={'#000'}
|
||||||
|
onClick={viewReceiptButtonOnClick}
|
||||||
|
>
|
||||||
|
{viewReceiptButtonLabel}
|
||||||
|
</SendMailReceipt.PrimaryButton>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<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'}
|
||||||
|
borderBottomColor={'#000'}
|
||||||
|
>
|
||||||
|
<x.span fontWeight={500}>{subtotalLabel}</x.span>
|
||||||
|
<x.span fontWeight={600} fontSize={15}>
|
||||||
|
{subtotal}
|
||||||
|
</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>
|
||||||
|
</Stack>
|
||||||
|
</Stack>
|
||||||
|
</SendMailReceipt>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -89,7 +89,7 @@ function ReceiptsDataTable({
|
|||||||
|
|
||||||
// Handle send mail receipt.
|
// Handle send mail receipt.
|
||||||
const handleSendMailReceipt = ({ id }) => {
|
const handleSendMailReceipt = ({ id }) => {
|
||||||
openDialog(DialogsName.ReceiptMail, { receiptId: id });
|
openDrawer(DRAWERS.RECEIPT_SEND_MAIL, { receiptId: id });
|
||||||
};
|
};
|
||||||
|
|
||||||
// Local storage memorizing columns widths.
|
// Local storage memorizing columns widths.
|
||||||
|
|||||||
@@ -269,3 +269,33 @@ export function useGetReceiptState(
|
|||||||
{ ...options },
|
{ ...options },
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface GetReceiptHtmlResponse {
|
||||||
|
htmlContent: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the sale receipt html content.
|
||||||
|
* @param {number} receiptId
|
||||||
|
* @param {UseQueryOptions<string, Error>} options
|
||||||
|
* @returns {UseQueryResult<GetReceiptHtmlResponse, Error>}
|
||||||
|
*/
|
||||||
|
export const useGetSaleReceiptHtml = (
|
||||||
|
receiptId: number,
|
||||||
|
options?: UseQueryOptions<string, Error>,
|
||||||
|
): UseQueryResult<GetReceiptHtmlResponse, Error> => {
|
||||||
|
const apiRequest = useApiRequest();
|
||||||
|
|
||||||
|
return useQuery<GetReceiptHtmlResponse, Error>(
|
||||||
|
['SALE_RECEIPT_HTML', receiptId],
|
||||||
|
() =>
|
||||||
|
apiRequest
|
||||||
|
.get(`sales/receipts/${receiptId}`, {
|
||||||
|
headers: {
|
||||||
|
Accept: 'application/json+html',
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.then((res) => transformToCamelCase(res.data)),
|
||||||
|
{ ...options },
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user