From 0111b0e6ff42282b5b6451f9a8783aa986b416c8 Mon Sep 17 00:00:00 2001 From: Ahmed Bouhuolia Date: Mon, 28 Oct 2024 12:00:17 +0200 Subject: [PATCH] feat: hook up the request to the send mail form --- .../InvoiceSendMailContentBoot.tsx | 2 + .../InvoiceSendMailFields.tsx | 106 ++++++++---------- .../InvoiceSendMailForm.schema.ts | 11 ++ .../InvoiceSendMailForm.tsx | 49 +++++--- .../Invoices/InvoiceSendMailDrawer/_hooks.ts | 18 +++ .../Invoices/InvoiceSendMailDrawer/_types.ts | 7 ++ packages/webapp/src/hooks/query/invoices.tsx | 49 +++++++- 7 files changed, 162 insertions(+), 80 deletions(-) create mode 100644 packages/webapp/src/containers/Sales/Invoices/InvoiceSendMailDrawer/InvoiceSendMailForm.schema.ts create mode 100644 packages/webapp/src/containers/Sales/Invoices/InvoiceSendMailDrawer/_hooks.ts create mode 100644 packages/webapp/src/containers/Sales/Invoices/InvoiceSendMailDrawer/_types.ts diff --git a/packages/webapp/src/containers/Sales/Invoices/InvoiceSendMailDrawer/InvoiceSendMailContentBoot.tsx b/packages/webapp/src/containers/Sales/Invoices/InvoiceSendMailDrawer/InvoiceSendMailContentBoot.tsx index fde83ff94..b181d771e 100644 --- a/packages/webapp/src/containers/Sales/Invoices/InvoiceSendMailDrawer/InvoiceSendMailContentBoot.tsx +++ b/packages/webapp/src/containers/Sales/Invoices/InvoiceSendMailDrawer/InvoiceSendMailContentBoot.tsx @@ -6,6 +6,7 @@ import { useDrawerContext } from '@/components/Drawer/DrawerProvider'; interface InvoiceSendMailBootValues { invoice: any; + invoiceId: number; isInvoiceLoading: boolean; } interface InvoiceSendMailBootProps { @@ -31,6 +32,7 @@ export const InvoiceSendMailBoot = ({ children }: InvoiceSendMailBootProps) => { const value = { invoice, isInvoiceLoading, + invoiceId, }; return ( diff --git a/packages/webapp/src/containers/Sales/Invoices/InvoiceSendMailDrawer/InvoiceSendMailFields.tsx b/packages/webapp/src/containers/Sales/Invoices/InvoiceSendMailDrawer/InvoiceSendMailFields.tsx index f83f56244..4f680f6bd 100644 --- a/packages/webapp/src/containers/Sales/Invoices/InvoiceSendMailDrawer/InvoiceSendMailFields.tsx +++ b/packages/webapp/src/containers/Sales/Invoices/InvoiceSendMailDrawer/InvoiceSendMailFields.tsx @@ -16,6 +16,7 @@ import { import { useDrawerContext } from '@/components/Drawer/DrawerProvider'; import { useDrawerActions } from '@/hooks/state'; import { SelectOptionProps } from '@blueprintjs-formik/select'; +import { useInvoiceMailItems } from './_hooks'; // Create new account renderer. const createNewItemRenderer = (query, active, handleClick) => { @@ -44,20 +45,24 @@ const styleEmailButton = css` } `; +const fieldsWrapStyle = css` + > :not(:first-of-type) .bp4-input { + border-top-color: transparent; + border-top-right-radius: 0; + border-top-left-radius: 0; + } + > :not(:last-of-type) .bp4-input { + border-bottom-left-radius: 0; + border-bottom-right-radius: 0; + } +`; + export function InvoiceSendMailFields() { const [showCCField, setShowCCField] = useState(false); const [showBccField, setShowBccField] = useState(false); const { values, setFieldValue } = useFormikContext(); - - const items = chain([...values?.to, ...values?.cc, ...values?.bcc]) - .filter((email) => !!email?.trim()) - .uniq() - .map((email) => ({ - value: email, - text: email, - })) - .value(); + const items = useInvoiceMailItems(); const handleClickCcBtn = (event) => { event.preventDefault(); @@ -74,19 +79,44 @@ export function InvoiceSendMailFields() { }; const handleCreateToItemSelect = (value: SelectOptionProps) => { - const _value = [...values?.to, value?.name]; - setFieldValue('to', _value); + setFieldValue('to', [...values?.to, value?.name]); }; const handleCreateCcItemSelect = (value: SelectOptionProps) => { - const _value = [...values?.cc, value?.name]; - setFieldValue('cc', _value); + setFieldValue('cc', [...values?.cc, value?.name]); }; const handleCreateBccItemSelect = (value: SelectOptionProps) => { - const _value = [...values?.bcc, value?.name]; - setFieldValue('bcc', _value); + setFieldValue('bcc', [...values?.bcc, value?.name]); }; + const rightElementsToField = ( + + + + + + ); + return ( - + - :not(:first-of-type) .bp4-input { - border-top-color: transparent; - border-top-right-radius: 0; - border-top-left-radius: 0; - } - > :not(:last-of-type) .bp4-input { - border-bottom-left-radius: 0; - border-bottom-right-radius: 0; - } - `} - > + - - - - - ), }} createNewItemRenderer={createNewItemRenderer} createNewItemFromQuery={createNewItemFromQuery} @@ -252,3 +243,4 @@ function InvoiceSendMailFooter() { ); } + diff --git a/packages/webapp/src/containers/Sales/Invoices/InvoiceSendMailDrawer/InvoiceSendMailForm.schema.ts b/packages/webapp/src/containers/Sales/Invoices/InvoiceSendMailDrawer/InvoiceSendMailForm.schema.ts new file mode 100644 index 000000000..c40d0d7a9 --- /dev/null +++ b/packages/webapp/src/containers/Sales/Invoices/InvoiceSendMailDrawer/InvoiceSendMailForm.schema.ts @@ -0,0 +1,11 @@ +import * as Yup from 'yup'; + +export const InvoiceSendMailFormSchema = Yup.object().shape({ + subject: Yup.string().required('Subject is required'), + message: Yup.string().required('Message is required'), + to: Yup.array() + .of(Yup.string().email('Invalid email')) + .required('To address is required'), + cc: Yup.array().of(Yup.string().email('Invalid email')), + bcc: Yup.array().of(Yup.string().email('Invalid email')), +}); diff --git a/packages/webapp/src/containers/Sales/Invoices/InvoiceSendMailDrawer/InvoiceSendMailForm.tsx b/packages/webapp/src/containers/Sales/Invoices/InvoiceSendMailDrawer/InvoiceSendMailForm.tsx index 83442d588..3b108851f 100644 --- a/packages/webapp/src/containers/Sales/Invoices/InvoiceSendMailDrawer/InvoiceSendMailForm.tsx +++ b/packages/webapp/src/containers/Sales/Invoices/InvoiceSendMailDrawer/InvoiceSendMailForm.tsx @@ -1,6 +1,14 @@ import * as Yup from 'yup'; 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 { AppToaster } from '@/components'; +import { useInvoiceSendMailBoot } from './InvoiceSendMailContentBoot'; +import { useDrawerActions } from '@/hooks/state'; +import { useDrawerContext } from '@/components/Drawer/DrawerProvider'; const initialValues = { subject: 'invoice INV-0002 for AED 0.00', @@ -17,33 +25,40 @@ If you have any questions, please let us know, Thanks, Mohamed`, to: ['a.bouhuolia@gmail.com'], - cc: [], - bcc: [], }; -interface InvoiceSendMailFormValues { - subject: string; - message: string; - to: string; - cc: string; -} - -const InvoiceSendMailFormSchema = Yup.object().shape({ - subject: Yup.string().required('Subject is required'), - message: Yup.string().required('Message is required'), - to: Yup.string().email('Invalid email').required('To address is required'), - cc: Yup.string().email('Invalid email'), -}); interface InvoiceSendMailFormProps { children: React.ReactNode; } export function InvoiceSendMailForm({ children }: InvoiceSendMailFormProps) { - // + const { mutateAsync: sendInvoiceMail } = useSendSaleInvoiceMail(); + const { invoiceId } = useInvoiceSendMailBoot(); + const { name } = useDrawerContext(); + const { closeDrawer } = useDrawerActions(); + const handleSubmit = ( values: InvoiceSendMailFormValues, { setSubmitting }: FormikHelpers, - ) => {}; + ) => { + setSubmitting(true); + sendInvoiceMail({ id: invoiceId, values: { ...values } }) + .then(() => { + AppToaster.show({ + message: 'The invoice mail has been sent to the customer.', + intent: Intent.SUCCESS, + }); + setSubmitting(false); + closeDrawer(name); + }) + .catch((error) => { + setSubmitting(false); + AppToaster.show({ + message: 'Something went wrong!', + intent: Intent.SUCCESS, + }); + }); + }; return ( { + const { values } = useFormikContext(); + const cc = values?.cc || []; + const bcc = values?.bcc || []; + + return chain([...values?.to, ...cc, ...bcc]) + .filter((email) => !!email?.trim()) + .uniq() + .map((email) => ({ + value: email, + text: email, + })) + .value(); +}; diff --git a/packages/webapp/src/containers/Sales/Invoices/InvoiceSendMailDrawer/_types.ts b/packages/webapp/src/containers/Sales/Invoices/InvoiceSendMailDrawer/_types.ts new file mode 100644 index 000000000..33987d34a --- /dev/null +++ b/packages/webapp/src/containers/Sales/Invoices/InvoiceSendMailDrawer/_types.ts @@ -0,0 +1,7 @@ +export interface InvoiceSendMailFormValues { + subject: string; + message: string; + to: string[]; + cc?: string[]; + bcc?: string[]; +} diff --git a/packages/webapp/src/hooks/query/invoices.tsx b/packages/webapp/src/hooks/query/invoices.tsx index 46c927cf7..7887aecc2 100644 --- a/packages/webapp/src/hooks/query/invoices.tsx +++ b/packages/webapp/src/hooks/query/invoices.tsx @@ -1,5 +1,11 @@ // @ts-nocheck -import { useQueryClient, useMutation, useQuery } from 'react-query'; +import { + useQueryClient, + useMutation, + useQuery, + UseMutationOptions, + UseMutationResult, +} from 'react-query'; import { useRequestQuery } from '../useQueryRequest'; import { transformPagination, transformToCamelCase } from '@/utils'; import useApiRequest from '../useRequest'; @@ -312,18 +318,49 @@ export function useInvoicePaymentTransactions(invoiceId, props) { ); } -export function useSendSaleInvoiceMail(props) { +interface SendSaleInvoiceMailValues { + id: number; + values: { + subject: string; + message: string; + to: Array; + cc?: Array; + bcc?: Array; + attachInvoice?: boolean; + }; +} +interface SendSaleInvoiceMailResponse {} +/** + * Sends the sale invoice mail. + * @param {UseMutationOptions} + * @returns {UseMutationResult} + */ +export function useSendSaleInvoiceMail( + options?: UseMutationOptions< + SendSaleInvoiceMailResponse, + Error, + SendSaleInvoiceMailValues + >, +): UseMutationResult< + SendSaleInvoiceMailResponse, + Error, + SendSaleInvoiceMailValues +> { const queryClient = useQueryClient(); const apiRequest = useApiRequest(); - return useMutation( - ([id, values]) => apiRequest.post(`sales/invoices/${id}/mail`, values), + return useMutation< + SendSaleInvoiceMailResponse, + Error, + SendSaleInvoiceMailValues + >( + (value) => apiRequest.post(`sales/invoices/${value.id}/mail`, value.values), { - onSuccess: (res, [id, values]) => { + onSuccess: (res) => { // Common invalidate queries. commonInvalidateQueries(queryClient); }, - ...props, + ...options, }, ); }