refactoring: sales tables.

refacoring: purchases tables.
This commit is contained in:
a.bouhuolia
2021-02-11 20:45:06 +02:00
parent 3901c336df
commit d48532a7e6
210 changed files with 2799 additions and 5392 deletions

View File

@@ -1,249 +0,0 @@
import React, { useCallback, useMemo } from 'react';
import {
Intent,
Button,
Popover,
Menu,
MenuItem,
MenuDivider,
Position,
} from '@blueprintjs/core';
import { withRouter } from 'react-router';
import { FormattedMessage as T, useIntl } from 'react-intl';
import moment from 'moment';
import classNames from 'classnames';
import { compose, saveInvoke } from 'utils';
import { useIsValuePassed } from 'hooks';
import { CLASSES } from 'common/classes';
import { DataTable, Money, Icon, Choose, LoadingIndicator } from 'components';
import PaymentMadesEmptyStatus from './PaymentMadesEmptyStatus';
import withPaymentMade from './withPaymentMade';
import withPaymentMadeActions from './withPaymentMadeActions';
import withCurrentView from 'containers/Views/withCurrentView';
import withSettings from 'containers/Settings/withSettings';
/**
* Payment made datatable transactions.
*/
function PaymentMadeDataTable({
// #withPaymentMades
paymentMadeCurrentPage,
paymentMadePageination,
paymentMadesLoading,
paymentMadeTableQuery,
paymentMadesCurrentViewId,
// #withPaymentMadeActions
addPaymentMadesTableQueries,
// #withSettings
baseCurrency,
// #ownProps
onEditPaymentMade,
onDeletePaymentMade,
onSelectedRowsChange,
}) {
const isLoaded = useIsValuePassed(paymentMadesLoading, false);
const { formatMessage } = useIntl();
const handleEditPaymentMade = useCallback(
(paymentMade) => () => {
saveInvoke(onEditPaymentMade, paymentMade);
},
[onEditPaymentMade],
);
const handleDeletePaymentMade = useCallback(
(paymentMade) => () => {
saveInvoke(onDeletePaymentMade, paymentMade);
},
[onDeletePaymentMade],
);
const actionMenuList = useCallback(
(paymentMade) => (
<Menu>
<MenuItem
icon={<Icon icon="reader-18" />}
text={formatMessage({ id: 'view_details' })}
/>
<MenuDivider />
<MenuItem
icon={<Icon icon="pen-18" />}
text={formatMessage({ id: 'edit_payment_made' })}
onClick={handleEditPaymentMade(paymentMade)}
/>
<MenuItem
text={formatMessage({ id: 'delete_payment_made' })}
intent={Intent.DANGER}
onClick={handleDeletePaymentMade(paymentMade)}
icon={<Icon icon="trash-16" iconSize={16} />}
/>
</Menu>
),
[handleDeletePaymentMade, handleEditPaymentMade, formatMessage],
);
const onRowContextMenu = useCallback(
(cell) => {
return actionMenuList(cell.row.original);
},
[actionMenuList],
);
const columns = useMemo(
() => [
{
id: 'payment_date',
Header: formatMessage({ id: 'payment_date' }),
accessor: (r) => moment(r.payment_date).format('YYYY MMM DD'),
width: 140,
className: 'payment_date',
},
{
id: 'vendor_id',
Header: formatMessage({ id: 'vendor_name' }),
accessor: 'vendor.display_name',
width: 140,
className: 'vendor_id',
},
{
id: 'payment_number',
Header: formatMessage({ id: 'payment_number' }),
accessor: (row) =>
row.payment_number ? `#${row.payment_number}` : null,
width: 140,
className: 'payment_number',
},
{
id: 'payment_account_id',
Header: formatMessage({ id: 'payment_account' }),
accessor: 'payment_account.name',
width: 140,
className: 'payment_account_id',
},
{
id: 'amount',
Header: formatMessage({ id: 'amount' }),
accessor: (r) => <Money amount={r.amount} currency={baseCurrency} />,
width: 140,
className: 'amount',
},
{
id: 'reference',
Header: formatMessage({ id: 'reference' }),
accessor: 'reference',
width: 140,
className: 'reference',
},
{
id: 'actions',
Header: '',
Cell: ({ cell }) => (
<Popover
content={actionMenuList(cell.row.original)}
position={Position.RIGHT_BOTTOM}
>
<Button icon={<Icon icon="more-h-16" iconSize={16} />} />
</Popover>
),
className: 'actions',
width: 50,
disableResizing: true,
},
],
[actionMenuList, formatMessage],
);
const handleDataTableFetchData = useCallback(
({ pageIndex, pageSize, sortBy }) => {
addPaymentMadesTableQueries({
...(sortBy.length > 0
? {
column_sort_by: sortBy[0].id,
sort_order: sortBy[0].desc ? 'desc' : 'asc',
}
: {}),
page_size: pageSize,
page: pageIndex + 1,
});
},
[addPaymentMadesTableQueries],
);
const handleSelectedRowsChange = useCallback(
(selectedRows) => {
saveInvoke(
onSelectedRowsChange,
selectedRows.map((s) => s.original),
);
},
[onSelectedRowsChange],
);
const showEmptyStatuts = [
paymentMadeCurrentPage.length === 0,
paymentMadesCurrentViewId === -1,
].every((condition) => condition === true);
return (
<div className={classNames(CLASSES.DASHBOARD_DATATABLE)}>
<LoadingIndicator loading={paymentMadesLoading && !isLoaded}>
<Choose>
<Choose.When condition={showEmptyStatuts}>
<PaymentMadesEmptyStatus />
</Choose.When>
<Choose.Otherwise>
<DataTable
columns={columns}
data={paymentMadeCurrentPage}
onFetchData={handleDataTableFetchData}
manualSortBy={true}
selectionColumn={true}
noInitialFetch={true}
sticky={true}
onSelectedRowsChange={handleSelectedRowsChange}
rowContextMenu={onRowContextMenu}
pagination={true}
pagesCount={paymentMadePageination.pagesCount}
initialPageSize={paymentMadeTableQuery.page_size}
initialPageIndex={paymentMadeTableQuery.page - 1}
autoResetSortBy={false}
autoResetPage={false}
/>
</Choose.Otherwise>
</Choose>
</LoadingIndicator>
</div>
);
}
export default compose(
withRouter,
withCurrentView,
withPaymentMadeActions,
withPaymentMade(
({
paymentMadeCurrentPage,
paymentMadesLoading,
paymentMadePageination,
paymentMadeTableQuery,
paymentMadesCurrentViewId,
}) => ({
paymentMadeCurrentPage,
paymentMadesLoading,
paymentMadePageination,
paymentMadeTableQuery,
paymentMadesCurrentViewId,
}),
),
withSettings(({ organizationSettings }) => ({
baseCurrency: organizationSettings?.baseCurrency,
})),
)(PaymentMadeDataTable);

View File

@@ -4,7 +4,7 @@ import PaymentMadeDeleteAlert from 'containers/Alerts/PaymentMades/PaymentMadeDe
export default function PaymentMadesAlerts() {
return (
<div>
<PaymentMadeDeleteAlert dialogName={'payment-made-delete'} />
<PaymentMadeDeleteAlert name={'payment-made-delete'} />
</div>
);
}

View File

@@ -27,8 +27,8 @@ import { compose } from 'utils';
* Payment made actions bar.
*/
function PaymentMadeActionsBar({
//#withPaymentMadesActions
addPaymentMadesTableQueries,
// #withPaymentMadesActions
setPaymentMadesTableState,
}) {
const history = useHistory();
const { formatMessage } = useIntl();
@@ -41,12 +41,18 @@ function PaymentMadeActionsBar({
history.push('/payment-mades/new');
};
// Handle tab changing.
const handleTabChange = (customView) => {
setPaymentMadesTableState({ customViewId: customView.id || null });
};
return (
<DashboardActionsBar>
<NavbarGroup>
<DashboardActionViewsList
resourceName={'bill_payments'}
views={paymentMadesViews}
onChange={handleTabChange}
/>
<NavbarDivider />
<Button

View File

@@ -1,16 +1,17 @@
import React, { useEffect } from 'react';
import { FormattedMessage as T, useIntl } from 'react-intl';
import { useIntl } from 'react-intl';
import DashboardPageContent from 'components/Dashboard/DashboardPageContent';
import PaymentMadeActionsBar from './PaymentMadeActionsBar';
import PaymentMadesAlerts from './PaymentMadesAlerts';
import PaymentMadesAlerts from '../PaymentMadesAlerts';
import PaymentMadesTable from './PaymentMadesTable';
import { PaymentMadesListProvider } from './PaymentMadesListProvider';
import PaymentMadesView from './PaymentMadesView';
import PaymentMadeViewTabs from './PaymentMadeViewTabs';
import withDashboardActions from 'containers/Dashboard/withDashboardActions';
import withPaymentMades from './withPaymentMade';
import { compose } from 'utils';
import { compose, transformTableStateToQuery } from 'utils';
/**
* Payment mades list.
@@ -20,7 +21,7 @@ function PaymentMadeList({
changePageTitle,
// #withPaymentMades
paymentMadeTableQuery,
paymentMadesTableState,
}) {
const { formatMessage } = useIntl();
@@ -29,20 +30,24 @@ function PaymentMadeList({
}, [changePageTitle, formatMessage]);
return (
<PaymentMadesListProvider query={paymentMadeTableQuery}>
<PaymentMadesListProvider
query={transformTableStateToQuery(paymentMadesTableState)}
>
<PaymentMadeActionsBar />
<DashboardPageContent>
<PaymentMadesView />
<PaymentMadesAlerts />
<PaymentMadeViewTabs />
<PaymentMadesTable />
</DashboardPageContent>
<PaymentMadesAlerts />
</PaymentMadesListProvider>
);
}
export default compose(
withDashboardActions,
withPaymentMades(({ paymentMadeTableQuery }) => ({
paymentMadeTableQuery,
withPaymentMades(({ paymentMadesTableState }) => ({
paymentMadesTableState,
})),
)(PaymentMadeList);

View File

@@ -2,7 +2,6 @@ import React from 'react';
import { useHistory } from 'react-router';
import { FormattedMessage as T } from 'react-intl';
import { Alignment, Navbar, NavbarGroup } from '@blueprintjs/core';
import { useParams } from 'react-router-dom';
import { pick } from 'lodash';
import { DashboardViewsTabs } from 'components';
@@ -11,20 +10,27 @@ import { usePaymentMadesListContext } from './PaymentMadesListProvider';
import withPaymentMadeActions from './withPaymentMadeActions';
import { compose } from 'utils';
import withPaymentMade from './withPaymentMade';
/**
* Payment made views tabs.
*/
function PaymentMadeViewTabs({
//#withPaymentMadesActions
addPaymentMadesTableQueries,
// #withPaymentMadesActions
setPaymentMadesTableState,
// #withPaymentMade
paymentMadesTableState,
}) {
const history = useHistory();
const { custom_view_id: customViewId = null } = useParams();
// Payment receives list context.
const { paymentMadesViews } = usePaymentMadesListContext();
const handleTabsChange = (viewId) => {
addPaymentMadesTableQueries({
custom_view_id: viewId || null,
// Handle the active tab changning.
const handleTabsChange = (customView) => {
setPaymentMadesTableState({
customViewId: customView.id || null,
});
};
@@ -40,7 +46,7 @@ function PaymentMadeViewTabs({
<Navbar className={'navbar--dashboard-views'}>
<NavbarGroup align={Alignment.LEFT}>
<DashboardViewsTabs
initialViewId={customViewId}
customViewId={paymentMadesTableState.customViewId}
defaultTabText={<T id={'all_payments'} />}
tabs={tabs}
onNewViewTabClick={handleClickNewView}
@@ -51,4 +57,7 @@ function PaymentMadeViewTabs({
);
}
export default compose(withPaymentMadeActions)(PaymentMadeViewTabs);
export default compose(
withPaymentMadeActions,
withPaymentMade(({ paymentMadesTableState }) => ({ paymentMadesTableState }))
)(PaymentMadeViewTabs);

View File

@@ -3,8 +3,9 @@ import DashboardInsider from 'components/Dashboard/DashboardInsider';
import {
useResourceViews,
useResourceFields,
usePaymentReceives,
usePaymentMades,
} from 'hooks/query';
import { isTableEmptyStatus } from 'utils';
const PaymentMadesListContext = createContext();
@@ -26,20 +27,32 @@ function PaymentMadesListProvider({ accountsTableQuery, ...props }) {
// Fetch accounts list according to the given custom view id.
const {
data: { paymentMades, pagination },
isFetching: isInvoicesLoading,
} = usePaymentReceives(accountsTableQuery);
data: { paymentMades, pagination, filterMeta },
isLoading: isPaymentsLoading,
isFetching: isPaymentsFetching,
} = usePaymentMades(accountsTableQuery, { keepPreviousData: true });
// Detarmines the datatable empty status.
const isEmptyStatus =
isTableEmptyStatus({
data: paymentMades,
pagination,
filterMeta,
}) && !isPaymentsLoading;
// Provider payload.
const provider = {
paymentMades,
pagination,
filterMeta,
paymentMadesFields,
paymentMadesViews,
isInvoicesLoading,
isPaymentsLoading,
isPaymentsFetching,
isFieldsLoading,
isViewsLoading,
isEmptyStatus
};
return (

View File

@@ -0,0 +1,95 @@
import React, { useCallback } from 'react';
import classNames from 'classnames';
import { compose } from 'utils';
import { CLASSES } from 'common/classes';
import { DataTable } from 'components';
import PaymentMadesEmptyStatus from './PaymentMadesEmptyStatus';
import TableSkeletonRows from 'components/Datatable/TableSkeletonRows';
import TableSkeletonHeader from 'components/Datatable/TableHeaderSkeleton';
import withPaymentMadeActions from './withPaymentMadeActions';
import withSettings from 'containers/Settings/withSettings';
import withAlertsActions from 'containers/Alert/withAlertActions';
import { usePaymentMadesTableColumns, ActionsMenu } from './components';
import { usePaymentMadesListContext } from './PaymentMadesListProvider';
/**
* Payment made datatable transactions.
*/
function PaymentMadesTable({
// #withPaymentMadeActions
addPaymentMadesTableQueries,
// #withAlerts
openAlert
}) {
// Payment mades table columns.
const columns = usePaymentMadesTableColumns();
// Payment mades list context.
const {
paymentMades,
pagination,
isEmptyStatus,
isPaymentsLoading,
isPaymentsFetching,
} = usePaymentMadesListContext();
// Handles the edit payment made action.
const handleEditPaymentMade = (paymentMade) => {};
// Handles the delete payment made action.
const handleDeletePaymentMade = (paymentMade) => {
openAlert('payment-made-delete', { paymentMadeId: paymentMade.id })
};
// Handle datatable fetch data once the table state change.
const handleDataTableFetchData = useCallback(
({ pageIndex, pageSize, sortBy }) => {
addPaymentMadesTableQueries({ pageIndex, pageSize, sortBy });
},
[addPaymentMadesTableQueries],
);
if (isEmptyStatus) {
return <PaymentMadesEmptyStatus />;
}
return (
<div className={classNames(CLASSES.DASHBOARD_DATATABLE)}>
<DataTable
columns={columns}
data={paymentMades}
onFetchData={handleDataTableFetchData}
loading={isPaymentsLoading}
headerLoading={isPaymentsLoading}
progressBarLoading={isPaymentsFetching}
manualSortBy={true}
selectionColumn={true}
noInitialFetch={true}
sticky={true}
pagination={true}
pagesCount={pagination.pagesCount}
autoResetSortBy={false}
autoResetPage={false}
TableLoadingRenderer={TableSkeletonRows}
TableHeaderSkeletonRenderer={TableSkeletonHeader}
ContextMenu={ActionsMenu}
payload={{
onEdit: handleEditPaymentMade,
onDelete: handleDeletePaymentMade,
}}
/>
</div>
);
}
export default compose(
withPaymentMadeActions,
withAlertsActions,
withSettings(({ organizationSettings }) => ({
baseCurrency: organizationSettings?.baseCurrency,
})),
)(PaymentMadesTable);

View File

@@ -21,7 +21,7 @@ function PaymentMadesViewPage({
exact={true}
path={['/payment-mades/:custom_view_id/custom_view', '/payment-mades']}
>
<PaymentMadeViewTabs />
{/* <PaymentMadeDataTable
onDeletePaymentMade={handleDeletePaymentMade}
onEditPaymentMade={handleEditPaymentMade}

View File

@@ -0,0 +1,133 @@
import React from 'react';
import moment from 'moment';
import {
Intent,
Button,
Popover,
Menu,
MenuItem,
MenuDivider,
Position,
} from '@blueprintjs/core';
import { useIntl } from 'react-intl';
import { Icon, Money } from 'components';
import { safeCallback } from 'utils';
export function DateCell({ value }) {
return moment(value).format('YYYY MMM DD');
}
export function AmountAccessor(row) {
return <Money amount={row.amount} currency={'USD'} />
}
/**
* Actions menu.
*/
export function ActionsMenu({
row: { original },
payload: { onEdit, onDelete },
}) {
const { formatMessage } = useIntl();
return (
<Menu>
<MenuItem
icon={<Icon icon="reader-18" />}
text={formatMessage({ id: 'view_details' })}
/>
<MenuDivider />
<MenuItem
icon={<Icon icon="pen-18" />}
text={formatMessage({ id: 'edit_payment_made' })}
onClick={safeCallback(onEdit, original)}
/>
<MenuItem
text={formatMessage({ id: 'delete_payment_made' })}
intent={Intent.DANGER}
onClick={safeCallback(onDelete, original)}
icon={<Icon icon="trash-16" iconSize={16} />}
/>
</Menu>
);
}
/**
* Payment mades table actions cell.
*/
export function ActionsCell(props) {
return (
<Popover
content={<ActionsMenu {...props} />}
position={Position.RIGHT_BOTTOM}
>
<Button icon={<Icon icon="more-h-16" iconSize={16} />} />
</Popover>
);
}
/**
* Retrieve payment mades table columns.
*/
export function usePaymentMadesTableColumns() {
const { formatMessage } = useIntl();
return React.useMemo(
() => [
{
id: 'payment_date',
Header: formatMessage({ id: 'payment_date' }),
Cell: DateCell,
accessor: 'payment_date',
width: 140,
className: 'payment_date',
},
{
id: 'vendor_id',
Header: formatMessage({ id: 'vendor_name' }),
accessor: 'vendor.display_name',
width: 140,
className: 'vendor_id',
},
{
id: 'payment_number',
Header: formatMessage({ id: 'payment_number' }),
accessor: (row) =>
row.payment_number ? `#${row.payment_number}` : null,
width: 140,
className: 'payment_number',
},
{
id: 'payment_account_id',
Header: formatMessage({ id: 'payment_account' }),
accessor: 'payment_account.name',
width: 140,
className: 'payment_account_id',
},
{
id: 'amount',
Header: formatMessage({ id: 'amount' }),
accessor: AmountAccessor,
width: 140,
className: 'amount',
},
{
id: 'reference',
Header: formatMessage({ id: 'reference' }),
accessor: 'reference',
width: 140,
className: 'reference',
},
{
id: 'actions',
Header: '',
Cell: ActionsCell,
className: 'actions',
width: 50,
disableResizing: true,
},
],
[formatMessage],
);
}

View File

@@ -0,0 +1,16 @@
import { connect } from 'react-redux';
import {
getPaymentMadesTableStateFactory
} from 'store/PaymentMades/paymentMades.selector';
export default (mapState) => {
const getPaymentMadesTableState = getPaymentMadesTableStateFactory();
const mapStateToProps = (state, props) => {
const mapped = {
paymentMadesTableState: getPaymentMadesTableState(state, props),
};
return mapState ? mapState(mapped, state, props) : mapped;
};
return connect(mapStateToProps);
};

View File

@@ -0,0 +1,8 @@
import { connect } from 'react-redux';
import { setPaymentMadesTableState } from 'store/PaymentMades/paymentMades.actions';
const mapDispatchToProps = (dispatch) => ({
setPaymentMadesTableState: (state) =>
dispatch(setPaymentMadesTableState(state)),
});
export default connect(null, mapDispatchToProps);

View File

@@ -1,39 +0,0 @@
import { connect } from 'react-redux';
import { getResourceViews } from 'store/customViews/customViews.selectors';
import {
getPaymentMadeCurrentPageFactory,
getPaymentMadePaginationMetaFactory,
getPaymentMadeTableQuery,
getPaymentMadeEntriesFactory,
getPaymentMadesCurrentViewIdFactory
} from 'store/PaymentMades/paymentMade.selector';
export default (mapState) => {
const getPyamentMadesItems = getPaymentMadeCurrentPageFactory();
const getPyamentMadesPaginationMeta = getPaymentMadePaginationMetaFactory();
const getPaymentMadeEntries = getPaymentMadeEntriesFactory();
const getPaymentMadesCurrentViewId = getPaymentMadesCurrentViewIdFactory();
const mapStateToProps = (state, props) => {
const query = getPaymentMadeTableQuery(state, props);
const mapped = {
paymentMadeCurrentPage: getPyamentMadesItems(state, props, query),
paymentMadeViews: getResourceViews(state, props, 'bill_payments'),
paymentMadeItems: state.paymentMades.items,
paymentMadeTableQuery: query,
paymentMadePageination: getPyamentMadesPaginationMeta(
state,
props,
query,
),
paymentMadesLoading: state.paymentMades.loading,
nextPaymentNumberChanged: state.paymentMades.nextPaymentNumberChanged,
paymentMadeEntries: getPaymentMadeEntries(state, props),
paymentMadesCurrentViewId: getPaymentMadesCurrentViewId(state, props),
};
return mapState ? mapState(mapped, state, props) : mapped;
};
return connect(mapStateToProps);
};

View File

@@ -1,39 +0,0 @@
import { connect } from 'react-redux';
import t from 'store/types';
import {
submitPaymentMade,
editPaymentMade,
deletePaymentMade,
fetchPaymentMadesTable,
fetchPaymentMade,
fetchPaymentMadeBills,
} from 'store/PaymentMades/paymentMade.actions';
const mapDispatchToProps = (dispatch) => ({
requestSubmitPaymentMade: (form) => dispatch(submitPaymentMade({ form })),
requestFetchPaymentMade: (id) => dispatch(fetchPaymentMade({ id })),
requestEditPaymentMade: (id, form) => dispatch(editPaymentMade(id, form)),
requestDeletePaymentMade: (id) => dispatch(deletePaymentMade({ id })),
requestFetchPaymentMadesTable: (query = {}) =>
dispatch(fetchPaymentMadesTable({ query: { ...query } })),
requestFetchPaymentMadeBills: (paymentMadeId) => dispatch(fetchPaymentMadeBills({ paymentMadeId })),
changePaymentMadeView: (id) =>
dispatch({
type: t.PAYMENT_MADE_SET_CURRENT_VIEW,
currentViewId: parseInt(id, 10),
}),
addPaymentMadesTableQueries: (queries) =>
dispatch({
type: t.PAYMENT_MADES_TABLE_QUERIES_ADD,
payload: { queries },
}),
setPaymentNumberChange: (isChanged) =>
dispatch({
type: t.PAYMENT_MADES_NUMBER_CHANGED,
payload: { isChanged },
}),
});
export default connect(null, mapDispatchToProps);