From 7df316aa5674b29266f7ca11b2c578688b2fa86a Mon Sep 17 00:00:00 2001 From: Ahmed Bouhuolia Date: Mon, 18 Nov 2024 15:15:03 +0200 Subject: [PATCH] feat: wip send estimate mail preview --- .../api/controllers/Sales/SalesEstimates.ts | 14 +-- packages/server/src/models/SaleEstimate.ts | 13 +++ .../Estimates/GetSaleEstimateMailState.ts | 51 +++++++++ .../GetSaleEstimateMailStateTransformer.ts | 105 ++++++++++++++++++ .../Estimates/SaleEstimatesApplication.ts | 20 ++++ .../src/components/DialogsContainer.tsx | 2 - .../src/components/DrawersContainer.tsx | 2 + packages/webapp/src/constants/drawers.ts | 3 +- .../Dialogs/EstimateFormMailDeliverDialog.tsx | 39 ------- .../EstimateFormMailDeliverDialogContent.tsx | 40 ------- .../EstimateForm/EstimateFormDialogs.tsx | 6 - .../EstimateMailDialog/EstimateMailDialog.tsx | 35 ------ .../EstimateMailDialogBody.tsx | 33 ------ .../EstimateMailDialogBoot.tsx | 48 -------- .../EstimateMailDialogContent.tsx | 22 ---- .../EstimateMailDialogForm.tsx | 81 -------------- .../EstimateMailDialogFormContent.tsx | 66 ----------- .../Estimates/EstimateMailDialog/index.ts | 1 - .../EstimateSendMailBoot.tsx | 7 +- .../EstimateSendMailContent.tsx | 25 ++++- .../EstimateSendMailDrawer.tsx | 6 +- .../EstimateSendMailForm.tsx | 28 ++--- .../EstimateSendMailPreview.tsx | 7 ++ .../EstimateSnedMailFields.tsx | 74 ++++++++++++ .../Estimates/EstimateSendMailDrawer/index.ts | 1 + .../EstimatesLanding/EstimatesDataTable.tsx | 2 +- .../SendMailViewDrawer/SendMailViewLayout.tsx | 37 ++++++ .../SendMailViewMessageField.tsx | 3 +- .../SendMailViewPreview.tsx | 9 ++ .../SendMailViewPreviewTabs.tsx | 17 ++- .../SendMailViewToAddressField.tsx | 1 + .../InvoiceSendMailContent.tsx | 14 +-- .../InvoiceSendMailFields.tsx | 27 +---- packages/webapp/src/hooks/query/estimates.tsx | 25 +++-- 34 files changed, 405 insertions(+), 459 deletions(-) create mode 100644 packages/server/src/services/Sales/Estimates/GetSaleEstimateMailState.ts create mode 100644 packages/server/src/services/Sales/Estimates/GetSaleEstimateMailStateTransformer.ts delete mode 100644 packages/webapp/src/containers/Sales/Estimates/EstimateForm/Dialogs/EstimateFormMailDeliverDialog.tsx delete mode 100644 packages/webapp/src/containers/Sales/Estimates/EstimateForm/Dialogs/EstimateFormMailDeliverDialogContent.tsx delete mode 100644 packages/webapp/src/containers/Sales/Estimates/EstimateMailDialog/EstimateMailDialog.tsx delete mode 100644 packages/webapp/src/containers/Sales/Estimates/EstimateMailDialog/EstimateMailDialogBody.tsx delete mode 100644 packages/webapp/src/containers/Sales/Estimates/EstimateMailDialog/EstimateMailDialogBoot.tsx delete mode 100644 packages/webapp/src/containers/Sales/Estimates/EstimateMailDialog/EstimateMailDialogContent.tsx delete mode 100644 packages/webapp/src/containers/Sales/Estimates/EstimateMailDialog/EstimateMailDialogForm.tsx delete mode 100644 packages/webapp/src/containers/Sales/Estimates/EstimateMailDialog/EstimateMailDialogFormContent.tsx delete mode 100644 packages/webapp/src/containers/Sales/Estimates/EstimateMailDialog/index.ts create mode 100644 packages/webapp/src/containers/Sales/Estimates/EstimateSendMailDrawer/EstimateSendMailPreview.tsx create mode 100644 packages/webapp/src/containers/Sales/Estimates/EstimateSendMailDrawer/EstimateSnedMailFields.tsx create mode 100644 packages/webapp/src/containers/Sales/Estimates/SendMailViewDrawer/SendMailViewLayout.tsx diff --git a/packages/server/src/api/controllers/Sales/SalesEstimates.ts b/packages/server/src/api/controllers/Sales/SalesEstimates.ts index 68f7e593e..961b20fc1 100644 --- a/packages/server/src/api/controllers/Sales/SalesEstimates.ts +++ b/packages/server/src/api/controllers/Sales/SalesEstimates.ts @@ -140,10 +140,10 @@ export default class SalesEstimatesController extends BaseController { this.handleServiceErrors ); router.get( - '/:id/mail', + '/:id/mail/state', [...this.validateSpecificEstimateSchema], this.validationResult, - asyncMiddleware(this.getSaleEstimateMail.bind(this)), + asyncMiddleware(this.getSaleEstimateMailState.bind(this)), this.handleServiceErrors ); return router; @@ -540,18 +540,18 @@ export default class SalesEstimatesController extends BaseController { * @param {Response} res * @param {NextFunction} next */ - private getSaleEstimateMail = async ( - req: Request, + private getSaleEstimateMailState = async ( + req: Request<{ id: number }>, res: Response, next: NextFunction ) => { const { tenantId } = req; - const { id: invoiceId } = req.params; + const { id: estimateId } = req.params; try { - const data = await this.saleEstimatesApplication.getSaleEstimateMail( + const data = await this.saleEstimatesApplication.getSaleEstimateMailState( tenantId, - invoiceId + estimateId ); return res.status(200).send({ data }); } catch (error) { diff --git a/packages/server/src/models/SaleEstimate.ts b/packages/server/src/models/SaleEstimate.ts index 47ecebce5..8fd31e2d4 100644 --- a/packages/server/src/models/SaleEstimate.ts +++ b/packages/server/src/models/SaleEstimate.ts @@ -184,6 +184,7 @@ export default class SaleEstimate extends mixin(TenantModel, [ const Branch = require('models/Branch'); const Warehouse = require('models/Warehouse'); const Document = require('models/Document'); + const { PdfTemplate } = require('models/PdfTemplate'); return { customer: { @@ -252,6 +253,18 @@ export default class SaleEstimate extends mixin(TenantModel, [ query.where('model_ref', 'SaleEstimate'); }, }, + + /** + * Sale estimate may belongs to pdf branding template. + */ + pdfTemplate: { + relation: Model.BelongsToOneRelation, + modelClass: PdfTemplate, + join: { + from: 'sales_estimates.pdfTemplateId', + to: 'pdf_templates.id', + }, + }, }; } diff --git a/packages/server/src/services/Sales/Estimates/GetSaleEstimateMailState.ts b/packages/server/src/services/Sales/Estimates/GetSaleEstimateMailState.ts new file mode 100644 index 000000000..282e1a542 --- /dev/null +++ b/packages/server/src/services/Sales/Estimates/GetSaleEstimateMailState.ts @@ -0,0 +1,51 @@ +import { Inject } from 'typedi'; +import { SendSaleEstimateMail } from './SendSaleEstimateMail'; +import HasTenancyService from '@/services/Tenancy/TenancyService'; +import { GetSaleEstimateMailStateTransformer } from './GetSaleEstimateMailStateTransformer'; +import { TransformerInjectable } from '@/lib/Transformer/TransformerInjectable'; + +export class GetSaleEstimateMailState { + @Inject() + private estimateMail: SendSaleEstimateMail; + + @Inject() + private tenancy: HasTenancyService; + + @Inject() + private transformer: TransformerInjectable; + + /** + * Retrieves the estimate mail state of the given sale estimate. + * Estimate mail state includes the mail options, branding attributes and the estimate details. + * @param {number} tenantId + * @param {number} saleEstimateId + * @returns {Promise} + */ + async getEstimateMailState( + tenantId: number, + saleEstimateId: number + ): Promise { + const { SaleEstimate } = this.tenancy.models(tenantId); + + const saleEstimate = await SaleEstimate.query() + .findById(saleEstimateId) + .withGraphFetched('customer') + .withGraphFetched('entries.item') + .withGraphFetched('pdfTemplate') + .throwIfNotFound(); + + const mailOptions = await this.estimateMail.getMailOptions( + tenantId, + saleEstimateId + ); + const transformed = await this.transformer.transform( + tenantId, + saleEstimate, + new GetSaleEstimateMailStateTransformer(), + { + mailOptions, + } + ); + return transformed; + } +} diff --git a/packages/server/src/services/Sales/Estimates/GetSaleEstimateMailStateTransformer.ts b/packages/server/src/services/Sales/Estimates/GetSaleEstimateMailStateTransformer.ts new file mode 100644 index 000000000..885f02a7c --- /dev/null +++ b/packages/server/src/services/Sales/Estimates/GetSaleEstimateMailStateTransformer.ts @@ -0,0 +1,105 @@ +import { ItemEntryTransformer } from '../Invoices/ItemEntryTransformer'; +import { SaleEstimateTransfromer } from './SaleEstimateTransformer'; + +export class GetSaleEstimateMailStateTransformer extends SaleEstimateTransfromer { + public excludeAttributes = (): string[] => { + return ['*']; + }; + + public includeAttributes = (): string[] => { + return [ + 'estimateDate', + 'formattedEstimateDate', + + 'total', + 'totalFormatted', + + 'subtotal', + 'subtotalFormatted', + + 'estimateNo', + + 'entries', + + 'companyName', + 'companyLogoUri', + + 'primaryColor', + 'customerName', + ]; + }; + + /** + * Retrieves the customer name of the invoice. + * @returns {string} + */ + protected customerName = (invoice) => { + return invoice.customer.displayName; + }; + + /** + * Retrieves the company name. + * @returns {string} + */ + protected companyName = () => { + return this.context.organization.name; + }; + + /** + * Retrieves the company logo uri. + * @returns {string | null} + */ + protected companyLogoUri = (invoice) => { + return invoice.pdfTemplate?.companyLogoUri; + }; + + /** + * Retrieves the primary color. + * @returns {string} + */ + protected primaryColor = (invoice) => { + return invoice.pdfTemplate?.attributes?.primaryColor; + }; + + /** + * + * @param invoice + * @returns + */ + protected entries = (invoice) => { + return this.item( + invoice.entries, + new GetSaleEstimateMailStateEntryTransformer(), + { + currencyCode: invoice.currencyCode, + } + ); + }; + + /** + * Merges the mail options with the invoice object. + */ + public transform = (object: any) => { + return { + ...this.options.mailOptions, + ...object, + }; + }; +} + +class GetSaleEstimateMailStateEntryTransformer extends ItemEntryTransformer { + public excludeAttributes = (): string[] => { + return ['*']; + }; + + public includeAttributes = (): string[] => { + return [ + 'description', + 'quantity', + 'unitPrice', + 'unitPriceFormatted', + 'total', + 'totalFormatted', + ]; + }; +} diff --git a/packages/server/src/services/Sales/Estimates/SaleEstimatesApplication.ts b/packages/server/src/services/Sales/Estimates/SaleEstimatesApplication.ts index 233d40eac..ec7c6afa8 100644 --- a/packages/server/src/services/Sales/Estimates/SaleEstimatesApplication.ts +++ b/packages/server/src/services/Sales/Estimates/SaleEstimatesApplication.ts @@ -21,6 +21,7 @@ import { SaleEstimateNotifyBySms } from './SaleEstimateSmsNotify'; import { SaleEstimatesPdf } from './SaleEstimatesPdf'; import { SendSaleEstimateMail } from './SendSaleEstimateMail'; import { GetSaleEstimateState } from './GetSaleEstimateState'; +import { GetSaleEstimateMailState } from './GetSaleEstimateMailState'; @Service() export class SaleEstimatesApplication { @@ -57,6 +58,9 @@ export class SaleEstimatesApplication { @Inject() private sendEstimateMailService: SendSaleEstimateMail; + @Inject() + private getSaleEstimateMailStateService: GetSaleEstimateMailState; + @Inject() private getSaleEstimateStateService: GetSaleEstimateState; @@ -250,6 +254,22 @@ export class SaleEstimatesApplication { ); } + /** + * Retrieves the sale estimate mail state. + * @param {number} tenantId + * @param {number} saleEstimateId + * @returns {Promise} + */ + public getSaleEstimateMailState( + tenantId: number, + saleEstimateId: number + ): Promise { + return this.getSaleEstimateMailStateService.getEstimateMailState( + tenantId, + saleEstimateId + ); + } + /** * Retrieves the default mail options of the given sale estimate. * @param {number} tenantId diff --git a/packages/webapp/src/components/DialogsContainer.tsx b/packages/webapp/src/components/DialogsContainer.tsx index 3533cc6bc..8871f8adb 100644 --- a/packages/webapp/src/components/DialogsContainer.tsx +++ b/packages/webapp/src/components/DialogsContainer.tsx @@ -45,7 +45,6 @@ import ProjectBillableEntriesFormDialog from '@/containers/Projects/containers/P import TaxRateFormDialog from '@/containers/TaxRates/dialogs/TaxRateFormDialog/TaxRateFormDialog'; import { DialogsName } from '@/constants/dialogs'; import InvoiceExchangeRateChangeDialog from '@/containers/Sales/Invoices/InvoiceForm/Dialogs/InvoiceExchangeRateChangeDialog'; -import EstimateMailDialog from '@/containers/Sales/Estimates/EstimateMailDialog/EstimateMailDialog'; import ReceiptMailDialog from '@/containers/Sales/Receipts/ReceiptMailDialog/ReceiptMailDialog'; import PaymentMailDialog from '@/containers/Sales/PaymentsReceived/PaymentMailDialog/PaymentMailDialog'; import { ExportDialog } from '@/containers/Dialogs/ExportDialog'; @@ -143,7 +142,6 @@ export default function DialogsContainer() { - diff --git a/packages/webapp/src/components/DrawersContainer.tsx b/packages/webapp/src/components/DrawersContainer.tsx index 448f6e676..9d21008d1 100644 --- a/packages/webapp/src/components/DrawersContainer.tsx +++ b/packages/webapp/src/components/DrawersContainer.tsx @@ -32,6 +32,7 @@ import { BrandingTemplatesDrawer } from '@/containers/BrandingTemplates/Branding import { DRAWERS } from '@/constants/drawers'; import { InvoiceSendMailDrawer } from '@/containers/Sales/Invoices/InvoiceSendMailDrawer/InvoiceSendMailDrawer'; +import { EstimateSendMailDrawer } from '@/containers/Sales/Estimates/EstimateSendMailDrawer'; /** * Drawers container of the dashboard. @@ -81,6 +82,7 @@ export default function DrawersContainer() { /> + ); } diff --git a/packages/webapp/src/constants/drawers.ts b/packages/webapp/src/constants/drawers.ts index cd1d064d4..a461c7e83 100644 --- a/packages/webapp/src/constants/drawers.ts +++ b/packages/webapp/src/constants/drawers.ts @@ -34,5 +34,6 @@ export enum DRAWERS { BRANDING_TEMPLATES = 'BRANDING_TEMPLATES', PAYMENT_INVOICE_PREVIEW = 'PAYMENT_INVOICE_PREVIEW', STRIPE_PAYMENT_INTEGRATION_EDIT = 'STRIPE_PAYMENT_INTEGRATION_EDIT', - INVOICE_SEND_MAIL = 'INVOICE_SEND_MAIL' + INVOICE_SEND_MAIL = 'INVOICE_SEND_MAIL', + ESTIMATE_SEND_MAIL = 'ESTIMATE_SEND_MAIL', } diff --git a/packages/webapp/src/containers/Sales/Estimates/EstimateForm/Dialogs/EstimateFormMailDeliverDialog.tsx b/packages/webapp/src/containers/Sales/Estimates/EstimateForm/Dialogs/EstimateFormMailDeliverDialog.tsx deleted file mode 100644 index 6a0b832c3..000000000 --- a/packages/webapp/src/containers/Sales/Estimates/EstimateForm/Dialogs/EstimateFormMailDeliverDialog.tsx +++ /dev/null @@ -1,39 +0,0 @@ -// @ts-nocheck -import React from 'react'; -import { Dialog, DialogSuspense } from '@/components'; -import withDialogRedux from '@/components/DialogReduxConnect'; -import { compose } from '@/utils'; - -const EstimateFormMailDeliverDialogContent = React.lazy( - () => import('./EstimateFormMailDeliverDialogContent'), -); - -/** - * Estimate mail dialog. - */ -function EstimateFormMailDeliverDialog({ - dialogName, - payload: { estimateId = null }, - isOpen, -}) { - return ( - - - - - - ); -} - -export default compose(withDialogRedux())(EstimateFormMailDeliverDialog); diff --git a/packages/webapp/src/containers/Sales/Estimates/EstimateForm/Dialogs/EstimateFormMailDeliverDialogContent.tsx b/packages/webapp/src/containers/Sales/Estimates/EstimateForm/Dialogs/EstimateFormMailDeliverDialogContent.tsx deleted file mode 100644 index e77e3ee99..000000000 --- a/packages/webapp/src/containers/Sales/Estimates/EstimateForm/Dialogs/EstimateFormMailDeliverDialogContent.tsx +++ /dev/null @@ -1,40 +0,0 @@ -// @ts-nocheck -import * as R from 'ramda'; -import withDialogActions from '@/containers/Dialog/withDialogActions'; -import { useHistory } from 'react-router-dom'; -import EstimateMailDialogContent from '../../EstimateMailDialog/EstimateMailDialogContent'; -import { DialogsName } from '@/constants/dialogs'; - -interface EstimateFormDeliverDialogContent { - estimateId: number; -} - -function EstimateFormDeliverDialogContentRoot({ - estimateId, - - // #withDialogActions - closeDialog, -}: EstimateFormDeliverDialogContent) { - const history = useHistory(); - - const handleSubmit = () => { - closeDialog(DialogsName.EstimateFormMailDeliver); - history.push('/estimates'); - }; - const handleCancel = () => { - closeDialog(DialogsName.EstimateFormMailDeliver); - history.push('/estimates'); - }; - - return ( - - ); -} - -export default R.compose(withDialogActions)( - EstimateFormDeliverDialogContentRoot, -); diff --git a/packages/webapp/src/containers/Sales/Estimates/EstimateForm/EstimateFormDialogs.tsx b/packages/webapp/src/containers/Sales/Estimates/EstimateForm/EstimateFormDialogs.tsx index a50326486..33f8dbe3e 100644 --- a/packages/webapp/src/containers/Sales/Estimates/EstimateForm/EstimateFormDialogs.tsx +++ b/packages/webapp/src/containers/Sales/Estimates/EstimateForm/EstimateFormDialogs.tsx @@ -1,9 +1,6 @@ // @ts-nocheck import React from 'react'; -import { useFormikContext } from 'formik'; import EstimateNumberDialog from '@/containers/Dialogs/EstimateNumberDialog'; -import EstimateFormMailDeliverDialog from './Dialogs/EstimateFormMailDeliverDialog'; -import { DialogsName } from '@/constants/dialogs'; /** * Estimate form dialogs. @@ -27,9 +24,6 @@ export default function EstimateFormDialogs() { dialogName={'estimate-number-form'} onConfirm={handleEstimateNumberFormConfirm} /> - ); } diff --git a/packages/webapp/src/containers/Sales/Estimates/EstimateMailDialog/EstimateMailDialog.tsx b/packages/webapp/src/containers/Sales/Estimates/EstimateMailDialog/EstimateMailDialog.tsx deleted file mode 100644 index 0d13e07fb..000000000 --- a/packages/webapp/src/containers/Sales/Estimates/EstimateMailDialog/EstimateMailDialog.tsx +++ /dev/null @@ -1,35 +0,0 @@ -// @ts-nocheck -import React from 'react'; -import { Dialog, DialogSuspense } from '@/components'; -import withDialogRedux from '@/components/DialogReduxConnect'; -import { compose } from '@/utils'; - -const EstimateMailDialogBody = React.lazy( - () => import('./EstimateMailDialogBody'), -); - -/** - * Estimate mail dialog. - */ -function EstimateMailDialog({ - dialogName, - payload: { estimateId = null }, - isOpen, -}) { - return ( - - - - - - ); -} - -export default compose(withDialogRedux())(EstimateMailDialog); diff --git a/packages/webapp/src/containers/Sales/Estimates/EstimateMailDialog/EstimateMailDialogBody.tsx b/packages/webapp/src/containers/Sales/Estimates/EstimateMailDialog/EstimateMailDialogBody.tsx deleted file mode 100644 index 2fa1c0472..000000000 --- a/packages/webapp/src/containers/Sales/Estimates/EstimateMailDialog/EstimateMailDialogBody.tsx +++ /dev/null @@ -1,33 +0,0 @@ -// @ts-nocheck -import * as R from 'ramda'; -import withDialogActions from '@/containers/Dialog/withDialogActions'; -import EstimateMailDialogContent from './EstimateMailDialogContent'; -import { DialogsName } from '@/constants/dialogs'; - -interface EstimateMailDialogBodyProps { - estimateId: number; -} - -function EstimateMailDialogBodyRoot({ - estimateId, - - // #withDialogActions - closeDialog, -}: EstimateMailDialogBodyProps) { - const handleSubmit = () => { - closeDialog(DialogsName.EstimateMail); - }; - const handleCancelClick = () => { - closeDialog(DialogsName.EstimateMail); - }; - - return ( - - ); -} - -export default R.compose(withDialogActions)(EstimateMailDialogBodyRoot); diff --git a/packages/webapp/src/containers/Sales/Estimates/EstimateMailDialog/EstimateMailDialogBoot.tsx b/packages/webapp/src/containers/Sales/Estimates/EstimateMailDialog/EstimateMailDialogBoot.tsx deleted file mode 100644 index 65b05a9c1..000000000 --- a/packages/webapp/src/containers/Sales/Estimates/EstimateMailDialog/EstimateMailDialogBoot.tsx +++ /dev/null @@ -1,48 +0,0 @@ -// @ts-nocheck -import React, { createContext } from 'react'; -import { useSaleEstimateDefaultOptions } from '@/hooks/query'; -import { DialogContent } from '@/components'; - -interface EstimateMailDialogBootValues { - estimateId: number; - mailOptions: any; - redirectToEstimatesList: boolean; -} - -const EstimateMailDialagBoot = createContext(); - -interface EstimateMailDialogBootProps { - estimateId: number; - redirectToEstimatesList?: boolean; - children: React.ReactNode; -} - -/** - * Estimate mail dialog boot provider. - */ -function EstimateMailDialogBoot({ - estimateId, - redirectToEstimatesList, - ...props -}: EstimateMailDialogBootProps) { - const { data: mailOptions, isLoading: isMailOptionsLoading } = - useSaleEstimateDefaultOptions(estimateId); - - const provider = { - saleEstimateId: estimateId, - mailOptions, - isMailOptionsLoading, - redirectToEstimatesList, - }; - - return ( - - - - ); -} - -const useEstimateMailDialogBoot = () => - React.useContext(EstimateMailDialagBoot); - -export { EstimateMailDialogBoot, useEstimateMailDialogBoot }; diff --git a/packages/webapp/src/containers/Sales/Estimates/EstimateMailDialog/EstimateMailDialogContent.tsx b/packages/webapp/src/containers/Sales/Estimates/EstimateMailDialog/EstimateMailDialogContent.tsx deleted file mode 100644 index c673f71c6..000000000 --- a/packages/webapp/src/containers/Sales/Estimates/EstimateMailDialog/EstimateMailDialogContent.tsx +++ /dev/null @@ -1,22 +0,0 @@ -import { EstimateMailDialogBoot } from './EstimateMailDialogBoot'; -import { EstimateMailDialogForm } from './EstimateMailDialogForm'; - -interface EstimateMailDialogContentProps { - estimateId: number; - onFormSubmit?: () => void; - onCancelClick?: () => void; -} -export default function EstimateMailDialogContent({ - estimateId, - onFormSubmit, - onCancelClick, -}: EstimateMailDialogContentProps) { - return ( - - - - ); -} diff --git a/packages/webapp/src/containers/Sales/Estimates/EstimateMailDialog/EstimateMailDialogForm.tsx b/packages/webapp/src/containers/Sales/Estimates/EstimateMailDialog/EstimateMailDialogForm.tsx deleted file mode 100644 index a4d1324e5..000000000 --- a/packages/webapp/src/containers/Sales/Estimates/EstimateMailDialog/EstimateMailDialogForm.tsx +++ /dev/null @@ -1,81 +0,0 @@ -// @ts-nocheck -import { Formik } from 'formik'; -import * as R from 'ramda'; -import { Intent } from '@blueprintjs/core'; -import { useEstimateMailDialogBoot } from './EstimateMailDialogBoot'; -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 { AppToaster } from '@/components'; - -const initialFormValues = { - ...initialMailNotificationValues, - attachEstimate: true, -}; - -interface EstimateMailFormValues extends MailNotificationFormValues { - attachEstimate: boolean; -} - -function EstimateMailDialogFormRoot({ - onFormSubmit, - onCancelClick, - - // #withDialogClose - closeDialog, -}) { - const { mutateAsync: sendEstimateMail } = useSendSaleEstimateMail(); - const { mailOptions, saleEstimateId, } = - useEstimateMailDialogBoot(); - - const initialValues = transformMailFormToInitialValues( - mailOptions, - initialFormValues, - ); - // Handle the form submitting. - const handleSubmit = (values: EstimateMailFormValues, { setSubmitting }) => { - const reqValues = transformMailFormToRequest(values); - - setSubmitting(true); - sendEstimateMail([saleEstimateId, reqValues]) - .then(() => { - AppToaster.show({ - message: 'The mail notification has been sent successfully.', - intent: Intent.SUCCESS, - }); - closeDialog(DialogsName.EstimateMail); - setSubmitting(false); - onFormSubmit && onFormSubmit(); - }) - .catch(() => { - setSubmitting(false); - closeDialog(DialogsName.EstimateMail); - AppToaster.show({ - message: 'Something went wrong.', - intent: Intent.DANGER, - }); - onCancelClick && onCancelClick(); - }); - }; - - const handleClose = () => { - closeDialog(DialogsName.EstimateMail); - }; - - return ( - - - - ); -} - -export const EstimateMailDialogForm = R.compose(withDialogActions)( - EstimateMailDialogFormRoot, -); diff --git a/packages/webapp/src/containers/Sales/Estimates/EstimateMailDialog/EstimateMailDialogFormContent.tsx b/packages/webapp/src/containers/Sales/Estimates/EstimateMailDialog/EstimateMailDialogFormContent.tsx deleted file mode 100644 index 405c73c0f..000000000 --- a/packages/webapp/src/containers/Sales/Estimates/EstimateMailDialog/EstimateMailDialogFormContent.tsx +++ /dev/null @@ -1,66 +0,0 @@ -// @ts-nocheck -import { Form, useFormikContext } from 'formik'; -import { Button, Classes, Intent } from '@blueprintjs/core'; -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; -} - -export function EstimateMailDialogFormContent({ - onClose, -}: EstimateMailDialogFormContentProps) { - const { isSubmitting } = useFormikContext(); - const { mailOptions } = useEstimateMailDialogBoot(); - - const handleClose = () => { - saveInvoke(onClose); - }; - - return ( -
-
- - - - -
- -
-
- - - -
-
-
- ); -} - -const AttachFormGroup = styled(FFormGroup)` - background: #f8f9fb; - margin-top: 0.6rem; - padding: 4px 14px; - border-radius: 5px; - border: 1px solid #dcdcdd; -`; diff --git a/packages/webapp/src/containers/Sales/Estimates/EstimateMailDialog/index.ts b/packages/webapp/src/containers/Sales/Estimates/EstimateMailDialog/index.ts deleted file mode 100644 index bebbc8bef..000000000 --- a/packages/webapp/src/containers/Sales/Estimates/EstimateMailDialog/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './EstimateMailDialog'; \ No newline at end of file diff --git a/packages/webapp/src/containers/Sales/Estimates/EstimateSendMailDrawer/EstimateSendMailBoot.tsx b/packages/webapp/src/containers/Sales/Estimates/EstimateSendMailDrawer/EstimateSendMailBoot.tsx index 5c1072ee5..79355668c 100644 --- a/packages/webapp/src/containers/Sales/Estimates/EstimateSendMailDrawer/EstimateSendMailBoot.tsx +++ b/packages/webapp/src/containers/Sales/Estimates/EstimateSendMailDrawer/EstimateSendMailBoot.tsx @@ -1,11 +1,12 @@ import React, { createContext, useContext } from 'react'; import { Spinner } from '@blueprintjs/core'; import { useDrawerContext } from '@/components/Drawer/DrawerProvider'; +import { useSaleEstimateMailState } from '@/hooks/query'; interface EstimateSendMailBootValues { estimateId: number; - estimateMailState: GetSaleEstimateDefaultOptionsResponse | undefined; + estimateMailState: any; isEstimateMailState: boolean; } interface EstimateSendMailBootProps { @@ -15,7 +16,9 @@ interface EstimateSendMailBootProps { const EstimateSendMailContentBootContext = createContext({} as EstimateSendMailBootValues); -export const EstimateSendMailBoot = ({ children }: EstimateSendMailBootProps) => { +export const EstimateSendMailBoot = ({ + children, +}: EstimateSendMailBootProps) => { const { payload: { estimateId }, } = useDrawerContext(); diff --git a/packages/webapp/src/containers/Sales/Estimates/EstimateSendMailDrawer/EstimateSendMailContent.tsx b/packages/webapp/src/containers/Sales/Estimates/EstimateSendMailDrawer/EstimateSendMailContent.tsx index ae30ab402..6b894e118 100644 --- a/packages/webapp/src/containers/Sales/Estimates/EstimateSendMailDrawer/EstimateSendMailContent.tsx +++ b/packages/webapp/src/containers/Sales/Estimates/EstimateSendMailDrawer/EstimateSendMailContent.tsx @@ -1,9 +1,24 @@ - +import { Classes } from '@blueprintjs/core'; +import { EstimateSendMailBoot } from './EstimateSendMailBoot'; +import { Stack } from '@/components'; +import { EstimateSendMailForm } from './EstimateSendMailForm'; +import { SendMailViewHeader } from '../SendMailViewDrawer/SendMailViewHeader'; +import { SendMailViewLayout } from '../SendMailViewDrawer/SendMailViewLayout'; +import { EstimateSendMailFields } from './EstimateSnedMailFields'; +import { EstimateSendMailPreviewTabs } from './EstimateSendMailPreview'; export function EstimateSendMailContent() { - return ( - - + + + + } + fields={} + preview={} + /> + + + ); -} \ No newline at end of file +} diff --git a/packages/webapp/src/containers/Sales/Estimates/EstimateSendMailDrawer/EstimateSendMailDrawer.tsx b/packages/webapp/src/containers/Sales/Estimates/EstimateSendMailDrawer/EstimateSendMailDrawer.tsx index 752d6b81f..be9b17b81 100644 --- a/packages/webapp/src/containers/Sales/Estimates/EstimateSendMailDrawer/EstimateSendMailDrawer.tsx +++ b/packages/webapp/src/containers/Sales/Estimates/EstimateSendMailDrawer/EstimateSendMailDrawer.tsx @@ -4,9 +4,9 @@ import * as R from 'ramda'; import { Drawer, DrawerSuspense } from '@/components'; import withDrawers from '@/containers/Drawer/withDrawers'; -const EstimateSendMailDrawerProps = React.lazy(() => +const EstimateSendMailContent = React.lazy(() => import('./EstimateSendMailContent').then((module) => ({ - default: module.InvoiceSendMailContent, + default: module.EstimateSendMailContent, })), ); @@ -31,7 +31,7 @@ function EstimateSendMailDrawerRoot({ size={'calc(100% - 10px)'} > - + ); diff --git a/packages/webapp/src/containers/Sales/Estimates/EstimateSendMailDrawer/EstimateSendMailForm.tsx b/packages/webapp/src/containers/Sales/Estimates/EstimateSendMailDrawer/EstimateSendMailForm.tsx index 545fc9405..65b5f2fc9 100644 --- a/packages/webapp/src/containers/Sales/Estimates/EstimateSendMailDrawer/EstimateSendMailForm.tsx +++ b/packages/webapp/src/containers/Sales/Estimates/EstimateSendMailDrawer/EstimateSendMailForm.tsx @@ -1,17 +1,17 @@ +// @ts-nocheck import { Form, Formik, FormikHelpers } from 'formik'; import { css } from '@emotion/css'; import { Intent } from '@blueprintjs/core'; -import { InvoiceSendMailFormValues } from './_types'; -import { InvoiceSendMailFormSchema } from './InvoiceSendMailForm.schema'; -import { useSendSaleInvoiceMail } from '@/hooks/query'; +import { EstimateSendMailFormValues } from './_interfaces'; +import { EstimateSendMailSchema } from './EstimateSendMail.schema'; +import { useSendSaleEstimateMail } from '@/hooks/query'; import { AppToaster } from '@/components'; -import { useInvoiceSendMailBoot } from './InvoiceSendMailContentBoot'; import { useDrawerActions } from '@/hooks/state'; import { useDrawerContext } from '@/components/Drawer/DrawerProvider'; import { transformToForm } from '@/utils'; import { useEstimateSendMailBoot } from './EstimateSendMailBoot'; -const initialValues: InvoiceSendMailFormValues = { +const initialValues: EstimateSendMailFormValues = { subject: '', message: '', to: [], @@ -20,27 +20,27 @@ const initialValues: InvoiceSendMailFormValues = { attachPdf: true, }; -interface InvoiceSendMailFormProps { +interface EstimateSendMailFormProps { children: React.ReactNode; } -export function EstimateSendMailForm({ children }: InvoiceSendMailFormProps) { - const { mutateAsync: sendInvoiceMail } = useSendSaleInvoiceMail(); +export function EstimateSendMailForm({ children }: EstimateSendMailFormProps) { + const { mutateAsync: sendEstimateMail } = useSendSaleEstimateMail(); const { estimateId, estimateMailState } = useEstimateSendMailBoot(); const { name } = useDrawerContext(); const { closeDrawer } = useDrawerActions(); - const _initialValues: InvoiceSendMailFormValues = { + const _initialValues: EstimateSendMailFormValues = { ...initialValues, - ...transformToForm(invoiceMailState, initialValues), + ...transformToForm(estimateMailState, initialValues), }; const handleSubmit = ( - values: InvoiceSendMailFormValues, - { setSubmitting }: FormikHelpers, + values: EstimateSendMailFormValues, + { setSubmitting }: FormikHelpers, ) => { setSubmitting(true); - sendInvoiceMail({ id: invoiceId, values: { ...values } }) + sendEstimateMail({ id: estimateId, values: { ...values } }) .then(() => { AppToaster.show({ message: 'The invoice mail has been sent to the customer.', @@ -61,7 +61,7 @@ export function EstimateSendMailForm({ children }: InvoiceSendMailFormProps) { return (
asdfsdf + ) +} \ No newline at end of file diff --git a/packages/webapp/src/containers/Sales/Estimates/EstimateSendMailDrawer/EstimateSnedMailFields.tsx b/packages/webapp/src/containers/Sales/Estimates/EstimateSendMailDrawer/EstimateSnedMailFields.tsx new file mode 100644 index 000000000..74b4227c3 --- /dev/null +++ b/packages/webapp/src/containers/Sales/Estimates/EstimateSendMailDrawer/EstimateSnedMailFields.tsx @@ -0,0 +1,74 @@ +import { FCheckbox, FFormGroup, FInputGroup, Group, Stack } from "@/components"; +import { SendMailViewToAddressField } from "../SendMailViewDrawer/SendMailViewToAddressField"; +import { SendMailViewMessageField } from "../SendMailViewDrawer/SendMailViewMessageField"; +import { Button, Intent } from "@blueprintjs/core"; +import { useFormikContext } from "formik"; +import { useDrawerContext } from "@/components/Drawer/DrawerProvider"; +import { useDrawerActions } from "@/hooks/state"; + +const items: Array = []; +const argsOptions: Array = []; + +export function EstimateSendMailFields() { + return ( + + + + + + + + + + + + + + + + + ); +} + + +function EstimateSendMailFooter() { + const { isSubmitting } = useFormikContext(); + const { name } = useDrawerContext(); + const { closeDrawer } = useDrawerActions(); + + const handleClose = () => { + closeDrawer(name); + }; + + return ( + + + + + + + + ); +} diff --git a/packages/webapp/src/containers/Sales/Estimates/EstimateSendMailDrawer/index.ts b/packages/webapp/src/containers/Sales/Estimates/EstimateSendMailDrawer/index.ts index e69de29bb..e366aca3e 100644 --- a/packages/webapp/src/containers/Sales/Estimates/EstimateSendMailDrawer/index.ts +++ b/packages/webapp/src/containers/Sales/Estimates/EstimateSendMailDrawer/index.ts @@ -0,0 +1 @@ +export * from './EstimateSendMailDrawer'; diff --git a/packages/webapp/src/containers/Sales/Estimates/EstimatesLanding/EstimatesDataTable.tsx b/packages/webapp/src/containers/Sales/Estimates/EstimatesLanding/EstimatesDataTable.tsx index 4cc70e498..33cd4c2a1 100644 --- a/packages/webapp/src/containers/Sales/Estimates/EstimatesLanding/EstimatesDataTable.tsx +++ b/packages/webapp/src/containers/Sales/Estimates/EstimatesLanding/EstimatesDataTable.tsx @@ -107,7 +107,7 @@ function EstimatesDataTable({ // Handle mail send estimate. const handleMailSendEstimate = ({ id }) => { - openDialog(DialogsName.EstimateMail, { estimateId: id }); + openDrawer(DRAWERS.ESTIMATE_SEND_MAIL, { estimateId: id }); } // Local storage memorizing columns widths. diff --git a/packages/webapp/src/containers/Sales/Estimates/SendMailViewDrawer/SendMailViewLayout.tsx b/packages/webapp/src/containers/Sales/Estimates/SendMailViewDrawer/SendMailViewLayout.tsx new file mode 100644 index 000000000..4615f11fc --- /dev/null +++ b/packages/webapp/src/containers/Sales/Estimates/SendMailViewDrawer/SendMailViewLayout.tsx @@ -0,0 +1,37 @@ +import { Group, Stack } from '@/components'; +import React from 'react'; + +interface SendMailViewLayoutProps { + header?: React.ReactNode; + fields?: React.ReactNode; + preview?: React.ReactNode; +} + +export function SendMailViewLayout({ + header, + fields, + preview, +}: SendMailViewLayoutProps) { + return ( + + {header} + + + + {fields} + + + + {preview} + + + + ); +} diff --git a/packages/webapp/src/containers/Sales/Estimates/SendMailViewDrawer/SendMailViewMessageField.tsx b/packages/webapp/src/containers/Sales/Estimates/SendMailViewDrawer/SendMailViewMessageField.tsx index f5226d427..c9df847d7 100644 --- a/packages/webapp/src/containers/Sales/Estimates/SendMailViewDrawer/SendMailViewMessageField.tsx +++ b/packages/webapp/src/containers/Sales/Estimates/SendMailViewDrawer/SendMailViewMessageField.tsx @@ -1,14 +1,13 @@ // @ts-nocheck +import { useCallback, useRef } from 'react'; import { useFormikContext } from 'formik'; import { Button, Icon, Position } from '@blueprintjs/core'; import { SelectOptionProps } from '@blueprintjs-formik/select'; import { FormGroupProps, TextAreaProps } from '@blueprintjs-formik/core'; import { css } from '@emotion/css'; import { FFormGroup, FSelect, FTextArea, Group, Stack } from '@/components'; -import { useCallback, useRef } from 'react'; import { InvoiceSendMailFormValues } from '../../Invoices/InvoiceSendMailDrawer/_types'; - interface SendMailViewMessageFieldProps { argsOptions?: Array; formGroupProps?: Partial; diff --git a/packages/webapp/src/containers/Sales/Estimates/SendMailViewDrawer/SendMailViewPreview.tsx b/packages/webapp/src/containers/Sales/Estimates/SendMailViewDrawer/SendMailViewPreview.tsx index e69de29bb..510849abc 100644 --- a/packages/webapp/src/containers/Sales/Estimates/SendMailViewDrawer/SendMailViewPreview.tsx +++ b/packages/webapp/src/containers/Sales/Estimates/SendMailViewDrawer/SendMailViewPreview.tsx @@ -0,0 +1,9 @@ + + + +export function SendMailViewPreview() { + + return ( +

asdasd

+ ) +} \ No newline at end of file diff --git a/packages/webapp/src/containers/Sales/Estimates/SendMailViewDrawer/SendMailViewPreviewTabs.tsx b/packages/webapp/src/containers/Sales/Estimates/SendMailViewDrawer/SendMailViewPreviewTabs.tsx index 513b7dc9b..ae9441d78 100644 --- a/packages/webapp/src/containers/Sales/Estimates/SendMailViewDrawer/SendMailViewPreviewTabs.tsx +++ b/packages/webapp/src/containers/Sales/Estimates/SendMailViewDrawer/SendMailViewPreviewTabs.tsx @@ -1,6 +1,5 @@ import { css } from '@emotion/css'; import { Tabs } from '@blueprintjs/core'; -import { Stack } from '@/components'; interface SendMailViewPreviewTabsProps { children: React.ReactNode; @@ -10,11 +9,10 @@ export function SendMailViewPreviewTabs({ children, }: SendMailViewPreviewTabsProps) { return ( - - - {children} - - + > + {children} + ); } diff --git a/packages/webapp/src/containers/Sales/Estimates/SendMailViewDrawer/SendMailViewToAddressField.tsx b/packages/webapp/src/containers/Sales/Estimates/SendMailViewDrawer/SendMailViewToAddressField.tsx index 48de4dada..62865d823 100644 --- a/packages/webapp/src/containers/Sales/Estimates/SendMailViewDrawer/SendMailViewToAddressField.tsx +++ b/packages/webapp/src/containers/Sales/Estimates/SendMailViewDrawer/SendMailViewToAddressField.tsx @@ -1,3 +1,4 @@ +// @ts-nocheck import { useMemo, useState } from 'react'; import { Button, MenuItem } from '@blueprintjs/core'; import { SelectOptionProps } from '@blueprintjs-formik/select'; diff --git a/packages/webapp/src/containers/Sales/Invoices/InvoiceSendMailDrawer/InvoiceSendMailContent.tsx b/packages/webapp/src/containers/Sales/Invoices/InvoiceSendMailDrawer/InvoiceSendMailContent.tsx index 6e2fd5142..53cfe7642 100644 --- a/packages/webapp/src/containers/Sales/Invoices/InvoiceSendMailDrawer/InvoiceSendMailContent.tsx +++ b/packages/webapp/src/containers/Sales/Invoices/InvoiceSendMailDrawer/InvoiceSendMailContent.tsx @@ -5,20 +5,18 @@ import { InvoiceSendMailForm } from './InvoiceSendMailForm'; import { InvoiceSendMailPreview } from './InvoiceSendMailPreview'; import { InvoiceSendMailFields } from './InvoiceSendMailFields'; import { SendMailViewHeader } from '../../Estimates/SendMailViewDrawer/SendMailViewHeader'; +import { SendMailViewLayout } from '../../Estimates/SendMailViewDrawer/SendMailViewLayout'; export function InvoiceSendMailContent() { return ( - - - - - - - - + } + fields={} + preview={} + /> diff --git a/packages/webapp/src/containers/Sales/Invoices/InvoiceSendMailDrawer/InvoiceSendMailFields.tsx b/packages/webapp/src/containers/Sales/Invoices/InvoiceSendMailDrawer/InvoiceSendMailFields.tsx index 33a014cbe..36ac3bd56 100644 --- a/packages/webapp/src/containers/Sales/Invoices/InvoiceSendMailDrawer/InvoiceSendMailFields.tsx +++ b/packages/webapp/src/containers/Sales/Invoices/InvoiceSendMailDrawer/InvoiceSendMailFields.tsx @@ -1,34 +1,19 @@ // @ts-nocheck -import { Button, Intent, MenuItem, Position } from '@blueprintjs/core'; -import { useRef, useState, useMemo, useCallback } from 'react'; +import { Button, Intent } from '@blueprintjs/core'; import { useFormikContext } from 'formik'; -import { SelectOptionProps } from '@blueprintjs-formik/select'; -import { css } from '@emotion/css'; -import { - FCheckbox, - FFormGroup, - FInputGroup, - Group, - Stack, -} from '@/components'; +import { FCheckbox, FFormGroup, FInputGroup, Group, Stack } from '@/components'; import { useDrawerContext } from '@/components/Drawer/DrawerProvider'; import { useDrawerActions } from '@/hooks/state'; -import { useInvoiceMailItems, } from './_hooks'; +import { useInvoiceMailItems, useSendInvoiceFormatArgsOptions } from './_hooks'; import { SendMailViewToAddressField } from '../../Estimates/SendMailViewDrawer/SendMailViewToAddressField'; import { SendMailViewMessageField } from '../../Estimates/SendMailViewDrawer/SendMailViewMessageField'; export function InvoiceSendMailFields() { const items = useInvoiceMailItems(); + const argsOptions = useSendInvoiceFormatArgsOptions(); return ( - + - + diff --git a/packages/webapp/src/hooks/query/estimates.tsx b/packages/webapp/src/hooks/query/estimates.tsx index 49394b104..7aea63c03 100644 --- a/packages/webapp/src/hooks/query/estimates.tsx +++ b/packages/webapp/src/hooks/query/estimates.tsx @@ -241,7 +241,7 @@ export function useEstimateSMSDetail(estimateId, props, requestProps) { ); } -export function useSendSaleEstimateMail(props) { +export function useSendSaleEstimateMail(props = {}) { const queryClient = useQueryClient(); const apiRequest = useApiRequest(); @@ -257,17 +257,18 @@ export function useSendSaleEstimateMail(props) { ); } -export function useSaleEstimateDefaultOptions(estimateId, props) { - return useRequestQuery( - [t.SALE_ESTIMATE_MAIL_OPTIONS, estimateId], - { - method: 'get', - url: `sales/estimates/${estimateId}/mail`, - }, - { - select: (res) => res.data.data, - ...props, - }, +/** + * + * @param {number} estimateId + * @param props + * @returns + */ +export function useSaleEstimateMailState(estimateId: number, props?= {}) { + const apiRequest = useApiRequest(); + return useQuery([t.SALE_ESTIMATE_MAIL_OPTIONS, estimateId], () => + apiRequest + .get(`sales/estimates/${estimateId}/mail/state`) + .then((res) => transformToCamelCase(res.data.data)), ); }