Merge remote-tracking branch 'origin/master'

This commit is contained in:
Ahmed Bouhuolia
2020-11-02 21:42:58 +02:00
19 changed files with 388 additions and 196 deletions

View File

@@ -76,6 +76,7 @@ export default function AccountsSelectList({
popoverProps={{ minimal: true }}
filterable={true}
onItemSelect={onAccountSelect}
disabled={disabled}
>
<Button
disabled={disabled}

View File

@@ -1,13 +1,12 @@
import React, { useCallback } from 'react';
import {
ListSelect,
} from 'components';
import { ListSelect } from 'components';
export default function AccountsTypesSelect({
accountsTypes,
selectedTypeId,
defaultSelectText = 'Select account type',
onTypeSelected,
disabled = false,
...restProps
}) {
// Filters accounts types items.
@@ -28,7 +27,8 @@ export default function AccountsTypesSelect({
};
const items = accountsTypes.map((type) => ({
id: type.id, label: type.label,
id: type.id,
label: type.label,
}));
return (
@@ -40,7 +40,8 @@ export default function AccountsTypesSelect({
defaultText={defaultSelectText}
onItemSelect={handleItemSelected}
itemPredicate={filterAccountTypeItems}
disabled={disabled}
{...restProps}
/>
);
}
}

View File

@@ -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() {
<InvoiceNumberDialog dialogName={'invoice-number-form'} />
<CurrencyFormDialog dialogName={'currency-form'} />
<UserFormDialog dialogName={'user-form'} />
<ExchangeRateFormDialog dialogName={'exchangeRate-form'} />
</div>
);
}

View File

@@ -15,7 +15,7 @@ export default function ListSelect({
initialSelectedItem,
onItemSelect,
disabled = false,
...selectProps
}) {
const selectedItemObj = useMemo(
@@ -24,7 +24,10 @@ export default function ListSelect({
);
const selectedInitialItem = useMemo(
() => selectProps.items.find((i) => i[selectedItemProp] === initialSelectedItem),
() =>
selectProps.items.find(
(i) => i[selectedItemProp] === initialSelectedItem,
),
[initialSelectedItem],
);
@@ -65,10 +68,12 @@ export default function ListSelect({
onItemSelect={handleItemSelect}
{...selectProps}
noResults={noResults}
disabled={disabled}
>
<Button
text={currentItem ? currentItem[labelProp] : defaultText}
loading={isLoading}
disabled={disabled}
{...buttonProps}
/>
</Select>

View File

@@ -0,0 +1,5 @@
import React from 'react';
import { formattedExchangeRate } from 'utils';
export default function MoneyExchangeRate({ amount, currency }) {
return <span>{formattedExchangeRate(amount, currency)}</span>;
}

View File

@@ -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,
};

View File

@@ -282,7 +282,7 @@ function AccountFormDialogContent({
selectedTypeId={values.account_type_id}
defaultSelectText={<T id={'select_account_type'} />}
onTypeSelected={onChangeAccountType}
buttonProps={{ disabled: action === 'edit' }}
disabled={action === 'edit'}
popoverProps={{ minimal: true }}
/>
</FormGroup>

View File

@@ -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,
// );

View File

@@ -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 (
<Dialog
name={dialogName}
title={
payload.action === 'edit' ? (
<T id={'edit_exchange_rate'} />
) : (
<T id={'new_exchange_rate'} />
)
}
className={'dialog--exchangeRate-form'}
isOpen={isOpen}
autoFocus={true}
canEscapeKeyClose={true}
>
<DialogSuspense>
<ExchangeRateFormDialogContent
dialogName={dialogName}
action={payload.action}
exchangeRateId={payload.id}
/>
</DialogSuspense>
</Dialog>
);
}
export default compose(withDialogRedux())(ExchangeRateFormDialog);

View File

@@ -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(() => <span class="required">*</span>, []);
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 (
<Dialog
name={dialogName}
title={
payload.action === 'edit' ? (
<T id={'edit_exchange_rate'} />
) : (
<T id={'new_exchange_rate'} />
)
}
className={classNames(
{ 'dialog--loading': fetchExchangeRatesDialog.isFetching },
'dialog--exchangeRate-form',
)}
isOpen={isOpen}
onClosed={onDialogClosed}
onOpening={onDialogOpening}
isLoading={fetchExchangeRatesDialog.isFetching}
onClose={handleClose}
>
<DialogContent isLoading={fetchCurrencies.isFetching}>
<form onSubmit={handleSubmit}>
<div className={Classes.DIALOG_BODY}>
<FormGroup
label={<T id={'date'} />}
inline={true}
labelInfo={requiredSpan}
labelInfo={FieldRequiredHint}
intent={errors.date && touched.date && Intent.DANGER}
helperText={<ErrorMessage name="date" {...{ errors, touched }} />}
>
@@ -231,33 +217,12 @@ function ExchangeRateDialog({
value={tansformDateValue(values.date)}
onChange={handleDateChange('date')}
popoverProps={{ position: Position.BOTTOM, minimal: true }}
disabled={payload.action === 'edit'}
disabled={action === 'edit'}
/>
</FormGroup>
<FormGroup
label={<T id={'exchange_rate'} />}
labelInfo={requiredSpan}
intent={
errors.exchange_rate && touched.exchange_rate && Intent.DANGER
}
helperText={
<ErrorMessage name="exchange_rate" {...{ errors, touched }} />
}
inline={true}
>
<InputGroup
medium={true}
intent={
errors.exchange_rate && touched.exchange_rate && Intent.DANGER
}
{...getFieldProps('exchange_rate')}
/>
</FormGroup>
<FormGroup
label={<T id={'currency_code'} />}
labelInfo={requiredSpan}
labelInfo={FieldRequiredHint}
className={classNames('form-group--select-list', Classes.FILL)}
inline={true}
intent={
@@ -278,6 +243,26 @@ function ExchangeRateDialog({
selectedItemProp={'currency_code'}
defaultText={<T id={'select_currency_code'} />}
labelProp={'currency_code'}
disabled={action === 'edit'}
/>
</FormGroup>
<FormGroup
label={<T id={'exchange_rate'} />}
labelInfo={FieldRequiredHint}
intent={
errors.exchange_rate && touched.exchange_rate && Intent.DANGER
}
helperText={
<ErrorMessage name="exchange_rate" {...{ errors, touched }} />
}
inline={true}
>
<InputGroup
medium={true}
intent={
errors.exchange_rate && touched.exchange_rate && Intent.DANGER
}
{...getFieldProps('exchange_rate')}
/>
</FormGroup>
</div>
@@ -291,17 +276,19 @@ function ExchangeRateDialog({
type="submit"
disabled={isSubmitting}
>
{payload.action === 'edit' ? (
<T id={'edit'} />
) : (
<T id={'submit'} />
)}
{action === 'edit' ? <T id={'edit'} /> : <T id={'submit'} />}
</Button>
</div>
</div>
</form>
</Dialog>
</DialogContent>
);
}
export default withExchangeRatesDialog(ExchangeRateDialog);
export default compose(
withDialogActions,
withExchangeRatesActions,
withExchangeRateDetail,
withCurrenciesActions,
withCurrencies(({ currenciesList }) => ({ currenciesList })),
)(ExchangeRateFormDialogContent);

View File

@@ -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';
@@ -23,7 +23,7 @@ function ExchangeRateTable({
// #withExchangeRates
exchangeRatesList,
exchangeRatesLoading,
exchangeRatesPageination,
// #withDialogActions.
openDialog,
@@ -31,7 +31,6 @@ function ExchangeRateTable({
loading,
onFetchData,
onDeleteExchangeRate,
onEditExchangeRate,
onSelectedRowsChange,
}) {
const [initialMount, setInitialMount] = useState(false);
@@ -52,20 +51,25 @@ function ExchangeRateTable({
(ExchangeRate) => (
<Menu>
<MenuItem
text={<T id={'edit_exchange_rate'} />}
icon={<Icon icon="pen-18" />}
text={formatMessage({ id: 'edit_exchange_rate' })}
onClick={handelEditExchangeRate(ExchangeRate)}
/>
<MenuItem
text={<T id={'delete_exchange_rate'} />}
text={formatMessage({ id: 'delete_exchange_rate' })}
intent={Intent.DANGER}
onClick={handleDeleteExchangeRate(ExchangeRate)}
icon={<Icon icon="trash-16" iconSize={16} />}
/>
</Menu>
),
[handelEditExchangeRate, handleDeleteExchangeRate],
[handelEditExchangeRate, handleDeleteExchangeRate, formatMessage],
);
const rowContextMenu = (cell) => {
return actionMenuList(cell.row.original);
};
const columns = useMemo(
() => [
{
@@ -84,7 +88,12 @@ function ExchangeRateTable({
{
id: 'exchange_rate',
Header: formatMessage({ id: 'exchange_rate' }),
accessor: (r) => <Money amount={r.exchange_rate} currency={'USD'} />,
accessor: (r) => (
<MoneyExchangeRate
amount={r.exchange_rate}
currency={r.currency_code}
/>
),
className: 'exchange_rate',
width: 150,
},
@@ -94,14 +103,13 @@ function ExchangeRateTable({
Cell: ({ cell }) => (
<Popover
content={actionMenuList(cell.row.original)}
position={Position.RIGHT_BOTTOM}
position={Position.RIGHT_TOP}
>
<Button icon={<Icon icon="more-h-16" iconSize={16} />} />
</Popover>
),
className: 'actions',
width: 50,
disableResizing: false,
},
],
[actionMenuList, formatMessage],
@@ -143,7 +151,11 @@ function ExchangeRateTable({
expandable={true}
treeGraph={true}
onSelectedRowsChange={handelSelectedRowsChange}
spinnerProps={{ size: 30 }}
rowContextMenu={rowContextMenu}
pagination={true}
pagesCount={exchangeRatesPageination.pagesCount}
initialPageSize={exchangeRatesPageination.pageSize}
initialPageIndex={exchangeRatesPageination.page - 1}
/>
</LoadingIndicator>
);
@@ -152,8 +164,15 @@ function ExchangeRateTable({
export default compose(
withDialogActions,
withExchangeRatesActions,
withExchangeRates(({ exchangeRatesList, exchangeRatesLoading }) => ({
exchangeRatesList,
exchangeRatesLoading,
})),
withExchangeRates(
({
exchangeRatesList,
exchangeRatesLoading,
exchangeRatesPageination,
}) => ({
exchangeRatesList,
exchangeRatesLoading,
exchangeRatesPageination,
}),
),
)(ExchangeRateTable);

View File

@@ -1,11 +1,24 @@
import { connect } from 'react-redux';
import { getExchangeRatesList } from 'store/ExchangeRate/exchange.selector';
import {
getExchangeRatesList,
getExchangeRatePaginationMetaFactory,
getExchangeRatesTableQueryFactory,
} from 'store/ExchangeRate/exchange.selector';
export default (mapState) => {
const getExchangeRatesPaginationMeta = getExchangeRatePaginationMetaFactory();
const mapStateToProps = (state, props) => {
const query = getExchangeRatesTableQueryFactory(state, props);
const mapped = {
exchangeRatesList: getExchangeRatesList(state, props),
exchangeRatesLoading: state.exchangeRates.loading,
exchangeRatesPageination: getExchangeRatesPaginationMeta(
state,
props,
query,
),
};
return mapState ? mapState(mapped, state, props) : mapped;
};

View File

@@ -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',

View File

@@ -4,26 +4,40 @@ import t from 'store/types';
export const fetchExchangeRates = () => {
return (dispatch) =>
new Promise((resolve, reject) => {
dispatch({
type: t.SET_DASHBOARD_REQUEST_LOADING,
});
dispatch({
type: t.EXCHANGE_RATE_TABLE_LOADING,
loading: true,
payload: {
loading: true,
},
});
ApiService.get('exchange_rates')
.then((response) => {
dispatch({
type: t.EXCHANGE_RATES_PAGE_SET,
payload: {
exchange_rates: response.data.exchange_rates.results,
pagination: response.data.exchange_rates.pagination,
customViewId: response.data.exchange_rates.customViewId || -1,
},
});
dispatch({
type: t.EXCHANGE_RATE_LIST_SET,
exchange_rates: response.data.exchange_rates.results,
});
dispatch({
type: t.SET_DASHBOARD_REQUEST_COMPLETED,
type: t.EXCHANGE_RATES_PAGINATION_SET,
payload: {
pagination: response.data.exchange_rates.pagination,
customViewId: response.data.customViewId || -1,
},
});
dispatch({
type: t.EXCHANGE_RATE_TABLE_LOADING,
loading: false,
payload: {
loading: false,
},
});
resolve(response);
})

View File

@@ -1,11 +1,18 @@
import { createReducer } from '@reduxjs/toolkit';
import { createTableQueryReducers } from 'store/queryReducers';
import t from 'store/types';
const initialState = {
exchangeRates: {},
loading: false,
tableQuery: {
page_size: 5,
page: 1,
},
currentViewId: -1,
};
export default createReducer(initialState, {
const reducer = createReducer(initialState, {
[t.EXCHANGE_RATE_LIST_SET]: (state, action) => {
const _exchangeRates = {};
action.exchange_rates.forEach((exchange_rate) => {
@@ -17,8 +24,51 @@ export default createReducer(initialState, {
..._exchangeRates,
};
},
[t.EXCHANGE_RATE_TABLE_LOADING]: (state, action) => {
state.loading = action.loading;
const { loading } = action.payload;
state.loading = loading;
},
[t.ESTIMATES_PAGE_SET]: (state, action) => {
const { customViewId, exchange_rates, pagination } = action.payload;
const viewId = customViewId || -1;
const view = state.views[viewId] || {};
state.views[viewId] = {
...view,
pages: {
...(state.views?.[viewId]?.pages || {}),
[pagination.page]: {
ids: exchange_rates.map((i) => i.id),
},
},
};
},
[t.EXCHANGE_RATES_PAGINATION_SET]: (state, action) => {
const { pagination, customViewId } = action.payload;
const mapped = {
pageSize: parseInt(pagination.pageSize, 10),
page: parseInt(pagination.page, 10),
total: parseInt(pagination.total, 10),
};
const paginationMeta = {
...mapped,
pagesCount: Math.ceil(mapped.total / mapped.pageSize),
pageIndex: Math.max(mapped.page - 1, 0),
};
state.views = {
...state.views,
[customViewId]: {
...(state.views?.[customViewId] || {}),
paginationMeta,
},
};
},
[t.EXCHANGE_RATES_BULK_DELETE]: (state, action) => {
@@ -35,3 +85,5 @@ export default createReducer(initialState, {
}
},
});
export default createTableQueryReducers('exchange_rates', reducer);

View File

@@ -1,8 +1,18 @@
import { createSelector } from 'reselect';
import { pickItemsFromIds, getItemById } from 'store/selectors';
import {
pickItemsFromIds,
getItemById,
paginationLocationQuery,
} from 'store/selectors';
const exchangeRateItemsSelector = (state) => state.exchangeRates.exchangeRates;
const exchangeRateIdPropSelector = (state, props) => props.exchangeRateId;
const exchangeRateTableQuery = (state) => state.exchangeRates.tableQuery;
const exchangeRatesCurrentViewSelector = (state, props) => {
const viewId = state.exchangeRates.currentViewId;
return state.exchangeRates.views?.[viewId];
};
export const getExchangeRatesList = createSelector(
exchangeRateItemsSelector,
@@ -18,3 +28,20 @@ export const getExchangeRateById = createSelector(
return getItemById(exchangeRates, exchangeRateId);
},
);
export const getExchangeRatePaginationMetaFactory = () =>
createSelector(exchangeRatesCurrentViewSelector, (exchangeRateView) => {
return exchangeRateView?.paginationMeta || {};
});
export const getExchangeRatesTableQueryFactory = () =>
createSelector(
paginationLocationQuery,
exchangeRateTableQuery,
(locationQuery, tableQuery) => {
return {
...locationQuery,
...tableQuery,
};
},
);

View File

@@ -4,6 +4,8 @@ export default {
EXCHANGE_RATE_LIST_SET: 'EXCHANGE_RATE_LIST_SET',
CLEAR_EXCHANGE_RATE_FORM_ERRORS: 'CLEAR_EXCHANGE_RATE_FORM_ERRORS',
ExchangeRates_TABLE_QUERIES_ADD: 'ExchangeRates_TABLE_QUERIES_ADD',
EXCHANGE_RATE_TABLE_LOADING:'EXCHANGE_RATE_TABLE_LOADING',
EXCHANGE_RATE_TABLE_LOADING: 'EXCHANGE_RATE_TABLE_LOADING',
EXCHANGE_RATES_BULK_DELETE: 'EXCHANGE_RATES_BULK_DELETE',
EXCHANGE_RATES_PAGE_SET: 'EXCHANGE_RATES_PAGE_SET',
EXCHANGE_RATES_PAGINATION_SET: 'EXCHANGE_RATES_PAGINATION_SET',
};

View File

@@ -149,11 +149,11 @@ export const editAccount = (id, form) => {
};
export const activateAccount = ({ id }) => {
return (dispatch) => ApiService.post(`accounts/${id}/active`);
return (dispatch) => ApiService.post(`accounts/${id}/activate`);
};
export const inactiveAccount = ({ id }) => {
return (dispatch) => ApiService.post(`accounts/${id}/inactive`);
return (dispatch) => ApiService.post(`accounts/${id}/inactivate`);
};
export const bulkActivateAccounts = ({ ids }) => {

View File

@@ -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 }));
};
};