mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-23 16:19:49 +00:00
feat: wip preview invoice payment mail
This commit is contained in:
@@ -235,6 +235,7 @@ export enum SaleInvoiceAction {
|
|||||||
|
|
||||||
export interface SaleInvoiceMailOptions extends CommonMailOptions {
|
export interface SaleInvoiceMailOptions extends CommonMailOptions {
|
||||||
attachInvoice?: boolean;
|
attachInvoice?: boolean;
|
||||||
|
formatArgs?: Record<string, any>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface SendInvoiceMailDTO extends CommonMailOptionsDTO {
|
export interface SendInvoiceMailDTO extends CommonMailOptionsDTO {
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ export class SaleInvoicePdf {
|
|||||||
* Retrieve sale invoice pdf content.
|
* Retrieve sale invoice pdf content.
|
||||||
* @param {number} tenantId - Tenant Id.
|
* @param {number} tenantId - Tenant Id.
|
||||||
* @param {ISaleInvoice} saleInvoice -
|
* @param {ISaleInvoice} saleInvoice -
|
||||||
* @returns {Promise<Buffer>}
|
* @returns {Promise<[Buffer, string]>}
|
||||||
*/
|
*/
|
||||||
public async saleInvoicePdf(
|
public async saleInvoicePdf(
|
||||||
tenantId: number,
|
tenantId: number,
|
||||||
|
|||||||
@@ -48,11 +48,14 @@ export class SendSaleInvoiceMailCommon {
|
|||||||
tenantId,
|
tenantId,
|
||||||
saleInvoice.customerId
|
saleInvoice.customerId
|
||||||
);
|
);
|
||||||
|
const formatArgs = await this.getInvoiceFormatterArgs(tenantId, invoiceId);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...contactMailDefaultOptions,
|
...contactMailDefaultOptions,
|
||||||
attachInvoice: true,
|
|
||||||
subject: defaultSubject,
|
subject: defaultSubject,
|
||||||
message: defaultMessage,
|
message: defaultMessage,
|
||||||
|
attachInvoice: true,
|
||||||
|
formatArgs,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -103,7 +106,11 @@ export class SendSaleInvoiceMailCommon {
|
|||||||
tenantId,
|
tenantId,
|
||||||
invoiceId
|
invoiceId
|
||||||
);
|
);
|
||||||
|
const commonArgs =
|
||||||
|
await this.contactMailNotification.getCommonFormatArgs(tenantId);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
...commonArgs,
|
||||||
CustomerName: invoice.customer.displayName,
|
CustomerName: invoice.customer.displayName,
|
||||||
InvoiceNumber: invoice.invoiceNo,
|
InvoiceNumber: invoice.invoiceNo,
|
||||||
InvoiceDueAmount: invoice.dueAmountFormatted,
|
InvoiceDueAmount: invoice.dueAmountFormatted,
|
||||||
|
|||||||
@@ -15,9 +15,8 @@ import {
|
|||||||
parseMailOptions,
|
parseMailOptions,
|
||||||
validateRequiredMailOptions,
|
validateRequiredMailOptions,
|
||||||
} from '@/services/MailNotification/utils';
|
} from '@/services/MailNotification/utils';
|
||||||
import events from '@/subscribers/events';
|
|
||||||
import { EventPublisher } from '@/lib/EventPublisher/EventPublisher';
|
import { EventPublisher } from '@/lib/EventPublisher/EventPublisher';
|
||||||
import { ParsedNumberSearch } from 'libphonenumber-js';
|
import events from '@/subscribers/events';
|
||||||
|
|
||||||
@Service()
|
@Service()
|
||||||
export class SendSaleInvoiceMail {
|
export class SendSaleInvoiceMail {
|
||||||
|
|||||||
@@ -1,11 +1,15 @@
|
|||||||
import { Box } from '@/components';
|
import { Box, Group, Stack } from '@/components';
|
||||||
import { InvoiceMailReceiptPreview } from '../InvoiceCustomize/InvoiceMailReceiptPreview';
|
import { InvoiceMailReceiptPreview } from '../InvoiceCustomize/InvoiceMailReceiptPreview';
|
||||||
import { css } from '@emotion/css';
|
import { css } from '@emotion/css';
|
||||||
import { useInvoiceSendMailBoot } from './InvoiceSendMailContentBoot';
|
import { useInvoiceSendMailBoot } from './InvoiceSendMailContentBoot';
|
||||||
import { useMemo } from 'react';
|
import { useMemo } from 'react';
|
||||||
|
import { x } from '@xstyled/emotion';
|
||||||
|
import { InvoiceSendMailPreviewWithHeader } from './InvoiceSendMailHeaderPreview';
|
||||||
|
import { useSendInvoiceMailMessage } from './_hooks';
|
||||||
|
|
||||||
export function InvoiceMailReceiptPreviewConneceted() {
|
export function InvoiceMailReceiptPreviewConneceted() {
|
||||||
const { invoice } = useInvoiceSendMailBoot();
|
const { invoice } = useInvoiceSendMailBoot();
|
||||||
|
const mailMessage = useSendInvoiceMailMessage();
|
||||||
|
|
||||||
const items = useMemo(
|
const items = useMemo(
|
||||||
() =>
|
() =>
|
||||||
@@ -18,16 +22,19 @@ export function InvoiceMailReceiptPreviewConneceted() {
|
|||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box px={4} pt={8} pb={16}>
|
<InvoiceSendMailPreviewWithHeader>
|
||||||
<InvoiceMailReceiptPreview
|
<Box px={4} pt={8} pb={16}>
|
||||||
total={invoice.total_formatted}
|
<InvoiceMailReceiptPreview
|
||||||
dueDate={invoice.due_date_formatted}
|
total={invoice.total_formatted}
|
||||||
invoiceNumber={invoice.invoice_no}
|
dueDate={invoice.due_date_formatted}
|
||||||
items={items}
|
invoiceNumber={invoice.invoice_no}
|
||||||
className={css`
|
items={items}
|
||||||
margin: 0 auto;
|
message={mailMessage}
|
||||||
`}
|
className={css`
|
||||||
/>
|
margin: 0 auto;
|
||||||
</Box>
|
`}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
</InvoiceSendMailPreviewWithHeader>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,13 +1,20 @@
|
|||||||
// @ts-nocheck
|
// @ts-nocheck
|
||||||
import React, { createContext, useContext } from 'react';
|
import React, { createContext, useContext } from 'react';
|
||||||
import { Spinner } from '@blueprintjs/core';
|
import { Spinner } from '@blueprintjs/core';
|
||||||
import { useInvoice } from '@/hooks/query';
|
import {
|
||||||
|
GetSaleInvoiceDefaultOptionsResponse,
|
||||||
|
useInvoice,
|
||||||
|
useSaleInvoiceDefaultOptions,
|
||||||
|
} from '@/hooks/query';
|
||||||
import { useDrawerContext } from '@/components/Drawer/DrawerProvider';
|
import { useDrawerContext } from '@/components/Drawer/DrawerProvider';
|
||||||
|
|
||||||
interface InvoiceSendMailBootValues {
|
interface InvoiceSendMailBootValues {
|
||||||
invoice: any;
|
invoice: any;
|
||||||
invoiceId: number;
|
invoiceId: number;
|
||||||
isInvoiceLoading: boolean;
|
isInvoiceLoading: boolean;
|
||||||
|
|
||||||
|
invoiceMailOptions: GetSaleInvoiceDefaultOptionsResponse | undefined;
|
||||||
|
isInvoiceMailOptionsLoading: boolean;
|
||||||
}
|
}
|
||||||
interface InvoiceSendMailBootProps {
|
interface InvoiceSendMailBootProps {
|
||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
@@ -21,10 +28,15 @@ export const InvoiceSendMailBoot = ({ children }: InvoiceSendMailBootProps) => {
|
|||||||
payload: { invoiceId },
|
payload: { invoiceId },
|
||||||
} = useDrawerContext();
|
} = useDrawerContext();
|
||||||
|
|
||||||
|
// Invoice details.
|
||||||
const { data: invoice, isLoading: isInvoiceLoading } = useInvoice(invoiceId, {
|
const { data: invoice, isLoading: isInvoiceLoading } = useInvoice(invoiceId, {
|
||||||
enabled: !!invoiceId,
|
enabled: !!invoiceId,
|
||||||
});
|
});
|
||||||
const isLoading = isInvoiceLoading;
|
// Invoice mail options.
|
||||||
|
const { data: invoiceMailOptions, isLoading: isInvoiceMailOptionsLoading } =
|
||||||
|
useSaleInvoiceDefaultOptions(invoiceId);
|
||||||
|
|
||||||
|
const isLoading = isInvoiceLoading || isInvoiceMailOptionsLoading;
|
||||||
|
|
||||||
if (isLoading) {
|
if (isLoading) {
|
||||||
return <Spinner size={20} />;
|
return <Spinner size={20} />;
|
||||||
@@ -33,6 +45,8 @@ export const InvoiceSendMailBoot = ({ children }: InvoiceSendMailBootProps) => {
|
|||||||
invoice,
|
invoice,
|
||||||
isInvoiceLoading,
|
isInvoiceLoading,
|
||||||
invoiceId,
|
invoiceId,
|
||||||
|
invoiceMailOptions,
|
||||||
|
isInvoiceMailOptionsLoading,
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
// @ts-nocheck
|
// @ts-nocheck
|
||||||
import { useState } from 'react';
|
import { useRef, useState } from 'react';
|
||||||
import { Button, Intent, MenuItem } from '@blueprintjs/core';
|
import { Button, Intent, MenuItem, Position } from '@blueprintjs/core';
|
||||||
import { useFormikContext } from 'formik';
|
import { useFormikContext } from 'formik';
|
||||||
import { css } from '@emotion/css';
|
import { css } from '@emotion/css';
|
||||||
import { x } from '@xstyled/emotion';
|
import { x } from '@xstyled/emotion';
|
||||||
@@ -9,8 +9,10 @@ import {
|
|||||||
FFormGroup,
|
FFormGroup,
|
||||||
FInputGroup,
|
FInputGroup,
|
||||||
FMultiSelect,
|
FMultiSelect,
|
||||||
|
FSelect,
|
||||||
FTextArea,
|
FTextArea,
|
||||||
Group,
|
Group,
|
||||||
|
Icon,
|
||||||
Stack,
|
Stack,
|
||||||
} from '@/components';
|
} from '@/components';
|
||||||
import { useDrawerContext } from '@/components/Drawer/DrawerProvider';
|
import { useDrawerContext } from '@/components/Drawer/DrawerProvider';
|
||||||
@@ -60,6 +62,7 @@ const fieldsWrapStyle = css`
|
|||||||
export function InvoiceSendMailFields() {
|
export function InvoiceSendMailFields() {
|
||||||
const [showCCField, setShowCCField] = useState<boolean>(false);
|
const [showCCField, setShowCCField] = useState<boolean>(false);
|
||||||
const [showBccField, setShowBccField] = useState<boolean>(false);
|
const [showBccField, setShowBccField] = useState<boolean>(false);
|
||||||
|
const textareaRef = useRef(null);
|
||||||
|
|
||||||
const { values, setFieldValue } = useFormikContext();
|
const { values, setFieldValue } = useFormikContext();
|
||||||
const items = useInvoiceMailItems();
|
const items = useInvoiceMailItems();
|
||||||
@@ -117,6 +120,30 @@ export function InvoiceSendMailFields() {
|
|||||||
</Group>
|
</Group>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const handleTextareaChange = () => {
|
||||||
|
const textarea = textareaRef.current;
|
||||||
|
if (!textarea) return;
|
||||||
|
|
||||||
|
const { selectionStart, selectionEnd } = textarea;
|
||||||
|
const insertText = '{Variable}';
|
||||||
|
|
||||||
|
// Insert the text at the cursor position
|
||||||
|
const message =
|
||||||
|
text.substring(0, selectionStart) +
|
||||||
|
insertText +
|
||||||
|
text.substring(selectionEnd);
|
||||||
|
|
||||||
|
setFieldValue('message', message);
|
||||||
|
|
||||||
|
// Move the cursor to the end of the inserted text
|
||||||
|
setTimeout(() => {
|
||||||
|
textarea.selectionStart = textarea.selectionEnd =
|
||||||
|
selectionStart + insertText.length;
|
||||||
|
|
||||||
|
textarea.focus();
|
||||||
|
}, 0);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Stack
|
<Stack
|
||||||
bg="white"
|
bg="white"
|
||||||
@@ -144,6 +171,7 @@ export function InvoiceSendMailFields() {
|
|||||||
resetOnQuery
|
resetOnQuery
|
||||||
resetOnSelect
|
resetOnSelect
|
||||||
fill
|
fill
|
||||||
|
fastField
|
||||||
/>
|
/>
|
||||||
{showCCField && (
|
{showCCField && (
|
||||||
<FMultiSelect
|
<FMultiSelect
|
||||||
@@ -161,6 +189,7 @@ export function InvoiceSendMailFields() {
|
|||||||
resetOnQuery
|
resetOnQuery
|
||||||
resetOnSelect
|
resetOnSelect
|
||||||
fill
|
fill
|
||||||
|
fastField
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{showBccField && (
|
{showBccField && (
|
||||||
@@ -179,25 +208,62 @@ export function InvoiceSendMailFields() {
|
|||||||
resetOnQuery
|
resetOnQuery
|
||||||
resetOnSelect
|
resetOnSelect
|
||||||
fill
|
fill
|
||||||
|
fastField
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</Stack>
|
</Stack>
|
||||||
</FFormGroup>
|
</FFormGroup>
|
||||||
|
|
||||||
<FFormGroup label={'Submit'} name={'subject'}>
|
<FFormGroup label={'Submit'} name={'subject'}>
|
||||||
<FInputGroup name={'subject'} large />
|
<FInputGroup name={'subject'} large fastField />
|
||||||
</FFormGroup>
|
</FFormGroup>
|
||||||
|
|
||||||
<FFormGroup label={'Message'} name={'message'}>
|
<FFormGroup label={'Message'} name={'message'}>
|
||||||
<FTextArea
|
<Stack spacing={0}>
|
||||||
name={'message'}
|
<Group
|
||||||
large
|
border={'1px solid #ced4da'}
|
||||||
fill
|
borderBottom={0}
|
||||||
className={css`
|
borderRadius={'3px 3px 0 0'}
|
||||||
resize: vertical;
|
>
|
||||||
min-height: 200px;
|
<FSelect
|
||||||
`}
|
selectedItem={'customerName'}
|
||||||
/>
|
name={'item'}
|
||||||
|
items={[{ value: 'customerName', text: 'Customer Name' }]}
|
||||||
|
onItemChange={handleTextareaChange}
|
||||||
|
popoverProps={{
|
||||||
|
fill: false,
|
||||||
|
position: Position.BOTTOM_LEFT,
|
||||||
|
minimal: true,
|
||||||
|
}}
|
||||||
|
input={({ activeItem, text, label, value }) => (
|
||||||
|
<Button
|
||||||
|
minimal
|
||||||
|
rightIcon={
|
||||||
|
<Icon icon={'caret-down-16'} color={'#8F99A8'} />
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{text}
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
fill={false}
|
||||||
|
fastField
|
||||||
|
/>
|
||||||
|
</Group>
|
||||||
|
|
||||||
|
<FTextArea
|
||||||
|
ref={textareaRef}
|
||||||
|
name={'message'}
|
||||||
|
large
|
||||||
|
fill
|
||||||
|
fastField
|
||||||
|
className={css`
|
||||||
|
resize: vertical;
|
||||||
|
min-height: 200px;
|
||||||
|
border-top-right-radius: 0px;
|
||||||
|
border-top-left-radius: 0px;
|
||||||
|
`}
|
||||||
|
/>
|
||||||
|
</Stack>
|
||||||
</FFormGroup>
|
</FFormGroup>
|
||||||
</Stack>
|
</Stack>
|
||||||
|
|
||||||
@@ -243,4 +309,3 @@ function InvoiceSendMailFooter() {
|
|||||||
</Group>
|
</Group>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import * as Yup from 'yup';
|
|
||||||
import { Form, Formik, FormikHelpers } from 'formik';
|
import { Form, Formik, FormikHelpers } from 'formik';
|
||||||
import { css } from '@emotion/css';
|
import { css } from '@emotion/css';
|
||||||
import { Intent } from '@blueprintjs/core';
|
import { Intent } from '@blueprintjs/core';
|
||||||
@@ -9,22 +8,14 @@ import { AppToaster } from '@/components';
|
|||||||
import { useInvoiceSendMailBoot } from './InvoiceSendMailContentBoot';
|
import { useInvoiceSendMailBoot } from './InvoiceSendMailContentBoot';
|
||||||
import { useDrawerActions } from '@/hooks/state';
|
import { useDrawerActions } from '@/hooks/state';
|
||||||
import { useDrawerContext } from '@/components/Drawer/DrawerProvider';
|
import { useDrawerContext } from '@/components/Drawer/DrawerProvider';
|
||||||
|
import { transformToForm } from '@/utils';
|
||||||
|
|
||||||
const initialValues = {
|
const initialValues = {
|
||||||
subject: 'invoice INV-0002 for AED 0.00',
|
subject: '',
|
||||||
message: `Hi Ahmed,
|
message: '',
|
||||||
|
to: [],
|
||||||
Here’s invoice INV-0002 for AED 0.00
|
cc: [],
|
||||||
|
bcc: [],
|
||||||
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`,
|
|
||||||
to: ['a.bouhuolia@gmail.com'],
|
|
||||||
};
|
};
|
||||||
|
|
||||||
interface InvoiceSendMailFormProps {
|
interface InvoiceSendMailFormProps {
|
||||||
@@ -33,10 +24,14 @@ interface InvoiceSendMailFormProps {
|
|||||||
|
|
||||||
export function InvoiceSendMailForm({ children }: InvoiceSendMailFormProps) {
|
export function InvoiceSendMailForm({ children }: InvoiceSendMailFormProps) {
|
||||||
const { mutateAsync: sendInvoiceMail } = useSendSaleInvoiceMail();
|
const { mutateAsync: sendInvoiceMail } = useSendSaleInvoiceMail();
|
||||||
const { invoiceId } = useInvoiceSendMailBoot();
|
const { invoiceId, invoiceMailOptions } = useInvoiceSendMailBoot();
|
||||||
const { name } = useDrawerContext();
|
const { name } = useDrawerContext();
|
||||||
const { closeDrawer } = useDrawerActions();
|
const { closeDrawer } = useDrawerActions();
|
||||||
|
|
||||||
|
const _initialValues = {
|
||||||
|
...initialValues,
|
||||||
|
...transformToForm(invoiceMailOptions, initialValues),
|
||||||
|
};
|
||||||
const handleSubmit = (
|
const handleSubmit = (
|
||||||
values: InvoiceSendMailFormValues,
|
values: InvoiceSendMailFormValues,
|
||||||
{ setSubmitting }: FormikHelpers<InvoiceSendMailFormValues>,
|
{ setSubmitting }: FormikHelpers<InvoiceSendMailFormValues>,
|
||||||
@@ -62,7 +57,7 @@ export function InvoiceSendMailForm({ children }: InvoiceSendMailFormProps) {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Formik
|
<Formik
|
||||||
initialValues={initialValues}
|
initialValues={_initialValues}
|
||||||
validationSchema={InvoiceSendMailFormSchema}
|
validationSchema={InvoiceSendMailFormSchema}
|
||||||
onSubmit={handleSubmit}
|
onSubmit={handleSubmit}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -0,0 +1,75 @@
|
|||||||
|
import { css } from '@emotion/css';
|
||||||
|
import { x } from '@xstyled/emotion';
|
||||||
|
import { Box, Group, Stack } from '@/components';
|
||||||
|
import React from 'react';
|
||||||
|
import { useSendInvoiceMailSubject } from './_hooks';
|
||||||
|
|
||||||
|
export function InvoiceSendMailHeaderPreview() {
|
||||||
|
const mailSubject = useSendInvoiceMailSubject();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Stack
|
||||||
|
bg={'white'}
|
||||||
|
borderBottom={'1px solid #dcdcdd'}
|
||||||
|
padding={'22px 30px'}
|
||||||
|
spacing={8}
|
||||||
|
position={'sticky'}
|
||||||
|
top={0}
|
||||||
|
zIndex={1}
|
||||||
|
>
|
||||||
|
<Box>
|
||||||
|
<x.h2 fontWeight={600} fontSize={16}>
|
||||||
|
{mailSubject}
|
||||||
|
</x.h2>
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
<Group display="flex" gap={2}>
|
||||||
|
<Group display="flex" alignItems="center" gap={15}>
|
||||||
|
<x.abbr
|
||||||
|
role="presentation"
|
||||||
|
className={css`
|
||||||
|
background-color: #daa3e4;
|
||||||
|
color: #3f1946;
|
||||||
|
fill: #daa3e4;
|
||||||
|
height: 40px;
|
||||||
|
width: 40px;
|
||||||
|
line-height: 40px;
|
||||||
|
text-align: center;
|
||||||
|
border-radius: 40px;
|
||||||
|
font-size: 14px;
|
||||||
|
`}
|
||||||
|
>
|
||||||
|
A
|
||||||
|
</x.abbr>
|
||||||
|
|
||||||
|
<Stack spacing={2}>
|
||||||
|
<Group spacing={2}>
|
||||||
|
<Box fontWeight={600}>Ahmed </Box>
|
||||||
|
<Box color={'#738091'}>
|
||||||
|
<messaging-service@post.xero.com>
|
||||||
|
</Box>
|
||||||
|
</Group>
|
||||||
|
|
||||||
|
<Box fontSize={'sm'} color={'#738091'}>
|
||||||
|
Reply to: Ahmed <a.m.bouhuolia@gmail.com>
|
||||||
|
</Box>
|
||||||
|
</Stack>
|
||||||
|
</Group>
|
||||||
|
</Group>
|
||||||
|
</Stack>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function InvoiceSendMailPreviewWithHeader({
|
||||||
|
children,
|
||||||
|
}: {
|
||||||
|
children: React.ReactNode;
|
||||||
|
}) {
|
||||||
|
return (
|
||||||
|
<Box>
|
||||||
|
<InvoiceSendMailHeaderPreview />
|
||||||
|
|
||||||
|
<Box>{children}</Box>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -1,27 +1,30 @@
|
|||||||
import { Box } from "@/components";
|
import { Box } from '@/components';
|
||||||
import { InvoicePaperTemplate } from "../InvoiceCustomize/InvoicePaperTemplate";
|
import { InvoicePaperTemplate } from '../InvoiceCustomize/InvoicePaperTemplate';
|
||||||
import { css } from "@emotion/css";
|
import { css } from '@emotion/css';
|
||||||
import { useInvoiceSendMailBoot } from "./InvoiceSendMailContentBoot";
|
import { useInvoiceSendMailBoot } from './InvoiceSendMailContentBoot';
|
||||||
|
import { InvoiceSendMailPreviewWithHeader } from './InvoiceSendMailHeaderPreview';
|
||||||
|
|
||||||
export function InvoiceSendPdfPreviewConnected() {
|
export function InvoiceSendPdfPreviewConnected() {
|
||||||
const { invoice } = useInvoiceSendMailBoot();
|
const { invoice } = useInvoiceSendMailBoot();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box px={4} py={6}>
|
<InvoiceSendMailPreviewWithHeader>
|
||||||
<InvoicePaperTemplate
|
<Box px={4} py={6}>
|
||||||
dueDate={invoice.due_date_formatted}
|
<InvoicePaperTemplate
|
||||||
dateIssue={invoice.invoice_date_formatted}
|
dueDate={invoice.due_date_formatted}
|
||||||
invoiceNumber={invoice.invoice_no}
|
dateIssue={invoice.invoice_date_formatted}
|
||||||
total={invoice.total_formatted}
|
invoiceNumber={invoice.invoice_no}
|
||||||
subtotal={invoice.subtotal}
|
total={invoice.total_formatted}
|
||||||
discount={''}
|
subtotal={invoice.subtotal}
|
||||||
paymentMade={''}
|
discount={''}
|
||||||
balanceDue={invoice.due_amount_Formatted}
|
paymentMade={''}
|
||||||
statement={invoice.statement}
|
balanceDue={invoice.due_amount_Formatted}
|
||||||
className={css`
|
statement={invoice.statement}
|
||||||
margin: 0 auto;
|
className={css`
|
||||||
`}
|
margin: 0 auto;
|
||||||
/>
|
`}
|
||||||
</Box>
|
/>
|
||||||
|
</Box>
|
||||||
|
</InvoiceSendMailPreviewWithHeader>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
import { useFormikContext } from 'formik';
|
import { useFormikContext } from 'formik';
|
||||||
import { chain } from 'lodash';
|
import { camelCase, chain, defaultTo, mapKeys, upperFirst } from 'lodash';
|
||||||
import { InvoiceSendMailFormValues } from './_types';
|
import { InvoiceSendMailFormValues } from './_types';
|
||||||
|
import { useInvoiceSendMailBoot } from './InvoiceSendMailContentBoot';
|
||||||
|
import { useMemo } from 'react';
|
||||||
|
|
||||||
export const useInvoiceMailItems = () => {
|
export const useInvoiceMailItems = () => {
|
||||||
const { values } = useFormikContext<InvoiceSendMailFormValues>();
|
const { values } = useFormikContext<InvoiceSendMailFormValues>();
|
||||||
@@ -16,3 +18,42 @@ export const useInvoiceMailItems = () => {
|
|||||||
}))
|
}))
|
||||||
.value();
|
.value();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const useSendInvoiceMailFormatArgs = () => {
|
||||||
|
const { invoiceMailOptions } = useInvoiceSendMailBoot();
|
||||||
|
|
||||||
|
return useMemo(() => {
|
||||||
|
return mapKeys(invoiceMailOptions?.formatArgs, (_, key) =>
|
||||||
|
upperFirst(camelCase(key)),
|
||||||
|
);
|
||||||
|
}, [invoiceMailOptions]);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useSendInvoiceMailSubject = () => {
|
||||||
|
const { values } = useFormikContext<InvoiceSendMailFormValues>();
|
||||||
|
const formatArgs = useSendInvoiceMailFormatArgs();
|
||||||
|
|
||||||
|
return formatSmsMessage(values?.subject, formatArgs);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useSendInvoiceMailMessage = () => {
|
||||||
|
const { values } = useFormikContext<InvoiceSendMailFormValues>();
|
||||||
|
const formatArgs = useSendInvoiceMailFormatArgs();
|
||||||
|
|
||||||
|
return formatSmsMessage(values?.message, formatArgs);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const formatSmsMessage = (
|
||||||
|
message: string,
|
||||||
|
args: Record<string, any>,
|
||||||
|
) => {
|
||||||
|
let formattedMessage = message;
|
||||||
|
|
||||||
|
Object.keys(args).forEach((key) => {
|
||||||
|
const variable = `{${key}}`;
|
||||||
|
const value = defaultTo(args[key], '');
|
||||||
|
|
||||||
|
formattedMessage = formattedMessage.replace(variable, value);
|
||||||
|
});
|
||||||
|
return formattedMessage;
|
||||||
|
};
|
||||||
|
|||||||
@@ -2,6 +2,6 @@ export interface InvoiceSendMailFormValues {
|
|||||||
subject: string;
|
subject: string;
|
||||||
message: string;
|
message: string;
|
||||||
to: string[];
|
to: string[];
|
||||||
cc?: string[];
|
cc: string[];
|
||||||
bcc?: string[];
|
bcc: string[];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,8 @@ import {
|
|||||||
useQuery,
|
useQuery,
|
||||||
UseMutationOptions,
|
UseMutationOptions,
|
||||||
UseMutationResult,
|
UseMutationResult,
|
||||||
|
UseQueryOptions,
|
||||||
|
UseQueryResult,
|
||||||
} from 'react-query';
|
} from 'react-query';
|
||||||
import { useRequestQuery } from '../useQueryRequest';
|
import { useRequestQuery } from '../useQueryRequest';
|
||||||
import { transformPagination, transformToCamelCase } from '@/utils';
|
import { transformPagination, transformToCamelCase } from '@/utils';
|
||||||
@@ -364,17 +366,28 @@ export function useSendSaleInvoiceMail(
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function useSaleInvoiceDefaultOptions(invoiceId, props) {
|
export interface GetSaleInvoiceDefaultOptionsResponse {
|
||||||
return useRequestQuery(
|
to: Array<string>;
|
||||||
|
from: Array<String>;
|
||||||
|
subject: string;
|
||||||
|
message: string;
|
||||||
|
attachInvoice: boolean;
|
||||||
|
formatArgs: Record<string, string>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useSaleInvoiceDefaultOptions(
|
||||||
|
invoiceId: number,
|
||||||
|
options?: UseQueryOptions<GetSaleInvoiceDefaultOptionsResponse>,
|
||||||
|
): UseQueryResult<GetSaleInvoiceDefaultOptionsResponse> {
|
||||||
|
const apiRequest = useApiRequest();
|
||||||
|
|
||||||
|
return useQuery<GetSaleInvoiceDefaultOptionsResponse>(
|
||||||
[t.SALE_INVOICE_DEFAULT_OPTIONS, invoiceId],
|
[t.SALE_INVOICE_DEFAULT_OPTIONS, invoiceId],
|
||||||
{
|
() =>
|
||||||
method: 'get',
|
apiRequest
|
||||||
url: `sales/invoices/${invoiceId}/mail`,
|
.get(`/sales/invoices/${invoiceId}/mail`)
|
||||||
},
|
.then((res) => transformToCamelCase(res.data?.data)),
|
||||||
{
|
options,
|
||||||
select: (res) => res.data.data,
|
|
||||||
...props,
|
|
||||||
},
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user