mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-19 22:30:31 +00:00
refactoring: migrating to react-query to manage service-side state.
This commit is contained in:
84
client/src/containers/Sales/Estimate/EstiamtesViewPage.js
Normal file
84
client/src/containers/Sales/Estimate/EstiamtesViewPage.js
Normal file
@@ -0,0 +1,84 @@
|
||||
import React, { useCallback } from 'react';
|
||||
import { Switch, Route, useHistory } from 'react-router-dom';
|
||||
|
||||
import EstimateViewTabs from './EstimateViewTabs';
|
||||
import EstimatesDataTable from './EstimatesDataTable';
|
||||
|
||||
import withAlertsActions from 'containers/Alert/withAlertActions';
|
||||
import withDialogActions from 'containers/Dialog/withDialogActions';
|
||||
|
||||
import { compose } from 'utils';
|
||||
|
||||
/**
|
||||
* Estimates list view page.
|
||||
*/
|
||||
function EstimatesViewPage({
|
||||
// #withAlertActions
|
||||
openAlert,
|
||||
}) {
|
||||
const history = useHistory();
|
||||
|
||||
// handle delete estimate click
|
||||
const handleDeleteEstimate = useCallback(
|
||||
({ id }) => {
|
||||
openAlert('estimate-delete', { estimateId: id });
|
||||
},
|
||||
[openAlert],
|
||||
);
|
||||
|
||||
// Handle cancel/confirm estimate deliver.
|
||||
const handleDeliverEstimate = useCallback(
|
||||
({ id }) => {
|
||||
openAlert('estimate-deliver', { estimateId: id });
|
||||
},
|
||||
[openAlert],
|
||||
);
|
||||
|
||||
// Handle cancel/confirm estimate approve.
|
||||
const handleApproveEstimate = useCallback(
|
||||
({ id }) => {
|
||||
openAlert('estimate-Approve', { estimateId: id });
|
||||
},
|
||||
[openAlert],
|
||||
);
|
||||
|
||||
// Handle cancel/confirm estimate reject.
|
||||
const handleRejectEstimate = useCallback(
|
||||
({ id }) => {
|
||||
openAlert('estimate-reject', { estimateId: id });
|
||||
},
|
||||
[openAlert],
|
||||
);
|
||||
|
||||
const handleEditEstimate = useCallback(
|
||||
(estimate) => {
|
||||
history.push(`/estimates/${estimate.id}/edit`);
|
||||
},
|
||||
[history],
|
||||
);
|
||||
|
||||
return (
|
||||
<Switch>
|
||||
<Route
|
||||
exact={true}
|
||||
path={['/estimates/:custom_view_id/custom_view', '/estimates']}
|
||||
>
|
||||
<EstimateViewTabs />
|
||||
|
||||
{/* <EstimatesDataTable
|
||||
onDeleteEstimate={handleDeleteEstimate}
|
||||
onEditEstimate={handleEditEstimate}
|
||||
onDeliverEstimate={handleDeliverEstimate}
|
||||
onApproveEstimate={handleApproveEstimate}
|
||||
onRejectEstimate={handleRejectEstimate}
|
||||
onSelectedRowsChange={handleSelectedRowsChange}
|
||||
/> */}
|
||||
</Route>
|
||||
</Switch>
|
||||
);
|
||||
}
|
||||
|
||||
export default compose(
|
||||
withAlertsActions,
|
||||
withDialogActions,
|
||||
)(EstimatesViewPage)
|
||||
@@ -1,4 +1,4 @@
|
||||
import React, { useMemo, useCallback, useState } from 'react';
|
||||
import React, { useState } from 'react';
|
||||
import Icon from 'components/Icon';
|
||||
import {
|
||||
Button,
|
||||
@@ -11,65 +11,51 @@ import {
|
||||
Intent,
|
||||
} from '@blueprintjs/core';
|
||||
import classNames from 'classnames';
|
||||
import { useRouteMatch, useHistory } from 'react-router-dom';
|
||||
import { useHistory } from 'react-router-dom';
|
||||
import { FormattedMessage as T, useIntl } from 'react-intl';
|
||||
|
||||
import { If, DashboardActionViewsList } from 'components';
|
||||
import FilterDropdown from 'components/FilterDropdown';
|
||||
import DashboardActionsBar from 'components/Dashboard/DashboardActionsBar';
|
||||
|
||||
import withResourceDetail from 'containers/Resources/withResourceDetails';
|
||||
import withDialogActions from 'containers/Dialog/withDialogActions';
|
||||
import withEstimateActions from './withEstimateActions';
|
||||
|
||||
import withEstimates from './withEstimates';
|
||||
import { useEstimatesListContext } from './EstimatesListProvider';
|
||||
|
||||
import { compose } from 'utils';
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
/**
|
||||
* Estimates list actions bar.
|
||||
*/
|
||||
function EstimateActionsBar({
|
||||
// #withResourceDetail
|
||||
resourceFields,
|
||||
|
||||
//#withEstimates
|
||||
estimateViews,
|
||||
|
||||
// #withEstimateActions
|
||||
addEstimatesTableQueries,
|
||||
changeEstimateView,
|
||||
// #own Porps
|
||||
onFilterChanged,
|
||||
selectedRows = [],
|
||||
}) {
|
||||
const { path } = useRouteMatch();
|
||||
const history = useHistory();
|
||||
const [filterCount, setFilterCount] = useState(0);
|
||||
const { formatMessage } = useIntl();
|
||||
|
||||
const onClickNewEstimate = useCallback(() => {
|
||||
history.push('/estimates/new');
|
||||
}, [history]);
|
||||
const [filterCount, setFilterCount] = useState(0);
|
||||
|
||||
const hasSelectedRows = useMemo(() => selectedRows.length > 0, [
|
||||
selectedRows,
|
||||
]);
|
||||
// Estimates list context.
|
||||
const { estimatesViews } = useEstimatesListContext();
|
||||
|
||||
// Handle click a new sale estimate.
|
||||
const onClickNewEstimate = () => {
|
||||
history.push('/estimates/new');
|
||||
};
|
||||
|
||||
const handleTabChange = (viewId) => {
|
||||
changeEstimateView(viewId.id || -1);
|
||||
addEstimatesTableQueries({
|
||||
custom_view_id: viewId.id || null,
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
return (
|
||||
<DashboardActionsBar>
|
||||
<DashboardActionsBar>
|
||||
<NavbarGroup>
|
||||
<DashboardActionViewsList
|
||||
resourceName={'estimates'}
|
||||
views={estimateViews}
|
||||
views={estimatesViews}
|
||||
onChange={handleTabChange}
|
||||
/>
|
||||
|
||||
<NavbarDivider />
|
||||
<Button
|
||||
className={Classes.MINIMAL}
|
||||
@@ -79,7 +65,6 @@ function EstimateActionsBar({
|
||||
/>
|
||||
<Popover
|
||||
minimal={true}
|
||||
// content={filterDropdown}
|
||||
interactionKind={PopoverInteractionKind.CLICK}
|
||||
position={Position.BOTTOM_LEFT}
|
||||
>
|
||||
@@ -95,7 +80,7 @@ function EstimateActionsBar({
|
||||
icon={<Icon icon={'filter-16'} iconSize={16} />}
|
||||
/>
|
||||
</Popover>
|
||||
<If condition={hasSelectedRows}>
|
||||
<If condition={false}>
|
||||
<Button
|
||||
className={Classes.MINIMAL}
|
||||
icon={<Icon icon={'trash-16'} iconSize={16} />}
|
||||
@@ -125,19 +110,6 @@ function EstimateActionsBar({
|
||||
);
|
||||
}
|
||||
|
||||
const mapStateToProps = (state, props) => ({
|
||||
resourceName: 'sale_estimate',
|
||||
});
|
||||
const withEstimateActionsBar = connect(mapStateToProps);
|
||||
|
||||
export default compose(
|
||||
withEstimateActionsBar,
|
||||
withDialogActions,
|
||||
withEstimates(({ estimateViews }) => ({
|
||||
estimateViews,
|
||||
})),
|
||||
withResourceDetail(({ resourceFields }) => ({
|
||||
resourceFields,
|
||||
})),
|
||||
withEstimateActions,
|
||||
)(EstimateActionsBar);
|
||||
|
||||
@@ -27,8 +27,6 @@ import withMediaActions from 'containers/Media/withMediaActions';
|
||||
import withSettings from 'containers/Settings/withSettings';
|
||||
|
||||
import { AppToaster } from 'components';
|
||||
import Dragzone from 'components/Dragzone';
|
||||
import useMedia from 'hooks/useMedia';
|
||||
import { ERROR } from 'common/errors';
|
||||
|
||||
import {
|
||||
|
||||
@@ -1,66 +1,39 @@
|
||||
import React, { useEffect, useRef } from 'react';
|
||||
import { useHistory } from 'react-router';
|
||||
import React from 'react';
|
||||
|
||||
import { Alignment, Navbar, NavbarGroup } from '@blueprintjs/core';
|
||||
import { useParams, withRouter } from 'react-router-dom';
|
||||
import { connect } from 'react-redux';
|
||||
import { pick, debounce } from 'lodash';
|
||||
import { useParams } from 'react-router-dom';
|
||||
|
||||
import { pick } from 'lodash';
|
||||
|
||||
import { DashboardViewsTabs } from 'components';
|
||||
|
||||
import withEstimates from './withEstimates';
|
||||
import withEstimateActions from './withEstimateActions';
|
||||
import withDashboardActions from 'containers/Dashboard/withDashboardActions';
|
||||
import withViewDetails from 'containers/Views/withViewDetails';
|
||||
|
||||
import { useEstimatesListContext } from './EstimatesListProvider';
|
||||
import { compose } from 'utils';
|
||||
|
||||
/**
|
||||
* Estimates views tabs.
|
||||
*/
|
||||
function EstimateViewTabs({
|
||||
// #withExpenses
|
||||
estimateViews,
|
||||
|
||||
// #withViewDetails
|
||||
viewItem,
|
||||
|
||||
//#withEstimatesActions
|
||||
addEstimatesTableQueries,
|
||||
changeEstimateView,
|
||||
|
||||
// #withDashboardActions
|
||||
setTopbarEditView,
|
||||
changePageSubtitle,
|
||||
// props
|
||||
customViewChanged,
|
||||
onViewChanged,
|
||||
}) {
|
||||
const history = useHistory();
|
||||
const { custom_view_id: customViewId = null } = useParams();
|
||||
|
||||
useEffect(() => {
|
||||
setTopbarEditView(customViewId);
|
||||
changePageSubtitle(customViewId && viewItem ? viewItem.name : '');
|
||||
}, [customViewId]);
|
||||
|
||||
const tabs = estimateViews.map((view) => ({
|
||||
// Estimates list context.
|
||||
const { estimatesViews } = useEstimatesListContext();
|
||||
|
||||
const tabs = estimatesViews.map((view) => ({
|
||||
...pick(view, ['name', 'id']),
|
||||
}));
|
||||
|
||||
const handleTabsChange = (viewId) => {
|
||||
changeEstimateView(viewId || -1);
|
||||
addEstimatesTableQueries({
|
||||
custom_view_id: viewId || null,
|
||||
});
|
||||
setTopbarEditView(viewId);
|
||||
};
|
||||
|
||||
// Handle click a new view tab.
|
||||
const handleClickNewView = () => {
|
||||
setTopbarEditView(null);
|
||||
history.push('/custom_views/estimates/new');
|
||||
};
|
||||
|
||||
return (
|
||||
<Navbar className={'navbar--dashboard-views'}>
|
||||
<NavbarGroup align={Alignment.LEFT}>
|
||||
@@ -75,19 +48,6 @@ function EstimateViewTabs({
|
||||
);
|
||||
}
|
||||
|
||||
const mapStateToProps = (state, ownProps) => ({
|
||||
viewId: ownProps.match.params.custom_view_id,
|
||||
});
|
||||
|
||||
const withEstimatesViewTabs = connect(mapStateToProps);
|
||||
|
||||
export default compose(
|
||||
withRouter,
|
||||
withEstimatesViewTabs,
|
||||
withEstimateActions,
|
||||
withDashboardActions,
|
||||
withViewDetails(),
|
||||
withEstimates(({ estimateViews }) => ({
|
||||
estimateViews,
|
||||
})),
|
||||
)(EstimateViewTabs);
|
||||
|
||||
@@ -1,170 +1,48 @@
|
||||
import React, { useEffect, useCallback, useMemo, useState } from 'react';
|
||||
import { Route, Switch, useHistory } from 'react-router-dom';
|
||||
import { useQuery, queryCache } from 'react-query';
|
||||
import { Alert, Intent } from '@blueprintjs/core';
|
||||
|
||||
import AppToaster from 'components/AppToaster';
|
||||
import React, { useEffect } from 'react';
|
||||
import { FormattedMessage as T, useIntl } from 'react-intl';
|
||||
import DashboardPageContent from 'components/Dashboard/DashboardPageContent';
|
||||
import DashboardInsider from 'components/Dashboard/DashboardInsider';
|
||||
|
||||
import EstimatesAlerts from './EstimatesAlerts';
|
||||
import EstimatesDataTable from './EstimatesDataTable';
|
||||
import EstimateActionsBar from './EstimateActionsBar';
|
||||
import EstimateViewTabs from './EstimateViewTabs';
|
||||
import EstimatesAlerts from './EstimatesAlerts';
|
||||
import EstiamtesViewPage from './EstiamtesViewPage';
|
||||
|
||||
import withDashboardActions from 'containers/Dashboard/withDashboardActions';
|
||||
import withResourceActions from 'containers/Resources/withResourcesActions';
|
||||
import withEstimates from './withEstimates';
|
||||
import withEstimateActions from 'containers/Sales/Estimate/withEstimateActions';
|
||||
import withViewsActions from 'containers/Views/withViewsActions';
|
||||
import withAlertsActions from 'containers/Alert/withAlertActions';
|
||||
|
||||
import { EstimatesListProvider } from './EstimatesListProvider';
|
||||
import { compose } from 'utils';
|
||||
|
||||
/**
|
||||
* Sale estimates list page.
|
||||
*/
|
||||
function EstimatesList({
|
||||
// #withDashboardActions
|
||||
changePageTitle,
|
||||
|
||||
// #withViewsActions
|
||||
requestFetchResourceViews,
|
||||
requestFetchResourceFields,
|
||||
|
||||
// #withEstimate
|
||||
estimatesTableQuery,
|
||||
estimateViews,
|
||||
|
||||
// #withAlertsActions.
|
||||
openAlert,
|
||||
|
||||
//#withEistimateActions
|
||||
requestFetchEstimatesTable,
|
||||
requestDeliverdEstimate,
|
||||
requestApproveEstimate,
|
||||
requestRejectEstimate,
|
||||
addEstimatesTableQueries,
|
||||
}) {
|
||||
const history = useHistory();
|
||||
const { formatMessage } = useIntl();
|
||||
const [deliverEstimate, setDeliverEstimate] = useState(false);
|
||||
const [approveEstimate, setApproveEstimate] = useState(false);
|
||||
const [rejectEstimate, setRejectEstimate] = useState(false);
|
||||
|
||||
const [selectedRows, setSelectedRows] = useState([]);
|
||||
|
||||
const fetchResourceViews = useQuery(
|
||||
['resource-views', 'sale_estimate'],
|
||||
(key, resourceName) => requestFetchResourceViews(resourceName),
|
||||
);
|
||||
|
||||
const fetchResourceFields = useQuery(
|
||||
['resource-fields', 'sale_estimate'],
|
||||
(key, resourceName) => requestFetchResourceFields(resourceName),
|
||||
);
|
||||
|
||||
const fetchEstimate = useQuery(
|
||||
['estimates-table', estimatesTableQuery],
|
||||
(key, _query) => requestFetchEstimatesTable({ ..._query }),
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
changePageTitle(formatMessage({ id: 'estimates_list' }));
|
||||
}, [changePageTitle, formatMessage]);
|
||||
|
||||
// handle delete estimate click
|
||||
const handleDeleteEstimate = useCallback(
|
||||
({ id }) => {
|
||||
openAlert('estimate-delete', { estimateId: id });
|
||||
},
|
||||
[openAlert],
|
||||
);
|
||||
|
||||
// Handle cancel/confirm estimate deliver.
|
||||
const handleDeliverEstimate = useCallback(
|
||||
({ id }) => {
|
||||
openAlert('estimate-deliver', { estimateId: id });
|
||||
},
|
||||
[openAlert],
|
||||
);
|
||||
|
||||
// Handle cancel/confirm estimate approve.
|
||||
const handleApproveEstimate = useCallback(
|
||||
({ id }) => {
|
||||
openAlert('estimate-Approve', { estimateId: id });
|
||||
},
|
||||
[openAlert],
|
||||
);
|
||||
|
||||
// Handle cancel/confirm estimate reject.
|
||||
const handleRejectEstimate = useCallback(
|
||||
({ id }) => {
|
||||
openAlert('estimate-reject', { estimateId: id });
|
||||
},
|
||||
[openAlert],
|
||||
);
|
||||
|
||||
// Handle filter change to re-fetch data-table.
|
||||
const handleFilterChanged = useCallback(() => {}, []);
|
||||
|
||||
// Calculates the selected rows
|
||||
const selectedRowsCount = useMemo(() => Object.values(selectedRows).length, [
|
||||
selectedRows,
|
||||
]);
|
||||
|
||||
const handleEditEstimate = useCallback(
|
||||
(estimate) => {
|
||||
history.push(`/estimates/${estimate.id}/edit`);
|
||||
},
|
||||
[history],
|
||||
);
|
||||
|
||||
const handleSelectedRowsChange = useCallback(
|
||||
(estimate) => {
|
||||
setSelectedRows(estimate);
|
||||
},
|
||||
[setSelectedRows],
|
||||
);
|
||||
return (
|
||||
<DashboardInsider
|
||||
loading={fetchResourceViews.isFetching || fetchResourceFields.isFetching}
|
||||
name={'sale_estimate'}
|
||||
>
|
||||
<EstimateActionsBar
|
||||
// onBulkDelete={}
|
||||
selectedRows={selectedRows}
|
||||
onFilterChanged={handleFilterChanged}
|
||||
/>
|
||||
<EstimatesListProvider query={estimatesTableQuery}>
|
||||
<EstimateActionsBar />
|
||||
|
||||
<DashboardPageContent>
|
||||
<Switch>
|
||||
<Route
|
||||
exact={true}
|
||||
path={['/estimates/:custom_view_id/custom_view', '/estimates']}
|
||||
>
|
||||
<EstimateViewTabs />
|
||||
<EstimatesDataTable
|
||||
onDeleteEstimate={handleDeleteEstimate}
|
||||
onEditEstimate={handleEditEstimate}
|
||||
onDeliverEstimate={handleDeliverEstimate}
|
||||
onApproveEstimate={handleApproveEstimate}
|
||||
onRejectEstimate={handleRejectEstimate}
|
||||
onSelectedRowsChange={handleSelectedRowsChange}
|
||||
/>
|
||||
</Route>
|
||||
</Switch>
|
||||
<EstiamtesViewPage />
|
||||
<EstimatesAlerts />
|
||||
</DashboardPageContent>
|
||||
</DashboardInsider>
|
||||
</EstimatesListProvider>
|
||||
);
|
||||
}
|
||||
|
||||
export default compose(
|
||||
withResourceActions,
|
||||
withEstimateActions,
|
||||
withDashboardActions,
|
||||
withViewsActions,
|
||||
withEstimates(({ estimatesTableQuery, estimateViews }) => ({
|
||||
withEstimates(({ estimatesTableQuery }) => ({
|
||||
estimatesTableQuery,
|
||||
estimateViews,
|
||||
})),
|
||||
withAlertsActions,
|
||||
)(EstimatesList);
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
import React, { createContext } from 'react';
|
||||
import DashboardInsider from 'components/Dashboard/DashboardInsider';
|
||||
import { useResourceViews, useResourceFields, useEstimates } from 'hooks/query';
|
||||
|
||||
const EstimatesListContext = createContext();
|
||||
|
||||
/**
|
||||
* Sale estimates data provider.
|
||||
*/
|
||||
function EstimatesListProvider({ query, ...props }) {
|
||||
// Fetch estimates resource views and fields.
|
||||
const { data: estimatesViews, isFetching: isViewsLoading } = useResourceViews(
|
||||
'sale_estimates',
|
||||
);
|
||||
|
||||
// Fetch the estimates resource fields.
|
||||
const {
|
||||
data: estimatesFields,
|
||||
isFetching: isFieldsLoading,
|
||||
} = useResourceFields('sale_estimates');
|
||||
|
||||
// Fetch estimates list according to the given custom view id.
|
||||
const {
|
||||
data: { estimates, pagination },
|
||||
isFetching: isEstimatesLoading,
|
||||
} = useEstimates(query);
|
||||
|
||||
// Provider payload.
|
||||
const provider = {
|
||||
estimates,
|
||||
pagination,
|
||||
estimatesFields,
|
||||
estimatesViews,
|
||||
|
||||
isEstimatesLoading,
|
||||
isFieldsLoading,
|
||||
isViewsLoading,
|
||||
};
|
||||
|
||||
return (
|
||||
<DashboardInsider
|
||||
loading={isViewsLoading || isFieldsLoading}
|
||||
name={'sale_estimate'}
|
||||
>
|
||||
<EstimatesListContext.Provider value={provider} {...props} />
|
||||
</DashboardInsider>
|
||||
);
|
||||
}
|
||||
|
||||
const useEstimatesListContext = () => React.useContext(EstimatesListContext);
|
||||
|
||||
export { EstimatesListProvider, useEstimatesListContext };
|
||||
@@ -1,4 +1,4 @@
|
||||
import React, { useCallback, useState, useMemo } from 'react';
|
||||
import React, { useState } from 'react';
|
||||
import Icon from 'components/Icon';
|
||||
import {
|
||||
Button,
|
||||
@@ -12,71 +12,45 @@ import {
|
||||
} from '@blueprintjs/core';
|
||||
|
||||
import classNames from 'classnames';
|
||||
import { useRouteMatch, useHistory } from 'react-router-dom';
|
||||
import { useHistory } from 'react-router-dom';
|
||||
import { FormattedMessage as T, useIntl } from 'react-intl';
|
||||
|
||||
import { connect } from 'react-redux';
|
||||
import FilterDropdown from 'components/FilterDropdown';
|
||||
import DashboardActionsBar from 'components/Dashboard/DashboardActionsBar';
|
||||
|
||||
import { If, DashboardActionViewsList } from 'components';
|
||||
|
||||
import withResourceDetail from 'containers/Resources/withResourceDetails';
|
||||
import withDialogActions from 'containers/Dialog/withDialogActions';
|
||||
|
||||
import { useInvoicesListContext } from './InvoicesListProvider';
|
||||
import withInvoiceActions from './withInvoiceActions';
|
||||
import withInvoices from './withInvoices';
|
||||
|
||||
import { compose } from 'utils';
|
||||
|
||||
/**
|
||||
* Invoices table actions bar.
|
||||
*/
|
||||
function InvoiceActionsBar({
|
||||
// #withResourceDetail
|
||||
resourceFields,
|
||||
|
||||
//#withInvoice
|
||||
invoicesViews,
|
||||
|
||||
// #withInvoiceActions
|
||||
addInvoiceTableQueries,
|
||||
changeInvoiceView,
|
||||
// #own Porps
|
||||
onFilterChanged,
|
||||
selectedRows = [],
|
||||
}) {
|
||||
const history = useHistory();
|
||||
const [filterCount, setFilterCount] = useState(0);
|
||||
const { formatMessage } = useIntl();
|
||||
|
||||
const handleClickNewInvoice = useCallback(() => {
|
||||
const [filterCount, setFilterCount] = useState(0);
|
||||
|
||||
// Sale invoices list context.
|
||||
const { invoicesViews } = useInvoicesListContext();
|
||||
|
||||
// Handle new invoice button click.
|
||||
const handleClickNewInvoice = () => {
|
||||
history.push('/invoices/new');
|
||||
}, [history]);
|
||||
|
||||
const hasSelectedRows = useMemo(() => selectedRows.length > 0, [
|
||||
selectedRows,
|
||||
]);
|
||||
};
|
||||
|
||||
// Handle views tab change.
|
||||
const handleTabChange = (viewId) => {
|
||||
changeInvoiceView(viewId.id || -1);
|
||||
addInvoiceTableQueries({
|
||||
custom_view_id: viewId.id || null,
|
||||
});
|
||||
};
|
||||
|
||||
// const filterDropdown = FilterDropdown({
|
||||
// initialCondition: {
|
||||
// fieldKey: '',
|
||||
// compatator: '',
|
||||
// value: '',
|
||||
// },
|
||||
// fields: resourceFields,
|
||||
// onFilterChange: (filterConditions) => {
|
||||
// addInvoiceTableQueries({
|
||||
// filter_roles: filterConditions || '',
|
||||
// });
|
||||
// onFilterChanged && onFilterChanged(filterConditions);
|
||||
// },
|
||||
// });
|
||||
|
||||
return (
|
||||
<DashboardActionsBar>
|
||||
<NavbarGroup>
|
||||
@@ -110,7 +84,7 @@ function InvoiceActionsBar({
|
||||
icon={<Icon icon={'filter-16'} iconSize={16} />}
|
||||
/>
|
||||
</Popover>
|
||||
<If condition={hasSelectedRows}>
|
||||
<If condition={false}>
|
||||
<Button
|
||||
className={Classes.MINIMAL}
|
||||
icon={<Icon icon={'trash-16'} iconSize={16} />}
|
||||
@@ -139,18 +113,4 @@ function InvoiceActionsBar({
|
||||
);
|
||||
}
|
||||
|
||||
const mapStateToProps = (state, props) => ({
|
||||
resourceName: 'sales_invoices',
|
||||
});
|
||||
const withInvoiceActionsBar = connect(mapStateToProps);
|
||||
|
||||
export default compose(
|
||||
withInvoiceActionsBar,
|
||||
withResourceDetail(({ resourceFields }) => ({
|
||||
resourceFields,
|
||||
})),
|
||||
withInvoices(({ invoicesViews }) => ({
|
||||
invoicesViews,
|
||||
})),
|
||||
withInvoiceActions,
|
||||
)(InvoiceActionsBar);
|
||||
export default compose(withInvoiceActions)(InvoiceActionsBar);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React, { useCallback } from 'react';
|
||||
import React from 'react';
|
||||
import {
|
||||
Intent,
|
||||
Button,
|
||||
@@ -11,72 +11,67 @@ import {
|
||||
} from '@blueprintjs/core';
|
||||
import { useFormikContext } from 'formik';
|
||||
import { FormattedMessage as T } from 'react-intl';
|
||||
import { useHistory } from 'react-router-dom';
|
||||
import { CLASSES } from 'common/classes';
|
||||
import classNames from 'classnames';
|
||||
import { saveInvoke } from 'utils';
|
||||
|
||||
import { useInvoiceFormContext } from './InvoiceFormProvider';
|
||||
import { If, Icon } from 'components';
|
||||
|
||||
/**
|
||||
* Invoice floating actions bar.
|
||||
*/
|
||||
export default function InvoiceFloatingActions({
|
||||
isSubmitting,
|
||||
onSubmitClick,
|
||||
onCancelClick,
|
||||
invoice,
|
||||
}) {
|
||||
export default function InvoiceFloatingActions() {
|
||||
const history = useHistory();
|
||||
|
||||
// Formik context.
|
||||
const { isSubmitting } = useFormikContext();
|
||||
|
||||
// Formik context.
|
||||
const { resetForm, submitForm } = useFormikContext();
|
||||
|
||||
// Invoice form context.
|
||||
const { setSubmitPayload, invoice } = useInvoiceFormContext();
|
||||
|
||||
// Handle submit & deliver button click.
|
||||
const handleSubmitDeliverBtnClick = (event) => {
|
||||
saveInvoke(onSubmitClick, event, {
|
||||
redirect: true,
|
||||
deliver: true,
|
||||
});
|
||||
setSubmitPayload({ redirect: true, deliver: true });
|
||||
submitForm();
|
||||
};
|
||||
|
||||
// Handle submit, deliver & new button click.
|
||||
const handleSubmitDeliverAndNewBtnClick = (event) => {
|
||||
setSubmitPayload({ redirect: false, deliver: true, resetForm: true });
|
||||
submitForm();
|
||||
saveInvoke(onSubmitClick, event, {
|
||||
redirect: false,
|
||||
deliver: true,
|
||||
resetForm: true,
|
||||
});
|
||||
};
|
||||
|
||||
// Handle submit, deliver & continue editing button click.
|
||||
const handleSubmitDeliverContinueEditingBtnClick = (event) => {
|
||||
setSubmitPayload({ redirect: false, deliver: true });
|
||||
submitForm();
|
||||
saveInvoke(onSubmitClick, event, {
|
||||
redirect: false,
|
||||
deliver: true,
|
||||
});
|
||||
};
|
||||
|
||||
// Handle submit as draft button click.
|
||||
const handleSubmitDraftBtnClick = (event) => {
|
||||
saveInvoke(onSubmitClick, event, {
|
||||
redirect: true,
|
||||
deliver: false,
|
||||
});
|
||||
setSubmitPayload({ redirect: true, deliver: false });
|
||||
submitForm();
|
||||
};
|
||||
|
||||
// Handle submit as draft & new button click.
|
||||
const handleSubmitDraftAndNewBtnClick = (event) => {
|
||||
setSubmitPayload({ redirect: false, deliver: false, resetForm: true });
|
||||
submitForm();
|
||||
saveInvoke(onSubmitClick, event, {
|
||||
redirect: false,
|
||||
deliver: false,
|
||||
resetForm: true,
|
||||
});
|
||||
};
|
||||
|
||||
// Handle submit as draft & continue editing button click.
|
||||
const handleSubmitDraftContinueEditingBtnClick = (event) => {
|
||||
setSubmitPayload({ redirect: false, deliver: false });
|
||||
submitForm();
|
||||
saveInvoke(onSubmitClick, event, {
|
||||
redirect: false,
|
||||
deliver: false,
|
||||
});
|
||||
};
|
||||
|
||||
// Handle cancel button click.
|
||||
const handleCancelBtnClick = (event) => {
|
||||
saveInvoke(onCancelClick, event);
|
||||
history.goBack();
|
||||
};
|
||||
|
||||
const handleClearBtnClick = (event) => {
|
||||
@@ -90,8 +85,8 @@ export default function InvoiceFloatingActions({
|
||||
<ButtonGroup>
|
||||
<Button
|
||||
disabled={isSubmitting}
|
||||
loading={isSubmitting}
|
||||
intent={Intent.PRIMARY}
|
||||
type="submit"
|
||||
onClick={handleSubmitDeliverBtnClick}
|
||||
text={<T id={'save_and_deliver'} />}
|
||||
/>
|
||||
@@ -124,7 +119,6 @@ export default function InvoiceFloatingActions({
|
||||
<Button
|
||||
disabled={isSubmitting}
|
||||
className={'ml1'}
|
||||
type="submit"
|
||||
onClick={handleSubmitDraftBtnClick}
|
||||
text={<T id={'save_as_draft'} />}
|
||||
/>
|
||||
@@ -158,7 +152,6 @@ export default function InvoiceFloatingActions({
|
||||
<Button
|
||||
disabled={isSubmitting}
|
||||
intent={Intent.PRIMARY}
|
||||
type="submit"
|
||||
onClick={handleSubmitDeliverBtnClick}
|
||||
text={<T id={'save'} />}
|
||||
/>
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
import React, { useState, useMemo, useCallback, useEffect } from 'react';
|
||||
import React, { useMemo, useCallback, useEffect } from 'react';
|
||||
import { Formik, Form } from 'formik';
|
||||
import moment from 'moment';
|
||||
import { Intent } from '@blueprintjs/core';
|
||||
import { useIntl } from 'react-intl';
|
||||
import { pick, sumBy, omit } from 'lodash';
|
||||
import { pick, sumBy, omit, isEmpty } from 'lodash';
|
||||
import classNames from 'classnames';
|
||||
import { CLASSES } from 'common/classes';
|
||||
|
||||
import {
|
||||
CreateInvoiceFormSchema,
|
||||
EditInvoiceFormSchema,
|
||||
@@ -19,24 +18,21 @@ import InvoiceFormFooter from './InvoiceFormFooter';
|
||||
import InvoiceNumberChangeWatcher from './InvoiceNumberChangeWatcher';
|
||||
|
||||
import withInvoiceActions from './withInvoiceActions';
|
||||
import withInvoiceDetail from './withInvoiceDetail';
|
||||
|
||||
import withDashboardActions from 'containers/Dashboard/withDashboardActions';
|
||||
import withMediaActions from 'containers/Media/withMediaActions';
|
||||
import withSettings from 'containers/Settings/withSettings';
|
||||
|
||||
import { AppToaster } from 'components';
|
||||
|
||||
import useMedia from 'hooks/useMedia';
|
||||
import { ERROR } from 'common/errors';
|
||||
|
||||
import {
|
||||
compose,
|
||||
repeatValue,
|
||||
defaultToTransform,
|
||||
orderingLinesIndexes,
|
||||
transactionNumber,
|
||||
} from 'utils';
|
||||
import { useHistory } from 'react-router-dom';
|
||||
import { useInvoiceFormContext } from './InvoiceFormProvider';
|
||||
|
||||
const MIN_LINES_NUMBER = 4;
|
||||
|
||||
@@ -65,14 +61,6 @@ const defaultInitialValues = {
|
||||
* Invoice form.
|
||||
*/
|
||||
function InvoiceForm({
|
||||
// #WithMedia
|
||||
requestSubmitMedia,
|
||||
requestDeleteMedia,
|
||||
|
||||
// #WithInvoiceActions
|
||||
requestSubmitInvoice,
|
||||
requestEditInvoice,
|
||||
|
||||
// #withDashboard
|
||||
changePageTitle,
|
||||
changePageSubtitle,
|
||||
@@ -80,24 +68,25 @@ function InvoiceForm({
|
||||
// #withSettings
|
||||
invoiceNextNumber,
|
||||
invoiceNumberPrefix,
|
||||
|
||||
// #withInvoiceDetail
|
||||
invoice,
|
||||
|
||||
// #own Props
|
||||
invoiceId,
|
||||
onCancelForm,
|
||||
}) {
|
||||
const { formatMessage } = useIntl();
|
||||
const history = useHistory();
|
||||
const {
|
||||
items,
|
||||
invoiceId,
|
||||
invoice,
|
||||
createInvoiceMutate,
|
||||
editInvoiceMutate,
|
||||
submitPayload,
|
||||
} = useInvoiceFormContext();
|
||||
|
||||
const [submitPayload, setSubmitPayload] = useState({});
|
||||
const isNewMode = !invoiceId;
|
||||
|
||||
const invoiceNumber = invoiceNumberPrefix
|
||||
? `${invoiceNumberPrefix}-${invoiceNextNumber}`
|
||||
: invoiceNextNumber;
|
||||
|
||||
// Invoice number.
|
||||
const invoiceNumber = transactionNumber(
|
||||
invoiceNumberPrefix,
|
||||
invoiceNextNumber,
|
||||
);
|
||||
useEffect(() => {
|
||||
const transactionNumber = invoice ? invoice.invoice_no : invoiceNumber;
|
||||
|
||||
@@ -119,7 +108,7 @@ function InvoiceForm({
|
||||
|
||||
const initialValues = useMemo(
|
||||
() => ({
|
||||
...(invoice
|
||||
...(!isEmpty(invoice)
|
||||
? {
|
||||
...pick(invoice, Object.keys(defaultInitialValues)),
|
||||
entries: [
|
||||
@@ -205,23 +194,12 @@ function InvoiceForm({
|
||||
};
|
||||
|
||||
if (invoice && invoice.id) {
|
||||
requestEditInvoice(invoice.id, form).then(onSuccess).catch(onError);
|
||||
editInvoiceMutate(invoice.id, form).then(onSuccess).catch(onError);
|
||||
} else {
|
||||
requestSubmitInvoice(form).then(onSuccess).catch(onError);
|
||||
createInvoiceMutate(form).then(onSuccess).catch(onError);
|
||||
}
|
||||
};
|
||||
|
||||
const handleCancelClick = useCallback(() => {
|
||||
history.goBack();
|
||||
}, [history]);
|
||||
|
||||
const handleSubmitClick = useCallback(
|
||||
(event, payload) => {
|
||||
setSubmitPayload({ ...payload });
|
||||
},
|
||||
[setSubmitPayload],
|
||||
);
|
||||
|
||||
const handleInvoiceNumberChanged = useCallback(
|
||||
(invoiceNumber) => {
|
||||
changePageSubtitle(
|
||||
@@ -246,28 +224,22 @@ function InvoiceForm({
|
||||
initialValues={initialValues}
|
||||
onSubmit={handleSubmit}
|
||||
>
|
||||
{({ isSubmitting}) => (
|
||||
<Form>
|
||||
<InvoiceFormHeader
|
||||
onInvoiceNumberChanged={handleInvoiceNumberChanged}
|
||||
/>
|
||||
<InvoiceNumberChangeWatcher invoiceNumber={invoiceNumber} />
|
||||
<Form>
|
||||
<InvoiceFormHeader
|
||||
onInvoiceNumberChanged={handleInvoiceNumberChanged}
|
||||
/>
|
||||
<InvoiceNumberChangeWatcher invoiceNumber={invoiceNumber} />
|
||||
|
||||
<div className={classNames(CLASSES.PAGE_FORM_BODY)}>
|
||||
<EditableItemsEntriesTable
|
||||
defaultEntry={defaultInvoice}
|
||||
filterSellableItems={true}
|
||||
/>
|
||||
</div>
|
||||
<InvoiceFormFooter />
|
||||
<InvoiceFloatingActions
|
||||
isSubmitting={isSubmitting}
|
||||
invoice={invoice}
|
||||
onCancelClick={handleCancelClick}
|
||||
onSubmitClick={handleSubmitClick}
|
||||
<div className={classNames(CLASSES.PAGE_FORM_BODY)}>
|
||||
<EditableItemsEntriesTable
|
||||
items={items}
|
||||
defaultEntry={defaultInvoice}
|
||||
filterSellableItems={true}
|
||||
/>
|
||||
</Form>
|
||||
)}
|
||||
</div>
|
||||
<InvoiceFormFooter />
|
||||
<InvoiceFloatingActions />
|
||||
</Form>
|
||||
</Formik>
|
||||
</div>
|
||||
);
|
||||
@@ -277,7 +249,6 @@ export default compose(
|
||||
withInvoiceActions,
|
||||
withDashboardActions,
|
||||
withMediaActions,
|
||||
withInvoiceDetail(),
|
||||
withSettings(({ invoiceSettings }) => ({
|
||||
invoiceNextNumber: invoiceSettings?.nextNumber,
|
||||
invoiceNumberPrefix: invoiceSettings?.numberPrefix,
|
||||
|
||||
@@ -18,21 +18,25 @@ import {
|
||||
InputPrependButton,
|
||||
} from 'components';
|
||||
|
||||
import withCustomers from 'containers/Customers/withCustomers';
|
||||
import { useInvoiceFormContext } from './InvoiceFormProvider';
|
||||
|
||||
import withDialogActions from 'containers/Dialog/withDialogActions';
|
||||
|
||||
import { inputIntent, handleDateChange } from 'utils';
|
||||
|
||||
/**
|
||||
* Invoice form header fields.
|
||||
*/
|
||||
function InvoiceFormHeaderFields({
|
||||
// #withCustomers
|
||||
customers,
|
||||
|
||||
// #withDialogActions
|
||||
openDialog,
|
||||
|
||||
// #ownProps
|
||||
onInvoiceNumberChanged,
|
||||
}) {
|
||||
// Invoice form context.
|
||||
const { customers } = useInvoiceFormContext();
|
||||
|
||||
const handleInvoiceNumberChange = useCallback(() => {
|
||||
openDialog('invoice-number-form', {});
|
||||
}, [openDialog]);
|
||||
@@ -169,8 +173,5 @@ function InvoiceFormHeaderFields({
|
||||
}
|
||||
|
||||
export default compose(
|
||||
withCustomers(({ customers }) => ({
|
||||
customers,
|
||||
})),
|
||||
withDialogActions,
|
||||
)(InvoiceFormHeaderFields);
|
||||
|
||||
@@ -1,33 +1,19 @@
|
||||
import React, { useCallback, useEffect } from 'react';
|
||||
import { useParams, useHistory } from 'react-router-dom';
|
||||
import { useQuery } from 'react-query';
|
||||
|
||||
import InvoiceForm from './InvoiceForm';
|
||||
import DashboardInsider from 'components/Dashboard/DashboardInsider';
|
||||
|
||||
import withCustomersActions from 'containers/Customers/withCustomersActions';
|
||||
import withItemsActions from 'containers/Items/withItemsActions';
|
||||
import withInvoiceActions from './withInvoiceActions';
|
||||
import withSettingsActions from 'containers/Settings/withSettingsActions';
|
||||
import withDashboardActions from 'containers/Dashboard/withDashboardActions';
|
||||
|
||||
import { compose } from 'utils';
|
||||
|
||||
import 'style/pages/SaleInvoice/PageForm.scss';
|
||||
import { InvoiceFormProvider } from './InvoiceFormProvider';
|
||||
|
||||
/**
|
||||
* Invoice form page.
|
||||
*/
|
||||
function InvoiceFormPage({
|
||||
// #withCustomersActions
|
||||
requestFetchCustomers,
|
||||
|
||||
// #withItemsActions
|
||||
requestFetchItems,
|
||||
|
||||
// #withInvoiceActions
|
||||
requsetFetchInvoice,
|
||||
|
||||
// #withSettingsActions
|
||||
requestFetchOptions,
|
||||
|
||||
// #withDashboardActions
|
||||
setSidebarShrink,
|
||||
resetSidebarPreviousExpand,
|
||||
@@ -50,54 +36,27 @@ function InvoiceFormPage({
|
||||
};
|
||||
}, [resetSidebarPreviousExpand, setSidebarShrink, setDashboardBackLink]);
|
||||
|
||||
const fetchInvoice = useQuery(
|
||||
['invoice', id],
|
||||
(key, _id) => requsetFetchInvoice(_id),
|
||||
{ enabled: !!id },
|
||||
);
|
||||
|
||||
const fetchSettings = useQuery(['settings'], () => requestFetchOptions({}));
|
||||
|
||||
// Handle fetch Items data table or list
|
||||
const fetchItems = useQuery('items-table', () => requestFetchItems({}));
|
||||
|
||||
const handleFormSubmit = useCallback(
|
||||
(payload) => {
|
||||
payload.redirect && history.push('/invoices');
|
||||
},
|
||||
[history],
|
||||
);
|
||||
// Handle fetch customers data table or list
|
||||
const fetchCustomers = useQuery('customers-table', () =>
|
||||
requestFetchCustomers({}),
|
||||
);
|
||||
|
||||
const handleCancel = useCallback(() => {
|
||||
history.goBack();
|
||||
}, [history]);
|
||||
|
||||
return (
|
||||
<DashboardInsider
|
||||
loading={
|
||||
fetchCustomers.isFetching ||
|
||||
fetchItems.isFetching ||
|
||||
fetchInvoice.isFetching
|
||||
}
|
||||
name={'invoice-form'}
|
||||
>
|
||||
<InvoiceFormProvider invoiceId={id}>
|
||||
<InvoiceForm
|
||||
invoiceId={id}
|
||||
onFormSubmit={handleFormSubmit}
|
||||
onCancelForm={handleCancel}
|
||||
/>
|
||||
</DashboardInsider>
|
||||
</InvoiceFormProvider>
|
||||
);
|
||||
}
|
||||
|
||||
export default compose(
|
||||
withInvoiceActions,
|
||||
withCustomersActions,
|
||||
withItemsActions,
|
||||
withSettingsActions,
|
||||
withDashboardActions,
|
||||
)(InvoiceFormPage);
|
||||
|
||||
71
client/src/containers/Sales/Invoice/InvoiceFormProvider.js
Normal file
71
client/src/containers/Sales/Invoice/InvoiceFormProvider.js
Normal file
@@ -0,0 +1,71 @@
|
||||
import React, { createContext, useState } from 'react';
|
||||
import DashboardInsider from 'components/Dashboard/DashboardInsider';
|
||||
import {
|
||||
useInvoice,
|
||||
useItems,
|
||||
useCustomers,
|
||||
useCreateInvoice,
|
||||
useEditInvoice,
|
||||
} from 'hooks/query';
|
||||
|
||||
const InvoiceFormContext = createContext();
|
||||
|
||||
/**
|
||||
* Accounts chart data provider.
|
||||
*/
|
||||
function InvoiceFormProvider({ invoiceId, ...props }) {
|
||||
const { data: invoice, isFetching: isInvoiceLoading } = useInvoice(
|
||||
invoiceId,
|
||||
{
|
||||
enabled: !!invoiceId,
|
||||
},
|
||||
);
|
||||
|
||||
// Handle fetching the items table based on the given query.
|
||||
const {
|
||||
data: { items },
|
||||
isFetching: isItemsLoading,
|
||||
} = useItems();
|
||||
|
||||
// Handle fetch customers data table or list
|
||||
const {
|
||||
data: { customers },
|
||||
isFetching: isCustomersLoading,
|
||||
} = useCustomers();
|
||||
|
||||
// Create and edit invoice mutations.
|
||||
const { mutateAsync: createInvoiceMutate } = useCreateInvoice();
|
||||
const { mutateAsync: editInvoiceMutate } = useEditInvoice();
|
||||
|
||||
// Form submit payload.
|
||||
const [submitPayload, setSubmitPayload] = useState({});
|
||||
|
||||
// Provider payload.
|
||||
const provider = {
|
||||
invoice,
|
||||
items,
|
||||
customers,
|
||||
submitPayload,
|
||||
|
||||
isInvoiceLoading,
|
||||
isItemsLoading,
|
||||
isCustomersLoading,
|
||||
|
||||
createInvoiceMutate,
|
||||
editInvoiceMutate,
|
||||
setSubmitPayload,
|
||||
};
|
||||
|
||||
return (
|
||||
<DashboardInsider
|
||||
loading={isInvoiceLoading || isItemsLoading || isCustomersLoading}
|
||||
name={'invoice-form'}
|
||||
>
|
||||
<InvoiceFormContext.Provider value={provider} {...props} />
|
||||
</DashboardInsider>
|
||||
);
|
||||
}
|
||||
|
||||
const useInvoiceFormContext = () => React.useContext(InvoiceFormContext);
|
||||
|
||||
export { InvoiceFormProvider, useInvoiceFormContext };
|
||||
@@ -1,65 +1,42 @@
|
||||
import React, { useEffect, useRef } from 'react';
|
||||
import React from 'react';
|
||||
import { useHistory } from 'react-router';
|
||||
import { Alignment, Navbar, NavbarGroup } from '@blueprintjs/core';
|
||||
import { useParams, withRouter } from 'react-router-dom';
|
||||
import { connect } from 'react-redux';
|
||||
import { pick, debounce } from 'lodash';
|
||||
import { useParams } from 'react-router-dom';
|
||||
import { pick } from 'lodash';
|
||||
|
||||
import { DashboardViewsTabs } from 'components';
|
||||
import { useUpdateEffect } from 'hooks';
|
||||
|
||||
import withInvoices from './withInvoices';
|
||||
import withInvoiceActions from './withInvoiceActions';
|
||||
import withDashboardActions from 'containers/Dashboard/withDashboardActions';
|
||||
import withViewDetails from 'containers/Views/withViewDetails';
|
||||
|
||||
import { compose } from 'utils';
|
||||
import { useInvoicesListContext } from './InvoicesListProvider';
|
||||
|
||||
/**
|
||||
* Invoices views tabs.
|
||||
*/
|
||||
function InvoiceViewTabs({
|
||||
//#withInvoices
|
||||
invoicesViews,
|
||||
|
||||
// #withViewDetails
|
||||
viewItem,
|
||||
|
||||
//#withInvoiceActions
|
||||
changeInvoiceView,
|
||||
// #withInvoiceActions
|
||||
addInvoiceTableQueries,
|
||||
|
||||
// #withDashboardActions
|
||||
setTopbarEditView,
|
||||
changePageSubtitle,
|
||||
|
||||
// #ownProps
|
||||
customViewChanged,
|
||||
onViewChanged,
|
||||
}) {
|
||||
const history = useHistory();
|
||||
const { custom_view_id: customViewId = null } = useParams();
|
||||
|
||||
useEffect(() => {
|
||||
setTopbarEditView(customViewId);
|
||||
changePageSubtitle(customViewId && viewItem ? viewItem.name : '');
|
||||
}, [customViewId]);
|
||||
// Invoices list context.
|
||||
const { invoicesViews } = useInvoicesListContext();
|
||||
|
||||
const tabs = invoicesViews.map((view) => ({
|
||||
...pick(view, ['name', 'id']),
|
||||
}));
|
||||
|
||||
const handleTabsChange = (viewId) => {
|
||||
changeInvoiceView(viewId || -1);
|
||||
// Handle tab change.
|
||||
const handleTabsChange = (viewId) => {
|
||||
addInvoiceTableQueries({
|
||||
custom_view_id: customViewId || null,
|
||||
});
|
||||
setTopbarEditView(viewId);
|
||||
};
|
||||
|
||||
// Handle click a new view tab.
|
||||
const handleClickNewView = () => {
|
||||
setTopbarEditView(null);
|
||||
history.push('/custom_views/invoices/new');
|
||||
};
|
||||
|
||||
@@ -78,19 +55,6 @@ function InvoiceViewTabs({
|
||||
);
|
||||
}
|
||||
|
||||
const mapStateToProps = (state, ownProps) => ({
|
||||
viewId: ownProps.match.params.custom_view_id,
|
||||
});
|
||||
|
||||
const withInvoicesViewTabs = connect(mapStateToProps);
|
||||
|
||||
export default compose(
|
||||
withRouter,
|
||||
withInvoicesViewTabs,
|
||||
withInvoiceActions,
|
||||
withDashboardActions,
|
||||
withViewDetails(),
|
||||
withInvoices(({ invoicesViews }) => ({
|
||||
invoicesViews,
|
||||
})),
|
||||
)(InvoiceViewTabs);
|
||||
|
||||
@@ -1,139 +1,51 @@
|
||||
import React, { useEffect, useCallback, useMemo, useState } from 'react';
|
||||
import { Route, Switch, useHistory } from 'react-router-dom';
|
||||
import { useQuery} from 'react-query';
|
||||
import React, { useEffect } from 'react';
|
||||
import { useIntl } from 'react-intl';
|
||||
|
||||
import 'style/pages/SaleInvoice/List.scss';
|
||||
|
||||
import { FormattedMessage as T, useIntl } from 'react-intl';
|
||||
import DashboardPageContent from 'components/Dashboard/DashboardPageContent';
|
||||
import DashboardInsider from 'components/Dashboard/DashboardInsider';
|
||||
|
||||
import InvoicesDataTable from './InvoicesDataTable';
|
||||
import InvoiceActionsBar from './InvoiceActionsBar';
|
||||
import InvoiceViewTabs from './InvoiceViewTabs';
|
||||
import { InvoicesListProvider } from './InvoicesListProvider';
|
||||
|
||||
import InvoicesViewPage from './InvoicesViewPage';
|
||||
import InvoicesAlerts from './InvoicesAlerts';
|
||||
|
||||
import withDashboardActions from 'containers/Dashboard/withDashboardActions';
|
||||
import withResourceActions from 'containers/Resources/withResourcesActions';
|
||||
import withInvoices from './withInvoices';
|
||||
import withInvoiceActions from 'containers/Sales/Invoice/withInvoiceActions';
|
||||
import withViewsActions from 'containers/Views/withViewsActions';
|
||||
import withAlertsActions from 'containers/Alert/withAlertActions';
|
||||
|
||||
import { compose } from 'utils';
|
||||
|
||||
/**
|
||||
* Invoices list.
|
||||
* Sale invoices list.
|
||||
*/
|
||||
function InvoicesList({
|
||||
// #withDashboardActions
|
||||
changePageTitle,
|
||||
|
||||
// #withViewsActions
|
||||
requestFetchResourceViews,
|
||||
requestFetchResourceFields,
|
||||
|
||||
//#withInvoice
|
||||
// #withInvoice
|
||||
invoicesTableQuery,
|
||||
invoicesViews,
|
||||
|
||||
// #withAlertsActions.
|
||||
openAlert,
|
||||
|
||||
//#withInvoiceActions
|
||||
requestFetchInvoiceTable,
|
||||
|
||||
addInvoiceTableQueries,
|
||||
}) {
|
||||
const history = useHistory();
|
||||
const { formatMessage } = useIntl();
|
||||
const [selectedRows, setSelectedRows] = useState([]);
|
||||
|
||||
useEffect(() => {
|
||||
changePageTitle(formatMessage({ id: 'invoices_list' }));
|
||||
}, [changePageTitle, formatMessage]);
|
||||
|
||||
const fetchResourceViews = useQuery(
|
||||
['resource-views', 'sale_invoice'],
|
||||
(key, resourceName) => requestFetchResourceViews(resourceName),
|
||||
);
|
||||
|
||||
const fetchResourceFields = useQuery(
|
||||
['resource-fields', 'sale_invoice'],
|
||||
(key, resourceName) => requestFetchResourceFields(resourceName),
|
||||
);
|
||||
|
||||
const fetchInvoices = useQuery(
|
||||
['invoices-table', invoicesTableQuery],
|
||||
(key, query) => requestFetchInvoiceTable({ ...query }),
|
||||
);
|
||||
//handle delete Invoice
|
||||
const handleDeleteInvoice = useCallback(
|
||||
({ id }) => {
|
||||
openAlert('invoice-delete', { invoiceId: id });
|
||||
},
|
||||
[openAlert],
|
||||
);
|
||||
|
||||
// Handle cancel/confirm invoice deliver.
|
||||
const handleDeliverInvoice = useCallback(
|
||||
({id}) => {
|
||||
openAlert('invoice-deliver', { invoiceId: id });
|
||||
},
|
||||
[openAlert],
|
||||
);
|
||||
|
||||
|
||||
const handleEditInvoice = useCallback((invoice) => {
|
||||
history.push(`/invoices/${invoice.id}/edit`);
|
||||
});
|
||||
|
||||
// Handle filter change to re-fetch data-table.
|
||||
const handleFilterChanged = useCallback(() => {}, []);
|
||||
|
||||
// Handle selected rows change.
|
||||
const handleSelectedRowsChange = useCallback(
|
||||
(_invoices) => {
|
||||
setSelectedRows(_invoices);
|
||||
},
|
||||
[setSelectedRows],
|
||||
);
|
||||
return (
|
||||
<DashboardInsider
|
||||
loading={fetchResourceViews.isFetching || fetchResourceFields.isFetching}
|
||||
name={'sales-invoices-list'}
|
||||
>
|
||||
<InvoiceActionsBar
|
||||
selectedRows={selectedRows}
|
||||
onFilterChanged={handleFilterChanged}
|
||||
/>
|
||||
<DashboardPageContent>
|
||||
<Switch>
|
||||
<Route
|
||||
exact={true}
|
||||
path={['/invoices/:custom_view_id/custom_view', '/invoices']}
|
||||
>
|
||||
<InvoiceViewTabs />
|
||||
<InvoicesDataTable
|
||||
onDeleteInvoice={handleDeleteInvoice}
|
||||
onEditInvoice={handleEditInvoice}
|
||||
onDeliverInvoice={handleDeliverInvoice}
|
||||
onSelectedRowsChange={handleSelectedRowsChange}
|
||||
/>
|
||||
</Route>
|
||||
</Switch>
|
||||
<InvoicesListProvider query={invoicesTableQuery}>
|
||||
<InvoiceActionsBar />
|
||||
|
||||
<DashboardPageContent>
|
||||
<InvoicesViewPage />
|
||||
<InvoicesAlerts />
|
||||
</DashboardPageContent>
|
||||
</DashboardInsider>
|
||||
</InvoicesListProvider>
|
||||
);
|
||||
}
|
||||
|
||||
export default compose(
|
||||
withResourceActions,
|
||||
withInvoiceActions,
|
||||
withDashboardActions,
|
||||
withViewsActions,
|
||||
withInvoices(({ invoicesTableQuery }) => ({
|
||||
invoicesTableQuery,
|
||||
})),
|
||||
|
||||
52
client/src/containers/Sales/Invoice/InvoicesListProvider.js
Normal file
52
client/src/containers/Sales/Invoice/InvoicesListProvider.js
Normal file
@@ -0,0 +1,52 @@
|
||||
import React, { createContext } from 'react';
|
||||
import DashboardInsider from 'components/Dashboard/DashboardInsider';
|
||||
import { useResourceViews, useResourceFields, useInvoices } from 'hooks/query';
|
||||
|
||||
const InvoicesListContext = createContext();
|
||||
|
||||
/**
|
||||
* Accounts chart data provider.
|
||||
*/
|
||||
function InvoicesListProvider({ accountsTableQuery, ...props }) {
|
||||
// Fetch accounts resource views and fields.
|
||||
const { data: invoicesViews, isFetching: isViewsLoading } = useResourceViews(
|
||||
'sale_invoices',
|
||||
);
|
||||
|
||||
// Fetch the accounts resource fields.
|
||||
const {
|
||||
data: invoicesFields,
|
||||
isFetching: isFieldsLoading,
|
||||
} = useResourceFields('sale_invoices');
|
||||
|
||||
// Fetch accounts list according to the given custom view id.
|
||||
const {
|
||||
data: { invoices, pagination },
|
||||
isFetching: isInvoicesLoading,
|
||||
} = useInvoices(accountsTableQuery);
|
||||
|
||||
// Provider payload.
|
||||
const provider = {
|
||||
invoices,
|
||||
pagination,
|
||||
invoicesFields,
|
||||
invoicesViews,
|
||||
|
||||
isInvoicesLoading,
|
||||
isFieldsLoading,
|
||||
isViewsLoading,
|
||||
};
|
||||
|
||||
return (
|
||||
<DashboardInsider
|
||||
loading={isViewsLoading || isFieldsLoading}
|
||||
name={'sales-invoices-list'}
|
||||
>
|
||||
<InvoicesListContext.Provider value={provider} {...props} />
|
||||
</DashboardInsider>
|
||||
);
|
||||
}
|
||||
|
||||
const useInvoicesListContext = () => React.useContext(InvoicesListContext);
|
||||
|
||||
export { InvoicesListProvider, useInvoicesListContext };
|
||||
73
client/src/containers/Sales/Invoice/InvoicesViewPage.js
Normal file
73
client/src/containers/Sales/Invoice/InvoicesViewPage.js
Normal file
@@ -0,0 +1,73 @@
|
||||
import React, { useCallback } from 'react';
|
||||
import { Switch, Route, useHistory } from 'react-router-dom';
|
||||
|
||||
import InvoicesDataTable from './InvoicesDataTable';
|
||||
import InvoiceViewTabs from './InvoiceViewTabs';
|
||||
|
||||
import withAlertsActions from 'containers/Alert/withAlertActions';
|
||||
import withDialogActions from 'containers/Dialog/withDialogActions';
|
||||
|
||||
import { compose } from 'utils';
|
||||
|
||||
/**
|
||||
* Invoices list view page.
|
||||
*/
|
||||
function InvoicesViewPage({
|
||||
// #withAlertActions
|
||||
openAlert,
|
||||
}) {
|
||||
const history = useHistory();
|
||||
|
||||
// Handle delete sale invoice.
|
||||
const handleDeleteInvoice = useCallback(
|
||||
({ id }) => {
|
||||
openAlert('invoice-delete', { invoiceId: id });
|
||||
},
|
||||
[openAlert],
|
||||
);
|
||||
|
||||
// Handle cancel/confirm invoice deliver.
|
||||
const handleDeliverInvoice = useCallback(
|
||||
({ id }) => {
|
||||
openAlert('invoice-deliver', { invoiceId: id });
|
||||
},
|
||||
[openAlert],
|
||||
);
|
||||
|
||||
// Handle edit sale invoice.
|
||||
const handleEditInvoice = useCallback(
|
||||
(invoice) => {
|
||||
history.push(`/invoices/${invoice.id}/edit`);
|
||||
},
|
||||
[history],
|
||||
);
|
||||
|
||||
// Handle selected rows change.
|
||||
const handleSelectedRowsChange = useCallback(
|
||||
(invoices) => {
|
||||
|
||||
},
|
||||
[],
|
||||
);
|
||||
return (
|
||||
<Switch>
|
||||
<Route
|
||||
exact={true}
|
||||
path={['/invoices/:custom_view_id/custom_view', '/invoices']}
|
||||
>
|
||||
<InvoiceViewTabs />
|
||||
{/* <InvoicesDataTable
|
||||
onDeleteInvoice={handleDeleteInvoice}
|
||||
onEditInvoice={handleEditInvoice}
|
||||
onDeliverInvoice={handleDeliverInvoice}
|
||||
onSelectedRowsChange={handleSelectedRowsChange}
|
||||
/> */}
|
||||
</Route>
|
||||
</Switch>
|
||||
);
|
||||
}
|
||||
|
||||
export default compose(
|
||||
withAlertsActions,
|
||||
withDialogActions,
|
||||
)(InvoicesViewPage)
|
||||
@@ -0,0 +1,52 @@
|
||||
import React, { createContext } from 'react';
|
||||
import DashboardInsider from 'components/Dashboard/DashboardInsider';
|
||||
import { useResourceViews, useResourceFields, usePaymentReceives } from 'hooks/query';
|
||||
|
||||
const PaymentReceivesListContext = createContext();
|
||||
|
||||
/**
|
||||
* Payment receives list data provider.
|
||||
*/
|
||||
function PaymentReceivesListProvider({ query, ...props }) {
|
||||
// Fetch payment receives resource views and fields.
|
||||
const { data: paymentReceivesViews, isFetching: isViewsLoading } = useResourceViews(
|
||||
'payment_receives',
|
||||
);
|
||||
|
||||
// Fetch the payment receives resource fields.
|
||||
const {
|
||||
data: paymentReceivesFields,
|
||||
isFetching: isFieldsLoading,
|
||||
} = useResourceFields('payment_receives');
|
||||
|
||||
// Fetch payment receives list according to the given custom view id.
|
||||
const {
|
||||
data: { paymentReceives, pagination },
|
||||
isFetching: isPaymentReceivesLoading,
|
||||
} = usePaymentReceives(query);
|
||||
|
||||
// Provider payload.
|
||||
const provider = {
|
||||
paymentReceives,
|
||||
pagination,
|
||||
paymentReceivesFields,
|
||||
paymentReceivesViews,
|
||||
|
||||
isPaymentReceivesLoading,
|
||||
isFieldsLoading,
|
||||
isViewsLoading,
|
||||
};
|
||||
|
||||
return (
|
||||
<DashboardInsider
|
||||
loading={isViewsLoading || isFieldsLoading}
|
||||
name={'payment-receives'}
|
||||
>
|
||||
<PaymentReceivesListContext.Provider value={provider} {...props} />
|
||||
</DashboardInsider>
|
||||
);
|
||||
}
|
||||
|
||||
const usePaymentReceivesListContext = () => React.useContext(PaymentReceivesListContext);
|
||||
|
||||
export { PaymentReceivesListProvider, usePaymentReceivesListContext };
|
||||
@@ -12,7 +12,7 @@ import {
|
||||
import { FormattedMessage as T } from 'react-intl';
|
||||
import classNames from 'classnames';
|
||||
import { CLASSES } from 'common/classes';
|
||||
import { useFormikContext } from 'formik';
|
||||
|
||||
import { saveInvoke } from 'utils';
|
||||
import { Icon } from 'components';
|
||||
|
||||
|
||||
@@ -1,83 +1,44 @@
|
||||
import React, { useEffect, useRef } from 'react';
|
||||
import React from 'react';
|
||||
import { useHistory } from 'react-router';
|
||||
import { Alignment, Navbar, NavbarGroup } from '@blueprintjs/core';
|
||||
import { useParams, withRouter } from 'react-router-dom';
|
||||
import { useParams } from 'react-router-dom';
|
||||
import { FormattedMessage as T } from 'react-intl';
|
||||
import { connect } from 'react-redux';
|
||||
import { pick, debounce } from 'lodash';
|
||||
import { pick } from 'lodash';
|
||||
|
||||
import { DashboardViewsTabs } from 'components';
|
||||
import { useUpdateEffect } from 'hooks';
|
||||
|
||||
import withPaymentReceives from './withPaymentReceives';
|
||||
import withPaymentReceivesActions from './withPaymentReceivesActions';
|
||||
import withDashboardActions from 'containers/Dashboard/withDashboardActions';
|
||||
import withViewDetails from 'containers/Views/withViewDetails';
|
||||
import { usePaymentReceivesListContext } from './PaymentReceiptsListProvider';
|
||||
|
||||
import { compose } from 'utils';
|
||||
|
||||
/**
|
||||
* Payment receive view tabs.
|
||||
*/
|
||||
function PaymentReceiveViewTabs({
|
||||
//#withPaymentReceives
|
||||
paymentReceivesViews,
|
||||
|
||||
//#withPaymentReceivesActions
|
||||
changePaymentReceiveView,
|
||||
addPaymentReceivesTableQueries,
|
||||
|
||||
// #withViewDetails
|
||||
viewItem,
|
||||
|
||||
// #withDashboardActions
|
||||
setTopbarEditView,
|
||||
changePageSubtitle,
|
||||
|
||||
//#Own Props
|
||||
customViewChanged,
|
||||
onViewChanged,
|
||||
}) {
|
||||
const history = useHistory();
|
||||
const { paymentReceivesViews } = usePaymentReceivesListContext();
|
||||
|
||||
const { custom_view_id: customViewId = null } = useParams();
|
||||
|
||||
useEffect(() => {
|
||||
changePaymentReceiveView(customViewId || -1);
|
||||
setTopbarEditView(customViewId);
|
||||
changePageSubtitle(customViewId && viewItem ? viewItem.name : '');
|
||||
|
||||
addPaymentReceivesTableQueries({
|
||||
custom_view_id: customViewId,
|
||||
});
|
||||
return () => {
|
||||
setTopbarEditView(null);
|
||||
changePageSubtitle('');
|
||||
changePaymentReceiveView(null);
|
||||
};
|
||||
}, [customViewId, addPaymentReceivesTableQueries, changePaymentReceiveView]);
|
||||
|
||||
useUpdateEffect(() => {
|
||||
onViewChanged && onViewChanged(customViewId);
|
||||
}, [customViewId]);
|
||||
|
||||
const debounceChangeHistory = useRef(
|
||||
debounce((toUrl) => {
|
||||
history.push(toUrl);
|
||||
}, 250),
|
||||
);
|
||||
|
||||
const handleTabsChange = (viewId) => {
|
||||
const toPath = viewId ? `${viewId}/custom_view` : '';
|
||||
debounceChangeHistory.current(`/payment-receives/${toPath}`);
|
||||
setTopbarEditView(viewId);
|
||||
};
|
||||
|
||||
const tabs = paymentReceivesViews.map((view) => ({
|
||||
...pick(view, ['name', 'id']),
|
||||
}));
|
||||
|
||||
// Handle click a new view tab.
|
||||
const handleClickNewView = () => {
|
||||
setTopbarEditView(null);
|
||||
history.push('/custom_views/payment-receives/new');
|
||||
};
|
||||
|
||||
const handleTabsChange = (viewId) => {
|
||||
addPaymentReceivesTableQueries({
|
||||
custom_view_id: viewId || null,
|
||||
});
|
||||
}
|
||||
|
||||
return (
|
||||
<Navbar className={'navbar--dashboard-views'}>
|
||||
<NavbarGroup align={Alignment.LEFT}>
|
||||
@@ -93,19 +54,6 @@ function PaymentReceiveViewTabs({
|
||||
);
|
||||
}
|
||||
|
||||
const mapStateToProps = (state, ownProps) => ({
|
||||
viewId: ownProps.match.params.custom_view_id,
|
||||
});
|
||||
|
||||
const withPaymentReceivesViewTabs = connect(mapStateToProps);
|
||||
|
||||
export default compose(
|
||||
withRouter,
|
||||
withPaymentReceivesViewTabs,
|
||||
withPaymentReceivesActions,
|
||||
withDashboardActions,
|
||||
withViewDetails(),
|
||||
withPaymentReceives(({ paymentReceivesViews }) => ({
|
||||
paymentReceivesViews,
|
||||
})),
|
||||
)(PaymentReceiveViewTabs);
|
||||
|
||||
@@ -1,107 +1,48 @@
|
||||
import React, { useEffect, useCallback, useState } from 'react';
|
||||
import { Route, Switch, useHistory } from 'react-router-dom';
|
||||
import { useQuery } from 'react-query';
|
||||
import React, { useEffect } from 'react';
|
||||
import { useIntl } from 'react-intl';
|
||||
|
||||
import { FormattedMessage as T, useIntl } from 'react-intl';
|
||||
import DashboardPageContent from 'components/Dashboard/DashboardPageContent';
|
||||
import DashboardInsider from 'components/Dashboard/DashboardInsider';
|
||||
|
||||
import PaymentReceivesDataTable from './PaymentReceivesDataTable';
|
||||
import PaymentReceiveActionsBar from './PaymentReceiveActionsBar';
|
||||
import PaymentReceiveViewTabs from './PaymentReceiveViewTabs';
|
||||
import PaymentReceiveAlerts from './PaymentReceiveAlerts';
|
||||
import { PaymentReceivesListProvider } from './PaymentReceiptsListProvider';
|
||||
import PaymentReceivesViewPage from './PaymentReceivesViewPage';
|
||||
|
||||
import withDashboardActions from 'containers/Dashboard/withDashboardActions';
|
||||
import withResourceActions from 'containers/Resources/withResourcesActions';
|
||||
import withPaymentReceives from './withPaymentReceives';
|
||||
import withPaymentReceivesActions from './withPaymentReceivesActions';
|
||||
import withAlertsActions from 'containers/Alert/withAlertActions';
|
||||
|
||||
import { compose } from 'utils';
|
||||
|
||||
/**
|
||||
* Payment receives list.
|
||||
*/
|
||||
function PaymentReceiveList({
|
||||
// #withDashboardActions
|
||||
changePageTitle,
|
||||
|
||||
//#withPaymentReceives
|
||||
// #withPaymentReceives
|
||||
paymentReceivesTableQuery,
|
||||
|
||||
// #withAlertsActions.
|
||||
openAlert,
|
||||
|
||||
//#withPaymentReceivesActions
|
||||
requestFetchPaymentReceiveTable,
|
||||
}) {
|
||||
const history = useHistory();
|
||||
const { formatMessage } = useIntl();
|
||||
const [selectedRows, setSelectedRows] = useState([]);
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
changePageTitle(formatMessage({ id: 'payment_Receives_list' }));
|
||||
}, [changePageTitle, formatMessage]);
|
||||
|
||||
const fetchPaymentReceives = useQuery(
|
||||
['paymentReceives-table', paymentReceivesTableQuery],
|
||||
() => requestFetchPaymentReceiveTable(),
|
||||
);
|
||||
|
||||
// Handle delete Payment Receive
|
||||
const handleDeletePaymentReceive = useCallback(
|
||||
({ id }) => {
|
||||
openAlert('payment-receive-delete', { paymentReceiveId: id });
|
||||
},
|
||||
[openAlert],
|
||||
);
|
||||
|
||||
const handleEditPaymentReceive = useCallback((payment) => {
|
||||
history.push(`/payment-receives/${payment.id}/edit`);
|
||||
});
|
||||
|
||||
// Handle filter change to re-fetch data-table.
|
||||
const handleFilterChanged = useCallback(() => {}, [fetchPaymentReceives]);
|
||||
|
||||
// Handle selected rows change.
|
||||
const handleSelectedRowsChange = useCallback(
|
||||
(_payment) => {
|
||||
setSelectedRows(_payment);
|
||||
},
|
||||
[setSelectedRows],
|
||||
);
|
||||
|
||||
return (
|
||||
<DashboardInsider name={'payment_receives'}>
|
||||
<PaymentReceiveActionsBar
|
||||
selectedRows={selectedRows}
|
||||
onFilterChanged={handleFilterChanged}
|
||||
/>
|
||||
<PaymentReceivesListProvider query={paymentReceivesTableQuery}>
|
||||
<PaymentReceiveActionsBar />
|
||||
|
||||
<DashboardPageContent>
|
||||
<Switch>
|
||||
<Route
|
||||
exact={true}
|
||||
path={[
|
||||
'/payment-receives/:custom_view_id/custom_view',
|
||||
'/payment-receives',
|
||||
]}
|
||||
>
|
||||
<PaymentReceiveViewTabs />
|
||||
<PaymentReceivesDataTable
|
||||
onDeletePaymentReceive={handleDeletePaymentReceive}
|
||||
onEditPaymentReceive={handleEditPaymentReceive}
|
||||
onSelectedRowsChange={handleSelectedRowsChange}
|
||||
/>
|
||||
</Route>
|
||||
</Switch>
|
||||
<PaymentReceivesViewPage />
|
||||
<PaymentReceiveAlerts />
|
||||
</DashboardPageContent>
|
||||
</DashboardInsider>
|
||||
</PaymentReceivesListProvider>
|
||||
);
|
||||
}
|
||||
|
||||
export default compose(
|
||||
withResourceActions,
|
||||
withPaymentReceivesActions,
|
||||
withDashboardActions,
|
||||
withPaymentReceives(({ paymentReceivesTableQuery }) => ({
|
||||
paymentReceivesTableQuery,
|
||||
})),
|
||||
withAlertsActions,
|
||||
)(PaymentReceiveList);
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
import React, { createContext } from 'react';
|
||||
import DashboardInsider from 'components/Dashboard/DashboardInsider';
|
||||
import {
|
||||
useResourceViews,
|
||||
useResourceFields,
|
||||
usePaymentReceives,
|
||||
} from 'hooks/query';
|
||||
|
||||
const PaymentReceivesListContext = createContext();
|
||||
|
||||
/**
|
||||
* Payment receives data provider.
|
||||
*/
|
||||
function PaymentReceivesListProvider({ query, ...props }) {
|
||||
// Fetch accounts resource views and fields.
|
||||
const {
|
||||
data: paymentReceivesViews,
|
||||
isFetching: isViewsLoading,
|
||||
} = useResourceViews('payment_receives');
|
||||
|
||||
// Fetch the accounts resource fields.
|
||||
const {
|
||||
data: paymentReceivesFields,
|
||||
isFetching: isFieldsLoading,
|
||||
} = useResourceFields('payment_receives');
|
||||
|
||||
// Fetch accounts list according to the given custom view id.
|
||||
const {
|
||||
data: { paymentReceives, pagination },
|
||||
isFetching: isPaymentReceivesLoading,
|
||||
} = usePaymentReceives(query);
|
||||
|
||||
// Provider payload.
|
||||
const provider = {
|
||||
paymentReceives,
|
||||
paymentReceivesViews,
|
||||
paymentReceivesFields,
|
||||
pagination,
|
||||
|
||||
isViewsLoading,
|
||||
isFieldsLoading,
|
||||
isPaymentReceivesLoading,
|
||||
};
|
||||
|
||||
return (
|
||||
<DashboardInsider
|
||||
loading={isViewsLoading || isFieldsLoading}
|
||||
name={'payment_receives'}
|
||||
>
|
||||
<PaymentReceivesListContext.Provider value={provider} {...props} />
|
||||
</DashboardInsider>
|
||||
);
|
||||
}
|
||||
|
||||
const usePaymentReceivesListContext = () =>
|
||||
React.useContext(PaymentReceivesListContext);
|
||||
|
||||
export { PaymentReceivesListProvider, usePaymentReceivesListContext };
|
||||
@@ -0,0 +1,55 @@
|
||||
import React, { useCallback } from 'react';
|
||||
import { Switch, Route, useHistory } from 'react-router-dom';
|
||||
|
||||
import PaymentReceivesDataTable from './PaymentReceivesDataTable';
|
||||
import PaymentReceiveViewTabs from './PaymentReceiveViewTabs';
|
||||
|
||||
import withAlertsActions from 'containers/Alert/withAlertActions';
|
||||
import withDialogActions from 'containers/Dialog/withDialogActions';
|
||||
|
||||
import { compose } from 'utils';
|
||||
|
||||
/**
|
||||
* Payment receives view page.
|
||||
*/
|
||||
function PaymentReceivesViewPage({
|
||||
// #withAlertActions
|
||||
openAlert,
|
||||
}) {
|
||||
const history = useHistory();
|
||||
|
||||
// Handle delete Payment Receive
|
||||
const handleDeletePaymentReceive = ({ id }) => {
|
||||
openAlert('payment-receive-delete', { paymentReceiveId: id });
|
||||
};
|
||||
|
||||
// Handle edit payment receive.
|
||||
const handleEditPaymentReceive = (payment) => {
|
||||
history.push(`/payment-receives/${payment.id}/edit`);
|
||||
};
|
||||
|
||||
return (
|
||||
<Switch>
|
||||
<Route
|
||||
exact={true}
|
||||
path={[
|
||||
'/payment-receives/:custom_view_id/custom_view',
|
||||
'/payment-receives',
|
||||
]}
|
||||
>
|
||||
<PaymentReceiveViewTabs />
|
||||
|
||||
{/* <PaymentReceivesDataTable
|
||||
onDeletePaymentReceive={handleDeletePaymentReceive}
|
||||
onEditPaymentReceive={handleEditPaymentReceive}
|
||||
onSelectedRowsChange={handleSelectedRowsChange}
|
||||
/> */}
|
||||
</Route>
|
||||
</Switch>
|
||||
);
|
||||
}
|
||||
|
||||
export default compose(
|
||||
withAlertsActions,
|
||||
withDialogActions,
|
||||
)(PaymentReceivesViewPage)
|
||||
@@ -1,10 +1,8 @@
|
||||
import React, { useCallback, useState, useMemo } from 'react';
|
||||
import React, { useState, useMemo } from 'react';
|
||||
import Icon from 'components/Icon';
|
||||
import {
|
||||
Button,
|
||||
Classes,
|
||||
Menu,
|
||||
MenuItem,
|
||||
Popover,
|
||||
NavbarDivider,
|
||||
NavbarGroup,
|
||||
@@ -14,75 +12,50 @@ import {
|
||||
} from '@blueprintjs/core';
|
||||
|
||||
import classNames from 'classnames';
|
||||
import { useRouteMatch, useHistory } from 'react-router-dom';
|
||||
import { useHistory } from 'react-router-dom';
|
||||
import { FormattedMessage as T, useIntl } from 'react-intl';
|
||||
|
||||
import { connect } from 'react-redux';
|
||||
import { If, DashboardActionViewsList } from 'components';
|
||||
import FilterDropdown from 'components/FilterDropdown';
|
||||
import DashboardActionsBar from 'components/Dashboard/DashboardActionsBar';
|
||||
|
||||
import withResourceDetail from 'containers/Resources/withResourceDetails';
|
||||
import withDialogActions from 'containers/Dialog/withDialogActions';
|
||||
|
||||
import withReceiptActions from './withReceiptActions';
|
||||
import withReceipts from './withReceipts';
|
||||
import { useReceiptsListContext } from './ReceiptsListProvider';
|
||||
|
||||
import { compose } from 'utils';
|
||||
|
||||
/**
|
||||
* Receipts actions bar.
|
||||
*/
|
||||
function ReceiptActionsBar({
|
||||
// #withResourceDetail
|
||||
resourceFields,
|
||||
|
||||
//#withReceipts
|
||||
receiptview,
|
||||
//#withReceiptActions
|
||||
addReceiptsTableQueries,
|
||||
changeReceiptView,
|
||||
|
||||
//#OWn Props
|
||||
onFilterChanged,
|
||||
selectedRows = [],
|
||||
}) {
|
||||
const history = useHistory();
|
||||
const [filterCount, setFilterCount] = useState(0);
|
||||
const { formatMessage } = useIntl();
|
||||
|
||||
const [filterCount, setFilterCount] = useState(0);
|
||||
|
||||
const onClickNewReceipt = useCallback(() => {
|
||||
// Sale receipts list context.
|
||||
const { receiptsViews } = useReceiptsListContext();
|
||||
|
||||
const onClickNewReceipt = () => {
|
||||
history.push('/receipts/new');
|
||||
}, [history]);
|
||||
|
||||
const hasSelectedRows = useMemo(() => selectedRows.length > 0, [
|
||||
selectedRows,
|
||||
]);
|
||||
|
||||
};
|
||||
|
||||
const handleTabChange = (viewId) => {
|
||||
changeReceiptView(viewId.id || -1);
|
||||
addReceiptsTableQueries({
|
||||
custom_view_id: viewId.id || null,
|
||||
});
|
||||
};
|
||||
|
||||
// const filterDropdown = FilterDropdown({
|
||||
// initialCondition: {
|
||||
// fieldKey: '',
|
||||
// compatator: '',
|
||||
// value: '',
|
||||
// },
|
||||
// fields: resourceFields,
|
||||
// onFilterChange: (filterConditions) => {
|
||||
// addReceiptsTableQueries({
|
||||
// filter_roles: filterConditions || '',
|
||||
// });
|
||||
// onFilterChanged && onFilterChange(filterConditions);
|
||||
// },
|
||||
// });
|
||||
|
||||
|
||||
return (
|
||||
<DashboardActionsBar>
|
||||
<NavbarGroup>
|
||||
<DashboardActionViewsList
|
||||
resourceName={'receipts'}
|
||||
views={receiptview}
|
||||
views={receiptsViews}
|
||||
onChange={handleTabChange}
|
||||
/>
|
||||
|
||||
@@ -111,7 +84,7 @@ function ReceiptActionsBar({
|
||||
icon={<Icon icon={'filter-16'} iconSize={16} />}
|
||||
/>
|
||||
</Popover>
|
||||
<If condition={hasSelectedRows}>
|
||||
<If condition={false}>
|
||||
<Button
|
||||
className={Classes.MINIMAL}
|
||||
icon={<Icon icon={'trash-16'} iconSize={16} />}
|
||||
@@ -139,19 +112,7 @@ function ReceiptActionsBar({
|
||||
</DashboardActionsBar>
|
||||
);
|
||||
}
|
||||
const mapStateToProps = (state, props) => ({
|
||||
resourceName: 'sales_receipts',
|
||||
});
|
||||
|
||||
const withReceiptActionsBar = connect(mapStateToProps);
|
||||
|
||||
export default compose(
|
||||
withReceiptActionsBar,
|
||||
withResourceDetail(({ resourceFields }) => ({
|
||||
resourceFields,
|
||||
})),
|
||||
withReceipts(({ receiptview }) => ({
|
||||
receiptview,
|
||||
})),
|
||||
withReceiptActions,
|
||||
)(ReceiptActionsBar);
|
||||
|
||||
@@ -1,31 +1,30 @@
|
||||
import React, { useMemo, useCallback, useEffect, useState } from 'react';
|
||||
import React, { useMemo, useCallback, useEffect } from 'react';
|
||||
import { Formik, Form } from 'formik';
|
||||
import moment from 'moment';
|
||||
import { Intent } from '@blueprintjs/core';
|
||||
import { FormattedMessage as T, useIntl } from 'react-intl';
|
||||
import { pick, sumBy } from 'lodash';
|
||||
import { useIntl } from 'react-intl';
|
||||
import { pick, sumBy, isEmpty } from 'lodash';
|
||||
import classNames from 'classnames';
|
||||
import { useHistory } from 'react-router-dom';
|
||||
|
||||
import { CLASSES } from 'common/classes';
|
||||
import { ERROR } from 'common/errors';
|
||||
|
||||
import {
|
||||
EditReceiptFormSchema,
|
||||
CreateReceiptFormSchema,
|
||||
} from './ReceiptForm.schema';
|
||||
|
||||
import 'style/pages/SaleReceipt/PageForm.scss';
|
||||
|
||||
import { useReceiptFormContext } from './ReceiptFormProvider';
|
||||
|
||||
import ReceiptFromHeader from './ReceiptFormHeader';
|
||||
import ReceiptFormBody from './ReceiptFormBody';
|
||||
import ReceiptFormFloatingActions from './ReceiptFormFloatingActions';
|
||||
import ReceiptFormFooter from './ReceiptFormFooter';
|
||||
import ReceiptNumberWatcher from './ReceiptNumberWatcher';
|
||||
|
||||
import withReceiptActions from './withReceiptActions';
|
||||
import withReceiptDetail from './withReceiptDetail';
|
||||
|
||||
import withDashboardActions from 'containers/Dashboard/withDashboardActions';
|
||||
import withMediaActions from 'containers/Media/withMediaActions';
|
||||
import withSettings from 'containers/Settings/withSettings';
|
||||
|
||||
import { AppToaster } from 'components';
|
||||
@@ -34,10 +33,9 @@ import {
|
||||
repeatValue,
|
||||
orderingLinesIndexes,
|
||||
defaultToTransform,
|
||||
transactionNumber,
|
||||
} from 'utils';
|
||||
|
||||
import 'style/pages/SaleReceipt/PageForm.scss'
|
||||
|
||||
const MIN_LINES_NUMBER = 4;
|
||||
|
||||
const defaultReceipt = {
|
||||
@@ -65,17 +63,6 @@ const defaultInitialValues = {
|
||||
* Receipt form.
|
||||
*/
|
||||
function ReceiptForm({
|
||||
// #withMedia
|
||||
requestSubmitMedia,
|
||||
requestDeleteMedia,
|
||||
|
||||
// #withReceiptActions
|
||||
requestSubmitReceipt,
|
||||
requestEditReceipt,
|
||||
|
||||
// #withReceiptDetail
|
||||
receipt,
|
||||
|
||||
// #withDashboard
|
||||
changePageTitle,
|
||||
changePageSubtitle,
|
||||
@@ -84,22 +71,26 @@ function ReceiptForm({
|
||||
receiptNextNumber,
|
||||
receiptNumberPrefix,
|
||||
preferredDepositAccount,
|
||||
|
||||
//#own Props
|
||||
receiptId,
|
||||
onFormSubmit,
|
||||
onCancelForm,
|
||||
}) {
|
||||
const { formatMessage } = useIntl();
|
||||
const history = useHistory();
|
||||
|
||||
const [submitPayload, setSubmitPayload] = useState({});
|
||||
// Receipt form context.
|
||||
const {
|
||||
receiptId,
|
||||
receipt,
|
||||
editReceiptMutate,
|
||||
createReceiptMutate,
|
||||
submitPayload
|
||||
} = useReceiptFormContext();
|
||||
|
||||
const isNewMode = !receiptId;
|
||||
|
||||
const receiptNumber = receiptNumberPrefix
|
||||
? `${receiptNumberPrefix}-${receiptNextNumber}`
|
||||
: receiptNextNumber;
|
||||
|
||||
// The next receipt number.
|
||||
const receiptNumber = transactionNumber(
|
||||
receiptNumberPrefix,
|
||||
receiptNextNumber,
|
||||
);
|
||||
useEffect(() => {
|
||||
const transactionNumber = !isNewMode
|
||||
? receipt.receipt_number
|
||||
@@ -125,7 +116,7 @@ function ReceiptForm({
|
||||
// Initial values in create and edit mode.
|
||||
const initialValues = useMemo(
|
||||
() => ({
|
||||
...(receipt
|
||||
...(!isEmpty(receipt)
|
||||
? {
|
||||
...pick(receipt, Object.keys(defaultInitialValues)),
|
||||
entries: [
|
||||
@@ -145,7 +136,7 @@ function ReceiptForm({
|
||||
entries: orderingLinesIndexes(defaultInitialValues.entries),
|
||||
}),
|
||||
}),
|
||||
[receipt],
|
||||
[receipt, preferredDepositAccount, receiptNumber],
|
||||
);
|
||||
|
||||
// Transform response error to fields.
|
||||
@@ -167,7 +158,6 @@ function ReceiptForm({
|
||||
const entries = values.entries.filter(
|
||||
(item) => item.item_id && item.quantity,
|
||||
);
|
||||
|
||||
const totalQuantity = sumBy(entries, (entry) => parseInt(entry.quantity));
|
||||
|
||||
if (totalQuantity === 0) {
|
||||
@@ -218,9 +208,9 @@ function ReceiptForm({
|
||||
};
|
||||
|
||||
if (receipt && receipt.id) {
|
||||
requestEditReceipt(receipt.id, form).then(onSuccess).catch(onError);
|
||||
editReceiptMutate(receipt.id, form).then(onSuccess).catch(onError);
|
||||
} else {
|
||||
requestSubmitReceipt(form).then(onSuccess).catch(onError);
|
||||
createReceiptMutate(form).then(onSuccess).catch(onError);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -233,20 +223,6 @@ function ReceiptForm({
|
||||
[changePageSubtitle],
|
||||
);
|
||||
|
||||
const handleSubmitClick = useCallback(
|
||||
(event, payload) => {
|
||||
setSubmitPayload({ ...payload });
|
||||
},
|
||||
[setSubmitPayload],
|
||||
);
|
||||
|
||||
const handleCancelClick = useCallback(
|
||||
(event) => {
|
||||
history.goBack();
|
||||
},
|
||||
[history],
|
||||
);
|
||||
|
||||
return (
|
||||
<div
|
||||
className={classNames(
|
||||
@@ -262,32 +238,22 @@ function ReceiptForm({
|
||||
initialValues={initialValues}
|
||||
onSubmit={handleFormSubmit}
|
||||
>
|
||||
{({ isSubmitting}) => (
|
||||
<Form>
|
||||
<ReceiptFromHeader
|
||||
onReceiptNumberChanged={handleReceiptNumberChanged}
|
||||
/>
|
||||
<ReceiptNumberWatcher receiptNumber={receiptNumber} />
|
||||
<ReceiptFormBody defaultReceipt={defaultReceipt} />
|
||||
<ReceiptFormFooter />
|
||||
<ReceiptFormFloatingActions
|
||||
isSubmitting={isSubmitting}
|
||||
receipt={receipt}
|
||||
onSubmitClick={handleSubmitClick}
|
||||
onCancelClick={handleCancelClick}
|
||||
/>
|
||||
</Form>
|
||||
)}
|
||||
<Form>
|
||||
<ReceiptFromHeader
|
||||
onReceiptNumberChanged={handleReceiptNumberChanged}
|
||||
/>
|
||||
<ReceiptNumberWatcher receiptNumber={receiptNumber} />
|
||||
<ReceiptFormBody defaultReceipt={defaultReceipt} />
|
||||
<ReceiptFormFooter />
|
||||
<ReceiptFormFloatingActions />
|
||||
</Form>
|
||||
</Formik>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default compose(
|
||||
withReceiptActions,
|
||||
withReceiptDetail(),
|
||||
withDashboardActions,
|
||||
withMediaActions,
|
||||
withSettings(({ receiptSettings }) => ({
|
||||
receiptNextNumber: receiptSettings?.nextNumber,
|
||||
receiptNumberPrefix: receiptSettings?.numberPrefix,
|
||||
|
||||
@@ -3,11 +3,15 @@ import classNames from 'classnames';
|
||||
import { CLASSES } from 'common/classes';
|
||||
|
||||
import EditableItemsEntriesTable from 'containers/Entries/EditableItemsEntriesTable';
|
||||
import { useReceiptFormContext } from './ReceiptFormProvider';
|
||||
|
||||
export default function ExpenseFormBody({ defaultReceipt }) {
|
||||
const { items } = useReceiptFormContext();
|
||||
|
||||
return (
|
||||
<div className={classNames(CLASSES.PAGE_FORM_BODY)}>
|
||||
<EditableItemsEntriesTable
|
||||
items={items}
|
||||
defaultEntry={defaultReceipt}
|
||||
filterSellableItems={true}
|
||||
/>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React, { useCallback } from 'react';
|
||||
import React from 'react';
|
||||
import {
|
||||
Intent,
|
||||
Button,
|
||||
@@ -13,70 +13,57 @@ import { FormattedMessage as T } from 'react-intl';
|
||||
import { useFormikContext } from 'formik';
|
||||
import classNames from 'classnames';
|
||||
import { CLASSES } from 'common/classes';
|
||||
import { saveInvoke } from 'utils';
|
||||
import { If, Icon } from 'components';
|
||||
import { useReceiptFormContext } from './ReceiptFormProvider';
|
||||
|
||||
/**
|
||||
* Receipt floating actions bar.
|
||||
*/
|
||||
export default function ReceiptFormFloatingActions({
|
||||
isSubmitting,
|
||||
receipt,
|
||||
onSubmitClick,
|
||||
onCancelClick,
|
||||
}) {
|
||||
const { resetForm, submitForm } = useFormikContext();
|
||||
export default function ReceiptFormFloatingActions() {
|
||||
// Formik context.
|
||||
const { resetForm, submitForm, isSubmitting } = useFormikContext();
|
||||
|
||||
// Receipt form context.
|
||||
const { receipt, setSubmitPayload } = useReceiptFormContext();
|
||||
|
||||
// Handle submit & close button click.
|
||||
const handleSubmitCloseBtnClick = (event) => {
|
||||
saveInvoke(onSubmitClick, event, {
|
||||
redirect: true,
|
||||
status: true,
|
||||
});
|
||||
setSubmitPayload({ redirect: true, status: true });
|
||||
submitForm();
|
||||
};
|
||||
|
||||
// Handle submit, close & new button click.
|
||||
const handleSubmitCloseAndNewBtnClick = (event) => {
|
||||
setSubmitPayload({ redirect: false, status: true, resetForm: true });
|
||||
submitForm();
|
||||
saveInvoke(onSubmitClick, event, {
|
||||
redirect: false,
|
||||
status: true,
|
||||
resetForm: true,
|
||||
});
|
||||
};
|
||||
|
||||
// Handle submit, close & continue editing button click.
|
||||
const handleSubmitCloseContinueEditingBtnClick = (event) => {
|
||||
setSubmitPayload({ redirect: false, status: true });
|
||||
submitForm();
|
||||
saveInvoke(onSubmitClick, event, {
|
||||
redirect: false,
|
||||
status: true,
|
||||
});
|
||||
};
|
||||
|
||||
// Handle submit & draft button click.
|
||||
const handleSubmitDraftBtnClick = (event) => {
|
||||
saveInvoke(onSubmitClick, event, {
|
||||
redirect: true,
|
||||
status: false,
|
||||
});
|
||||
setSubmitPayload({ redirect: true, status: false });
|
||||
submitForm();
|
||||
};
|
||||
|
||||
// Handle submit, draft & new button click.
|
||||
const handleSubmitDraftAndNewBtnClick = (event) => {
|
||||
setSubmitPayload({ redirect: false, status: false, resetForm: true });
|
||||
submitForm();
|
||||
saveInvoke(onSubmitClick, event, {
|
||||
redirect: false,
|
||||
status: false,
|
||||
resetForm: true,
|
||||
});
|
||||
};
|
||||
|
||||
const handleSubmitDraftContinueEditingBtnClick = (event) => {
|
||||
setSubmitPayload({ redirect: false, status: false });
|
||||
submitForm();
|
||||
saveInvoke(onSubmitClick, event, {
|
||||
redirect: false,
|
||||
status: false,
|
||||
});
|
||||
};
|
||||
|
||||
// Handle cancel button click.
|
||||
const handleCancelBtnClick = (event) => {
|
||||
saveInvoke(onCancelClick, event);
|
||||
|
||||
};
|
||||
|
||||
const handleClearBtnClick = (event) => {
|
||||
@@ -91,7 +78,6 @@ export default function ReceiptFormFloatingActions({
|
||||
<Button
|
||||
disabled={isSubmitting}
|
||||
intent={Intent.PRIMARY}
|
||||
type="submit"
|
||||
onClick={handleSubmitCloseBtnClick}
|
||||
text={<T id={'save_close'} />}
|
||||
/>
|
||||
@@ -124,7 +110,6 @@ export default function ReceiptFormFloatingActions({
|
||||
<Button
|
||||
disabled={isSubmitting}
|
||||
className={'ml1'}
|
||||
type="submit"
|
||||
onClick={handleSubmitDraftBtnClick}
|
||||
text={<T id={'save_as_draft'} />}
|
||||
/>
|
||||
@@ -158,7 +143,6 @@ export default function ReceiptFormFloatingActions({
|
||||
<Button
|
||||
disabled={isSubmitting}
|
||||
intent={Intent.PRIMARY}
|
||||
type="submit"
|
||||
onClick={handleSubmitCloseBtnClick}
|
||||
text={<T id={'save'} />}
|
||||
/>
|
||||
|
||||
@@ -20,10 +20,7 @@ import {
|
||||
InputPrependButton,
|
||||
} from 'components';
|
||||
|
||||
import withCustomers from 'containers/Customers/withCustomers';
|
||||
import withAccounts from 'containers/Accounts/withAccounts';
|
||||
import withDialogActions from 'containers/Dialog/withDialogActions';
|
||||
|
||||
import {
|
||||
momentFormatter,
|
||||
compose,
|
||||
@@ -32,20 +29,20 @@ import {
|
||||
handleDateChange,
|
||||
inputIntent,
|
||||
} from 'utils';
|
||||
import { useReceiptFormContext } from './ReceiptFormProvider';
|
||||
|
||||
/**
|
||||
* Receipt form header fields.
|
||||
*/
|
||||
function ReceiptFormHeader({
|
||||
//#withCustomers
|
||||
customers,
|
||||
|
||||
//#withAccouts
|
||||
accountsList,
|
||||
|
||||
//#withDialogActions
|
||||
openDialog,
|
||||
|
||||
// #ownProps
|
||||
onReceiptNumberChanged,
|
||||
}) {
|
||||
const { accounts, customers } = useReceiptFormContext();
|
||||
|
||||
const handleReceiptNumberChange = useCallback(() => {
|
||||
openDialog('receipt-number-form', {});
|
||||
}, [openDialog]);
|
||||
@@ -92,13 +89,13 @@ function ReceiptFormHeader({
|
||||
helperText={<ErrorMessage name={'deposit_account_id'} />}
|
||||
>
|
||||
<AccountsSelectList
|
||||
accounts={accountsList}
|
||||
accounts={accounts}
|
||||
onAccountSelected={(account) => {
|
||||
form.setFieldValue('deposit_account_id', account.id);
|
||||
}}
|
||||
defaultSelectText={<T id={'select_deposit_account'} />}
|
||||
selectedAccountId={value}
|
||||
filterByTypes={['current_asset']}
|
||||
// filterByTypes={['current_asset']}
|
||||
popoverFill={true}
|
||||
/>
|
||||
</FormGroup>
|
||||
@@ -185,11 +182,5 @@ function ReceiptFormHeader({
|
||||
}
|
||||
|
||||
export default compose(
|
||||
withCustomers(({ customers }) => ({
|
||||
customers,
|
||||
})),
|
||||
withAccounts(({ accountsList }) => ({
|
||||
accountsList,
|
||||
})),
|
||||
withDialogActions,
|
||||
)(ReceiptFormHeader);
|
||||
|
||||
@@ -1,41 +1,22 @@
|
||||
import React, { useCallback, useEffect } from 'react';
|
||||
import React, { useEffect } from 'react';
|
||||
import { useParams, useHistory } from 'react-router-dom';
|
||||
import { useQuery } from 'react-query';
|
||||
|
||||
import ReceiptFrom from './ReceiptForm';
|
||||
import DashboardInsider from 'components/Dashboard/DashboardInsider';
|
||||
import { ReceiptFormProvider } from './ReceiptFormProvider';
|
||||
|
||||
import withCustomersActions from 'containers/Customers/withCustomersActions';
|
||||
import withAccountsActions from 'containers/Accounts/withAccountsActions';
|
||||
import withItemsActions from 'containers/Items/withItemsActions';
|
||||
import withReceiptActions from './withReceiptActions';
|
||||
import withSettingsActions from 'containers/Settings/withSettingsActions';
|
||||
import withDashboardActions from 'containers/Dashboard/withDashboardActions';
|
||||
|
||||
import { compose } from 'utils';
|
||||
|
||||
/**
|
||||
* Receipt form page.
|
||||
*/
|
||||
function ReceiptFormPage({
|
||||
//#withwithAccountsActions
|
||||
requestFetchAccounts,
|
||||
|
||||
//#withCustomersActions
|
||||
requestFetchCustomers,
|
||||
|
||||
//#withItemsActions
|
||||
requestFetchItems,
|
||||
|
||||
//#withReceiptsActions
|
||||
requestFetchReceipt,
|
||||
|
||||
// #withSettingsActions
|
||||
requestFetchOptions,
|
||||
|
||||
// #withDashboardActions
|
||||
setSidebarShrink,
|
||||
resetSidebarPreviousExpand,
|
||||
setDashboardBackLink,
|
||||
}) {
|
||||
const history = useHistory();
|
||||
const { id } = useParams();
|
||||
|
||||
useEffect(() => {
|
||||
@@ -52,59 +33,25 @@ function ReceiptFormPage({
|
||||
};
|
||||
}, [resetSidebarPreviousExpand, setSidebarShrink, setDashboardBackLink]);
|
||||
|
||||
const fetchReceipt = useQuery(
|
||||
['receipt', id],
|
||||
(key, _id) => requestFetchReceipt(_id),
|
||||
{ enabled: !!id },
|
||||
);
|
||||
const fetchAccounts = useQuery('accounts-list', (key) =>
|
||||
requestFetchAccounts(),
|
||||
);
|
||||
|
||||
// const handleFormSubmit = useCallback(
|
||||
// (payload) => {
|
||||
// payload.redirect && history.push('/receipts');
|
||||
// },
|
||||
// [history],
|
||||
// );
|
||||
|
||||
const fetchCustomers = useQuery('customers-table', () =>
|
||||
requestFetchCustomers({}),
|
||||
);
|
||||
|
||||
// Handle fetch Items data table or list
|
||||
const fetchItems = useQuery('items-table', () => requestFetchItems({}));
|
||||
|
||||
const fetchSettings = useQuery(['settings'], () => requestFetchOptions({}));
|
||||
|
||||
const handleFormSubmit = useCallback(
|
||||
(payload) => {
|
||||
payload.redirect && history.push('/receipts');
|
||||
},
|
||||
[history],
|
||||
);
|
||||
|
||||
const handleCancel = useCallback(() => {
|
||||
history.goBack();
|
||||
}, [history]);
|
||||
// const handleCancel = useCallback(() => {
|
||||
// history.goBack();
|
||||
// }, [history]);
|
||||
|
||||
return (
|
||||
<DashboardInsider
|
||||
loading={
|
||||
fetchCustomers.isFetching ||
|
||||
fetchItems.isFetching ||
|
||||
fetchAccounts.isFetching ||
|
||||
fetchReceipt.isFetching
|
||||
}
|
||||
name={'receipt-form'}
|
||||
>
|
||||
<ReceiptFrom
|
||||
onFormSubmit={handleFormSubmit}
|
||||
receiptId={id}
|
||||
onCancelForm={handleCancel}
|
||||
/>
|
||||
</DashboardInsider>
|
||||
<ReceiptFormProvider receiptId={id}>
|
||||
<ReceiptFrom />
|
||||
</ReceiptFormProvider>
|
||||
);
|
||||
}
|
||||
|
||||
export default compose(
|
||||
withReceiptActions,
|
||||
withCustomersActions,
|
||||
withItemsActions,
|
||||
withAccountsActions,
|
||||
withSettingsActions,
|
||||
withDashboardActions,
|
||||
)(ReceiptFormPage);
|
||||
|
||||
85
client/src/containers/Sales/Receipt/ReceiptFormProvider.js
Normal file
85
client/src/containers/Sales/Receipt/ReceiptFormProvider.js
Normal file
@@ -0,0 +1,85 @@
|
||||
import React, { createContext, useState } from 'react';
|
||||
import DashboardInsider from 'components/Dashboard/DashboardInsider';
|
||||
import {
|
||||
useReceipt,
|
||||
useAccounts,
|
||||
useSettings,
|
||||
useCustomers,
|
||||
useItems,
|
||||
useCreateReceipt,
|
||||
useEditReceipt
|
||||
} from 'hooks/query';
|
||||
|
||||
const ReceiptFormContext = createContext();
|
||||
|
||||
/**
|
||||
* Receipt form provider.
|
||||
*/
|
||||
function ReceiptFormProvider({ receiptId, ...props }) {
|
||||
// Fetch sale receipt details.
|
||||
const { data: receipt, isFetching: isReceiptLoading } = useReceipt(
|
||||
receiptId,
|
||||
{
|
||||
enabled: !!receiptId,
|
||||
},
|
||||
);
|
||||
// Fetch accounts list.
|
||||
const { data: accounts, isFetching: isAccountsLoading } = useAccounts();
|
||||
|
||||
// Fetch customers list.
|
||||
const {
|
||||
data: { customers },
|
||||
isFetching: isCustomersLoading,
|
||||
} = useCustomers();
|
||||
|
||||
// Handle fetch Items data table or list
|
||||
const {
|
||||
data: { items },
|
||||
isFetching: isItemsLoading,
|
||||
} = useItems();
|
||||
|
||||
// Fetch receipt settings.
|
||||
const { isFetching: isSettingLoading } = useSettings();
|
||||
|
||||
const { mutateAsync: createReceiptMutate } = useCreateReceipt();
|
||||
const { mutateAsync: editReceiptMutate } = useEditReceipt();
|
||||
|
||||
const [submitPayload, setSubmitPayload] = useState({});
|
||||
|
||||
const provider = {
|
||||
receiptId,
|
||||
receipt,
|
||||
accounts,
|
||||
customers,
|
||||
items,
|
||||
submitPayload,
|
||||
|
||||
isReceiptLoading,
|
||||
isAccountsLoading,
|
||||
isCustomersLoading,
|
||||
isItemsLoading,
|
||||
isSettingLoading,
|
||||
|
||||
createReceiptMutate,
|
||||
editReceiptMutate,
|
||||
setSubmitPayload
|
||||
};
|
||||
return (
|
||||
<DashboardInsider
|
||||
loading={
|
||||
isReceiptLoading ||
|
||||
isAccountsLoading ||
|
||||
isCustomersLoading ||
|
||||
isItemsLoading ||
|
||||
isSettingLoading
|
||||
}
|
||||
name={'receipt-form'}
|
||||
>
|
||||
<ReceiptFormContext.Provider value={provider} {...props} />
|
||||
</DashboardInsider>
|
||||
);
|
||||
}
|
||||
|
||||
const useReceiptFormContext = () => React.useContext(ReceiptFormContext);
|
||||
|
||||
export { ReceiptFormProvider, useReceiptFormContext };
|
||||
@@ -1,69 +1,32 @@
|
||||
import React, { useEffect, useRef } from 'react';
|
||||
import React from 'react';
|
||||
import { useHistory } from 'react-router';
|
||||
import { Alignment, Navbar, NavbarGroup } from '@blueprintjs/core';
|
||||
import { useParams, withRouter } from 'react-router-dom';
|
||||
import { connect } from 'react-redux';
|
||||
import { pick, debounce } from 'lodash';
|
||||
import { useParams } from 'react-router-dom';
|
||||
import { pick } from 'lodash';
|
||||
|
||||
import { DashboardViewsTabs } from 'components';
|
||||
import { useUpdateEffect } from 'hooks';
|
||||
|
||||
import withReceipts from './withReceipts';
|
||||
import withReceiptActions from './withReceiptActions';
|
||||
import withDashboardActions from 'containers/Dashboard/withDashboardActions';
|
||||
import withViewDetails from 'containers/Views/withViewDetails';
|
||||
|
||||
import { compose } from 'utils';
|
||||
import { useReceiptsListContext } from './ReceiptsListProvider';
|
||||
|
||||
/**
|
||||
* Receipt views tabs.
|
||||
*/
|
||||
function ReceiptViewTabs({
|
||||
//#withReceipts
|
||||
receiptview,
|
||||
// #withViewDetails
|
||||
viewItem,
|
||||
|
||||
//#withReceiptActions
|
||||
changeReceiptView,
|
||||
addReceiptsTableQueries,
|
||||
|
||||
// #withDashboardActions
|
||||
setTopbarEditView,
|
||||
changePageSubtitle,
|
||||
|
||||
//# own Props
|
||||
customViewChanged,
|
||||
onViewChanged,
|
||||
}) {
|
||||
const history = useHistory();
|
||||
function ReceiptViewTabs({ addReceiptsTableQueries }) {
|
||||
const { custom_view_id: customViewId = null } = useParams();
|
||||
|
||||
useEffect(() => {
|
||||
setTopbarEditView(customViewId);
|
||||
changePageSubtitle(customViewId && viewItem ? viewItem.name : '');
|
||||
// changeReceiptView(customViewId || -1);
|
||||
// addReceiptsTableQueries({
|
||||
// custom_view_id: customViewId || null,
|
||||
// });
|
||||
}, [customViewId, addReceiptsTableQueries]);
|
||||
const { receiptsViews } = useReceiptsListContext();
|
||||
|
||||
const tabs = receiptview.map((view) => ({
|
||||
const tabs = receiptsViews.map((view) => ({
|
||||
...pick(view, ['name', 'id']),
|
||||
}));
|
||||
|
||||
const handleTabsChange = (viewId) => {
|
||||
changeReceiptView(viewId || -1);
|
||||
addReceiptsTableQueries({
|
||||
custom_view_id: viewId || null,
|
||||
});
|
||||
setTopbarEditView(viewId);
|
||||
};
|
||||
|
||||
// Handle click a new view tab.
|
||||
const handleClickNewView = () => {
|
||||
setTopbarEditView(null);
|
||||
history.push('/custom_views/receipts/new');
|
||||
};
|
||||
|
||||
return (
|
||||
@@ -80,17 +43,4 @@ function ReceiptViewTabs({
|
||||
);
|
||||
}
|
||||
|
||||
const mapStateToProps = (state, ownProps) => ({
|
||||
viewId: ownProps.match.params.custom_view_id,
|
||||
});
|
||||
|
||||
const withReceiptsViewTabs = connect(mapStateToProps);
|
||||
|
||||
export default compose(
|
||||
withRouter,
|
||||
withReceiptsViewTabs,
|
||||
withReceiptActions,
|
||||
withDashboardActions,
|
||||
withViewDetails(),
|
||||
withReceipts(({ receiptview }) => ({ receiptview })),
|
||||
)(ReceiptViewTabs);
|
||||
export default compose(withReceiptActions)(ReceiptViewTabs);
|
||||
|
||||
@@ -1,127 +1,50 @@
|
||||
import React, { useEffect, useCallback, useState } from 'react';
|
||||
import { Route, Switch, useHistory } from 'react-router-dom';
|
||||
import { useQuery } from 'react-query';
|
||||
import React, { useEffect } from 'react';
|
||||
import { useIntl } from 'react-intl';
|
||||
|
||||
import { FormattedMessage as T, useIntl } from 'react-intl';
|
||||
import DashboardPageContent from 'components/Dashboard/DashboardPageContent';
|
||||
import DashboardInsider from 'components/Dashboard/DashboardInsider';
|
||||
|
||||
import ReceiptsDataTable from './ReceiptsDataTable';
|
||||
import ReceiptActionsBar from './ReceiptActionsBar';
|
||||
import ReceiptViewTabs from './ReceiptViewTabs';
|
||||
import ReceiptsViewPage from './ReceiptsViewPage';
|
||||
import ReceiptsAlerts from './ReceiptsAlerts';
|
||||
|
||||
import withDashboardActions from 'containers/Dashboard/withDashboardActions';
|
||||
import withResourceActions from 'containers/Resources/withResourcesActions';
|
||||
import withReceipts from './withReceipts';
|
||||
import withReceiptActions from './withReceiptActions';
|
||||
import withViewsActions from 'containers/Views/withViewsActions';
|
||||
import withAlertsActions from 'containers/Alert/withAlertActions';
|
||||
|
||||
import { ReceiptsListProvider } from './ReceiptsListProvider';
|
||||
|
||||
import { compose } from 'utils';
|
||||
|
||||
/**
|
||||
* Receipts list page.
|
||||
*/
|
||||
function ReceiptsList({
|
||||
// #withDashboardActions
|
||||
changePageTitle,
|
||||
|
||||
// #withViewsActions
|
||||
requestFetchResourceViews,
|
||||
|
||||
//#withReceipts
|
||||
// #withReceipts
|
||||
receiptTableQuery,
|
||||
|
||||
// #withAlertsActions,
|
||||
openAlert,
|
||||
|
||||
//#withReceiptActions
|
||||
requestFetchReceiptsTable,
|
||||
addReceiptsTableQueries,
|
||||
}) {
|
||||
const history = useHistory();
|
||||
const { formatMessage } = useIntl();
|
||||
const [selectedRows, setSelectedRows] = useState([]);
|
||||
|
||||
const fetchReceipts = useQuery(
|
||||
['receipts-table', receiptTableQuery],
|
||||
(key, query) => requestFetchReceiptsTable({ ...query }),
|
||||
);
|
||||
|
||||
const fetchResourceViews = useQuery(
|
||||
['resource-views', 'sale_receipt'],
|
||||
(key, resourceName) => requestFetchResourceViews(resourceName),
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
changePageTitle(formatMessage({ id: 'receipts_list' }));
|
||||
}, [changePageTitle, formatMessage]);
|
||||
|
||||
// handle delete receipt click
|
||||
const handleDeleteReceipt = useCallback(
|
||||
({ id }) => {
|
||||
openAlert('receipt-delete', { receiptId: id });
|
||||
},
|
||||
[openAlert],
|
||||
);
|
||||
|
||||
// Handle cancel/confirm receipt deliver.
|
||||
const handleCloseReceipt = useCallback(({ id }) => {
|
||||
openAlert('receipt-close', { receiptId: id });
|
||||
}, []);
|
||||
|
||||
// Handle filter change to re-fetch data-table.
|
||||
const handleFilterChanged = useCallback(() => {}, [fetchReceipts]);
|
||||
|
||||
const handleEditReceipt = useCallback(
|
||||
(receipt) => {
|
||||
history.push(`/receipts/${receipt.id}/edit`);
|
||||
},
|
||||
[history],
|
||||
);
|
||||
|
||||
const handleSelectedRowsChange = useCallback(
|
||||
(estimate) => {
|
||||
setSelectedRows(estimate);
|
||||
},
|
||||
[setSelectedRows],
|
||||
);
|
||||
|
||||
return (
|
||||
<DashboardInsider
|
||||
name={'sales_receipts'}
|
||||
loading={fetchResourceViews.isFetching}
|
||||
>
|
||||
<ReceiptsListProvider query={receiptTableQuery}>
|
||||
<DashboardPageContent>
|
||||
<ReceiptActionsBar
|
||||
selectedRows={selectedRows}
|
||||
onFilterChanged={handleFilterChanged}
|
||||
/>
|
||||
<Switch>
|
||||
<Route
|
||||
exact={true}
|
||||
path={['/receipts/:custom_view_id/custom_view', '/receipts']}
|
||||
>
|
||||
<ReceiptViewTabs />
|
||||
<ReceiptsDataTable
|
||||
onDeleteReceipt={handleDeleteReceipt}
|
||||
onEditReceipt={handleEditReceipt}
|
||||
onCloseReceipt={handleCloseReceipt}
|
||||
onSelectedRowsChange={handleSelectedRowsChange}
|
||||
/>
|
||||
</Route>
|
||||
</Switch>
|
||||
<ReceiptActionsBar />
|
||||
|
||||
<ReceiptsViewPage />
|
||||
<ReceiptsAlerts />
|
||||
</DashboardPageContent>
|
||||
</DashboardInsider>
|
||||
</ReceiptsListProvider>
|
||||
);
|
||||
}
|
||||
|
||||
export default compose(
|
||||
withResourceActions,
|
||||
withReceiptActions,
|
||||
withDashboardActions,
|
||||
withViewsActions,
|
||||
withReceipts(({ receiptTableQuery }) => ({
|
||||
receiptTableQuery,
|
||||
})),
|
||||
withAlertsActions,
|
||||
)(ReceiptsList);
|
||||
|
||||
48
client/src/containers/Sales/Receipt/ReceiptsListProvider.js
Normal file
48
client/src/containers/Sales/Receipt/ReceiptsListProvider.js
Normal file
@@ -0,0 +1,48 @@
|
||||
import React, { createContext } from 'react';
|
||||
import DashboardInsider from 'components/Dashboard/DashboardInsider';
|
||||
import { useResourceViews, useResourceFields, useReceipts } from 'hooks/query';
|
||||
|
||||
|
||||
const ReceiptsListContext = createContext();
|
||||
|
||||
// Receipts list provider.
|
||||
function ReceiptsListProvider({ query, ...props }) {
|
||||
// Fetch receipts resource views and fields.
|
||||
const { data: receiptsViews, isFetching: isViewsLoading } = useResourceViews(
|
||||
'sale_receipt',
|
||||
);
|
||||
|
||||
// Fetches the sale receipts resource fields.
|
||||
// const {
|
||||
// data: receiptsFields,
|
||||
// isFetching: isFieldsLoading,
|
||||
// } = useResourceFields('sale_receipt');
|
||||
|
||||
const {
|
||||
data: { receipts, pagination },
|
||||
isFetching: isReceiptsLoading,
|
||||
} = useReceipts(query);
|
||||
|
||||
const provider = {
|
||||
receipts,
|
||||
pagination,
|
||||
// receiptsFields,
|
||||
receiptsViews,
|
||||
isViewsLoading,
|
||||
// isFieldsLoading,
|
||||
isReceiptsLoading,
|
||||
};
|
||||
|
||||
return (
|
||||
<DashboardInsider
|
||||
loading={isViewsLoading}
|
||||
name={'sales_receipts'}
|
||||
>
|
||||
<ReceiptsListContext.Provider value={provider} {...props} />
|
||||
</DashboardInsider>
|
||||
);
|
||||
}
|
||||
|
||||
const useReceiptsListContext = () => React.useContext(ReceiptsListContext);
|
||||
|
||||
export { ReceiptsListProvider, useReceiptsListContext };
|
||||
66
client/src/containers/Sales/Receipt/ReceiptsViewPage.js
Normal file
66
client/src/containers/Sales/Receipt/ReceiptsViewPage.js
Normal file
@@ -0,0 +1,66 @@
|
||||
import React, { useCallback } from 'react';
|
||||
import { Switch, Route, useHistory } from 'react-router-dom';
|
||||
|
||||
import ReceiptViewTabs from './ReceiptViewTabs';
|
||||
|
||||
import withAlertsActions from 'containers/Alert/withAlertActions';
|
||||
import withDialogActions from 'containers/Dialog/withDialogActions';
|
||||
|
||||
import { compose } from 'utils';
|
||||
|
||||
/**
|
||||
* Sale receipts view page.
|
||||
*/
|
||||
function ReceiptsViewPage({
|
||||
// #withAlertActions
|
||||
openAlert,
|
||||
|
||||
// #withDialog.
|
||||
openDialog,
|
||||
}) {
|
||||
const history = useHistory();
|
||||
|
||||
// handle delete receipt click
|
||||
const handleDeleteReceipt = useCallback(
|
||||
({ id }) => {
|
||||
openAlert('receipt-delete', { receiptId: id });
|
||||
},
|
||||
[openAlert],
|
||||
);
|
||||
|
||||
const handleSelectedRowsChange = useCallback((estimate) => {}, []);
|
||||
|
||||
const handleEditReceipt = useCallback(
|
||||
(receipt) => {
|
||||
history.push(`/receipts/${receipt.id}/edit`);
|
||||
},
|
||||
[history],
|
||||
);
|
||||
|
||||
// Handle cancel/confirm receipt deliver.
|
||||
const handleCloseReceipt = useCallback(
|
||||
({ id }) => {
|
||||
openAlert('receipt-close', { receiptId: id });
|
||||
},
|
||||
[openAlert],
|
||||
);
|
||||
|
||||
return (
|
||||
<Switch>
|
||||
<Route
|
||||
exact={true}
|
||||
path={['/receipts/:custom_view_id/custom_view', '/receipts']}
|
||||
>
|
||||
<ReceiptViewTabs />
|
||||
{/* <ReceiptsDataTable
|
||||
onDeleteReceipt={handleDeleteReceipt}
|
||||
onEditReceipt={handleEditReceipt}
|
||||
onCloseReceipt={handleCloseReceipt}
|
||||
onSelectedRowsChange={handleSelectedRowsChange}
|
||||
/> */}
|
||||
</Route>
|
||||
</Switch>
|
||||
);
|
||||
}
|
||||
|
||||
export default compose(withAlertsActions, withDialogActions)(ReceiptsViewPage);
|
||||
Reference in New Issue
Block a user