feat(webapp): the mail notifications dialogs

This commit is contained in:
Ahmed Bouhuolia
2023-12-29 17:31:51 +02:00
parent dc762567b5
commit 2a85fe2f3c
13 changed files with 192 additions and 150 deletions

View File

@@ -1,27 +1,26 @@
// @ts-nocheck
import { Formik } from 'formik';
import * as R from 'ramda';
import { castArray } from 'lodash';
import { useEstimateMailDialogBoot } from './EstimateMailDialogBoot';
import { transformToForm } from '@/utils';
import { DialogsName } from '@/constants/dialogs';
import withDialogActions from '@/containers/Dialog/withDialogActions';
import { useSendSaleEstimateMail } from '@/hooks/query';
import { EstimateMailDialogFormContent } from './EstimateMailDialogFormContent';
import {
initialMailNotificationValues,
MailNotificationFormValues,
transformMailFormToInitialValues,
transformMailFormToRequest,
} from '@/containers/SendMailNotification/utils';
import { Intent } from '@blueprintjs/core';
import { AppToaster } from '@/components';
const initialFormValues = {
from: [],
to: [],
subject: '',
body: '',
...initialMailNotificationValues,
attachEstimate: true,
};
interface EstimateMailFormValues {
from: string[];
to: string[];
subject: string;
body: string;
interface EstimateMailFormValues extends MailNotificationFormValues {
attachEstimate: boolean;
}
@@ -32,21 +31,31 @@ function EstimateMailDialogFormRoot({
const { mutateAsync: sendEstimateMail } = useSendSaleEstimateMail();
const { mailOptions, saleEstimateId } = useEstimateMailDialogBoot();
const initialValues = {
...initialFormValues,
...transformToForm(mailOptions, initialFormValues),
from: mailOptions.from ? castArray(mailOptions.from) : [],
to: mailOptions.to ? castArray(mailOptions.to) : [],
};
const initialValues = transformMailFormToInitialValues(
mailOptions,
initialFormValues,
);
// Handle the form submitting.
const handleSubmit = (values: EstimateMailFormValues, { setSubmitting }) => {
const reqValues = transformMailFormToRequest(values);
setSubmitting(true);
sendEstimateMail([saleEstimateId, values])
sendEstimateMail([saleEstimateId, reqValues])
.then(() => {
AppToaster.show({
message: 'The mail notification has been sent successfully.',
intent: Intent.SUCCESS,
});
closeDialog(DialogsName.EstimateMail);
setSubmitting(false);
})
.catch((error) => {
setSubmitting(false);
closeDialog(DialogsName.EstimateMail);
AppToaster.show({
message: 'Something went wrong.',
intent: Intent.DANGER,
});
});
};

View File

@@ -5,6 +5,7 @@ import styled from 'styled-components';
import { FFormGroup, FSwitch } from '@/components';
import { MailNotificationForm } from '@/containers/SendMailNotification';
import { saveInvoke } from '@/utils';
import { useEstimateMailDialogBoot } from './EstimateMailDialogBoot';
interface EstimateMailDialogFormContentProps {
onClose?: () => void;
@@ -14,6 +15,7 @@ export function EstimateMailDialogFormContent({
onClose,
}: EstimateMailDialogFormContentProps) {
const { isSubmitting } = useFormikContext();
const { mailOptions } = useEstimateMailDialogBoot();
const handleClose = () => {
saveInvoke(onClose);
@@ -22,8 +24,10 @@ export function EstimateMailDialogFormContent({
return (
<Form>
<div className={Classes.DIALOG_BODY}>
<MailNotificationForm fromAddresses={[]} toAddresses={[]} />
<MailNotificationForm
fromAddresses={mailOptions.from_addresses}
toAddresses={mailOptions.to_addresses}
/>
<AttachFormGroup name={'attachEstimate'} inline>
<FSwitch name={'attachEstimate'} label={'Attach Estimate'} />
</AttachFormGroup>

View File

@@ -1,28 +1,27 @@
// @ts-nocheck
import { Formik } from 'formik';
import { castArray } from 'lodash';
import * as R from 'ramda';
import { Intent } from '@blueprintjs/core';
import { useInvoiceMailDialogBoot } from './InvoiceMailDialogBoot';
import { transformToForm } from '@/utils';
import { DialogsName } from '@/constants/dialogs';
import { AppToaster } from '@/components';
import { useSendSaleInvoiceMail } from '@/hooks/query';
import { InvoiceMailDialogFormContent } from './InvoiceMailDialogFormContent';
import withDialogActions from '@/containers/Dialog/withDialogActions';
import { InvoiceMailDialogFormContent } from './InvoiceMailDialogFormContent';
import { InvoiceMailFormSchema } from './InvoiceMailDialogForm.schema';
import {
MailNotificationFormValues,
initialMailNotificationValues,
transformMailFormToRequest,
transformMailFormToInitialValues,
} from '@/containers/SendMailNotification/utils';
const initialFormValues = {
from: [],
to: [],
subject: '',
body: '',
...initialMailNotificationValues,
attachInvoice: true,
};
interface InvoiceMailFormValues {
from: string[];
to: string[];
subject: string;
body: string;
interface InvoiceMailFormValues extends MailNotificationFormValues {
attachInvoice: boolean;
}
@@ -33,20 +32,29 @@ function InvoiceMailDialogFormRoot({
const { mailOptions, saleInvoiceId } = useInvoiceMailDialogBoot();
const { mutateAsync: sendInvoiceMail } = useSendSaleInvoiceMail();
const initialValues = {
...initialFormValues,
...transformToForm(mailOptions, initialFormValues),
from: mailOptions.from ? castArray(mailOptions.from) : [],
to: mailOptions.to ? castArray(mailOptions.to) : [],
};
const initialValues = transformMailFormToInitialValues(
mailOptions,
initialFormValues,
);
// Handle the form submitting.
const handleSubmit = (values: InvoiceMailFormValues, { setSubmitting }) => {
const reqValues = transformMailFormToRequest(values);
setSubmitting(true);
sendInvoiceMail([saleInvoiceId, values])
sendInvoiceMail([saleInvoiceId, reqValues])
.then(() => {
AppToaster.show({
message: 'The mail notification has been sent successfully.',
intent: Intent.SUCCESS,
});
closeDialog(DialogsName.InvoiceMail);
setSubmitting(false);
})
.catch(() => {
AppToaster.show({
message: 'Something went wrong.',
intent: Intent.DANGER,
});
setSubmitting(false);
});
};

View File

@@ -5,6 +5,7 @@ import styled from 'styled-components';
import { FFormGroup, FSwitch } from '@/components';
import { MailNotificationForm } from '@/containers/SendMailNotification';
import { saveInvoke } from '@/utils';
import { useInvoiceMailDialogBoot } from './InvoiceMailDialogBoot';
interface SendMailNotificationFormProps {
onClose?: () => void;
@@ -14,6 +15,7 @@ export function InvoiceMailDialogFormContent({
onClose,
}: SendMailNotificationFormProps) {
const { isSubmitting } = useFormikContext();
const { mailOptions } = useInvoiceMailDialogBoot();
const handleClose = () => {
saveInvoke(onClose);
@@ -22,8 +24,10 @@ export function InvoiceMailDialogFormContent({
return (
<Form>
<div className={Classes.DIALOG_BODY}>
<MailNotificationForm fromAddresses={[]} toAddresses={[]} />
<MailNotificationForm
fromAddresses={mailOptions.from_addresses}
toAddresses={mailOptions.to_addresses}
/>
<AttachFormGroup name={'attachInvoice'} inline>
<FSwitch name={'attachInvoice'} label={'Attach Invoice'} />
</AttachFormGroup>

View File

@@ -1,27 +1,26 @@
// @ts-nocheck
import { Formik, FormikBag } from 'formik';
import { castArray } from 'lodash';
import * as R from 'ramda';
import { Intent } from '@blueprintjs/core';
import { usePaymentMailDialogBoot } from './PaymentMailDialogBoot';
import withDialogActions from '@/containers/Dialog/withDialogActions';
import { DialogsName } from '@/constants/dialogs';
import { useSendPaymentReceiveMail } from '@/hooks/query';
import { PaymentMailDialogFormContent } from './PaymentMailDialogFormContent';
import { transformToForm } from '@/utils';
import {
MailNotificationFormValues,
initialMailNotificationValues,
transformMailFormToRequest,
transformMailFormToInitialValues,
} from '@/containers/SendMailNotification/utils';
import { AppToaster } from '@/components';
const initialFormValues = {
from: [],
to: [],
subject: '',
body: '',
...initialMailNotificationValues,
attachPayment: true,
};
interface PaymentMailFormValue {
from: string[];
to: string[];
subject: string;
body: string;
interface PaymentMailFormValue extends MailNotificationFormValues {
attachPayment: boolean;
}
@@ -32,24 +31,34 @@ export function PaymentMailDialogFormRoot({
const { mailOptions, paymentId } = usePaymentMailDialogBoot();
const { mutateAsync: sendPaymentMail } = useSendPaymentReceiveMail();
const initialValues = {
...initialFormValues,
...transformToForm(mailOptions, initialFormValues),
from: mailOptions.from ? castArray(mailOptions.from) : [],
to: mailOptions.to ? castArray(mailOptions.to) : [],
};
const initialValues = transformMailFormToInitialValues(
mailOptions,
initialFormValues,
);
// Handles the form submitting.
const handleSubmit = (
values: PaymentMailFormValue,
{ setSubmitting }: FormikBag<PaymentMailFormValue>,
) => {
const reqValues = transformMailFormToRequest(values);
setSubmitting(true);
sendPaymentMail([paymentId, values])
sendPaymentMail([paymentId, reqValues])
.then(() => {
AppToaster.show({
message: 'The mail notification has been sent successfully.',
intent: Intent.SUCCESS,
});
setSubmitting(false);
closeDialog(DialogsName.PaymentMail);
})
.catch((error) => {
.catch(() => {
AppToaster.show({
message: 'Something went wrong.',
intent: Intent.DANGER,
});
setSubmitting(false);
closeDialog(DialogsName.PaymentMail);
});
};

View File

@@ -5,6 +5,7 @@ import styled from 'styled-components';
import { FFormGroup, FSwitch } from '@/components';
import { MailNotificationForm } from '@/containers/SendMailNotification';
import { saveInvoke } from '@/utils';
import { usePaymentMailDialogBoot } from './PaymentMailDialogBoot';
interface PaymentMailDialogFormContentProps {
onClose?: () => void;
@@ -13,6 +14,7 @@ interface PaymentMailDialogFormContentProps {
export function PaymentMailDialogFormContent({
onClose,
}: PaymentMailDialogFormContentProps) {
const { mailOptions } = usePaymentMailDialogBoot();
const { isSubmitting } = useFormikContext();
const handleClose = () => {
@@ -22,8 +24,10 @@ export function PaymentMailDialogFormContent({
return (
<Form>
<div className={Classes.DIALOG_BODY}>
<MailNotificationForm fromAddresses={[]} toAddresses={[]} />
<MailNotificationForm
fromAddresses={mailOptions.from_addresses}
toAddresses={mailOptions.to_addresses}
/>
<AttachFormGroup name={'attachPayment'} inline>
<FSwitch name={'attachPayment'} label={'Attach Payment'} />
</AttachFormGroup>

View File

@@ -1,26 +1,25 @@
// @ts-nocheck
import { Formik, FormikBag } from 'formik';
import { castArray } from 'lodash';
import * as R from 'ramda';
import { Intent } from '@blueprintjs/core';
import { useReceiptMailDialogBoot } from './ReceiptMailDialogBoot';
import { transformToForm } from '@/utils';
import withDialogActions from '@/containers/Dialog/withDialogActions';
import { DialogsName } from '@/constants/dialogs';
import { useSendSaleReceiptMail } from '@/hooks/query';
import { ReceiptMailDialogFormContent } from './ReceiptMailDialogFormContent';
import {
initialMailNotificationValues,
MailNotificationFormValues,
transformMailFormToInitialValues,
transformMailFormToRequest,
} from '@/containers/SendMailNotification/utils';
import { AppToaster } from '@/components';
const initialFormValues = {
from: [],
to: [],
subject: '',
body: '',
...initialMailNotificationValues,
attachReceipt: true,
};
interface ReceiptMailFormValues {
from: string[];
to: string[];
subject: string;
body: string;
interface ReceiptMailFormValues extends MailNotificationFormValues {
attachReceipt: boolean;
}
@@ -28,26 +27,37 @@ function ReceiptMailDialogFormRoot({ closeDialog }) {
const { mailOptions, saleReceiptId } = useReceiptMailDialogBoot();
const { mutateAsync: sendReceiptMail } = useSendSaleReceiptMail();
const initialValues = {
...initialFormValues,
...transformToForm(mailOptions, initialFormValues),
from: mailOptions.from ? castArray(mailOptions.from) : [],
to: mailOptions.to ? castArray(mailOptions.to) : [],
};
// Transformes mail options to initial form values.
const initialValues = transformMailFormToInitialValues(
mailOptions,
initialFormValues,
);
// Handle the form submitting.
const handleSubmit = (
values: ReceiptMailFormValues,
{ setSubmitting }: FormikBag<ReceiptMailFormValues>,
) => {
const reqValues = transformMailFormToRequest(values);
setSubmitting(true);
sendReceiptMail([saleReceiptId, values])
sendReceiptMail([saleReceiptId, reqValues])
.then(() => {
AppToaster.show({
message: 'The mail notification has been sent successfully.',
intent: Intent.SUCCESS,
});
closeDialog(DialogsName.ReceiptMail);
setSubmitting(false);
})
.catch((error) => {
.catch(() => {
AppToaster.show({
message: 'Something went wrong.',
intent: Intent.DANGER,
});
setSubmitting(false);
});
};
// Handle the close button click.
const handleClose = () => {
closeDialog(DialogsName.ReceiptMail);
};

View File

@@ -4,6 +4,7 @@ import { Button, Classes, Intent } from '@blueprintjs/core';
import styled from 'styled-components';
import { FFormGroup, FSwitch } from '@/components';
import { MailNotificationForm } from '@/containers/SendMailNotification';
import { useReceiptMailDialogBoot } from './ReceiptMailDialogBoot';
import { saveInvoke } from '@/utils';
interface SendMailNotificationFormProps {
@@ -13,6 +14,7 @@ interface SendMailNotificationFormProps {
export function ReceiptMailDialogFormContent({
onClose,
}: SendMailNotificationFormProps) {
const { mailOptions } = useReceiptMailDialogBoot();
const { isSubmitting } = useFormikContext();
const handleClose = () => {
@@ -22,8 +24,10 @@ export function ReceiptMailDialogFormContent({
return (
<Form>
<div className={Classes.DIALOG_BODY}>
<MailNotificationForm fromAddresses={[]} toAddresses={[]} />
<MailNotificationForm
fromAddresses={mailOptions.from_addresses}
toAddresses={mailOptions.to_addresses}
/>
<AttachFormGroup name={'attachReceipt:'} inline>
<FSwitch name={'attachReceipt:'} label={'Attach Receipt'} />
</AttachFormGroup>

View File

@@ -16,6 +16,14 @@ interface MailNotificationFormProps {
toAddresses: SelectOptionProps[];
}
const commonAddressSelect = {
placeholder: '',
labelAccessor: '',
valueAccessor: 'mail',
tagAccessor: (item) => `<${item.label}> (${item.mail})`,
textAccessor: (item) => `<${item.label}> (${item.mail})`,
};
export function MailNotificationForm({
fromAddresses,
toAddresses,
@@ -38,12 +46,12 @@ export function MailNotificationForm({
<FMultiSelect
items={fromAddresses}
name={'from'}
placeholder=""
popoverProps={{ minimal: true, fill: true }}
tagInputProps={{
tagProps: { round: true, minimal: true, large: true },
}}
fill={true}
{...commonAddressSelect}
/>
</FFormGroup>
@@ -57,6 +65,7 @@ export function MailNotificationForm({
tagProps: { round: true, minimal: true, large: true },
}}
fill={true}
{...commonAddressSelect}
/>
</FFormGroup>

View File

@@ -1,63 +0,0 @@
// @ts-nocheck
import './styles.scss';
import { Color } from '@tiptap/extension-color';
import ListItem from '@tiptap/extension-list-item';
import TextStyle from '@tiptap/extension-text-style';
import { EditorProvider } from '@tiptap/react';
import StarterKit from '@tiptap/starter-kit';
import { Box } from '@/components';
import styled from 'styled-components';
import { useUncontrolled } from '@/hooks/useUncontrolled';
const extensions = [
Color.configure({ types: [TextStyle.name, ListItem.name] }),
TextStyle.configure({ types: [ListItem.name] }),
StarterKit.configure({
bulletList: {
keepMarks: true,
keepAttributes: false,
},
orderedList: {
keepMarks: true,
keepAttributes: false,
},
}),
];
export interface RichEditorProps {
value?: string;
initialValue?: string;
onChange?: (value: string) => void;
className?: string;
}
export const RichEditor = ({
value,
initialValue,
onChange,
className,
}: RichEditorProps) => {
const [content, handleChange] = useUncontrolled({
value,
initialValue,
finalValue: '',
onChange,
});
return (
<Root>
<EditorProvider
extensions={extensions}
content={content}
onBlur={handleChange}
/>
</Root>
);
};
const Root = styled(Box)`
padding: 15px;
border: 1px solid #dedfe9;
border-top: 0;
border-bottom-left-radius: 5px;
border-bottom-right-radius: 5px;
`;

View File

@@ -1 +1 @@
export * from './SendMailNotificationForm';
export * from './MailNotificationForm';

View File

@@ -0,0 +1,44 @@
import { castArray, first } from 'lodash';
import { transformToForm } from '@/utils';
export const initialMailNotificationValues = {
from: [],
to: [],
subject: '',
body: '',
};
export interface MailNotificationFormValues {
from: string[];
to: string[];
subject: string;
body: string;
}
export const transformMailFormToRequest = (
values: MailNotificationFormValues,
) => {
return {
...values,
from: first(values.from),
to: values.to?.join(', '),
};
};
/**
* Transformes the mail options response values to form initial values.
* @param {any} mailOptions
* @param {MailNotificationFormValues} initialValues
* @returns {MailNotificationFormValues}
*/
export const transformMailFormToInitialValues = (
mailOptions: any,
initialValues: MailNotificationFormValues,
): MailNotificationFormValues => {
return {
...initialValues,
...transformToForm(mailOptions, initialValues),
from: mailOptions.from ? castArray(mailOptions.from) : [],
to: mailOptions.to ? castArray(mailOptions.to) : [],
};
};

View File

@@ -216,7 +216,7 @@ export function useSendSaleReceiptMail(props) {
const apiRequest = useApiRequest();
return useMutation(
(id, values) => apiRequest.post(`sales/receipts/${id}/mail`, values),
([id, values]) => apiRequest.post(`sales/receipts/${id}/mail`, values),
{
onSuccess: () => {
// Invalidate queries.