refactoring: migrating to react-query to manage service-side state.

This commit is contained in:
a.bouhuolia
2021-02-07 08:10:21 +02:00
parent e093be0663
commit adac2386bb
284 changed files with 8255 additions and 6610 deletions

View 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)

View File

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

View File

@@ -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 {

View File

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

View File

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

View File

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

View File

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

View File

@@ -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'} />}
/>

View File

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

View File

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

View File

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

View 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 };

View File

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

View File

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

View 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 };

View 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)

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -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}
/>

View File

@@ -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'} />}
/>

View File

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

View File

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

View 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 };

View File

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

View File

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

View 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 };

View 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);