mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-18 05:40:31 +00:00
refactoring: sales tables.
refacoring: purchases tables.
This commit is contained in:
@@ -1,296 +0,0 @@
|
||||
import React, { useCallback, useMemo } from 'react';
|
||||
import {
|
||||
Intent,
|
||||
Button,
|
||||
Popover,
|
||||
Menu,
|
||||
MenuItem,
|
||||
MenuDivider,
|
||||
Position,
|
||||
Tag,
|
||||
} 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 Icon from 'components/Icon';
|
||||
import { compose, saveInvoke, isBlank } from 'utils';
|
||||
import { CLASSES } from 'common/classes';
|
||||
import { useIsValuePassed } from 'hooks';
|
||||
|
||||
import { LoadingIndicator, Money, Choose, If } from 'components';
|
||||
import DataTable from 'components/DataTable';
|
||||
import BillsEmptyStatus from './BillsEmptyStatus';
|
||||
|
||||
import withDialogActions from 'containers/Dialog/withDialogActions';
|
||||
import withDashboardActions from 'containers/Dashboard/withDashboardActions';
|
||||
import withViewDetails from 'containers/Views/withViewDetails';
|
||||
|
||||
import withBills from './withBills';
|
||||
import withBillActions from './withBillActions';
|
||||
import withCurrentView from 'containers/Views/withCurrentView';
|
||||
import withSettings from 'containers/Settings/withSettings';
|
||||
|
||||
// Bills transactions datatable.
|
||||
function BillsDataTable({
|
||||
// #withBills
|
||||
billsCurrentPage,
|
||||
billsLoading,
|
||||
billsPageination,
|
||||
billsCurrentViewId,
|
||||
|
||||
// #withDashboardActions
|
||||
changeCurrentView,
|
||||
changePageSubtitle,
|
||||
setTopbarEditView,
|
||||
|
||||
// #withBillsActions
|
||||
addBillsTableQueries,
|
||||
|
||||
// #withView
|
||||
viewMeta,
|
||||
|
||||
// #withSettings
|
||||
baseCurrency,
|
||||
|
||||
// #ownProps
|
||||
loading,
|
||||
onFetchData,
|
||||
onEditBill,
|
||||
onDeleteBill,
|
||||
onOpenBill,
|
||||
onSelectedRowsChange,
|
||||
}) {
|
||||
const { formatMessage } = useIntl();
|
||||
|
||||
const handleFetchData = useCallback(
|
||||
({ pageIndex, pageSize, sortBy }) => {
|
||||
const page = pageIndex + 1;
|
||||
|
||||
addBillsTableQueries({
|
||||
...(sortBy.length > 0
|
||||
? {
|
||||
column_sort_by: sortBy[0].id,
|
||||
sort_order: sortBy[0].desc ? 'desc' : 'asc',
|
||||
}
|
||||
: {}),
|
||||
page_size: pageSize,
|
||||
page,
|
||||
});
|
||||
},
|
||||
[addBillsTableQueries],
|
||||
);
|
||||
|
||||
const handleEditBill = useCallback(
|
||||
(_bill) => () => {
|
||||
saveInvoke(onEditBill, _bill);
|
||||
},
|
||||
[onEditBill],
|
||||
);
|
||||
|
||||
const handleDeleteBill = useCallback(
|
||||
(_bill) => () => {
|
||||
saveInvoke(onDeleteBill, _bill);
|
||||
},
|
||||
[onDeleteBill],
|
||||
);
|
||||
|
||||
const actionMenuList = useCallback(
|
||||
(bill) => (
|
||||
<Menu>
|
||||
<MenuItem
|
||||
icon={<Icon icon="reader-18" />}
|
||||
text={formatMessage({ id: 'view_details' })}
|
||||
/>
|
||||
<MenuDivider />
|
||||
<MenuItem
|
||||
icon={<Icon icon="pen-18" />}
|
||||
text={formatMessage({ id: 'edit_bill' })}
|
||||
onClick={handleEditBill(bill)}
|
||||
/>
|
||||
<If condition={!bill.is_open}>
|
||||
<MenuItem
|
||||
text={formatMessage({ id: 'mark_as_opened' })}
|
||||
onClick={() => onOpenBill(bill)}
|
||||
/>
|
||||
</If>
|
||||
|
||||
<MenuItem
|
||||
text={formatMessage({ id: 'delete_bill' })}
|
||||
intent={Intent.DANGER}
|
||||
onClick={handleDeleteBill(bill)}
|
||||
icon={<Icon icon="trash-16" iconSize={16} />}
|
||||
/>
|
||||
</Menu>
|
||||
),
|
||||
[handleDeleteBill, handleEditBill, formatMessage],
|
||||
);
|
||||
|
||||
const onRowContextMenu = useCallback(
|
||||
(cell) => {
|
||||
return actionMenuList(cell.row.original);
|
||||
},
|
||||
[actionMenuList],
|
||||
);
|
||||
|
||||
const columns = useMemo(
|
||||
() => [
|
||||
{
|
||||
id: 'bill_date',
|
||||
Header: formatMessage({ id: 'bill_date' }),
|
||||
accessor: (r) => moment(r.bill_date).format('YYYY MMM DD'),
|
||||
width: 140,
|
||||
className: 'bill_date',
|
||||
},
|
||||
{
|
||||
id: 'vendor_id',
|
||||
Header: formatMessage({ id: 'vendor_name' }),
|
||||
accessor: 'vendor.display_name',
|
||||
width: 140,
|
||||
className: 'vendor_id',
|
||||
},
|
||||
{
|
||||
id: 'bill_number',
|
||||
Header: formatMessage({ id: 'bill_number' }),
|
||||
accessor: (row) => (row.bill_number ? `#${row.bill_number}` : null),
|
||||
width: 140,
|
||||
className: 'bill_number',
|
||||
},
|
||||
{
|
||||
id: 'due_date',
|
||||
Header: formatMessage({ id: 'due_date' }),
|
||||
accessor: (r) => moment(r.due_date).format('YYYY MMM DD'),
|
||||
width: 140,
|
||||
className: 'due_date',
|
||||
},
|
||||
{
|
||||
id: 'amount',
|
||||
Header: formatMessage({ id: 'amount' }),
|
||||
accessor: (row) =>
|
||||
!isBlank(row.amount) ? (
|
||||
<Money amount={row.amount} currency={baseCurrency} />
|
||||
) : (
|
||||
''
|
||||
),
|
||||
width: 140,
|
||||
className: 'amount',
|
||||
},
|
||||
{
|
||||
id: 'status',
|
||||
Header: formatMessage({ id: 'status' }),
|
||||
accessor: (row) => (
|
||||
<Choose>
|
||||
<Choose.When condition={row.is_open}>
|
||||
<Tag minimal={true} intent={Intent.SUCCESS}>
|
||||
<T id={'opened'} />
|
||||
</Tag>
|
||||
</Choose.When>
|
||||
|
||||
<Choose.Otherwise>
|
||||
<Tag minimal={true}>
|
||||
<T id={'draft'} />
|
||||
</Tag>
|
||||
</Choose.Otherwise>
|
||||
</Choose>
|
||||
),
|
||||
width: 140,
|
||||
className: 'status',
|
||||
},
|
||||
{
|
||||
id: 'reference_no',
|
||||
Header: formatMessage({ id: 'reference_no' }),
|
||||
accessor: 'reference_no',
|
||||
width: 140,
|
||||
className: 'reference_no',
|
||||
},
|
||||
{
|
||||
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 handleSelectedRowsChange = useCallback(
|
||||
(selectedRows) => {
|
||||
saveInvoke(
|
||||
onSelectedRowsChange,
|
||||
selectedRows.map((s) => s.original),
|
||||
);
|
||||
},
|
||||
[onSelectedRowsChange],
|
||||
);
|
||||
|
||||
const showEmptyStatus = [
|
||||
billsCurrentViewId === -1,
|
||||
billsCurrentPage.length === 0,
|
||||
].every((condition) => condition === true);
|
||||
|
||||
return (
|
||||
<div className={classNames(CLASSES.DASHBOARD_DATATABLE)}>
|
||||
<Choose>
|
||||
<Choose.When condition={showEmptyStatus}>
|
||||
<BillsEmptyStatus />
|
||||
</Choose.When>
|
||||
|
||||
<Choose.Otherwise>
|
||||
<DataTable
|
||||
columns={columns}
|
||||
data={billsCurrentPage}
|
||||
onFetchData={handleFetchData}
|
||||
manualSortBy={true}
|
||||
selectionColumn={true}
|
||||
noInitialFetch={true}
|
||||
sticky={true}
|
||||
onSelectedRowsChange={handleSelectedRowsChange}
|
||||
rowContextMenu={onRowContextMenu}
|
||||
pagination={true}
|
||||
pagesCount={billsPageination.pagesCount}
|
||||
initialPageSize={billsPageination.pageSize}
|
||||
initialPageIndex={billsPageination.page - 1}
|
||||
/>
|
||||
</Choose.Otherwise>
|
||||
</Choose>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default compose(
|
||||
withRouter,
|
||||
withCurrentView,
|
||||
withDialogActions,
|
||||
withDashboardActions,
|
||||
withBillActions,
|
||||
withBills(
|
||||
({
|
||||
billsCurrentPage,
|
||||
billsLoading,
|
||||
billsPageination,
|
||||
billsTableQuery,
|
||||
billsCurrentViewId,
|
||||
}) => ({
|
||||
billsCurrentPage,
|
||||
billsLoading,
|
||||
billsPageination,
|
||||
billsTableQuery,
|
||||
billsCurrentViewId,
|
||||
}),
|
||||
),
|
||||
withSettings(({ organizationSettings }) => ({
|
||||
baseCurrency: organizationSettings?.baseCurrency,
|
||||
})),
|
||||
withViewDetails(),
|
||||
)(BillsDataTable);
|
||||
@@ -1,49 +0,0 @@
|
||||
import React, { useCallback } from 'react';
|
||||
|
||||
import { Switch, Route, useHistory } from 'react-router-dom';
|
||||
import BillViewTabs from './BillViewTabs';
|
||||
import BillsDataTable from './BillsDataTable';
|
||||
|
||||
import withAlertsActions from 'containers/Alert/withAlertActions';
|
||||
import withDialogActions from 'containers/Dialog/withDialogActions';
|
||||
|
||||
import { compose } from 'utils';
|
||||
|
||||
function BillsViewPage() {
|
||||
const history = useHistory();
|
||||
|
||||
const handleEditBill = useCallback((bill) => {
|
||||
history.push(`/bills/${bill.id}/edit`);
|
||||
});
|
||||
|
||||
const handleDeleteBill = () => {
|
||||
|
||||
};
|
||||
|
||||
|
||||
const handleOpenBill = () => {
|
||||
|
||||
};
|
||||
|
||||
const handleSelectedRowsChange = () => {
|
||||
|
||||
}
|
||||
|
||||
return (
|
||||
<Switch>
|
||||
<Route
|
||||
exact={true}
|
||||
path={['/bills/:custom_view_id/custom_view', '/bills']}
|
||||
>
|
||||
<BillViewTabs />
|
||||
{/* <BillsDataTable
|
||||
onDeleteBill={handleDeleteBill}
|
||||
onEditBill={handleEditBill}
|
||||
onOpenBill={handleOpenBill}
|
||||
onSelectedRowsChange={handleSelectedRowsChange}
|
||||
/> */}
|
||||
</Route>
|
||||
</Switch>
|
||||
);
|
||||
}
|
||||
export default compose(withAlertsActions, withDialogActions)(BillsViewPage);
|
||||
@@ -1,40 +0,0 @@
|
||||
import { connect } from 'react-redux';
|
||||
import {
|
||||
submitBill,
|
||||
deleteBill,
|
||||
editBill,
|
||||
fetchBillsTable,
|
||||
fetchBill,
|
||||
fetchDueBills,
|
||||
openBill,
|
||||
} from 'store/Bills/bills.actions';
|
||||
import t from 'store/types';
|
||||
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
requestSubmitBill: (form) => dispatch(submitBill({ form })),
|
||||
requestFetchBill: (id) => dispatch(fetchBill({ id })),
|
||||
requestEditBill: (id, form) => dispatch(editBill(id, form)),
|
||||
requestDeleteBill: (id) => dispatch(deleteBill({ id })),
|
||||
requestFetchBillsTable: (query = {}) =>
|
||||
dispatch(fetchBillsTable({ query: { ...query } })),
|
||||
requestFetchDueBills: (vendorId) => dispatch(fetchDueBills({ vendorId })),
|
||||
requestOpenBill: (id) => dispatch(openBill({ id })),
|
||||
changeBillView: (id) =>
|
||||
dispatch({
|
||||
type: t.BILLS_SET_CURRENT_VIEW,
|
||||
currentViewId: parseInt(id, 10),
|
||||
}),
|
||||
|
||||
addBillsTableQueries: (queries) =>
|
||||
dispatch({
|
||||
type: t.BILLS_TABLE_QUERIES_ADD,
|
||||
payload: { queries }
|
||||
}),
|
||||
setBillNumberChanged: (isChanged) =>
|
||||
dispatch({
|
||||
type: t.BILL_NUMBER_CHANGED,
|
||||
payload: { isChanged },
|
||||
}),
|
||||
});
|
||||
|
||||
export default connect(null, mapDispatchToProps);
|
||||
@@ -1,11 +0,0 @@
|
||||
import { connect } from 'react-redux';
|
||||
import { getBillByIdFactory } from 'store/Bills/bills.selectors';
|
||||
|
||||
export default () => {
|
||||
const getBillById = getBillByIdFactory();
|
||||
|
||||
const mapStateToProps = (state, props) => ({
|
||||
bill: getBillById(state, props),
|
||||
});
|
||||
return connect(mapStateToProps);
|
||||
};
|
||||
@@ -1,40 +0,0 @@
|
||||
import { connect } from 'react-redux';
|
||||
import { getResourceViews } from 'store/customViews/customViews.selectors';
|
||||
import {
|
||||
getBillCurrentPageFactory,
|
||||
getBillPaginationMetaFactory,
|
||||
getBillTableQueryFactory,
|
||||
getVendorPayableBillsFactory,
|
||||
getVendorPayableBillsEntriesFactory,
|
||||
getBillsCurrentViewIdFactory,
|
||||
} from 'store/Bills/bills.selectors';
|
||||
|
||||
export default (mapState) => {
|
||||
const getBillsItems = getBillCurrentPageFactory();
|
||||
const getBillsPaginationMeta = getBillPaginationMetaFactory();
|
||||
const getBillTableQuery = getBillTableQueryFactory();
|
||||
const getVendorPayableBills = getVendorPayableBillsFactory();
|
||||
const getVendorPayableBillsEntries = getVendorPayableBillsEntriesFactory();
|
||||
const getBillsCurrentViewId = getBillsCurrentViewIdFactory();
|
||||
|
||||
const mapStateToProps = (state, props) => {
|
||||
const tableQuery = getBillTableQuery(state, props);
|
||||
|
||||
const mapped = {
|
||||
billsCurrentPage: getBillsItems(state, props, tableQuery),
|
||||
billsCurrentViewId: getBillsCurrentViewId(state),
|
||||
billsViews: getResourceViews(state, props, 'bills'),
|
||||
billsItems: state.bills.items,
|
||||
billsTableQuery: tableQuery,
|
||||
|
||||
billsPageination: getBillsPaginationMeta(state, props, tableQuery),
|
||||
billsLoading: state.bills.loading,
|
||||
nextBillNumberChanged: state.bills.nextBillNumberChanged,
|
||||
vendorPayableBills: getVendorPayableBills(state, props),
|
||||
vendorPayableBillsEntries: getVendorPayableBillsEntries(state, props),
|
||||
|
||||
};
|
||||
return mapState ? mapState(mapped, state, props) : mapped;
|
||||
};
|
||||
return connect(mapStateToProps);
|
||||
};
|
||||
@@ -18,7 +18,7 @@ import { FormattedMessage as T, useIntl } from 'react-intl';
|
||||
import DashboardActionsBar from 'components/Dashboard/DashboardActionsBar';
|
||||
import { If, DashboardActionViewsList } from 'components';
|
||||
|
||||
import withBillActions from './withBillActions';
|
||||
import withBillsActions from './withBillsActions';
|
||||
import { useBillsListContext } from './BillsListProvider';
|
||||
|
||||
import { compose } from 'utils';
|
||||
@@ -27,28 +27,31 @@ import { compose } from 'utils';
|
||||
* Bills actions bar.
|
||||
*/
|
||||
function BillActionsBar({
|
||||
//#withBillActions
|
||||
addBillsTableQueries,
|
||||
// #withBillActions
|
||||
setBillsTableState,
|
||||
}) {
|
||||
const history = useHistory();
|
||||
|
||||
// React intl.
|
||||
const { formatMessage } = useIntl();
|
||||
|
||||
|
||||
// Bills list context.
|
||||
const { billsViews } = useBillsListContext();
|
||||
|
||||
|
||||
const [filterCount] = useState(0);
|
||||
|
||||
// Handle click a new bill.
|
||||
const handleClickNewBill = () => {
|
||||
history.push('/bills/new');
|
||||
};
|
||||
|
||||
// Handle tab change.
|
||||
const handleTabChange = (viewId) => {
|
||||
addBillsTableQueries({
|
||||
custom_view_id: viewId.id || null,
|
||||
const handleTabChange = (customView) => {
|
||||
setBillsTableState({
|
||||
customViewId: customView.id || null,
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
return (
|
||||
<DashboardActionsBar>
|
||||
<NavbarGroup>
|
||||
@@ -111,6 +114,4 @@ function BillActionsBar({
|
||||
);
|
||||
}
|
||||
|
||||
export default compose(
|
||||
withBillActions,
|
||||
)(BillActionsBar);
|
||||
export default compose(withBillsActions)(BillActionsBar);
|
||||
@@ -4,15 +4,16 @@ import { useIntl } from 'react-intl';
|
||||
import DashboardPageContent from 'components/Dashboard/DashboardPageContent';
|
||||
|
||||
import { BillsListProvider } from './BillsListProvider';
|
||||
import BillsDataTable from './BillsDataTable';
|
||||
|
||||
import BillsActionsBar from './BillsActionsBar';
|
||||
import BillsAlerts from './BillsAlerts';
|
||||
import BillsViewPage from './BillsViewPage';
|
||||
import BillsViewsTabs from './BillsViewsTabs';
|
||||
import BillsTable from './BillsTable';
|
||||
|
||||
import withDashboardActions from 'containers/Dashboard/withDashboardActions';
|
||||
import withBills from './withBills';
|
||||
|
||||
import { compose } from 'utils';
|
||||
import { transformTableStateToQuery, compose } from 'utils';
|
||||
|
||||
/**
|
||||
* Bills list.
|
||||
@@ -22,7 +23,7 @@ function BillsList({
|
||||
changePageTitle,
|
||||
|
||||
// #withBills
|
||||
billsTableQuery,
|
||||
billsTableState,
|
||||
}) {
|
||||
const { formatMessage } = useIntl();
|
||||
|
||||
@@ -31,20 +32,20 @@ function BillsList({
|
||||
}, [changePageTitle, formatMessage]);
|
||||
|
||||
return (
|
||||
<BillsListProvider query={billsTableQuery}>
|
||||
<BillsListProvider query={transformTableStateToQuery(billsTableState)}>
|
||||
<BillsActionsBar />
|
||||
|
||||
<DashboardPageContent>
|
||||
<BillsViewPage />
|
||||
<BillsAlerts />
|
||||
<BillsViewsTabs />
|
||||
<BillsTable />
|
||||
</DashboardPageContent>
|
||||
|
||||
<BillsAlerts />
|
||||
</BillsListProvider>
|
||||
);
|
||||
}
|
||||
|
||||
export default compose(
|
||||
withDashboardActions,
|
||||
withBills(({ billsTableQuery }) => ({
|
||||
billsTableQuery,
|
||||
})),
|
||||
withBills(({ billsTableState }) => ({ billsTableState })),
|
||||
)(BillsList);
|
||||
@@ -1,6 +1,7 @@
|
||||
import React, { createContext } from 'react';
|
||||
import DashboardInsider from 'components/Dashboard/DashboardInsider';
|
||||
import { useResourceViews, useResourceFields, useBills } from 'hooks/query';
|
||||
import { isTableEmptyStatus } from 'utils';
|
||||
|
||||
const BillsListContext = createContext();
|
||||
|
||||
@@ -21,9 +22,15 @@ function BillsListProvider({ query, ...props }) {
|
||||
|
||||
// Fetch accounts list according to the given custom view id.
|
||||
const {
|
||||
data: { bills, pagination },
|
||||
isFetching: isBillsLoading,
|
||||
} = useBills(query);
|
||||
data: { bills, pagination, filterMeta },
|
||||
isLoading: isBillsLoading,
|
||||
isFetching: isBillsFetching,
|
||||
} = useBills(query, { keepPreviousData: true });
|
||||
|
||||
// Detarmines the datatable empty status.
|
||||
const isEmptyStatus = isTableEmptyStatus({
|
||||
data: bills, pagination, filterMeta,
|
||||
}) && !isBillsFetching;
|
||||
|
||||
// Provider payload.
|
||||
const provider = {
|
||||
@@ -33,8 +40,10 @@ function BillsListProvider({ query, ...props }) {
|
||||
billsViews,
|
||||
|
||||
isBillsLoading,
|
||||
isBillsFetching,
|
||||
isFieldsLoading,
|
||||
isViewsLoading,
|
||||
isEmptyStatus
|
||||
};
|
||||
|
||||
return (
|
||||
108
client/src/containers/Purchases/Bills/BillsLanding/BillsTable.js
Normal file
108
client/src/containers/Purchases/Bills/BillsLanding/BillsTable.js
Normal file
@@ -0,0 +1,108 @@
|
||||
import React, { useCallback } from 'react';
|
||||
import classNames from 'classnames';
|
||||
import { useHistory } from 'react-router-dom';
|
||||
|
||||
import { compose } from 'utils';
|
||||
import { CLASSES } from 'common/classes';
|
||||
|
||||
import DataTable from 'components/DataTable';
|
||||
import TableSkeletonRows from 'components/Datatable/TableSkeletonRows';
|
||||
import TableSkeletonHeader from 'components/Datatable/TableHeaderSkeleton';
|
||||
|
||||
import BillsEmptyStatus from './BillsEmptyStatus';
|
||||
|
||||
import withBillActions from './withBillsActions';
|
||||
import withSettings from 'containers/Settings/withSettings';
|
||||
import withAlertsActions from 'containers/Alert/withAlertActions';
|
||||
import { useBillsTableColumns, ActionsMenu } from './components';
|
||||
import { useBillsListContext } from './BillsListProvider';
|
||||
|
||||
/**
|
||||
* Bills transactions datatable.
|
||||
*/
|
||||
function BillsDataTable({
|
||||
// #withBillsActions
|
||||
setBillsTableState,
|
||||
|
||||
// #withAlerts
|
||||
openAlert
|
||||
}) {
|
||||
// Bills list context.
|
||||
const {
|
||||
bills,
|
||||
pagination,
|
||||
isBillsLoading,
|
||||
isBillsFetching,
|
||||
isEmptyStatus,
|
||||
} = useBillsListContext();
|
||||
|
||||
const history = useHistory();
|
||||
|
||||
// Bills table columns.
|
||||
const columns = useBillsTableColumns();
|
||||
|
||||
const handleFetchData = useCallback(
|
||||
({ pageIndex, pageSize, sortBy }) => {
|
||||
setBillsTableState({
|
||||
pageIndex,
|
||||
pageSize,
|
||||
sortBy,
|
||||
});
|
||||
},
|
||||
[setBillsTableState],
|
||||
);
|
||||
|
||||
// Handle bill edit action.
|
||||
const handleEditBill = (bill) => {
|
||||
history.push(`/bills/${bill.id}/edit`);
|
||||
};
|
||||
|
||||
// Handle bill delete action.
|
||||
const handleDeleteBill = (bill) => {
|
||||
openAlert('bill-delete', { billId: bill.id });
|
||||
};
|
||||
|
||||
// Handle bill open action.
|
||||
const handleOpenBill = (bill) => {
|
||||
openAlert('bill-open', { billId: bill.id });
|
||||
};
|
||||
|
||||
if (isEmptyStatus) {
|
||||
return <BillsEmptyStatus />;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={classNames(CLASSES.DASHBOARD_DATATABLE)}>
|
||||
<DataTable
|
||||
columns={columns}
|
||||
data={bills}
|
||||
loading={isBillsLoading}
|
||||
headerLoading={isBillsLoading}
|
||||
progressBarLoading={isBillsFetching}
|
||||
onFetchData={handleFetchData}
|
||||
manualSortBy={true}
|
||||
selectionColumn={true}
|
||||
noInitialFetch={true}
|
||||
sticky={true}
|
||||
pagination={true}
|
||||
pagesCount={pagination.pagesCount}
|
||||
TableLoadingRenderer={TableSkeletonRows}
|
||||
TableHeaderSkeletonRenderer={TableSkeletonHeader}
|
||||
ContextMenu={ActionsMenu}
|
||||
payload={{
|
||||
onDelete: handleDeleteBill,
|
||||
onEdit: handleEditBill,
|
||||
onOpen: handleOpenBill
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default compose(
|
||||
withBillActions,
|
||||
withAlertsActions,
|
||||
withSettings(({ organizationSettings }) => ({
|
||||
baseCurrency: organizationSettings?.baseCurrency,
|
||||
})),
|
||||
)(BillsDataTable);
|
||||
@@ -7,7 +7,7 @@ import { pick } from 'lodash';
|
||||
import { DashboardViewsTabs } from 'components';
|
||||
|
||||
import { useBillsListContext } from './BillsListProvider';
|
||||
import withBillActions from './withBillActions';
|
||||
import withBillActions from './withBillsActions';
|
||||
|
||||
import { compose } from 'utils';
|
||||
|
||||
@@ -16,16 +16,17 @@ import { compose } from 'utils';
|
||||
*/
|
||||
function BillViewTabs({
|
||||
//#withBillActions
|
||||
addBillsTableQueries,
|
||||
setBillsTableState,
|
||||
}) {
|
||||
const { custom_view_id: customViewId = null } = useParams();
|
||||
|
||||
// Bills list context.
|
||||
const { billsViews } = useBillsListContext();
|
||||
|
||||
const handleTabsChange = (viewId) => {
|
||||
addBillsTableQueries({
|
||||
custom_view_id: customViewId || null,
|
||||
// Handle tab chaging.
|
||||
const handleTabsChange = (customView) => {
|
||||
setBillsTableState({
|
||||
customViewId: customView || null,
|
||||
});
|
||||
};
|
||||
|
||||
167
client/src/containers/Purchases/Bills/BillsLanding/components.js
Normal file
167
client/src/containers/Purchases/Bills/BillsLanding/components.js
Normal file
@@ -0,0 +1,167 @@
|
||||
import React from 'react';
|
||||
import {
|
||||
Intent,
|
||||
Menu,
|
||||
MenuItem,
|
||||
MenuDivider,
|
||||
Popover,
|
||||
Button,
|
||||
Position,
|
||||
Tag,
|
||||
} from '@blueprintjs/core';
|
||||
import { useIntl, FormattedMessage as T } from 'react-intl';
|
||||
import { Icon, If, Choose, Money } from 'components';
|
||||
import { safeCallback, isBlank } from 'utils';
|
||||
import moment from 'moment';
|
||||
|
||||
/**
|
||||
* Actions menu.
|
||||
*/
|
||||
export function ActionsMenu({
|
||||
payload: { onEdit, onOpen, onDelete },
|
||||
row: { original },
|
||||
}) {
|
||||
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_bill' })}
|
||||
onClick={safeCallback(onEdit, original)}
|
||||
/>
|
||||
|
||||
<If condition={!original.is_open}>
|
||||
<MenuItem
|
||||
text={formatMessage({ id: 'mark_as_opened' })}
|
||||
onClick={safeCallback(onOpen, original)}
|
||||
/>
|
||||
</If>
|
||||
|
||||
<MenuItem
|
||||
text={formatMessage({ id: 'delete_bill' })}
|
||||
intent={Intent.DANGER}
|
||||
onClick={safeCallback(onDelete, original)}
|
||||
icon={<Icon icon="trash-16" iconSize={16} />}
|
||||
/>
|
||||
</Menu>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Amount accessor.
|
||||
*/
|
||||
export function AmountAccessor(bill) {
|
||||
return !isBlank(bill.amount) ? (
|
||||
<Money amount={bill.amount} currency={'USD'} />
|
||||
) : (
|
||||
''
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Status accessor.
|
||||
*/
|
||||
export function StatusAccessor(bill) {
|
||||
return (
|
||||
<Choose>
|
||||
<Choose.When condition={bill.is_open}>
|
||||
<Tag minimal={true} intent={Intent.SUCCESS}>
|
||||
<T id={'opened'} />
|
||||
</Tag>
|
||||
</Choose.When>
|
||||
|
||||
<Choose.Otherwise>
|
||||
<Tag minimal={true}>
|
||||
<T id={'draft'} />
|
||||
</Tag>
|
||||
</Choose.Otherwise>
|
||||
</Choose>
|
||||
);
|
||||
}
|
||||
|
||||
export function ActionsCell(props) {
|
||||
return (
|
||||
<Popover
|
||||
content={<ActionsMenu {...props} />}
|
||||
position={Position.RIGHT_BOTTOM}
|
||||
>
|
||||
<Button icon={<Icon icon="more-h-16" iconSize={16} />} />
|
||||
</Popover>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve bills table columns.
|
||||
*/
|
||||
export function useBillsTableColumns() {
|
||||
const { formatMessage } = useIntl();
|
||||
|
||||
return React.useMemo(
|
||||
() => [
|
||||
{
|
||||
id: 'bill_date',
|
||||
Header: formatMessage({ id: 'bill_date' }),
|
||||
accessor: (r) => moment(r.bill_date).format('YYYY MMM DD'),
|
||||
width: 140,
|
||||
className: 'bill_date',
|
||||
},
|
||||
{
|
||||
id: 'vendor_id',
|
||||
Header: formatMessage({ id: 'vendor_name' }),
|
||||
accessor: 'vendor.display_name',
|
||||
width: 140,
|
||||
className: 'vendor_id',
|
||||
},
|
||||
{
|
||||
id: 'bill_number',
|
||||
Header: formatMessage({ id: 'bill_number' }),
|
||||
accessor: (row) => (row.bill_number ? `#${row.bill_number}` : null),
|
||||
width: 140,
|
||||
className: 'bill_number',
|
||||
},
|
||||
{
|
||||
id: 'due_date',
|
||||
Header: formatMessage({ id: 'due_date' }),
|
||||
accessor: (r) => moment(r.due_date).format('YYYY MMM DD'),
|
||||
width: 140,
|
||||
className: 'due_date',
|
||||
},
|
||||
{
|
||||
id: 'amount',
|
||||
Header: formatMessage({ id: 'amount' }),
|
||||
accessor: AmountAccessor,
|
||||
width: 140,
|
||||
className: 'amount',
|
||||
},
|
||||
{
|
||||
id: 'status',
|
||||
Header: formatMessage({ id: 'status' }),
|
||||
accessor: StatusAccessor,
|
||||
width: 140,
|
||||
className: 'status',
|
||||
},
|
||||
{
|
||||
id: 'reference_no',
|
||||
Header: formatMessage({ id: 'reference_no' }),
|
||||
accessor: 'reference_no',
|
||||
width: 140,
|
||||
className: 'reference_no',
|
||||
},
|
||||
{
|
||||
id: 'actions',
|
||||
Header: '',
|
||||
Cell: ActionsCell,
|
||||
className: 'actions',
|
||||
width: 50,
|
||||
disableResizing: true,
|
||||
},
|
||||
],
|
||||
[formatMessage],
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
import { connect } from 'react-redux';
|
||||
import { getBillsTableStateFactory } from 'store/Bills/bills.selectors';
|
||||
|
||||
export default (mapState) => {
|
||||
const getBillsTableState = getBillsTableStateFactory();
|
||||
|
||||
const mapStateToProps = (state, props) => {
|
||||
const mapped = {
|
||||
billsTableState: getBillsTableState(state, props),
|
||||
};
|
||||
return mapState ? mapState(mapped, state, props) : mapped;
|
||||
};
|
||||
return connect(mapStateToProps);
|
||||
};
|
||||
@@ -0,0 +1,8 @@
|
||||
import { connect } from 'react-redux';
|
||||
import { setBillsTableState } from 'store/Bills/bills.actions';
|
||||
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
setBillsTableState: (queries) => dispatch(setBillsTableState(queries)),
|
||||
});
|
||||
|
||||
export default connect(null, mapDispatchToProps);
|
||||
@@ -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);
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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
|
||||
@@ -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);
|
||||
@@ -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);
|
||||
@@ -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 (
|
||||
@@ -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);
|
||||
@@ -21,7 +21,7 @@ function PaymentMadesViewPage({
|
||||
exact={true}
|
||||
path={['/payment-mades/:custom_view_id/custom_view', '/payment-mades']}
|
||||
>
|
||||
<PaymentMadeViewTabs />
|
||||
|
||||
{/* <PaymentMadeDataTable
|
||||
onDeletePaymentMade={handleDeletePaymentMade}
|
||||
onEditPaymentMade={handleEditPaymentMade}
|
||||
@@ -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],
|
||||
);
|
||||
}
|
||||
@@ -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);
|
||||
};
|
||||
@@ -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);
|
||||
@@ -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);
|
||||
};
|
||||
@@ -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);
|
||||
Reference in New Issue
Block a user