diff --git a/client/src/components/DialogsContainer.js b/client/src/components/DialogsContainer.js
index 897653a12..90693c190 100644
--- a/client/src/components/DialogsContainer.js
+++ b/client/src/components/DialogsContainer.js
@@ -6,7 +6,7 @@ import UserFormDialog from 'containers/Dialogs/UserFormDialog';
// import ItemCategoryDialog from 'containers/Dialogs/ItemCategoryDialog';
import CurrencyFormDialog from 'containers/Dialogs/CurrencyFormDialog';
// import InviteUserDialog from 'containers/Dialogs/InviteUserDialog';
-// import ExchangeRateDialog from 'containers/Dialogs/ExchangeRateDialog';
+import ExchangeRateFormDialog from 'containers/Dialogs/ExchangeRateFormDialog';
import JournalNumberDialog from 'containers/Dialogs/JournalNumberDialog';
import BillNumberDialog from 'containers/Dialogs/BillNumberDialog';
import PaymentNumberDialog from 'containers/Dialogs/PaymentNumberDialog';
@@ -25,6 +25,7 @@ export default function DialogsContainer() {
+
);
}
diff --git a/client/src/components/MoneyExchangeRate.js b/client/src/components/MoneyExchangeRate.js
new file mode 100644
index 000000000..488a696ef
--- /dev/null
+++ b/client/src/components/MoneyExchangeRate.js
@@ -0,0 +1,5 @@
+import React from 'react';
+import { formattedExchangeRate } from 'utils';
+export default function MoneyExchangeRate({ amount, currency }) {
+ return {formattedExchangeRate(amount, currency)};
+}
diff --git a/client/src/components/index.js b/client/src/components/index.js
index eec72812c..fd027601e 100644
--- a/client/src/components/index.js
+++ b/client/src/components/index.js
@@ -29,6 +29,7 @@ import CategoriesSelectList from './CategoriesSelectList';
import Row from './Grid/Row';
import Col from './Grid/Col';
import CloudLoadingIndicator from './CloudLoadingIndicator';
+import MoneyExchangeRate from './MoneyExchangeRate';
const Hint = FieldHint;
@@ -65,4 +66,5 @@ export {
Col,
Row,
CloudLoadingIndicator,
+ MoneyExchangeRate,
};
diff --git a/client/src/containers/Dialogs/ExchangeRateDialog.container.js b/client/src/containers/Dialogs/ExchangeRateDialog.container.js
index 8c9373994..d107d266b 100644
--- a/client/src/containers/Dialogs/ExchangeRateDialog.container.js
+++ b/client/src/containers/Dialogs/ExchangeRateDialog.container.js
@@ -1,33 +1,33 @@
-import { connect } from 'react-redux';
-import { compose } from 'utils';
+// import { connect } from 'react-redux';
+// import { compose } from 'utils';
-import withDialogActions from 'containers/Dialog/withDialogActions';
-import withDialogRedux from 'components/DialogReduxConnect';
-import withExchangeRateDetail from 'containers/ExchangeRates/withExchangeRateDetail';
-import withExchangeRatesActions from 'containers/ExchangeRates/withExchangeRatesActions';
-import withExchangeRates from 'containers/ExchangeRates/withExchangeRates';
-import withCurrencies from 'containers/Currencies/withCurrencies';
+// import withDialogActions from 'containers/Dialog/withDialogActions';
+// import withDialogRedux from 'components/DialogReduxConnect';
+// import withExchangeRateDetail from 'containers/ExchangeRates/withExchangeRateDetail';
+// import withExchangeRatesActions from 'containers/ExchangeRates/withExchangeRatesActions';
+// import withExchangeRates from 'containers/ExchangeRates/withExchangeRates';
+// import withCurrencies from 'containers/Currencies/withCurrencies';
-const mapStateToProps = (state, props) => ({
- dialogName: 'exchangeRate-form',
- exchangeRateId:
- props.payload.action === 'edit' && props.payload.id
- ? props.payload.id
- : null,
-});
+// const mapStateToProps = (state, props) => ({
+// dialogName: 'exchangeRate-form',
+// exchangeRateId:
+// props.payload.action === 'edit' && props.payload.id
+// ? props.payload.id
+// : null,
+// });
-const withExchangeRateDialog = connect(mapStateToProps);
+// const withExchangeRateDialog = connect(mapStateToProps);
-export default compose(
- withDialogRedux(null, 'exchangeRate-form'),
- withExchangeRateDialog,
- withCurrencies(({ currenciesList }) => ({
- currenciesList,
- })),
- withExchangeRatesActions,
- withExchangeRateDetail,
- withExchangeRates(({ exchangeRatesList }) => ({
- exchangeRatesList,
- })),
- withDialogActions,
-);
+// export default compose(
+// withDialogRedux(null, 'exchangeRate-form'),
+// withExchangeRateDialog,
+// withCurrencies(({ currenciesList }) => ({
+// currenciesList,
+// })),
+// withExchangeRatesActions,
+// withExchangeRateDetail,
+// withExchangeRates(({ exchangeRatesList }) => ({
+// exchangeRatesList,
+// })),
+// withDialogActions,
+// );
diff --git a/client/src/containers/Dialogs/ExchangeRateFormDialog.js b/client/src/containers/Dialogs/ExchangeRateFormDialog.js
new file mode 100644
index 000000000..044447c54
--- /dev/null
+++ b/client/src/containers/Dialogs/ExchangeRateFormDialog.js
@@ -0,0 +1,48 @@
+import React, { lazy } from 'react';
+import { FormattedMessage as T, useIntl } from 'react-intl';
+import {
+ Dialog,
+ DialogSuspense,
+} from 'components';
+import withDialogRedux from 'components/DialogReduxConnect';
+import { compose } from 'utils';
+
+const ExchangeRateFormDialogContent = lazy(() =>
+ import('./ExchangeRateFormDialogContent'),
+);
+
+/**
+ * Exchange rate form dialog.
+ */
+function ExchangeRateFormDialog({
+ dialogName,
+ payload = { action: '', id: null },
+ isOpen,
+}) {
+ return (
+
+ ) : (
+
+ )
+ }
+ className={'dialog--exchangeRate-form'}
+ isOpen={isOpen}
+ autoFocus={true}
+ canEscapeKeyClose={true}
+ >
+
+
+
+
+ );
+}
+
+export default compose(withDialogRedux())(ExchangeRateFormDialog);
diff --git a/client/src/containers/Dialogs/ExchangeRateDialog.js b/client/src/containers/Dialogs/ExchangeRateFormDialogContent.js
similarity index 78%
rename from client/src/containers/Dialogs/ExchangeRateDialog.js
rename to client/src/containers/Dialogs/ExchangeRateFormDialogContent.js
index 1f1e77f9c..0b9812786 100644
--- a/client/src/containers/Dialogs/ExchangeRateDialog.js
+++ b/client/src/containers/Dialogs/ExchangeRateFormDialogContent.js
@@ -10,25 +10,32 @@ import {
} from '@blueprintjs/core';
import { pick } from 'lodash';
import * as Yup from 'yup';
-import { FormattedMessage as T, useIntl } from 'react-intl';
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, Dialog, ErrorMessage, ListSelect } from 'components';
+import {
+ AppToaster,
+ Dialog,
+ ErrorMessage,
+ ListSelect,
+ DialogContent,
+ FieldRequiredHint,
+} from 'components';
import classNames from 'classnames';
-import withExchangeRatesDialog from './ExchangeRateDialog.container';
+import withExchangeRateDetail from 'containers/ExchangeRates/withExchangeRateDetail';
+import withExchangeRatesActions from 'containers/ExchangeRates/withExchangeRatesActions';
-/**
- * Exchange rate dialog.
- */
-function ExchangeRateDialog({
- dialogName,
- payload = {},
- isOpen,
+import withCurrencies from 'containers/Currencies/withCurrencies';
+import withCurrenciesActions from 'containers/Currencies/withCurrenciesActions';
+import withDialogActions from 'containers/Dialog/withDialogActions';
- // #withDialog
+import { compose } from 'utils';
+
+function ExchangeRateFormDialogContent({
+ // #withDialogActions
closeDialog,
// #withCurrencies
@@ -39,12 +46,25 @@ function ExchangeRateDialog({
// #withExchangeRatesActions
requestSubmitExchangeRate,
- requestFetchExchangeRates,
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()
@@ -66,12 +86,6 @@ function ExchangeRateDialog({
[],
);
- const fetchExchangeRatesDialog = useQuery(
- 'exchange-rates-dialog',
- () => requestFetchExchangeRates(),
- { manual: true },
- );
-
const {
values,
touched,
@@ -85,12 +99,12 @@ function ExchangeRateDialog({
enableReinitialize: true,
validationSchema,
initialValues: {
- ...(payload.action === 'edit' &&
- pick(exchangeRate, Object.keys(initialValues))),
+ ...initialValues,
+ ...(action === 'edit' && pick(exchangeRate, Object.keys(initialValues))),
},
onSubmit: (values, { setSubmitting, setErrors }) => {
- if (payload.action === 'edit') {
- requestEditExchangeRate(payload.id, values)
+ if (action === 'edit') {
+ requestEditExchangeRate(exchangeRateId, values)
.then((response) => {
closeDialog(dialogName);
AppToaster.show({
@@ -100,7 +114,7 @@ function ExchangeRateDialog({
intent: Intent.SUCCESS,
});
setSubmitting(false);
- queryCache.invalidateQueries('exchange-rates-dialog');
+ queryCache.invalidateQueries('exchange-rates-table');
})
.catch((error) => {
setSubmitting(false);
@@ -134,20 +148,10 @@ function ExchangeRateDialog({
},
});
- const requiredSpan = useMemo(() => *, []);
-
const handleClose = useCallback(() => {
closeDialog(dialogName);
- }, [dialogName, closeDialog]);
-
- const onDialogClosed = useCallback(() => {
resetForm();
- closeDialog(dialogName);
- }, [closeDialog, dialogName, resetForm]);
-
- const onDialogOpening = useCallback(() => {
- fetchExchangeRatesDialog.refetch();
- }, [fetchExchangeRatesDialog]);
+ }, [dialogName, closeDialog]);
const handleDateChange = useCallback(
(date_filed) => (date) => {
@@ -197,31 +201,13 @@ function ExchangeRateDialog({
}, []);
return (
-
- ) : (
-
- )
- }
- className={classNames(
- { 'dialog--loading': fetchExchangeRatesDialog.isFetching },
- 'dialog--exchangeRate-form',
- )}
- isOpen={isOpen}
- onClosed={onDialogClosed}
- onOpening={onDialogOpening}
- isLoading={fetchExchangeRatesDialog.isFetching}
- onClose={handleClose}
- >
+
-
+
);
}
-export default withExchangeRatesDialog(ExchangeRateDialog);
+export default compose(
+ withDialogActions,
+ withExchangeRatesActions,
+ withExchangeRateDetail,
+ withCurrenciesActions,
+ withCurrencies(({ currenciesList }) => ({ currenciesList })),
+)(ExchangeRateFormDialogContent);
diff --git a/client/src/containers/ExchangeRates/ExchangeRateTable.js b/client/src/containers/ExchangeRates/ExchangeRateTable.js
index 5badc2075..fb363d06b 100644
--- a/client/src/containers/ExchangeRates/ExchangeRateTable.js
+++ b/client/src/containers/ExchangeRates/ExchangeRateTable.js
@@ -1,4 +1,4 @@
-import React, { useCallback, useMemo, useState, useEffect } from 'react';
+import React, { useCallback, useMemo, useState } from 'react';
import {
Button,
Popover,
@@ -10,7 +10,7 @@ import {
import { FormattedMessage as T, useIntl } from 'react-intl';
import moment from 'moment';
-import { DataTable, Money, Icon } from 'components';
+import { DataTable, Icon, MoneyExchangeRate } from 'components';
import LoadingIndicator from 'components/LoadingIndicator';
import withDialogActions from 'containers/Dialog/withDialogActions';
@@ -31,7 +31,6 @@ function ExchangeRateTable({
loading,
onFetchData,
onDeleteExchangeRate,
- onEditExchangeRate,
onSelectedRowsChange,
}) {
const [initialMount, setInitialMount] = useState(false);
@@ -52,20 +51,25 @@ function ExchangeRateTable({
(ExchangeRate) => (
),
- [handelEditExchangeRate, handleDeleteExchangeRate],
+ [handelEditExchangeRate, handleDeleteExchangeRate, formatMessage],
);
+ const rowContextMenu = (cell) => {
+ return actionMenuList(cell.row.original);
+ };
+
const columns = useMemo(
() => [
{
@@ -84,7 +88,9 @@ function ExchangeRateTable({
{
id: 'exchange_rate',
Header: formatMessage({ id: 'exchange_rate' }),
- accessor: (r) => ,
+ accessor: (r) => (
+
+ ),
className: 'exchange_rate',
width: 150,
},
@@ -94,14 +100,13 @@ function ExchangeRateTable({
Cell: ({ cell }) => (
} />
),
className: 'actions',
width: 50,
- disableResizing: false,
},
],
[actionMenuList, formatMessage],
@@ -143,7 +148,7 @@ function ExchangeRateTable({
expandable={true}
treeGraph={true}
onSelectedRowsChange={handelSelectedRowsChange}
- spinnerProps={{ size: 30 }}
+ rowContextMenu={rowContextMenu}
/>
);
diff --git a/client/src/lang/en/index.js b/client/src/lang/en/index.js
index 54ff4d23c..658678f20 100644
--- a/client/src/lang/en/index.js
+++ b/client/src/lang/en/index.js
@@ -73,7 +73,7 @@ export default {
new_currency: 'New Currency',
currency_name: 'Currency Name',
currency_code: 'Currency Code',
- select_currency_code: 'select Currency Code',
+ select_currency_code: 'Select Currency Code',
edit_exchange_rate: 'Edit Exchange Rate',
new_exchange_rate: 'New Exchange Rate',
delete_exchange_rate: 'Delete Exchange Rate',
diff --git a/client/src/utils.js b/client/src/utils.js
index e30558cca..dbc506aeb 100644
--- a/client/src/utils.js
+++ b/client/src/utils.js
@@ -4,20 +4,19 @@ import Currency from 'js-money/lib/currency';
import PProgress from 'p-progress';
import accounting from 'accounting';
-
export function removeEmptyFromObject(obj) {
obj = Object.assign({}, obj);
var keys = Object.keys(obj);
- keys.forEach(function(key) {
+ keys.forEach(function (key) {
const value = obj[key];
- if (value === '' || value === null || value === undefined ) {
+ if (value === '' || value === null || value === undefined) {
delete obj[key];
}
});
return obj;
-};
+}
export const optionsMapToArray = (optionsMap, service = '') => {
return Object.keys(optionsMap).map((optionKey) => {
@@ -27,7 +26,7 @@ export const optionsMapToArray = (optionsMap, service = '') => {
key: service ? `${service}_${optionKey}` : `${optionKey}`,
value: optionValue,
};
- })
+ });
};
export const optionsArrayToMap = (optionsArray) => {
@@ -37,7 +36,7 @@ export const optionsArrayToMap = (optionsArray) => {
}, {});
};
-export function numberComma(number){
+export function numberComma(number) {
number = typeof number === 'number' ? String(number) : number;
const parts = number.split('.');
@@ -51,11 +50,11 @@ export function numberComma(number){
export const momentFormatter = (format) => {
return {
- formatDate: date => moment(date).format(format),
- parseDate: str => moment(str, format).toDate(),
+ formatDate: (date) => moment(date).format(format),
+ parseDate: (str) => moment(str, format).toDate(),
placeholder: `${format}`,
};
-}
+};
/** Event handler that exposes the target element's value as a boolean. */
export const handleBooleanChange = (handler) => {
@@ -69,7 +68,7 @@ export const handleStringChange = (handler) => {
/** Event handler that exposes the target element's value as a number. */
export const handleNumberChange = (handler) => {
- return handleStringChange(value => handler(+value));
+ return handleStringChange((value) => handler(+value));
};
export const objectKeysTransform = (obj, transform) => {
@@ -81,29 +80,35 @@ export const objectKeysTransform = (obj, transform) => {
};
export const compose = (...funcs) =>
- funcs.reduce((a, b) => (...args) => a(b(...args)), arg => arg);
+ funcs.reduce(
+ (a, b) => (...args) => a(b(...args)),
+ (arg) => arg,
+ );
export const getObjectDiff = (a, b) => {
- return _.reduce(a, (result, value, key) => {
- return _.isEqual(value, b[key]) ?
- result : result.concat(key);
- }, []);
-}
+ return _.reduce(
+ a,
+ (result, value, key) => {
+ return _.isEqual(value, b[key]) ? result : result.concat(key);
+ },
+ [],
+ );
+};
export const parseDateRangeQuery = (keyword) => {
const queries = {
- 'today': {
+ today: {
range: 'day',
},
- 'this_year': {
+ this_year: {
range: 'year',
},
- 'this_month': {
- range: 'month'
+ this_month: {
+ range: 'month',
+ },
+ this_week: {
+ range: 'week',
},
- 'this_week': {
- range: 'week'
- }
};
if (typeof queries[keyword] === 'undefined') {
@@ -117,7 +122,6 @@ export const parseDateRangeQuery = (keyword) => {
};
};
-
export const defaultExpanderReducer = (tableRows, level) => {
let currentLevel = 1;
const expended = [];
@@ -126,7 +130,7 @@ export const defaultExpanderReducer = (tableRows, level) => {
return rows.forEach((row, index) => {
const _index = parentIndex ? `${parentIndex}.${index}` : `${index}`;
expended[_index] = true;
-
+
if (row.children && currentLevel < level) {
walker(row.children, _index);
}
@@ -135,7 +139,7 @@ export const defaultExpanderReducer = (tableRows, level) => {
};
walker(tableRows);
return expended;
-}
+};
export function formattedAmount(cents, currency) {
const { symbol, decimal_digits: precision } = Currency[currency];
@@ -143,16 +147,27 @@ export function formattedAmount(cents, currency) {
return accounting.formatMoney(amount, { symbol, precision });
}
+export function formattedExchangeRate(amount, currency) {
+ const options = {
+ style: 'currency',
+ currency: currency,
+ minimumFractionDigits: 2,
+ };
-export const ConditionalWrapper = ({ condition, wrapper, children }) =>
+ const formatter = new Intl.NumberFormat(undefined, options);
+
+ return formatter.format(amount);
+}
+
+export const ConditionalWrapper = ({ condition, wrapper, children }) =>
condition ? wrapper(children) : children;
export const checkRequiredProperties = (obj, properties) => {
return properties.some((prop) => {
const value = obj[prop];
- return (value === '' || value === null || value === undefined);
- })
-}
+ return value === '' || value === null || value === undefined;
+ });
+};
export const saveFilesInAsync = (files, actionCb, extraTasks) => {
const opers = [];
@@ -164,13 +179,17 @@ export const saveFilesInAsync = (files, actionCb, extraTasks) => {
actionCb(formData, file, (requestProgress) => {
progress(requestProgress);
})
- .then((data) => { resolve(data); })
- .catch(error => { reject(error); })
+ .then((data) => {
+ resolve(data);
+ })
+ .catch((error) => {
+ reject(error);
+ });
});
opers.push(oper);
});
return PProgress.all(opers);
-}
+};
export const firstLettersArgs = (...args) => {
let letters = [];
@@ -181,22 +200,18 @@ export const firstLettersArgs = (...args) => {
}
});
return letters.join('').toUpperCase();
-}
-
+};
export const uniqueMultiProps = (items, props) => {
return _.uniqBy(items, (item) => {
return JSON.stringify(_.pick(item, props));
});
-}
-
+};
export const transformUpdatedRows = (rows, rowIndex, columnIdOrObj, value) => {
- const columnId =
- typeof columnIdOrObj !== 'object' ? columnIdOrObj : null;
+ const columnId = typeof columnIdOrObj !== 'object' ? columnIdOrObj : null;
- const updateTable =
- typeof columnIdOrObj === 'object' ? columnIdOrObj : null;
+ const updateTable = typeof columnIdOrObj === 'object' ? columnIdOrObj : null;
const newData = updateTable ? updateTable : { [columnId]: value };
@@ -206,7 +221,7 @@ export const transformUpdatedRows = (rows, rowIndex, columnIdOrObj, value) => {
}
return { ...row };
});
-}
+};
export const tansformDateValue = (date) => {
return moment(date).toDate() || new Date();
@@ -222,7 +237,7 @@ export const repeatValue = (value, len) => {
export const flatToNestedArray = (
data,
- config = { id: 'id', parentId: 'parent_id' }
+ config = { id: 'id', parentId: 'parent_id' },
) => {
const map = {};
const nestedArray = [];
@@ -246,4 +261,4 @@ export const flatToNestedArray = (
export const orderingLinesIndexes = (lines, attribute = 'index') => {
return lines.map((line, index) => ({ ...line, [attribute]: index + 1 }));
-};
\ No newline at end of file
+};