refactoring: exchanges rates list.

This commit is contained in:
elforjani3
2021-02-18 17:58:01 +02:00
parent 5171cf7ef5
commit 4b0c266f34
9 changed files with 437 additions and 355 deletions

View File

@@ -0,0 +1,71 @@
import React, { useState } from 'react';
import { FormattedMessage as T, useIntl } from 'react-intl';
import { Intent, Alert } from '@blueprintjs/core';
import { size } from 'lodash';
import { AppToaster } from 'components';
import withAlertStoreConnect from 'containers/Alert/withAlertStoreConnect';
import withAlertActions from 'containers/Alert/withAlertActions';
import { compose } from 'utils';
/**
* Exchange rate bulk delete alert.
*/
function ExchangeRateBulkDeleteAlert({
name,
// #withAlertStoreConnect
isOpen,
payload: { exchangeRatesIds },
// #withAlertActions
closeAlert,
}) {
// handle cancel item bulk delete alert.
const handleCancelBulkDelete = () => {
closeAlert(name);
};
// handle confirm Exchange Rates bulk delete.
// const handleConfirmBulkDelete = () => {
// bulkDeleteExchangeRate(exchangeRatesIds)
// .then(() => {
// AppToaster.show({
// message: formatMessage({
// id: 'the_exchange_rates_has_been_successfully_deleted',
// }),
// intent: Intent.SUCCESS,
// });
// })
// .catch(({ errors }) => {
// handleDeleteErrors(errors);
// });
// };
return (
<Alert
cancelButtonText={<T id={'cancel'} />}
confirmButtonText={
<T id={'delete_count'} values={{ count: size(exchangeRatesIds) }} />
}
icon="trash"
intent={Intent.DANGER}
isOpen={isOpen}
onCancel={handleCancelBulkDelete}
// onConfirm={}
// loading={isLoading}
>
<p>
<T
id={'once_delete_these_exchange_rates_you_will_not_able_restore_them'}
/>
</p>
</Alert>
);
}
export default compose(
withAlertStoreConnect(),
withAlertActions,
)(ExchangeRateBulkDeleteAlert);

View File

@@ -0,0 +1,76 @@
import React from 'react';
import {
FormattedMessage as T,
FormattedHTMLMessage,
useIntl,
} from 'react-intl';
import { Intent, Alert } from '@blueprintjs/core';
import { AppToaster } from 'components';
import { useDeleteExchangeRate } from 'hooks/query';
import withAlertStoreConnect from 'containers/Alert/withAlertStoreConnect';
import withAlertActions from 'containers/Alert/withAlertActions';
import { compose } from 'utils';
/**
* exchange rate delete alerts.
*/
function ExchangeRateDeleteAlert({
name,
// #withAlertStoreConnect
isOpen,
payload: { exchangeRateId },
// #withAlertActions
closeAlert,
}) {
const {
mutateAsync: deleteExchangeRate,
isLoading,
} = useDeleteExchangeRate();
const { formatMessage } = useIntl();
// Handle cancel delete exchange rate alert.
const handleCancelExchangeRateDelete = () => closeAlert(name);
const handelConfirmExchangeRateDelete = () => {
deleteExchangeRate(exchangeRateId)
.then((response) => {
AppToaster.show({
message: formatMessage({
id: 'the_exchange_rates_has_been_deleted_successfully',
}),
intent: Intent.SUCCESS,
});
closeAlert(name);
})
.catch(() => {
closeAlert(name);
});
};
return (
<Alert
cancelButtonText={<T id={'cancel'} />}
confirmButtonText={<T id={'delete'} />}
intent={Intent.DANGER}
isOpen={isOpen}
onCancel={handleCancelExchangeRateDelete}
onConfirm={handelConfirmExchangeRateDelete}
loading={isLoading}
>
<p>
<FormattedHTMLMessage
id={'once_delete_this_exchange_rate_you_will_able_to_restore_it'}
/>
</p>
</Alert>
);
}
export default compose(
withAlertStoreConnect(),
withAlertActions,
)(ExchangeRateDeleteAlert);

View File

@@ -1,183 +1,75 @@
import React, { useCallback, useMemo, useState } from 'react';
import {
Button,
Popover,
Menu,
MenuItem,
Position,
Intent,
} from '@blueprintjs/core';
import { FormattedMessage as T, useIntl } from 'react-intl';
import moment from 'moment';
import classNames from 'classnames';
import React from 'react';
import { CLASSES } from 'common/classes';
import { DataTable } from 'components';
import TableSkeletonRows from 'components/Datatable/TableSkeletonRows';
import { DataTable, Icon, Money } from 'components';
import LoadingIndicator from 'components/LoadingIndicator';
import { useExchangeRatesContext } from './ExchangeRatesProvider';
import { useExchangeRatesTableColumns, ActionMenuList } from './components';
import withDialogActions from 'containers/Dialog/withDialogActions';
import withExchangeRatesActions from 'containers/ExchangeRates/withExchangeRatesActions';
import withExchangeRates from 'containers/ExchangeRates/withExchangeRates';
import withAlertActions from 'containers/Alert/withAlertActions';
import { compose } from 'utils';
/**
* Exchange rates table.
*/
function ExchangeRateTable({
// #withExchangeRates
exchangeRatesList,
exchangeRatesLoading,
exchangeRatesPageination,
// #ownProps
tableProps,
// #withDialogActions.
openDialog,
// own properties
loading,
onFetchData,
onDeleteExchangeRate,
onSelectedRowsChange,
// #withAlertActions
openAlert,
}) {
const [initialMount, setInitialMount] = useState(false);
const { formatMessage } = useIntl();
const {
isExchangeRatesFetching,
isExchangeRatesLoading,
const handelEditExchangeRate = useCallback(
(exchange_rate) => () => {
openDialog('exchangeRate-form', { action: 'edit', id: exchange_rate.id });
},
[openDialog],
);
exchangesRates,
pagination,
} = useExchangeRatesContext();
const handleDeleteExchangeRate = (exchange_rate) => () => {
onDeleteExchangeRate(exchange_rate);
// Table columns.
const columns = useExchangeRatesTableColumns();
// Handle delete exchange rate.
const handleDeleteExchangeRate = ({ id }) => {
openAlert('exchange-rate-delete', { exchangeRateId: id });
};
const actionMenuList = useCallback(
(ExchangeRate) => (
<Menu>
<MenuItem
icon={<Icon icon="pen-18" />}
text={formatMessage({ id: 'edit_exchange_rate' })}
onClick={handelEditExchangeRate(ExchangeRate)}
/>
<MenuItem
text={formatMessage({ id: 'delete_exchange_rate' })}
intent={Intent.DANGER}
onClick={handleDeleteExchangeRate(ExchangeRate)}
icon={<Icon icon="trash-16" iconSize={16} />}
/>
</Menu>
),
[handelEditExchangeRate, handleDeleteExchangeRate, formatMessage],
);
const rowContextMenu = (cell) => {
return actionMenuList(cell.row.original);
// Handle Edit exchange rate.
const handelEditExchangeRate = (exchangeRate) => {
openDialog('exchangeRate-form', {
action: 'edit',
exchangeRate: exchangeRate,
});
};
const columns = useMemo(
() => [
{
id: 'date',
Header: formatMessage({ id: 'date' }),
accessor: (r) => moment(r.date).format('YYYY MMM DD'),
width: 150,
},
{
id: 'currency_code',
Header: formatMessage({ id: 'currency_code' }),
accessor: 'currency_code',
className: 'currency_code',
width: 150,
},
{
id: 'exchange_rate',
Header: formatMessage({ id: 'exchange_rate' }),
accessor: (r) => (
<Money
amount={r.exchange_rate}
currency={r.currency_code}
/>
),
className: 'exchange_rate',
width: 150,
},
{
id: 'actions',
Header: '',
Cell: ({ cell }) => (
<Popover
content={actionMenuList(cell.row.original)}
position={Position.RIGHT_TOP}
>
<Button icon={<Icon icon="more-h-16" iconSize={16} />} />
</Popover>
),
className: 'actions',
width: 50,
},
],
[actionMenuList, formatMessage],
);
const selectionColumn = useMemo(
() => ({
minWidth: 42,
width: 42,
maxWidth: 42,
}),
[],
);
const handelFetchData = useCallback(
(...params) => {
onFetchData && onFetchData(...params);
},
[onFetchData],
);
const handelSelectedRowsChange = useCallback(
(selectRows) => {
onSelectedRowsChange &&
onSelectedRowsChange(selectRows.map((c) => c.original));
},
[onSelectedRowsChange],
);
return (
<div className={classNames(CLASSES.DASHBOARD_DATATABLE)}>
<LoadingIndicator loading={loading} mount={false}>
<DataTable
columns={columns}
data={exchangeRatesList}
onFetchData={handelFetchData}
loading={exchangeRatesLoading && !initialMount}
manualSortBy={true}
selectionColumn={selectionColumn}
expandable={true}
treeGraph={true}
onSelectedRowsChange={handelSelectedRowsChange}
rowContextMenu={rowContextMenu}
pagination={true}
pagesCount={exchangeRatesPageination.pagesCount}
initialPageSize={exchangeRatesPageination.pageSize}
initialPageIndex={exchangeRatesPageination.page - 1}
/>
</LoadingIndicator>
</div>
<DataTable
columns={columns}
data={exchangesRates}
noInitialFetch={true}
loading={isExchangeRatesLoading}
headerLoading={isExchangeRatesLoading}
progressBarLoading={isExchangeRatesFetching}
selectionColumn={true}
manualSortBy={true}
expandable={true}
sticky={true}
// pagination={true}
TableLoadingRenderer={TableSkeletonRows}
payload={{
onDeleteExchangeRate: handleDeleteExchangeRate,
onEditExchangeRate: handelEditExchangeRate,
}}
ContextMenu={ActionMenuList}
{...tableProps}
/>
);
}
export default compose(
withDialogActions,
withExchangeRatesActions,
withExchangeRates(
({
exchangeRatesList,
exchangeRatesLoading,
exchangeRatesPageination,
}) => ({
exchangeRatesList,
exchangeRatesLoading,
exchangeRatesPageination,
}),
),
)(ExchangeRateTable);
export default compose(withDialogActions, withAlertActions)(ExchangeRateTable);

View File

@@ -0,0 +1,12 @@
import React from 'react';
import ExchangeRateDeleteAlert from 'containers/Alerts/ExchangeRates/ExchangeRateDeleteAlert';
// import ExchangeRateBulkDeleteAlert from 'containers/Alerts/ExchangeRates/ExchangeRateBulkDeleteAlert';
export default function ExchangeRatesAlerts() {
return (
<div>
<ExchangeRateDeleteAlert name={'exchange-rate-delete'} />
</div>
);
}

View File

@@ -1,204 +1,27 @@
import React, { useEffect, useState, useCallback, useMemo } from 'react';
import { useQuery, queryCache } from 'react-query';
import { useParams } from 'react-router-dom';
import { Alert, Intent } from '@blueprintjs/core';
import {
FormattedMessage as T,
useIntl,
FormattedHTMLMessage,
} from 'react-intl';
import AppToaster from 'components/AppToaster';
import React from 'react';
import { DashboardContentTable, DashboardPageContent } from 'components';
import DashboardPageContent from 'components/Dashboard/DashboardPageContent';
import DashboardInsider from 'components/Dashboard/DashboardInsider';
import ExchangeRateTable from './ExchangeRateTable';
import ExchangeRateActionsBar from './ExchangeRateActionsBar';
import withDialogActions from 'containers/Dialog/withDialogActions';
import withDashboardActions from 'containers/Dashboard/withDashboardActions';
import withResourceActions from 'containers/Resources/withResourcesActions';
import withExchangeRatesActions from 'containers/ExchangeRates/withExchangeRatesActions';
import { compose } from 'utils';
function ExchangeRatesList({
// #withDashboardActions
changePageTitle,
// #withResourceActions
requestFetchResourceFields,
// #withExchangeRatesActions
requestFetchExchangeRates,
requestDeleteExchangeRate,
addExchangeRatesTableQueries,
requestDeleteBulkExchangeRates,
// #withDialog
openDialog,
}) {
const { id } = useParams();
const [deleteExchangeRate, setDeleteExchangeRate] = useState(false);
const [selectedRows, setSelectedRows] = useState([]);
const { formatMessage } = useIntl();
const [bulkDelete, setBulkDelete] = useState(false);
const [filter, setFilter] = useState({});
const fetchExchangeRates = useQuery('exchange-rates-table', () =>
requestFetchExchangeRates(),
);
useEffect(() => {
id
? changePageTitle(formatMessage({ id: 'exchange_rate_details' }))
: changePageTitle(formatMessage({ id: 'exchange_rates_list' }));
}, [id, changePageTitle, formatMessage]);
const handelDeleteExchangeRate = useCallback(
(exchange_rate) => {
setDeleteExchangeRate(exchange_rate);
},
[setDeleteExchangeRate],
);
const handelEditExchangeRate = (exchange_rate) => {
openDialog('exchangeRate-form', { action: 'edit', id: exchange_rate.id });
};
const handelCancelExchangeRateDelete = useCallback(() => {
setDeleteExchangeRate(false);
}, [setDeleteExchangeRate]);
const handelConfirmExchangeRateDelete = useCallback(() => {
requestDeleteExchangeRate(deleteExchangeRate.id)
.then(() => {
setDeleteExchangeRate(false);
AppToaster.show({
message: formatMessage({
id: 'the_exchange_rates_has_been_deleted_successfully',
}),
intent: Intent.SUCCESS,
});
})
.catch(() => {
setDeleteExchangeRate(false);
});
}, [deleteExchangeRate, requestDeleteExchangeRate, formatMessage]);
// Handle fetch data of Exchange_rates datatable.
const handleFetchData = useCallback(
({ pageIndex, pageSize, sortBy }) => {
addExchangeRatesTableQueries({
...(sortBy.length > 0
? {
column_sort_by: sortBy[0].id,
sort_order: sortBy[0].desc ? 'desc' : 'asc',
}
: {}),
});
},
[addExchangeRatesTableQueries],
);
// Handle selected rows change.
const handleSelectedRowsChange = useCallback(
(exchange_rates) => {
setSelectedRows(exchange_rates);
},
[setSelectedRows],
);
// Handle Exchange Rates bulk delete.
const handleBulkDelete = useCallback(
(exchangeRatesIds) => {
setBulkDelete(exchangeRatesIds);
},
[setBulkDelete],
);
//Handel cancel itemCategories bulk delete.
const handleCancelBulkDelete = useCallback(() => {
setBulkDelete(false);
}, []);
// handle confirm Exchange Rates bulk delete.
const handleConfirmBulkDelete = useCallback(() => {
requestDeleteBulkExchangeRates(bulkDelete)
.then(() => {
setBulkDelete(false);
AppToaster.show({
message: formatMessage({
id: 'the_exchange_rates_has_been_successfully_deleted',
}),
intent: Intent.SUCCESS,
});
})
.catch((errors) => {
setBulkDelete(false);
});
}, [requestDeleteBulkExchangeRates, bulkDelete, formatMessage]);
// Calculates the data table selected rows count.
const selectedRowsCount = useMemo(() => Object.values(selectedRows).length, [
selectedRows,
]);
import { ExchangeRatesProvider } from './ExchangeRatesProvider';
import ExchangeRatesAlerts from './ExchangeRatesAlerts';
/**
* Exchange Rates list.
*/
export default function ExchangeRatesList() {
return (
<DashboardInsider loading={fetchExchangeRates.isFetching}>
<ExchangeRateActionsBar
onDeleteExchangeRate={handelDeleteExchangeRate}
selectedRows={selectedRows}
onBulkDelete={handleBulkDelete}
/>
<ExchangeRatesProvider>
<ExchangeRateActionsBar />
<DashboardPageContent>
<ExchangeRateTable
onDeleteExchangeRate={handelDeleteExchangeRate}
onEditExchangeRate={handelEditExchangeRate}
onFetchData={handleFetchData}
onSelectedRowsChange={handleSelectedRowsChange}
/>
<Alert
cancelButtonText={<T id={'cancel'} />}
confirmButtonText={<T id={'delete'} />}
icon="trash"
intent={Intent.DANGER}
isOpen={deleteExchangeRate}
onCancel={handelCancelExchangeRateDelete}
onConfirm={handelConfirmExchangeRateDelete}
>
<p>
<FormattedHTMLMessage
id={'once_delete_this_exchange_rate_you_will_able_to_restore_it'}
/>
</p>
</Alert>
<Alert
cancelButtonText={<T id={'cancel'} />}
confirmButtonText={`${formatMessage({
id: 'delete',
})} (${selectedRowsCount})`}
icon="trash"
intent={Intent.DANGER}
isOpen={bulkDelete}
onCancel={handleCancelBulkDelete}
onConfirm={handleConfirmBulkDelete}
>
<p>
<FormattedHTMLMessage
id={
'once_delete_these_exchange_rates_you_will_not_able_restore_them'
}
/>
</p>
</Alert>
<DashboardContentTable>
<ExchangeRateTable />
</DashboardContentTable>
</DashboardPageContent>
</DashboardInsider>
<ExchangeRatesAlerts />
</ExchangeRatesProvider>
);
}
export default compose(
withExchangeRatesActions,
withResourceActions,
withDashboardActions,
withDialogActions,
)(ExchangeRatesList);

View File

@@ -0,0 +1,35 @@
import React, { createContext } from 'react';
import DashboardInsider from 'components/Dashboard/DashboardInsider';
import { useExchangeRates } from 'hooks/query';
const ExchangesRatesContext = createContext();
/**
* Exchanges rates list provider.
*/
function ExchangeRatesProvider({ query, ...props }) {
const {
data: { exchangesRates, pagination },
isFetching: isExchangeRatesFetching,
isLoading: isExchangeRatesLoading,
} = useExchangeRates();
const state = {
isExchangeRatesFetching,
isExchangeRatesLoading,
exchangesRates,
pagination,
query,
};
return (
<DashboardInsider name={'exchange-rate-list'}>
<ExchangesRatesContext.Provider value={state} {...props} />
</DashboardInsider>
);
}
const useExchangeRatesContext = () => React.useContext(ExchangesRatesContext);
export { ExchangeRatesProvider, useExchangeRatesContext };

View File

@@ -0,0 +1,94 @@
import React, { useMemo } from 'react';
import {
Menu,
Popover,
Button,
Position,
MenuItem,
MenuDivider,
Intent,
} from '@blueprintjs/core';
import { useIntl } from 'react-intl';
import { Icon, Money } from 'components';
import moment from 'moment';
import { safeCallback } from 'utils';
/**
* Row actions menu list.
*/
export function ActionMenuList({
row: { original },
payload: { onEditExchangeRate, onDeleteExchangeRate },
}) {
const { formatMessage } = useIntl();
return (
<Menu>
<MenuItem
icon={<Icon icon="pen-18" />}
text={formatMessage({ id: 'edit_exchange_rate' })}
onClick={safeCallback(onEditExchangeRate, original)}
/>
<MenuDivider />
<MenuItem
text={formatMessage({ id: 'delete_exchange_rate' })}
intent={Intent.DANGER}
onClick={safeCallback(onDeleteExchangeRate, original)}
icon={<Icon icon="trash-16" iconSize={16} />}
/>
</Menu>
);
}
/**
* Table actions cell.
*/
export function TableActionsCell(props) {
return (
<Popover
content={<ActionMenuList {...props} />}
position={Position.RIGHT_TOP}
>
<Button icon={<Icon icon="more-h-16" iconSize={16} />} />
</Popover>
);
}
export function useExchangeRatesTableColumns() {
const { formatMessage } = useIntl();
return useMemo(
() => [
{
id: 'date',
Header: formatMessage({ id: 'date' }),
accessor: (r) => moment(r.date).format('YYYY MMM DD'),
width: 150,
},
{
id: 'currency_code',
Header: formatMessage({ id: 'currency_code' }),
accessor: 'currency_code',
className: 'currency_code',
width: 150,
},
{
id: 'exchange_rate',
Header: formatMessage({ id: 'exchange_rate' }),
accessor: (r) => (
<Money amount={r.exchange_rate} currency={r.currency_code} />
),
className: 'exchange_rate',
width: 150,
},
{
id: 'actions',
Header: '',
Cell: TableActionsCell,
className: 'actions',
width: 50,
},
],
[formatMessage],
);
}