WIP/ Feature : Estimate & Receipt & Bill & Invoice

This commit is contained in:
elforjani3
2020-08-12 21:19:52 +02:00
parent 58c6b5466c
commit c86f0b29bf
66 changed files with 4288 additions and 535 deletions

View File

@@ -1,4 +1,4 @@
import React, { useMemo, useCallback } from 'react';
import React, { useCallback, useState, useMemo } from 'react';
import Icon from 'components/Icon';
import {
Button,
@@ -15,49 +15,59 @@ import {
import classNames from 'classnames';
import { useRouteMatch, useHistory } from 'react-router-dom';
import { FormattedMessage as T } from 'react-intl';
import { FormattedMessage as T, useIntl } from 'react-intl';
import { If } from 'components';
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 withInvoiceActions from './withInvoices';
import withInvoiceActions from './withInvoiceActions';
import withInvoices from './withInvoices';
import { compose } from 'utils';
import { connect } from 'react-redux';
function InvoiceActionsBar({
// #withResourceDetail
resourceFields,
//#withInvoice
InvoiceViews,
invoicesViews,
// #withInvoiceActions
addInvoiceTableQueries,
// #own Porps
onFilterChanged,
selectedRows,
selectedRows = [],
}) {
const history = useHistory();
const { path } = useRouteMatch();
const [filterCount, setFilterCount] = useState(0);
const { formatMessage } = useIntl();
const FilterDropdown = FilterDropdown({
initialCondition: {
fieldKey: '',
compatator: '',
value: '',
},
fields: resourceFields,
onFilterChange: (filterConditions) => {
addInvoiceTableQueries({
filter_roles: filterConditions || '',
});
onFilterChanged && onFilterChanged(filterConditions);
},
});
const handleClickNewInvoice = useCallback(() => {
history.push('/invoices/new');
}, [history]);
// const filterDropdown = FilterDropdown({
// initialCondition: {
// fieldKey: 'reference_no',
// compatator: 'contains',
// value: '',
// },
// fields: resourceFields,
// onFilterChange: (filterConditions) => {
// addInvoiceTableQueries({
// filter_roles: filterConditions || '',
// });
// onFilterChanged && onFilterChanged(filterConditions);
// },
// });
const hasSelectedRows = useMemo(() => selectedRows.length > 0, [
selectedRows,
@@ -66,22 +76,32 @@ function InvoiceActionsBar({
return (
<DashboardActionsBar>
<NavbarGroup>
<DashboardActionViewsList
resourceName={'sales_invoices'}
views={invoicesViews}
/>
<NavbarDivider />
<Button
className={Classes.MINIMAL}
icon={<Icon icon={'plus'} />}
text={<T id={'new_invoice'} />}
onClick={onClickNewInvoice}
onClick={handleClickNewInvoice}
/>
<Popover
minimal={true}
content={FilterDropdown}
// content={filterDropdown}
interactionKind={PopoverInteractionKind.CLICK}
position={Position.BOTTOM_LEFT}
>
<Button
className={classNames(Classes.MINIMAL)}
text={'Filter'}
text={
filterCount <= 0 ? (
<T id={'filter'} />
) : (
`${filterCount} ${formatMessage({ id: 'filters_applied' })}`
)
}
icon={<Icon icon={'filter-16'} iconSize={16} />}
/>
</Popover>
@@ -115,19 +135,17 @@ function InvoiceActionsBar({
}
const mapStateToProps = (state, props) => ({
resourceName: 'invoice',
resourceName: 'sales_invoices',
});
const withInvoiceActionsBar = connect(mapStateToProps);
export default compose(
withInvoiceActionsBar,
withDialogActions,
withResourceDetail(({ resourceFields }) => ({
resourceFields,
})),
// withInvoices(({ invoiceViews }) => ({
// invoiceViews,
// })),
withInvoices(({ invoicesViews }) => ({
invoicesViews,
})),
withInvoiceActions,
)(InvoiceActionsBar);

View File

@@ -10,15 +10,16 @@ import { useFormik } from 'formik';
import moment from 'moment';
import { Intent, FormGroup, TextArea, Button } from '@blueprintjs/core';
import { FormattedMessage as T, useIntl } from 'react-intl';
import { pick, omit } from 'lodash';
import { pick } from 'lodash';
import InvoiceFormHeader from './InvoiceFormHeader';
import EstimatesItemsTable from 'containers/Sales/Estimate/EntriesItemsTable';
import InvoiceFormFooter from './InvoiceFormFooter';
import withInvoiceActions from './withInvoiceActions';
import withInvoiceDetail from './withInvoiceDetail';
import withDashboardActions from 'containers/Dashboard/withDashboardActions';
import withMediaActions from 'containers/Media/withMediaActions';
import withInvoiceActions from './withInvoiceActions';
import { AppToaster } from 'components';
import Dragzone from 'components/Dragzone';
@@ -35,7 +36,8 @@ function InvoiceForm({
//#WithInvoiceActions
requestSubmitInvoice,
requestEditInvoice,
//#withDashboard
changePageTitle,
changePageSubtitle,
@@ -44,12 +46,12 @@ function InvoiceForm({
invoice,
//#own Props
InvoiceId,
invoiceId,
onFormSubmit,
onCancelForm,
}) {
const { formatMessage } = useIntl();
const [payload, setPaload] = useState({});
const [payload, setPayload] = useState({});
const {
setFiles,
@@ -70,7 +72,6 @@ function InvoiceForm({
const clearSavedMediaIds = () => {
savedMediaIds.current = [];
};
useEffect(() => {
if (invoice && invoice.id) {
changePageTitle(formatMessage({ id: 'edit_invoice' }));
@@ -115,7 +116,6 @@ function InvoiceForm({
is: (quantity, rate) => quantity || rate,
then: Yup.number().required(),
}),
total: Yup.number().nullable(),
discount: Yup.number().nullable(),
description: Yup.string().nullable(),
}),
@@ -134,18 +134,19 @@ function InvoiceForm({
index: 0,
item_id: null,
rate: null,
discount: null,
discount: 0,
quantity: null,
description: '',
}),
[],
);
const defaultInitialValues = useMemo(
() => ({
customer_id: '',
invoice_date: moment(new Date()).format('YYYY-MM-DD'),
due_date: moment(new Date()).format('YYYY-MM-DD'),
status: 'status',
status: 'SEND',
invoice_no: '',
reference_no: '',
invoice_message: '',
@@ -161,15 +162,38 @@ function InvoiceForm({
index: index + 1,
}));
};
// debugger;
const initialValues = useMemo(
() => ({
...defaultInitialValues,
entries: orderingIndex(defaultInitialValues.entries),
...(invoice
? {
...pick(invoice, Object.keys(defaultInitialValues)),
entries: [
...invoice.entries.map((invoice) => ({
...pick(invoice, Object.keys(defaultInvoice)),
})),
...repeatValue(
defaultInvoice,
Math.max(MIN_LINES_NUMBER - invoice.entries.length, 0),
),
],
}
: {
...defaultInitialValues,
entries: orderingIndex(defaultInitialValues.entries),
}),
}),
[defaultInvoice, defaultInitialValues, invoice],
[invoice, defaultInitialValues, defaultInvoice],
);
// const initialValues = useMemo(
// () => ({
// ...defaultInitialValues,
// entries: orderingIndex(defaultInitialValues.entries),
// }),
// [defaultInvoice, defaultInitialValues, invoice],
// );
const initialAttachmentFiles = useMemo(() => {
return invoice && invoice.media
? invoice.media.map((attach) => ({
@@ -188,52 +212,58 @@ function InvoiceForm({
},
onSubmit: async (values, { setSubmitting, setErrors, resetForm }) => {
setSubmitting(true);
const entries = values.entries.map((item) => omit(item, ['total']));
const entries = values.entries.filter(
(item) => item.item_id && item.quantity,
);
const form = {
...values,
entries,
};
const saveInvoice = (mediaIds) =>
new Promise((resolve, reject) => {
const requestForm = { ...form, media_ids: mediaIds };
requestSubmitInvoice(requestForm)
.then((response) => {
AppToaster.show({
message: formatMessage(
{ id: 'the_invocie_has_been_successfully_created' },
{ number: values.invoice_no },
),
intent: Intent.SUCCESS,
});
setSubmitting(false);
resetForm();
saveInvokeSubmit({ action: 'new', ...payload });
clearSavedMediaIds();
})
.catch((errors) => {
setSubmitting(false);
const requestForm = { ...form };
if (invoice && invoice.id) {
requestEditInvoice(invoice.id, requestForm)
.then((response) => {
AppToaster.show({
message: formatMessage({
id: 'the_invoice_has_been_successfully_edited',
}),
intent: Intent.SUCCESS,
});
});
Promise.all([saveMedia(), deleteMedia()])
.then(([savedMediaResponses]) => {
const mediaIds = savedMediaResponses.map((res) => res.data.media.id);
savedMediaIds.current = mediaIds;
return savedMediaResponses;
})
.then(() => {
return saveInvoice(savedMediaIds.current);
});
setSubmitting(false);
saveInvokeSubmit({ action: 'update', ...payload });
resetForm();
})
.catch((error) => {
setSubmitting(false);
});
} else {
requestSubmitInvoice(requestForm)
.then((response) => {
AppToaster.show({
message: formatMessage(
{ id: 'the_invocie_has_been_successfully_created' },
{ number: values.invoice_no },
),
intent: Intent.SUCCESS,
});
setSubmitting(false);
saveInvokeSubmit({ action: 'new', ...payload });
resetForm();
})
.catch((errors) => {
setSubmitting(false);
});
}
},
});
const handleSubmitClick = useCallback(
(payload) => {
setPaload(payload);
setPayload(payload);
formik.submitForm();
},
[setPaload, formik],
[setPayload, formik],
);
const handleCancelClick = useCallback(
@@ -243,7 +273,6 @@ function InvoiceForm({
[onCancelForm],
);
console.log(formik.errors, 'Errors');
const handleDeleteFile = useCallback(
(_deletedFiles) => {
_deletedFiles.forEach((deletedFile) => {
@@ -303,13 +332,13 @@ function InvoiceForm({
onDeleteFile={handleDeleteFile}
hint={'Attachments: Maxiumum size: 20MB'}
/>
<InvoiceFormFooter
formik={formik}
onSubmitClick={handleSubmitClick}
onCancelClick={handleCancelClick}
/>
</form>
<InvoiceFormFooter
formik={formik}
onSubmitClick={handleSubmitClick}
invoice={invoice}
onCancelClick={handleCancelClick}
/>
</div>
);
}
@@ -318,4 +347,5 @@ export default compose(
withInvoiceActions,
withDashboardActions,
withMediaActions,
withInvoiceDetail(),
)(InvoiceForm);

View File

@@ -6,11 +6,19 @@ export default function EstimateFormFooter({
formik: { isSubmitting },
onSubmitClick,
onCancelClick,
invoice,
}) {
return (
<div className={'estimate-form__floating-footer'}>
<Button disabled={isSubmitting} intent={Intent.PRIMARY} type="submit">
<T id={'save_send'} />
<Button
disabled={isSubmitting}
intent={Intent.PRIMARY}
type="submit"
onClick={() => {
onSubmitClick({ redirect: true });
}}
>
{invoice && invoice.id ? <T id={'edit'} /> : <T id={'save_send'} />}
</Button>
<Button
@@ -19,6 +27,9 @@ export default function EstimateFormFooter({
className={'ml1'}
name={'save'}
type="submit"
onClick={() => {
onSubmitClick({ redirect: false });
}}
>
<T id={'save'} />
</Button>

View File

@@ -8,23 +8,34 @@ import { FormattedMessage as T, useIntl } from 'react-intl';
import DashboardPageContent from 'components/Dashboard/DashboardPageContent';
import DashboardInsider from 'components/Dashboard/DashboardInsider';
import withDashboardActions from 'containers/Dashboard/withDashboardActions';
// import withInvoiceActions from './withInvoiceActions'
import InvoicesDataTable from './InvoicesDataTable';
import InvoiceActionsBar from './InvoiceActionsBar';
import InvoiceViewTabs from './InvoiceViewTabs';
// import InvoiceActionsBar from './InvoiceActionsBar';
import withDashboardActions from 'containers/Dashboard/withDashboardActions';
import withResourceActions from 'containers/Resources/withResourcesActions';
import withInvoices from './withInvoices';
import withInvoiceActions from './withInvoiceActions';
import withViewsActions from 'containers/Views/withViewsActions';
import { compose } from 'utils';
import InvoiceActionsBar from './InvoiceActionsBar';
function InvoiceList({
// #withDashboardActions
changePageTitle,
// #withViewsActions
requestFetchResourceViews,
requestFetchResourceFields,
//#withInvoice
invoicesTableQuery,
invoicesViews,
//#withInvoiceActions
requestFetchInvoiceTable,
requestDeleteInvoice,
addInvoiceTableQueries,
}) {
const history = useHistory();
const { formatMessage } = useIntl();
@@ -35,6 +46,19 @@ function InvoiceList({
changePageTitle(formatMessage({ id: 'invoice_list' }));
}, [changePageTitle, formatMessage]);
const fetchResourceViews = useQuery(
['resource-views', 'sales_invoices'],
(key, resourceName) => requestFetchResourceViews(resourceName),
);
const fetchResourceFields = useQuery(
['resource-fields', 'sales_invoices'],
(key, resourceName) => requestFetchResourceFields(resourceName),
);
const fetchInvoices = useQuery(['invoices-table', invoicesTableQuery], () =>
requestFetchInvoiceTable(),
);
//handle dalete Invoice
const handleDeleteInvoice = useCallback(
(invoice) => {
@@ -59,15 +83,16 @@ function InvoiceList({
});
setDeleteInvoice(false);
});
}, [setDeleteInvoice, requestDeleteInvoice]);
}, [deleteInvoice, requestDeleteInvoice, formatMessage]);
const handleEditInvoice = useCallback((invoice) => {
history.push(`/invoices/${invoice.id}/edit`);
});
const fetchInvoice = useQuery(['invoice-table'], () =>
requsetFetchInvoiceTable(),
);
// Calculates the selected rows count.
const selectedRowsCount = useMemo(() => Object.values(selectedRows).length, [
selectedRows,
]);
const handleFetchData = useCallback(
({ pageIndex, pageSize, sortBy }) => {
@@ -86,24 +111,67 @@ function InvoiceList({
},
[addInvoiceTableQueries],
);
const handleSelectedRowsChange = useCallback((_invoice) => {
selectedRows(_invoice);
});
// Handle filter change to re-fetch data-table.
const handleFilterChanged = useCallback(() => {}, [fetchInvoices]);
// Handle selected rows change.
const handleSelectedRowsChange = useCallback(
(_invoices) => {
setSelectedRows(_invoices);
},
[setSelectedRows],
);
return (
<DashboardInsider name={'invoices'}>
<InvoiceActionsBar
// onBulkDelete={}
selectedRows={selectedRows}
// onFilterChanged={}
<DashboardInsider
loading={fetchResourceViews.isFetching || fetchResourceFields.isFetching}
name={'sales_invoices'}
>
<InvoiceActionsBar
// onBulkDelete={}
selectedRows={selectedRows}
onFilterChanged={handleFilterChanged}
/>
<DashboardPageContent>
<Switch>
<Route></Route>
<Route
exact={true}
path={['/invoices/:custom_view_id/custom_view', '/invoices']}
>
<InvoiceViewTabs />
<InvoicesDataTable
loading={fetchInvoices.isFetching}
onDeleteInvoice={handleDeleteInvoice}
onFetchData={handleFetchData}
onEditInvoice={handleEditInvoice}
onSelectedRowsChange={handleSelectedRowsChange}
/>
</Route>
</Switch>
<Alert
cancelButtonText={<T id={'cancel'} />}
confirmButtonText={<T id={'delete'} />}
icon={'trash'}
intent={Intent.DANGER}
isOpen={deleteInvoice}
onCancel={handleCancelInvoiceDelete}
onConfirm={handleConfirmInvoiceDelete}
>
<p>
<T id={'once_delete_this_invoice_you_will_able_to_restore_it'} />
</p>
</Alert>
</DashboardPageContent>
</DashboardInsider>
);
}
export default InvoiceList;
export default compose(
withResourceActions,
withInvoiceActions,
withDashboardActions,
withViewsActions,
withInvoices(({ invoicesTableQuery }) => ({
invoicesTableQuery,
})),
)(InvoiceList);

View File

@@ -0,0 +1,113 @@
import React, { useEffect, useRef } 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 { 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';
function InvoiceViewTabs({
//#withInvoices
invoicesViews,
// #withViewDetails
viewItem,
//#withInvoiceActions
changeInvoiceView,
addInvoiceTableQueries,
// #withDashboardActions
setTopbarEditView,
changePageSubtitle,
// #ownProps
customViewChanged,
onViewChanged,
}) {
const history = useHistory();
const { custom_view_id: customViewId = null } = useParams();
useEffect(() => {
changeInvoiceView(customViewId || -1);
setTopbarEditView(customViewId);
changePageSubtitle(customViewId && viewItem ? viewItem.name : '');
addInvoiceTableQueries({
custom_view_id: customViewId,
});
return () => {
setTopbarEditView(null);
changePageSubtitle('');
changeInvoiceView(null);
};
}, [customViewId, addInvoiceTableQueries, changeInvoiceView]);
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(`/invoices/${toPath}`);
setTopbarEditView(viewId);
};
const tabs = invoicesViews.map((view) => ({
...pick(view, ['name', 'id']),
}));
// Handle click a new view tab.
const handleClickNewView = () => {
setTopbarEditView(null);
history.push('/custom_views/invoices/new');
};
console.log(invoicesViews, 'invoicesViews');
return (
<Navbar className={'navbar--dashboard-views'}>
<NavbarGroup align={Alignment.LEFT}>
<DashboardViewsTabs
initialViewId={customViewId}
baseUrl={'/invoices'}
tabs={tabs}
onNewViewTabClick={handleClickNewView}
onChange={handleTabsChange}
/>
</NavbarGroup>
</Navbar>
);
}
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

@@ -11,31 +11,50 @@ import withInvoiceActions from './withInvoiceActions';
import { compose } from 'utils';
function Invoices({ requestFetchCustomers, requestFetchItems }) {
function Invoices({
requestFetchCustomers,
requestFetchItems,
requsetFetchInvoice,
}) {
const history = useHistory();
const { id } = useParams();
// Handle fetch Items data table or list
const fetchItems = useQuery('items-table', () => requestFetchItems({}));
const handleFormSubmit = useCallback((payload) => {}, [history]);
const handleFormSubmit = useCallback(
(payload) => {
payload.redirect && history.push('/invoices');
},
[history],
);
// Handle fetch customers data table or list
const fetchCustomers = useQuery('customers-table', () =>
requestFetchCustomers({}),
);
const fetchInvoice = useQuery(
['invoice', id],
(key, _id) => requsetFetchInvoice(_id),
{ enabled: !!id },
);
const handleCancel = useCallback(() => {
history.goBack();
}, [history]);
return (
<DashboardInsider
loading={fetchCustomers.isFetching || fetchItems.isFetching}
loading={
fetchCustomers.isFetching ||
fetchItems.isFetching ||
fetchInvoice.isFetching
}
name={'invoice-form'}
>
<InvoiceForm
onFormSubmit={handleFormSubmit}
// InvoiceId={id}
invoiceId={id}
onCancelForm={handleCancel}
/>
</DashboardInsider>

View File

@@ -4,35 +4,36 @@ import {
Button,
Classes,
Popover,
Tooltip,
Menu,
MenuItem,
MenuDivider,
Position,
Tag,
} from '@blueprintjs/core';
import { useParams } from 'react-router-dom';
import { withRouter } from 'react-router';
import { FormattedMessage as T, useIntl } from 'react-intl';
import moment from 'moment';
import Icon from 'components/Icon';
import { compose } from 'utils';
import { useUpdateEffect } from 'hooks';
import LoadingIndicator from 'components/LoadingIndicator';
import { If } from 'components';
import DataTable from 'components/DataTable';
import { DataTable, Money, Icon } from 'components';
import withDialogActions from 'containers/Dialog/withDialogActions';
import withDashboardActions from 'containers/Dashboard/withDashboardActions';
import withViewDetails from 'containers/Views/withViewDetails';
import witInvoice from './withInvoice';
import withInvoices from './withInvoices';
import withInvoiceActions from './withInvoiceActions';
import withCurrentView from 'containers/Views/withCurrentView';
function InvoicesDataTable({
//#withInvoices
invoicesCurrentPage,
invoicesLoading,
invoicesPageination,
invoicesItems,
// #withDashboardActions
changeCurrentView,
@@ -45,8 +46,8 @@ function InvoicesDataTable({
//#OwnProps
loading,
onFetchData,
onEditEstimate,
onDeleteEstimate,
onEditInvoice,
onDeleteInvoice,
onSelectedRowsChange,
}) {
const [initialMount, setInitialMount] = useState(false);
@@ -55,79 +56,120 @@ function InvoicesDataTable({
useEffect(() => {
setInitialMount(false);
}, []);
}, [customViewId]);
useEffect(() => {
if (customViewId) {
changeCurrentView(customViewId);
setTopbarEditView(customViewId);
useUpdateEffect(() => {
if (!invoicesLoading) {
setInitialMount(true);
}
changePageSubtitle(customViewId && viewMeta ? viewMeta.name : '');
}, [
customViewId,
changeCurrentView,
changePageSubtitle,
setTopbarEditView,
viewMeta,
]);
}, [invoicesLoading, setInitialMount]);
// useEffect(() => {
// if (customViewId) {
// changeCurrentView(customViewId);
// setTopbarEditView(customViewId);
// }
// changePageSubtitle(customViewId && viewMeta ? viewMeta.name : '');
// }, [
// customViewId,
// changeCurrentView,
// changePageSubtitle,
// setTopbarEditView,
// viewMeta,
// ]);
const handleEditInvoice = useCallback(
(_invoice) => {
(_invoice) => () => {
onEditInvoice && onEditInvoice(_invoice);
},
[onEditInvoice],
);
const handleDeleteInvoice = useCallback(() => {
onDeleteInvoice && onDeleteInvoice();
}, [onDeleteInvoice]);
const handleDeleteInvoice = useCallback(
(_invoice) => () => {
onDeleteInvoice && onDeleteInvoice(_invoice);
},
[onDeleteInvoice],
);
const actionsMenuList = useCallback(
(invoice) => {
const actionMenuList = useCallback(
(invoice) => (
<Menu>
<MenuItem text={formatMessage({ id: 'view_details' })} />
<MenuDivider />
<MenuItem
text={formatMessage({ id: 'edit_invoice' })}
// onClick={handleEditInvoice(invoice)}
onClick={handleEditInvoice(invoice)}
/>
</Menu>;
},
<MenuItem
text={formatMessage({ id: 'delete_invoice' })}
intent={Intent.DANGER}
onClick={handleDeleteInvoice(invoice)}
icon={<Icon icon="trash-16" iconSize={16} />}
/>
</Menu>
),
[handleDeleteInvoice, handleEditInvoice, formatMessage],
);
const onRowContextMenu = useCallback(
(cell) => {
return actionMenuList(cell.row.original);
},
[actionMenuList],
);
const columns = useMemo(
() => [
{
id: '',
Header: formatMessage({ id: '' }),
accessor: '',
className: '',
id: 'invoice_date',
Header: formatMessage({ id: 'invoice_date' }),
accessor: (r) => moment(r.invoice_date).format('YYYY MMM DD'),
width: 140,
className: 'invoice_date',
},
{
id: '',
Header: formatMessage({ id: '' }),
accessor: '',
className: '',
id: 'customer_id',
Header: formatMessage({ id: 'customer_name' }),
accessor: (row) => row.customer_id,
width: 140,
className: 'customer_id',
},
{
id: '',
Header: formatMessage({ id: '' }),
accessor: '',
className: '',
id: 'invoice_no',
Header: formatMessage({ id: 'invoice_no__' }),
accessor: (row) => `#${row.invoice_no}`,
width: 140,
className: 'invoice_no',
},
{
id: 'due_date',
Header: formatMessage({ id: 'due_date' }),
accessor: (r) => moment(r.due_date).format('YYYY MMM DD'),
width: 140,
className: 'due_date',
},
{
id: '',
Header: formatMessage({ id: '' }),
accessor: '',
className: '',
id: 'balance',
Header: formatMessage({ id: 'balance' }),
accessor: (r) => <Money amount={r.balance} currency={'USD'} />,
width: 140,
className: 'balance',
},
{
id: '',
Header: formatMessage({ id: '' }),
accessor: '',
className: '',
id: 'reference_no',
Header: formatMessage({ id: 'reference_no' }),
accessor: 'reference_no',
width: 140,
className: 'reference_no',
},
{
id: 'status',
Header: formatMessage({ id: 'status' }),
accessor: 'status',
width: 140,
className: 'status',
},
{
id: 'actions',
@@ -149,12 +191,11 @@ function InvoicesDataTable({
);
const handleDataTableFetchData = useCallback(
(...arguments) => {
onFetchData && onFetchData(...arguments);
(...args) => {
onFetchData && onFetchData(...args);
},
[onFetchData],
);
const handleSelectedRowsChange = useCallback(
(selectedRows) => {
onSelectedRowsChange &&
@@ -163,14 +204,33 @@ function InvoicesDataTable({
[onSelectedRowsChange],
);
const selectionColumn = useMemo(
() => ({
minWidth: 40,
width: 40,
maxWidth: 40,
}),
[],
);
return (
<div>
<LoadingIndicator>
<LoadingIndicator loading={loading} mount={false}>
<DataTable
columns={columns}
data={[]}
data={invoicesCurrentPage}
onFetchData={handleDataTableFetchData}
manualSortBy={true}
selectionColumn={true}
noInitialFetch={true}
sticky={true}
loading={invoicesLoading && !initialMount}
onSelectedRowsChange={handleSelectedRowsChange}
rowContextMenu={onRowContextMenu}
pagination={true}
pagesCount={invoicesPageination.pagesCount}
initialPageSize={invoicesPageination.pageSize}
initialPageIndex={invoicesPageination.page - 1}
/>
</LoadingIndicator>
</div>
@@ -179,11 +239,16 @@ function InvoicesDataTable({
export default compose(
withRouter,
withCurrentView,
withDialogActions,
withDashboardActions,
withInvoiceActions,
// withInvoices(({})=>({
// }))
withInvoices(
({ invoicesCurrentPage, invoicesLoading, invoicesPageination }) => ({
invoicesCurrentPage,
invoicesLoading,
invoicesPageination,
}),
),
withViewDetails(),
)(InvoicesDataTable);

View File

@@ -11,14 +11,14 @@ import t from 'store/types';
const mapDipatchToProps = (dispatch) => ({
requestSubmitInvoice: (form) => dispatch(submitInvoice({ form })),
requsetFetchInvoice: (id) => dispatch(fetchInvoice({ id })),
requestEditInvoice: (id, form) => dispatch(editInvoice({ id, form })),
requestEditInvoice: (id, form) => dispatch(editInvoice( id, form )),
requestFetchInvoiceTable: (query = {}) =>
dispatch(fetchInvoicesTable({ query: { ...query } })),
requestDeleteInvoice: (id) => dispatch(deleteInvoice({ id })),
changeInvoiceView: (id) =>
dispatch({
type: t.INVOICES_SET_CURREMT_VIEW,
type: t.INVOICES_SET_CURRENT_VIEW,
currentViewId: parseInt(id, 10),
}),
addInvoiceTableQueries: (_queries) =>

View File

@@ -1,9 +1,9 @@
import { connect } from 'react-redux';
import { getInvoiceById } from 'store/Invoice/invoices.selector';
import { getInvoiecsByIdFactory } from 'store/Invoice/invoices.selector';
export default () => {
const getInvoiceById = getInvoiceById();
const getInvoiceById = getInvoiecsByIdFactory();
const mapStateToProps = (state, props) => ({
invoice: getInvoiceById(state, props),
});

View File

@@ -0,0 +1,25 @@
import { connect } from 'react-redux';
import { getResourceViews } from 'store/customViews/customViews.selectors';
import {
getInvoiceCurrentPageFactory,
getInvoicePaginationMetaFactory,
getInvoiceTableQuery,
} from 'store/Invoice/invoices.selector';
export default (mapState) => {
const getInvoicesItems = getInvoiceCurrentPageFactory();
const getInvoicesPaginationMeta = getInvoicePaginationMetaFactory();
const mapStateToProps = (state, props) => {
const query = getInvoiceTableQuery(state, props);
const mapped = {
invoicesCurrentPage: getInvoicesItems(state, props, query),
invoicesViews: getResourceViews(state, props, 'sales_invoices'),
invoicesItems: state.sales_invoices.items,
invoicesTableQuery: query,
invoicesPageination: getInvoicesPaginationMeta(state, props, query),
invoicesLoading: state.sales_invoices.loading,
};
return mapState ? mapState(mapped, state, props) : mapped;
};
return connect(mapStateToProps);
};