diff --git a/client/src/containers/Dialogs/ExchangeRateFormDialog/ExchangeRateForm.js b/client/src/containers/Dialogs/ExchangeRateFormDialog/ExchangeRateForm.js
new file mode 100644
index 000000000..bec5b7b6b
--- /dev/null
+++ b/client/src/containers/Dialogs/ExchangeRateFormDialog/ExchangeRateForm.js
@@ -0,0 +1,114 @@
+import React, { useMemo } from 'react';
+import { Intent } from '@blueprintjs/core';
+import { Formik } from 'formik';
+import moment from 'moment';
+import { FormattedMessage as T, useIntl } from 'react-intl';
+import { AppToaster } from 'components';
+import {
+ CreateExchangeRateFormSchema,
+ EditExchangeRateFormSchema,
+} from './ExchangeRateForm.schema';
+import ExchangeRateFormContent from './ExchangeRateFormContent';
+import { useExchangeRateFromContext } from './ExchangeRateFormProvider';
+import withDialogActions from 'containers/Dialog/withDialogActions';
+
+import { compose, transformToForm } from 'utils';
+
+const defaultInitialValues = {
+ exchange_rate: '',
+ currency_code: '',
+ date: moment(new Date()).format('YYYY-MM-DD'),
+};
+
+/**
+ * Exchange rate form.
+ */
+function ExchangeRateForm({
+ // #withDialogActions
+ closeDialog,
+}) {
+ const { formatMessage } = useIntl();
+ const {
+ createExchangeRateMutate,
+ editExchangeRateMutate,
+ isNewMode,
+ dialogName,
+ exchangeRate,
+ } = useExchangeRateFromContext();
+
+ // Form validation schema in create and edit mode.
+ const validationSchema = isNewMode
+ ? CreateExchangeRateFormSchema
+ : EditExchangeRateFormSchema;
+ const initialValues = useMemo(
+ () => ({
+ ...defaultInitialValues,
+ ...transformToForm(exchangeRate, defaultInitialValues),
+ }),
+ [],
+ );
+
+ // Transformers response errors.
+ const transformErrors = (errors, { setErrors }) => {
+ if (
+ errors.find((error) => error.type === 'EXCHANGE.RATE.DATE.PERIOD.DEFINED')
+ ) {
+ setErrors({
+ exchange_rate: formatMessage({
+ id: 'there_is_exchange_rate_in_this_date_with_the_same_currency',
+ }),
+ });
+ }
+ };
+
+ // Handle the form submit.
+ const handleFormSubmit = (values, { setSubmitting, setErrors }) => {
+ setSubmitting(true);
+
+ // Handle close the dialog after success response.
+ const afterSubmit = () => {
+ closeDialog(dialogName);
+ };
+ const onSuccess = ({ response }) => {
+ AppToaster.show({
+ message: formatMessage({
+ id: !isNewMode
+ ? 'the_exchange_rate_has_been_edited_successfully'
+ : 'the_exchange_rate_has_been_created_successfully',
+ }),
+ intent: Intent.SUCCESS,
+ });
+ afterSubmit(response);
+ };
+ // Handle the response error.
+ const onError = (error) => {
+ const {
+ response: {
+ data: { errors },
+ },
+ } = error;
+
+ transformErrors(errors, { setErrors });
+ setSubmitting(false);
+ };
+ if (isNewMode) {
+ createExchangeRateMutate(values).then(onSuccess).catch(onError);
+ } else {
+ editExchangeRateMutate([exchangeRate.id, values])
+ .then(onSuccess)
+ .catch(onError);
+ }
+ };
+
+ return (
+
+
+
+ );
+}
+
+export default compose(withDialogActions)(ExchangeRateForm);
diff --git a/client/src/containers/Dialogs/ExchangeRateFormDialog/ExchangeRateForm.schema.js b/client/src/containers/Dialogs/ExchangeRateFormDialog/ExchangeRateForm.schema.js
new file mode 100644
index 000000000..29ced17ca
--- /dev/null
+++ b/client/src/containers/Dialogs/ExchangeRateFormDialog/ExchangeRateForm.schema.js
@@ -0,0 +1,18 @@
+import * as Yup from 'yup';
+import { formatMessage } from 'services/intl';
+import { DATATYPES_LENGTH } from 'common/dataTypes';
+
+const Schema = Yup.object().shape({
+ exchange_rate: Yup.number()
+ .required()
+ .label(formatMessage({ id: 'exchange_rate_' })),
+ currency_code: Yup.string()
+ .max(3)
+ .required(formatMessage({ id: 'currency_code_' })),
+ date: Yup.date()
+ .required()
+ .label(formatMessage({ id: 'date' })),
+});
+
+export const CreateExchangeRateFormSchema = Schema;
+export const EditExchangeRateFormSchema = Schema;
diff --git a/client/src/containers/Dialogs/ExchangeRateFormDialog/ExchangeRateFormContent.js b/client/src/containers/Dialogs/ExchangeRateFormDialog/ExchangeRateFormContent.js
new file mode 100644
index 000000000..3557ad92b
--- /dev/null
+++ b/client/src/containers/Dialogs/ExchangeRateFormDialog/ExchangeRateFormContent.js
@@ -0,0 +1,13 @@
+import React from 'react';
+import { Form } from 'formik';
+import ExchangeRateFormFields from './ExchangeRateFormFields';
+import ExchangeRateFormFooter from './ExchangeRateFormFooter';
+
+export default function ExchangeRateFormContent() {
+ return (
+
+ );
+}
diff --git a/client/src/containers/Dialogs/ExchangeRateFormDialog/ExchangeRateFormDialogContent.js b/client/src/containers/Dialogs/ExchangeRateFormDialog/ExchangeRateFormDialogContent.js
index cc99f3d70..ad2a39e69 100644
--- a/client/src/containers/Dialogs/ExchangeRateFormDialog/ExchangeRateFormDialogContent.js
+++ b/client/src/containers/Dialogs/ExchangeRateFormDialog/ExchangeRateFormDialogContent.js
@@ -1,35 +1,9 @@
-import React, { useState, useMemo, useCallback } from 'react';
-import {
- Button,
- Classes,
- FormGroup,
- InputGroup,
- Intent,
- Position,
-} from '@blueprintjs/core';
-import { pick } from 'lodash';
-import * as Yup from 'yup';
-import { useFormik } from 'formik';
-import { useQuery, queryCache } from 'react-query';
-import moment from 'moment';
-import { DateInput } from '@blueprintjs/datetime';
-import { FormattedMessage as T, useIntl } from 'react-intl';
-import { momentFormatter, tansformDateValue } from 'utils';
-import {
- AppToaster,
- ErrorMessage,
- DialogContent,
- FieldRequiredHint,
- CurrencySelectList,
-} from 'components';
-import classNames from 'classnames';
+import React from 'react';
+
+import ExchangeRateForm from './ExchangeRateForm';
+import { ExchangeRateFormProvider } from './ExchangeRateFormProvider';
+
import withExchangeRateDetail from 'containers/ExchangeRates/withExchangeRateDetail';
-import withExchangeRatesActions from 'containers/ExchangeRates/withExchangeRatesActions';
-
-import withCurrencies from 'containers/Currencies/withCurrencies';
-import withCurrenciesActions from 'containers/Currencies/withCurrenciesActions';
-import withDialogActions from 'containers/Dialog/withDialogActions';
-
import { compose } from 'utils';
import 'style/pages/ExchangeRate/ExchangeRateDialog.scss';
@@ -38,226 +12,20 @@ import 'style/pages/ExchangeRate/ExchangeRateDialog.scss';
* Exchange rate form content.
*/
function ExchangeRateFormDialogContent({
- // #withDialogActions
- closeDialog,
-
- // #withCurrencies
- currenciesList,
-
- //#WithExchangeRateDetail
- exchangeRate,
-
- // #withExchangeRatesActions
- requestSubmitExchangeRate,
- requestEditExchangeRate,
-
- // #wihtCurrenciesActions
- requestFetchCurrencies,
-
// #ownProp
action,
exchangeRateId,
dialogName,
}) {
- const { formatMessage } = useIntl();
- const [selectedItems, setSelectedItems] = useState({});
-
- const fetchCurrencies = useQuery(
- 'currencies',
- () => requestFetchCurrencies(),
- { enabled: true },
- );
-
- const validationSchema = Yup.object().shape({
- exchange_rate: Yup.number()
- .required()
- .label(formatMessage({ id: 'exchange_rate_' })),
- currency_code: Yup.string()
- .max(3)
- .required(formatMessage({ id: 'currency_code_' })),
- date: Yup.date()
- .required()
- .label(formatMessage({ id: 'date' })),
- });
-
- const initialValues = useMemo(
- () => ({
- exchange_rate: '',
- currency_code: '',
- date: moment(new Date()).format('YYYY-MM-DD'),
- }),
- [],
- );
-
- const {
- values,
- touched,
- errors,
- isSubmitting,
- handleSubmit,
- getFieldProps,
- setFieldValue,
- resetForm,
- } = useFormik({
- enableReinitialize: true,
- validationSchema,
- initialValues: {
- ...initialValues,
- ...(action === 'edit' && pick(exchangeRate, Object.keys(initialValues))),
- },
- onSubmit: (values, { setSubmitting, setErrors }) => {
- if (action === 'edit') {
- requestEditExchangeRate(exchangeRateId, values)
- .then((response) => {
- closeDialog(dialogName);
- AppToaster.show({
- message: formatMessage({
- id: 'the_exchange_rate_has_been_edited_successfully',
- }),
- intent: Intent.SUCCESS,
- });
- setSubmitting(false);
- queryCache.invalidateQueries('exchange-rates-table');
- })
- .catch((error) => {
- setSubmitting(false);
- });
- } else {
- requestSubmitExchangeRate(values)
- .then((response) => {
- closeDialog(dialogName);
- AppToaster.show({
- message: formatMessage({
- id: 'the_exchange_rate_has_been_created_successfully',
- }),
- intent: Intent.SUCCESS,
- });
- setSubmitting(false);
- queryCache.invalidateQueries('exchange-rates-table');
- })
- .catch((errors) => {
- if (
- errors.find((e) => e.type === 'EXCHANGE.RATE.DATE.PERIOD.DEFINED')
- ) {
- setErrors({
- exchange_rate: formatMessage({
- id:
- 'there_is_exchange_rate_in_this_date_with_the_same_currency',
- }),
- });
- }
- });
- }
- },
- });
-
- const handleClose = useCallback(() => {
- closeDialog(dialogName);
- resetForm();
- }, [dialogName, closeDialog]);
-
- const handleDateChange = useCallback(
- (date_filed) => (date) => {
- const formatted = moment(date).format('YYYY-MM-DD');
- setFieldValue(date_filed, formatted);
- },
- [setFieldValue],
- );
-
- const onItemsSelect = useCallback(
- (filedName) => {
- return (filed) => {
- setSelectedItems({
- ...selectedItems,
- [filedName]: filed,
- });
- setFieldValue(filedName, filed.currency_code);
- };
- },
- [setFieldValue, selectedItems],
- );
return (
-
-
-
+
+
+
);
}
-export default compose(
- withDialogActions,
- withExchangeRatesActions,
- withExchangeRateDetail,
- withCurrenciesActions,
- withCurrencies(({ currenciesList }) => ({ currenciesList })),
-)(ExchangeRateFormDialogContent);
+export default compose(withExchangeRateDetail)(ExchangeRateFormDialogContent);
diff --git a/client/src/containers/Dialogs/ExchangeRateFormDialog/ExchangeRateFormFields.js b/client/src/containers/Dialogs/ExchangeRateFormDialog/ExchangeRateFormFields.js
new file mode 100644
index 000000000..1b5ae13a9
--- /dev/null
+++ b/client/src/containers/Dialogs/ExchangeRateFormDialog/ExchangeRateFormFields.js
@@ -0,0 +1,88 @@
+import React from 'react';
+import { Classes, FormGroup, InputGroup, Position } from '@blueprintjs/core';
+import { FastField } from 'formik';
+import { DateInput } from '@blueprintjs/datetime';
+import { FormattedMessage as T } from 'react-intl';
+import {
+ momentFormatter,
+ tansformDateValue,
+ handleDateChange,
+ inputIntent,
+} from 'utils';
+import {
+ ErrorMessage,
+ FieldRequiredHint,
+ CurrencySelectList,
+} from 'components';
+import { useExchangeRateFromContext } from './ExchangeRateFormProvider';
+
+import classNames from 'classnames';
+
+export default function ExchangeRateFormFields() {
+ const { action, currencies } = useExchangeRateFromContext();
+
+ return (
+
+ {/* ----------- Date ----------- */}
+
+ {({ form, field: { value }, meta: { error, touched } }) => (
+ }
+ labelInfo={FieldRequiredHint}
+ className={classNames('form-group--select-list', Classes.FILL)}
+ intent={inputIntent({ error, touched })}
+ helperText={}
+ inline={true}
+ >
+ {
+ form.setFieldValue('date', formattedDate);
+ })}
+ popoverProps={{ position: Position.BOTTOM, minimal: true }}
+ disabled={action === 'edit'}
+ />
+
+ )}
+
+ {/* ----------- Currency Code ----------- */}
+
+ {({ form, field: { value }, meta: { error, touched } }) => (
+ }
+ labelInfo={}
+ className={classNames('form-group--currency', Classes.FILL)}
+ intent={inputIntent({ error, touched })}
+ helperText={}
+ inline={true}
+ >
+ {
+ form.setFieldValue('currency_code', currency_code);
+ }}
+ disabled={action === 'edit'}
+ />
+
+ )}
+
+
+ {/*------------ Exchange Rate -----------*/}
+
+ {({ form, field, meta: { error, touched } }) => (
+ }
+ labelInfo={}
+ intent={inputIntent({ error, touched })}
+ helperText={}
+ inline={true}
+ >
+
+
+ )}
+
+
+ );
+}
diff --git a/client/src/containers/Dialogs/ExchangeRateFormDialog/ExchangeRateFormFooter.js b/client/src/containers/Dialogs/ExchangeRateFormDialog/ExchangeRateFormFooter.js
new file mode 100644
index 000000000..d0790a5f6
--- /dev/null
+++ b/client/src/containers/Dialogs/ExchangeRateFormDialog/ExchangeRateFormFooter.js
@@ -0,0 +1,35 @@
+import React from 'react';
+import { useFormikContext } from 'formik';
+
+import { Button, Classes, Intent } from '@blueprintjs/core';
+import { FormattedMessage as T } from 'react-intl';
+import { useExchangeRateFromContext } from './ExchangeRateFormProvider';
+import withDialogActions from 'containers/Dialog/withDialogActions';
+import { compose } from 'utils';
+
+function ExchangeRateFormFooter({
+ // #withDialogActions
+ closeDialog,
+}) {
+ const { isSubmitting } = useFormikContext();
+ const { dialogName, action } = useExchangeRateFromContext();
+
+ const handleClose = () => {
+ closeDialog(dialogName);
+ };
+
+ return (
+
+
+
+
+
+
+ );
+}
+
+export default compose(withDialogActions)(ExchangeRateFormFooter);
diff --git a/client/src/containers/Dialogs/ExchangeRateFormDialog/ExchangeRateFormProvider.js b/client/src/containers/Dialogs/ExchangeRateFormDialog/ExchangeRateFormProvider.js
new file mode 100644
index 000000000..3c2cf8829
--- /dev/null
+++ b/client/src/containers/Dialogs/ExchangeRateFormDialog/ExchangeRateFormProvider.js
@@ -0,0 +1,52 @@
+import React, { createContext, useContext } from 'react';
+import {
+ useCreateExchangeRate,
+ useEdiExchangeRate,
+ useCurrencies,
+ useExchangeRates,
+} from 'hooks/query';
+import { DialogContent } from 'components';
+
+const ExchangeRateFormContext = createContext();
+
+/**
+ * Exchange rate Form page provider.
+ */
+function ExchangeRateFormProvider({
+ exchangeRate,
+ action,
+ dialogName,
+ ...props
+}) {
+ // Create and edit exchange rate mutations.
+ const { mutateAsync: createExchangeRateMutate } = useCreateExchangeRate();
+ const { mutateAsync: editExchangeRateMutate } = useEdiExchangeRate();
+
+ // Load Currencies list.
+ const { data: currencies, isFetching: isCurrenciesLoading } = useCurrencies();
+ const { isFetching: isExchangeRatesLoading } = useExchangeRates();
+
+ const isNewMode = !exchangeRate;
+
+ // Provider state.
+ const provider = {
+ createExchangeRateMutate,
+ editExchangeRateMutate,
+ dialogName,
+ exchangeRate,
+ action,
+ currencies,
+ isExchangeRatesLoading,
+ isNewMode,
+ };
+
+ return (
+
+
+
+ );
+}
+
+const useExchangeRateFromContext = () => useContext(ExchangeRateFormContext);
+
+export { ExchangeRateFormProvider, useExchangeRateFromContext };
diff --git a/client/src/containers/Dialogs/ExchangeRateFormDialog/index.js b/client/src/containers/Dialogs/ExchangeRateFormDialog/index.js
index 044447c54..3c4c8f6ff 100644
--- a/client/src/containers/Dialogs/ExchangeRateFormDialog/index.js
+++ b/client/src/containers/Dialogs/ExchangeRateFormDialog/index.js
@@ -16,7 +16,7 @@ const ExchangeRateFormDialogContent = lazy(() =>
*/
function ExchangeRateFormDialog({
dialogName,
- payload = { action: '', id: null },
+ payload = { action: '', id: null , exchangeRate:"" },
isOpen,
}) {
return (
@@ -38,7 +38,7 @@ function ExchangeRateFormDialog({
diff --git a/client/src/hooks/query/index.js b/client/src/hooks/query/index.js
index 640214bfd..98eb3dc85 100644
--- a/client/src/hooks/query/index.js
+++ b/client/src/hooks/query/index.js
@@ -17,4 +17,5 @@ export * from './receipts';
export * from './paymentReceives';
export * from './paymentMades';
export * from './settings';
-export * from './users';
\ No newline at end of file
+export * from './users';
+export * from './exchangeRates';
\ No newline at end of file