feat: add send mail icon

This commit is contained in:
Ahmed Bouhuolia
2023-12-26 22:57:41 +02:00
parent de1b7f132c
commit c46948049c
26 changed files with 369 additions and 82 deletions

View File

@@ -84,20 +84,18 @@ function EstimateDetailActionsBar({
<NavbarDivider /> <NavbarDivider />
</Can> </Can>
<Can I={SaleEstimateAction.View} a={AbilitySubject.Estimate}> <Can I={SaleEstimateAction.View} a={AbilitySubject.Estimate}>
<Button
className={Classes.MINIMAL}
icon={<Icon icon="envelope" />}
text={'Send Mail'}
onClick={handleMailEstimate}
/>
<Button <Button
className={Classes.MINIMAL} className={Classes.MINIMAL}
icon={<Icon icon="print-16" />} icon={<Icon icon="print-16" />}
text={<T id={'print'} />} text={<T id={'print'} />}
onClick={handlePrintEstimate} onClick={handlePrintEstimate}
/> />
</Can>
<Can I={SaleEstimateAction.View} a={AbilitySubject.Estimate}>
<Button
className={Classes.MINIMAL}
text={'Mail'}
onClick={handleMailEstimate}
/>
<NavbarDivider /> <NavbarDivider />
</Can> </Can>
<Can I={SaleEstimateAction.Delete} a={AbilitySubject.Estimate}> <Can I={SaleEstimateAction.Delete} a={AbilitySubject.Estimate}>

View File

@@ -123,18 +123,18 @@ function InvoiceDetailActionsBar({
</Can> </Can>
<Can I={SaleInvoiceAction.View} a={AbilitySubject.Invoice}> <Can I={SaleInvoiceAction.View} a={AbilitySubject.Invoice}>
<Button <Button
text={'Mail'} text={'Send Mail'}
icon={<Icon icon="envelope" />}
onClick={handleMailInvoice} onClick={handleMailInvoice}
className={Classes.MINIMAL} className={Classes.MINIMAL}
/> />
</Can>
<Can I={SaleInvoiceAction.View} a={AbilitySubject.Invoice}>
<Button <Button
className={Classes.MINIMAL} className={Classes.MINIMAL}
icon={<Icon icon="print-16" />} icon={<Icon icon="print-16" />}
text={<T id={'print'} />} text={<T id={'print'} />}
onClick={handlePrintInvoice} onClick={handlePrintInvoice}
/> />
<NavbarDivider />
</Can> </Can>
<Can I={SaleInvoiceAction.Delete} a={AbilitySubject.Invoice}> <Can I={SaleInvoiceAction.Delete} a={AbilitySubject.Invoice}>
<Button <Button

View File

@@ -86,19 +86,19 @@ function PaymentReceiveActionsBar({
<NavbarDivider /> <NavbarDivider />
</Can> </Can>
<Can I={PaymentReceiveAction.View} a={AbilitySubject.PaymentReceive}> <Can I={PaymentReceiveAction.View} a={AbilitySubject.PaymentReceive}>
<Button
className={Classes.MINIMAL}
text={'Send Mail'}
icon={<Icon icon="envelope" />}
onClick={handleMailPaymentReceive}
/>
<Button <Button
className={Classes.MINIMAL} className={Classes.MINIMAL}
icon={<Icon icon="print-16" />} icon={<Icon icon="print-16" />}
text={<T id={'print'} />} text={<T id={'print'} />}
onClick={handlePrintPaymentReceive} onClick={handlePrintPaymentReceive}
/> />
</Can> <NavbarDivider />
<Can I={PaymentReceiveAction.View} a={AbilitySubject.PaymentReceive}>
<Button
className={Classes.MINIMAL}
text={'Mail'}
onClick={handleMailPaymentReceive}
/>
</Can> </Can>
<Can I={PaymentReceiveAction.Delete} a={AbilitySubject.PaymentReceive}> <Can I={PaymentReceiveAction.Delete} a={AbilitySubject.PaymentReceive}>
<Button <Button

View File

@@ -63,7 +63,7 @@ function ReceiptDetailActionBar({
}; };
const handleReceiptMail = () => { const handleReceiptMail = () => {
openDialog(DialogsName.ReceiptMail, { receiptId }); openDialog(DialogsName.ReceiptMail, { receiptId });
} };
return ( return (
<DrawerActionsBar> <DrawerActionsBar>
@@ -78,6 +78,12 @@ function ReceiptDetailActionBar({
<NavbarDivider /> <NavbarDivider />
</Can> </Can>
<Can I={SaleReceiptAction.View} a={AbilitySubject.Receipt}> <Can I={SaleReceiptAction.View} a={AbilitySubject.Receipt}>
<Button
className={Classes.MINIMAL}
text={'Send Mail'}
icon={<Icon icon="envelope" />}
onClick={handleReceiptMail}
/>
<Button <Button
className={Classes.MINIMAL} className={Classes.MINIMAL}
icon={<Icon icon="print-16" />} icon={<Icon icon="print-16" />}
@@ -85,13 +91,6 @@ function ReceiptDetailActionBar({
onClick={safeCallback(onPrintReceipt)} onClick={safeCallback(onPrintReceipt)}
/> />
</Can> </Can>
<Can I={SaleReceiptAction.View} a={AbilitySubject.Receipt}>
<Button
className={Classes.MINIMAL}
text={'Mail'}
onClick={handleReceiptMail}
/>
</Can>
<Can I={SaleReceiptAction.Delete} a={AbilitySubject.Receipt}> <Can I={SaleReceiptAction.Delete} a={AbilitySubject.Receipt}>
<Button <Button
className={Classes.MINIMAL} className={Classes.MINIMAL}

View File

@@ -23,6 +23,7 @@ function EstimateMailDialog({
isOpen={isOpen} isOpen={isOpen}
canEscapeJeyClose={true} canEscapeJeyClose={true}
autoFocus={true} autoFocus={true}
style={{ width: 600 }}
> >
<DialogSuspense> <DialogSuspense>
<EstimateMailDialogContent <EstimateMailDialogContent
@@ -33,4 +34,5 @@ function EstimateMailDialog({
</Dialog> </Dialog>
); );
} }
export default compose(withDialogRedux())(EstimateMailDialog); export default compose(withDialogRedux())(EstimateMailDialog);

View File

@@ -26,6 +26,7 @@ function EstimateMailDialogBoot({
useSaleEstimateDefaultOptions(estimateId); useSaleEstimateDefaultOptions(estimateId);
const provider = { const provider = {
saleEstimateId: estimateId,
mailOptions, mailOptions,
isMailOptionsLoading, isMailOptionsLoading,
}; };

View File

@@ -1,8 +1,13 @@
// @ts-nocheck
import { Formik } from 'formik'; import { Formik } from 'formik';
import * as R from 'ramda';
import { castArray } from 'lodash';
import { useEstimateMailDialogBoot } from './EstimateMailDialogBoot'; import { useEstimateMailDialogBoot } from './EstimateMailDialogBoot';
import { transformToForm } from '@/utils'; import { transformToForm } from '@/utils';
import { SendMailNotificationForm } from '@/containers/SendMailNotification'; import { SendMailNotificationForm } from '@/containers/SendMailNotification';
import { castArray } from 'lodash'; import withDialogActions from '@/containers/Dialog/withDialogActions';
import { DialogsName } from '@/constants/dialogs';
import { useSendSaleEstimateMail } from '@/hooks/query';
const initialFormValues = { const initialFormValues = {
from: [], from: [],
@@ -11,8 +16,20 @@ const initialFormValues = {
message: '', message: '',
}; };
export function EstimateMailDialogForm() { interface EstimateMailFormValues {
const { mailOptions } = useEstimateMailDialogBoot(); from: string[];
to: string[];
subject: string;
message: string;
attachEstimate: boolean;
}
function EstimateMailDialogFormRoot(
// #withDialogClose
closeDialog,
) {
const { mutateAsync: sendEstimateMail } = useSendSaleEstimateMail();
const { mailOptions, saleEstimateId } = useEstimateMailDialogBoot();
const initialValues = { const initialValues = {
...initialFormValues, ...initialFormValues,
@@ -20,12 +37,29 @@ export function EstimateMailDialogForm() {
from: mailOptions.from ? castArray(mailOptions.from) : [], from: mailOptions.from ? castArray(mailOptions.from) : [],
to: mailOptions.to ? castArray(mailOptions.to) : [], to: mailOptions.to ? castArray(mailOptions.to) : [],
}; };
// Handle the form submitting.
const handleSubmit = (values: EstimateMailFormValues, { setSubmitting }) => {
setSubmitting(true);
sendEstimateMail([saleEstimateId, values])
.then(() => {
setSubmitting(false);
})
.catch((error) => {
setSubmitting(false);
});
};
const handleSubmit = () => {}; const handleClose = () => {
closeDialog(DialogsName.EstimateMail);
};
return ( return (
<Formik initialValues={initialValues} onSubmit={handleSubmit}> <Formik initialValues={initialValues} onSubmit={handleSubmit}>
<SendMailNotificationForm /> <SendMailNotificationForm onClose={handleClose} />
</Formik> </Formik>
); );
} }
export const EstimateMailDialogForm = R.compose(withDialogActions)(
EstimateMailDialogFormRoot,
);

View File

@@ -22,6 +22,7 @@ import { useEstimatesListContext } from './EstimatesListProvider';
import { useMemorizedColumnsWidths } from '@/hooks'; import { useMemorizedColumnsWidths } from '@/hooks';
import { compose } from '@/utils'; import { compose } from '@/utils';
import { DRAWERS } from '@/constants/drawers'; import { DRAWERS } from '@/constants/drawers';
import { DialogsName } from '@/constants/dialogs';
/** /**
* Estimates datatable. * Estimates datatable.
@@ -100,6 +101,11 @@ function EstimatesDataTable({
openDrawer(DRAWERS.ESTIMATE_DETAILS, { estimateId: cell.row.original.id }); openDrawer(DRAWERS.ESTIMATE_DETAILS, { estimateId: cell.row.original.id });
}; };
// Handle mail send estimate.
const handleMailSendEstimate = ({ id }) => {
openDialog(DialogsName.EstimateMail, { estimateId: id });
}
// Local storage memorizing columns widths. // Local storage memorizing columns widths.
const [initialColumnsWidths, , handleColumnResizing] = const [initialColumnsWidths, , handleColumnResizing] =
useMemorizedColumnsWidths(TABLES.ESTIMATES); useMemorizedColumnsWidths(TABLES.ESTIMATES);
@@ -153,6 +159,7 @@ function EstimatesDataTable({
onConvert: handleConvertToInvoice, onConvert: handleConvertToInvoice,
onViewDetails: handleViewDetailEstimate, onViewDetails: handleViewDetailEstimate,
onPrint: handlePrintEstimate, onPrint: handlePrintEstimate,
onSendMail: handleMailSendEstimate,
}} }}
/> />
</DashboardContentTable> </DashboardContentTable>

View File

@@ -64,6 +64,7 @@ export function ActionsMenu({
onConvert, onConvert,
onViewDetails, onViewDetails,
onPrint, onPrint,
onSendMail
}, },
}) { }) {
return ( return (
@@ -129,6 +130,11 @@ export function ActionsMenu({
</Choose> </Choose>
</Can> </Can>
<Can I={SaleEstimateAction.View} a={AbilitySubject.Estimate}> <Can I={SaleEstimateAction.View} a={AbilitySubject.Estimate}>
<MenuItem
icon={<Icon icon={'envelope'} iconSize={16} />}
text={'Send Mail'}
onClick={safeCallback(onSendMail, original)}
/>
<MenuItem <MenuItem
icon={<Icon icon={'print-16'} iconSize={16} />} icon={<Icon icon={'print-16'} iconSize={16} />}
text={intl.get('print')} text={intl.get('print')}

View File

@@ -23,6 +23,7 @@ function InvoiceMailDialog({
isOpen={isOpen} isOpen={isOpen}
canEscapeJeyClose={true} canEscapeJeyClose={true}
autoFocus={true} autoFocus={true}
style={{ width: 600 }}
> >
<DialogSuspense> <DialogSuspense>
<InvoiceMailDialogContent <InvoiceMailDialogContent

View File

@@ -26,6 +26,7 @@ function InvoiceMailDialogBoot({
useSaleInvoiceDefaultOptions(invoiceId); useSaleInvoiceDefaultOptions(invoiceId);
const provider = { const provider = {
saleInvoiceId: invoiceId,
mailOptions, mailOptions,
isMailOptionsLoading, isMailOptionsLoading,
}; };

View File

@@ -1,8 +1,13 @@
// @ts-nocheck
import { Formik } from 'formik'; import { Formik } from 'formik';
import { castArray } from 'lodash'; import { castArray } from 'lodash';
import * as R from 'ramda';
import { SendMailNotificationForm } from '@/containers/SendMailNotification'; import { SendMailNotificationForm } from '@/containers/SendMailNotification';
import { useInvoiceMailDialogBoot } from './InvoiceMailDialogBoot'; import { useInvoiceMailDialogBoot } from './InvoiceMailDialogBoot';
import { transformToForm } from '@/utils'; import { transformToForm } from '@/utils';
import withDialogActions from '@/containers/Dialog/withDialogActions';
import { DialogsName } from '@/constants/dialogs';
import { useSendSaleInvoiceMail } from '@/hooks/query';
const initialFormValues = { const initialFormValues = {
from: [], from: [],
@@ -10,8 +15,21 @@ const initialFormValues = {
subject: '', subject: '',
message: '', message: '',
}; };
export function InvoiceMailDialogForm() {
const { mailOptions } = useInvoiceMailDialogBoot(); interface InvoiceMailFormValues {
from: string[];
to: string[];
subject: string;
message: string;
attachInvoice: boolean;
}
function InvoiceMailDialogFormRoot({
// #withDialogActions
closeDialog,
}) {
const { mailOptions, saleInvoiceId } = useInvoiceMailDialogBoot();
const { mutateAsync: sendInvoiceMail } = useSendSaleInvoiceMail();
const initialValues = { const initialValues = {
...initialFormValues, ...initialFormValues,
@@ -19,11 +37,29 @@ export function InvoiceMailDialogForm() {
from: mailOptions.from ? castArray(mailOptions.from) : [], from: mailOptions.from ? castArray(mailOptions.from) : [],
to: mailOptions.to ? castArray(mailOptions.to) : [], to: mailOptions.to ? castArray(mailOptions.to) : [],
}; };
const handleSubmit = () => {}; // Handle the form submitting.
const handleSubmit = (values: InvoiceMailFormValues, { setSubmitting }) => {
setSubmitting(true);
sendInvoiceMail([saleInvoiceId, values])
.then(() => {
setSubmitting(false);
})
.catch(() => {
setSubmitting(false);
});
};
// Handle the close button click.
const handleClose = () => {
closeDialog(DialogsName.InvoiceMail);
};
return ( return (
<Formik initialValues={initialValues} onSubmit={handleSubmit}> <Formik initialValues={initialValues} onSubmit={handleSubmit}>
<SendMailNotificationForm /> <SendMailNotificationForm onClose={handleClose} />
</Formik> </Formik>
); );
} }
export const InvoiceMailDialogForm = R.compose(withDialogActions)(
InvoiceMailDialogFormRoot,
);

View File

@@ -26,6 +26,7 @@ import { useInvoicesListContext } from './InvoicesListProvider';
import { compose } from '@/utils'; import { compose } from '@/utils';
import { DRAWERS } from '@/constants/drawers'; import { DRAWERS } from '@/constants/drawers';
import { DialogsName } from '@/constants/dialogs';
/** /**
* Invoices datatable. * Invoices datatable.
@@ -98,6 +99,11 @@ function InvoicesDataTable({
openDialog('invoice-pdf-preview', { invoiceId: id }); openDialog('invoice-pdf-preview', { invoiceId: id });
}; };
// Handle send mail invoice.
const handleSendMailInvoice = ({ id }) => {
openDialog(DialogsName.InvoiceMail, { invoiceId: id });
};
// Handle cell click. // Handle cell click.
const handleCellClick = (cell, event) => { const handleCellClick = (cell, event) => {
openDrawer(DRAWERS.INVOICE_DETAILS, { invoiceId: cell.row.original.id }); openDrawer(DRAWERS.INVOICE_DETAILS, { invoiceId: cell.row.original.id });
@@ -157,6 +163,7 @@ function InvoicesDataTable({
onViewDetails: handleViewDetailInvoice, onViewDetails: handleViewDetailInvoice,
onPrint: handlePrintInvoice, onPrint: handlePrintInvoice,
onConvert: handleConvertToCreitNote, onConvert: handleConvertToCreitNote,
onSendMail: handleSendMailInvoice
}} }}
/> />
</DashboardContentTable> </DashboardContentTable>

View File

@@ -128,6 +128,7 @@ export function ActionsMenu({
onQuick, onQuick,
onViewDetails, onViewDetails,
onPrint, onPrint,
onSendMail
}, },
row: { original }, row: { original },
}) { }) {
@@ -150,7 +151,6 @@ export function ActionsMenu({
text={intl.get('invoice.convert_to_credit_note')} text={intl.get('invoice.convert_to_credit_note')}
onClick={safeCallback(onConvert, original)} onClick={safeCallback(onConvert, original)}
/> />
<If condition={!original.is_delivered}> <If condition={!original.is_delivered}>
<MenuItem <MenuItem
icon={<Icon icon="send" iconSize={16} />} icon={<Icon icon="send" iconSize={16} />}
@@ -169,6 +169,11 @@ export function ActionsMenu({
</If> </If>
</Can> </Can>
<Can I={SaleInvoiceAction.View} a={AbilitySubject.Invoice}> <Can I={SaleInvoiceAction.View} a={AbilitySubject.Invoice}>
<MenuItem
icon={<Icon icon={'envelope'} iconSize={16} />}
text={'Send Mail'}
onClick={safeCallback(onSendMail, original)}
/>
<MenuItem <MenuItem
icon={<Icon icon={'print-16'} iconSize={16} />} icon={<Icon icon={'print-16'} iconSize={16} />}
text={intl.get('print')} text={intl.get('print')}

View File

@@ -23,6 +23,7 @@ function PaymentMailDialog({
isOpen={isOpen} isOpen={isOpen}
canEscapeJeyClose={true} canEscapeJeyClose={true}
autoFocus={true} autoFocus={true}
style={{ width: 600 }}
> >
<DialogSuspense> <DialogSuspense>
<PaymentMailDialogContent <PaymentMailDialogContent

View File

@@ -1,8 +1,13 @@
import { Formik } from 'formik'; // @ts-nocheck
import { Formik, FormikBag } from 'formik';
import { castArray } from 'lodash'; import { castArray } from 'lodash';
import * as R from 'ramda';
import { SendMailNotificationForm } from '@/containers/SendMailNotification'; import { SendMailNotificationForm } from '@/containers/SendMailNotification';
import { usePaymentMailDialogBoot } from './PaymentMailDialogBoot'; import { usePaymentMailDialogBoot } from './PaymentMailDialogBoot';
import { transformToForm } from '@/utils'; import { transformToForm } from '@/utils';
import withDialogActions from '@/containers/Dialog/withDialogActions';
import { DialogsName } from '@/constants/dialogs';
import { useSendPaymentReceiveMail } from '@/hooks/query';
const initialFormValues = { const initialFormValues = {
from: [], from: [],
@@ -10,8 +15,20 @@ const initialFormValues = {
subject: '', subject: '',
message: '', message: '',
}; };
export function PaymentMailDialogForm() {
const { mailOptions } = usePaymentMailDialogBoot(); interface PaymentMailFormValue {
from: string[];
to: string[];
subject: string;
message: string;
}
export function PaymentMailDialogFormRoot({
// #withDialogActions
closeDialog,
}) {
const { mailOptions, paymentId } = usePaymentMailDialogBoot();
const { mutateAsync: sendPaymentMail } = useSendPaymentReceiveMail();
const initialValues = { const initialValues = {
...initialFormValues, ...initialFormValues,
@@ -19,11 +36,32 @@ export function PaymentMailDialogForm() {
from: mailOptions.from ? castArray(mailOptions.from) : [], from: mailOptions.from ? castArray(mailOptions.from) : [],
to: mailOptions.to ? castArray(mailOptions.to) : [], to: mailOptions.to ? castArray(mailOptions.to) : [],
}; };
const handleSubmit = () => {}; // Handles the form submitting.
const handleSubmit = (
values: PaymentMailFormValue,
{ setSubmitting }: FormikBag<PaymentMailFormValue>,
) => {
setSubmitting(true);
sendPaymentMail([paymentId, values])
.then(() => {
setSubmitting(false);
})
.catch((error) => {
setSubmitting(false);
});
};
const handleClose = () => {
closeDialog(DialogsName.PaymentMail);
};
return ( return (
<Formik initialValues={initialValues} onSubmit={handleSubmit}> <Formik initialValues={initialValues} onSubmit={handleSubmit}>
<SendMailNotificationForm /> <SendMailNotificationForm onClose={handleClose} />
</Formik> </Formik>
); );
} }
export const PaymentMailDialogForm = R.compose(withDialogActions)(
PaymentMailDialogFormRoot,
);

View File

@@ -17,12 +17,14 @@ import withPaymentReceives from './withPaymentReceives';
import withPaymentReceivesActions from './withPaymentReceivesActions'; import withPaymentReceivesActions from './withPaymentReceivesActions';
import withAlertsActions from '@/containers/Alert/withAlertActions'; import withAlertsActions from '@/containers/Alert/withAlertActions';
import withDrawerActions from '@/containers/Drawer/withDrawerActions'; import withDrawerActions from '@/containers/Drawer/withDrawerActions';
import withDialogActions from '@/containers/Dialog/withDialogActions';
import withSettings from '@/containers/Settings/withSettings'; import withSettings from '@/containers/Settings/withSettings';
import { usePaymentReceivesColumns, ActionsMenu } from './components'; import { usePaymentReceivesColumns, ActionsMenu } from './components';
import { usePaymentReceivesListContext } from './PaymentReceiptsListProvider'; import { usePaymentReceivesListContext } from './PaymentReceiptsListProvider';
import { useMemorizedColumnsWidths } from '@/hooks'; import { useMemorizedColumnsWidths } from '@/hooks';
import { DRAWERS } from '@/constants/drawers'; import { DRAWERS } from '@/constants/drawers';
import { DialogsName } from '@/constants/dialogs';
/** /**
* Payment receives datatable. * Payment receives datatable.
@@ -31,15 +33,15 @@ function PaymentReceivesDataTable({
// #withPaymentReceivesActions // #withPaymentReceivesActions
setPaymentReceivesTableState, setPaymentReceivesTableState,
// #withPaymentReceives
paymentReceivesTableState,
// #withAlertsActions // #withAlertsActions
openAlert, openAlert,
// #withDrawerActions // #withDrawerActions
openDrawer, openDrawer,
// #withDialogActions
openDialog,
// #withSettings // #withSettings
paymentReceivesTableSize, paymentReceivesTableSize,
}) { }) {
@@ -73,6 +75,11 @@ function PaymentReceivesDataTable({
openDrawer(DRAWERS.PAYMENT_RECEIVE_DETAILS, { paymentReceiveId: id }); openDrawer(DRAWERS.PAYMENT_RECEIVE_DETAILS, { paymentReceiveId: id });
}; };
// Handle mail send payment receive.
const handleSendMailPayment = ({ id }) => {
openDialog(DialogsName.PaymentMail, { paymentReceiveId: id });
};
// Handle cell click. // Handle cell click.
const handleCellClick = (cell, event) => { const handleCellClick = (cell, event) => {
openDrawer(DRAWERS.PAYMENT_RECEIVE_DETAILS, { openDrawer(DRAWERS.PAYMENT_RECEIVE_DETAILS, {
@@ -129,6 +136,7 @@ function PaymentReceivesDataTable({
onDelete: handleDeletePaymentReceive, onDelete: handleDeletePaymentReceive,
onEdit: handleEditPaymentReceive, onEdit: handleEditPaymentReceive,
onViewDetails: handleViewDetailPaymentReceive, onViewDetails: handleViewDetailPaymentReceive,
onSendMail: handleSendMailPayment,
}} }}
/> />
</DashboardContentTable> </DashboardContentTable>
@@ -139,6 +147,7 @@ export default compose(
withPaymentReceivesActions, withPaymentReceivesActions,
withAlertsActions, withAlertsActions,
withDrawerActions, withDrawerActions,
withDialogActions,
withPaymentReceives(({ paymentReceivesTableState }) => ({ withPaymentReceives(({ paymentReceivesTableState }) => ({
paymentReceivesTableState, paymentReceivesTableState,
})), })),

View File

@@ -15,14 +15,17 @@ import {
import { FormatDateCell, Money, Icon, Can } from '@/components'; import { FormatDateCell, Money, Icon, Can } from '@/components';
import { safeCallback } from '@/utils'; import { safeCallback } from '@/utils';
import { CLASSES } from '@/constants/classes'; import { CLASSES } from '@/constants/classes';
import { PaymentReceiveAction, AbilitySubject } from '@/constants/abilityOption'; import {
PaymentReceiveAction,
AbilitySubject,
} from '@/constants/abilityOption';
/** /**
* Table actions menu. * Table actions menu.
*/ */
export function ActionsMenu({ export function ActionsMenu({
row: { original: paymentReceive }, row: { original: paymentReceive },
payload: { onEdit, onDelete, onViewDetails }, payload: { onEdit, onDelete, onViewDetails, onSendMail },
}) { }) {
return ( return (
<Menu> <Menu>
@@ -31,6 +34,11 @@ export function ActionsMenu({
text={intl.get('view_details')} text={intl.get('view_details')}
onClick={safeCallback(onViewDetails, paymentReceive)} onClick={safeCallback(onViewDetails, paymentReceive)}
/> />
<MenuItem
icon={<Icon icon={'envelope'} iconSize={16} />}
text={'Send Mail'}
onClick={safeCallback(onSendMail, paymentReceive)}
/>
<Can I={PaymentReceiveAction.Edit} a={AbilitySubject.PaymentReceive}> <Can I={PaymentReceiveAction.Edit} a={AbilitySubject.PaymentReceive}>
<MenuDivider /> <MenuDivider />
<MenuItem <MenuItem

View File

@@ -23,6 +23,7 @@ function ReceiptMailDialog({
isOpen={isOpen} isOpen={isOpen}
canEscapeJeyClose={true} canEscapeJeyClose={true}
autoFocus={true} autoFocus={true}
style={{ width: 600 }}
> >
<DialogSuspense> <DialogSuspense>
<ReceiptMailDialogContent <ReceiptMailDialogContent

View File

@@ -27,6 +27,7 @@ function ReceiptMailDialogBoot({
useSaleReceiptDefaultOptions(receiptId); useSaleReceiptDefaultOptions(receiptId);
const provider = { const provider = {
saleReceiptId: receiptId,
mailOptions, mailOptions,
isMailOptionsLoading, isMailOptionsLoading,
}; };

View File

@@ -1,8 +1,13 @@
import { Formik } from 'formik'; // @ts-nocheck
import { Formik, FormikBag } from 'formik';
import { castArray } from 'lodash'; import { castArray } from 'lodash';
import * as R from 'ramda';
import { SendMailNotificationForm } from '@/containers/SendMailNotification'; import { SendMailNotificationForm } from '@/containers/SendMailNotification';
import { useReceiptMailDialogBoot } from './ReceiptMailDialogBoot'; import { useReceiptMailDialogBoot } from './ReceiptMailDialogBoot';
import { transformToForm } from '@/utils'; import { transformToForm } from '@/utils';
import withDialogActions from '@/containers/Dialog/withDialogActions';
import { DialogsName } from '@/constants/dialogs';
import { useSendSaleReceiptMail } from '@/hooks/query';
const initialFormValues = { const initialFormValues = {
from: [], from: [],
@@ -10,8 +15,16 @@ const initialFormValues = {
subject: '', subject: '',
message: '', message: '',
}; };
export function ReceiptMailDialogForm() { interface ReceiptMailFormValues {
const { mailOptions } = useReceiptMailDialogBoot(); from: string[];
to: string[];
subject: string;
message: string;
}
function ReceiptMailDialogFormRoot({ closeDialog }) {
const { mailOptions, saleReceiptId } = useReceiptMailDialogBoot();
const { mutateAsync: sendReceiptMail } = useSendSaleReceiptMail();
const initialValues = { const initialValues = {
...initialFormValues, ...initialFormValues,
@@ -19,11 +32,31 @@ export function ReceiptMailDialogForm() {
from: mailOptions.from ? castArray(mailOptions.from) : [], from: mailOptions.from ? castArray(mailOptions.from) : [],
to: mailOptions.to ? castArray(mailOptions.to) : [], to: mailOptions.to ? castArray(mailOptions.to) : [],
}; };
const handleSubmit = () => {}; const handleSubmit = (
values: ReceiptMailFormValues,
{ setSubmitting }: FormikBag<ReceiptMailFormValues>,
) => {
setSubmitting(true);
sendReceiptMail([saleReceiptId, values])
.then(() => {
setSubmitting(false);
})
.catch((error) => {
setSubmitting(false);
});
};
const handleClose = () => {
closeDialog(DialogsName.ReceiptMail);
};
return ( return (
<Formik initialValues={initialValues} onSubmit={handleSubmit}> <Formik initialValues={initialValues} onSubmit={handleSubmit}>
<SendMailNotificationForm /> <SendMailNotificationForm onClose={handleClose} />
</Formik> </Formik>
); );
} }
export const ReceiptMailDialogForm = R.compose(withDialogActions)(
ReceiptMailDialogFormRoot,
);

View File

@@ -140,7 +140,6 @@ function ReceiptActionsBar({
icon={<Icon icon={'file-export-16'} iconSize={'16'} />} icon={<Icon icon={'file-export-16'} iconSize={'16'} />}
text={<T id={'export'} />} text={<T id={'export'} />}
/> />
<NavbarDivider /> <NavbarDivider />
<DashboardRowsHeightButton <DashboardRowsHeightButton
initialValue={receiptsTableSize} initialValue={receiptsTableSize}

View File

@@ -24,6 +24,7 @@ import { useReceiptsListContext } from './ReceiptsListProvider';
import { useReceiptsTableColumns, ActionsMenu } from './components'; import { useReceiptsTableColumns, ActionsMenu } from './components';
import { useMemorizedColumnsWidths } from '@/hooks'; import { useMemorizedColumnsWidths } from '@/hooks';
import { DRAWERS } from '@/constants/drawers'; import { DRAWERS } from '@/constants/drawers';
import { DialogsName } from '@/constants/dialogs';
/** /**
* Sale receipts datatable. * Sale receipts datatable.
@@ -86,6 +87,11 @@ function ReceiptsDataTable({
openDialog('receipt-pdf-preview', { receiptId: id }); openDialog('receipt-pdf-preview', { receiptId: id });
}; };
// Handle send mail receipt.
const handleSendMailReceipt = ({ id }) => {
openDialog(DialogsName.ReceiptMail, { receiptId: id });
};
// Local storage memorizing columns widths. // Local storage memorizing columns widths.
const [initialColumnsWidths, , handleColumnResizing] = const [initialColumnsWidths, , handleColumnResizing] =
useMemorizedColumnsWidths(TABLES.RECEIPTS); useMemorizedColumnsWidths(TABLES.RECEIPTS);
@@ -141,6 +147,7 @@ function ReceiptsDataTable({
onClose: handleCloseReceipt, onClose: handleCloseReceipt,
onViewDetails: handleViewDetailReceipt, onViewDetails: handleViewDetailReceipt,
onPrint: handlePrintInvoice, onPrint: handlePrintInvoice,
onSendMail: handleSendMailReceipt,
}} }}
/> />
</DashboardContentTable> </DashboardContentTable>

View File

@@ -24,7 +24,7 @@ import { SaleReceiptAction, AbilitySubject } from '@/constants/abilityOption';
* @returns {React.JSX} * @returns {React.JSX}
*/ */
export function ActionsMenu({ export function ActionsMenu({
payload: { onEdit, onDelete, onClose, onDrawer, onViewDetails, onPrint }, payload: { onEdit, onDelete, onClose, onSendMail, onViewDetails, onPrint },
row: { original: receipt }, row: { original: receipt },
}) { }) {
return ( return (
@@ -51,6 +51,11 @@ export function ActionsMenu({
</If> </If>
</Can> </Can>
<Can I={SaleReceiptAction.View} a={AbilitySubject.Receipt}> <Can I={SaleReceiptAction.View} a={AbilitySubject.Receipt}>
<MenuItem
icon={<Icon icon={'envelope'} iconSize={16} />}
text={'Send Mail'}
onClick={safeCallback(onSendMail, receipt)}
/>
<MenuItem <MenuItem
icon={<Icon icon={'print-16'} iconSize={16} />} icon={<Icon icon={'print-16'} iconSize={16} />}
text={intl.get('print')} text={intl.get('print')}

View File

@@ -1,36 +1,118 @@
// @ts-nocheck // @ts-nocheck
import { Form } from 'formik'; import { Form, useFormikContext } from 'formik';
import { FFormGroup, FInputGroup, FMultiSelect } from '@/components'; import { FFormGroup, FInputGroup, FMultiSelect } from '@/components';
import styled from 'styled-components';
import { Button, Classes, Intent } from '@blueprintjs/core';
import { saveInvoke } from '@/utils';
interface SendMailNotificationFormProps {
onClose?: () => void;
}
export function SendMailNotificationForm({
onClose,
}: SendMailNotificationFormProps) {
const { isSubmitting } = useFormikContext();
const handleClose = () => {
saveInvoke(onClose);
};
export function SendMailNotificationForm() {
return ( return (
<Form> <Form>
<FFormGroup label={'From'} name={'from'} inline={true} fastField={true}> <div className={Classes.DIALOG_BODY}>
<FMultiSelect <HeaderBox>
items={[]} <FFormGroup
name={'from'} label={'From'}
placeholder="" name={'from'}
popoverProps={{ minimal: true }} inline={true}
/> fastField={true}
</FFormGroup> >
<FMultiSelect
items={[]}
name={'from'}
placeholder=""
popoverProps={{ minimal: true, fill: true }}
fill={true}
/>
</FFormGroup>
<FFormGroup label={'To'} name={'to'} inline={true} fastField={true}> <FFormGroup label={'To'} name={'to'} inline={true} fastField={true}>
<FMultiSelect <FMultiSelect
items={[]} items={[]}
name={'to'} name={'to'}
placeholder="" placeholder=""
popoverProps={{ minimal: true }} popoverProps={{ minimal: true, fill: true }}
/> fill={true}
</FFormGroup> />
</FFormGroup>
<FFormGroup <FFormGroup
label={'Subject'} label={'Subject'}
name={'subject'} name={'subject'}
inline={true} inline={true}
fastField={true} fastField={true}
> >
<FInputGroup name={'subject'} /> <FInputGroup name={'subject'} fill={true} />
</FFormGroup> </FFormGroup>
</HeaderBox>
</div>
<div className={Classes.DIALOG_FOOTER}>
<div className={Classes.DIALOG_FOOTER_ACTIONS}>
<Button
disabled={isSubmitting}
onClick={handleClose}
style={{ minWidth: '65px' }}
>
Close
</Button>
<Button
intent={Intent.PRIMARY}
loading={isSubmitting}
style={{ minWidth: '75px' }}
type="submit"
>
Send
</Button>
</div>
</div>
</Form> </Form>
); );
} }
const HeaderBox = styled('div')`
border-top-right-radius: 5px;
border-top-left-radius: 5px;
border: 1px solid #dddfe9;
padding: 15px;
.bp4-form-group {
margin: 0;
padding-top: 12px;
padding-bottom: 12px;
&:not(:last-of-type) {
border-bottom: 1px solid #dddfe9;
}
&:first-of-type {
padding-top: 0;
}
&:last-of-type {
padding-bottom: 0;
}
}
.bp4-form-content {
flex: 1 0;
}
.bp4-label {
min-width: 65px;
color: #738091;
}
.bp4-input {
}
`;

View File

@@ -561,8 +561,14 @@ export default {
}, },
'content-copy': { 'content-copy': {
path: [ path: [
'M15 0H5c-.55 0-1 .45-1 1v2h2V2h8v7h-1v2h2c.55 0 1-.45 1-1V1c0-.55-.45-1-1-1zm-4 4H1c-.55 0-1 .45-1 1v10c0 .55.45 1 1 1h10c.55 0 1-.45 1-1V5c0-.55-.45-1-1-1zm-1 10H2V6h8v8z' 'M15 0H5c-.55 0-1 .45-1 1v2h2V2h8v7h-1v2h2c.55 0 1-.45 1-1V1c0-.55-.45-1-1-1zm-4 4H1c-.55 0-1 .45-1 1v10c0 .55.45 1 1 1h10c.55 0 1-.45 1-1V5c0-.55-.45-1-1-1zm-1 10H2V6h8v8z',
], ],
viewBox: '0 0 16 16' viewBox: '0 0 16 16',
} },
envelope: {
path: [
'M0 4.01v11.91l6.27-6.27L0 4.01zm18.91-1.03H1.09L10 10.97l8.91-7.99zm-5.18 6.66L20 15.92V4.01l-6.27 5.63zm-3.23 2.9c-.13.12-.31.19-.5.19s-.37-.07-.5-.19l-2.11-1.89-6.33 6.33h17.88l-6.33-6.33-2.11 1.89z',
],
viewBox: '0 0 20 20',
},
}; };