mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-21 23:30:32 +00:00
WIP Version 0.0.1
This commit is contained in:
@@ -72,6 +72,7 @@
|
|||||||
"react-hook-form": "^4.9.4",
|
"react-hook-form": "^4.9.4",
|
||||||
"react-intl": "^3.12.0",
|
"react-intl": "^3.12.0",
|
||||||
"react-loadable": "^5.5.0",
|
"react-loadable": "^5.5.0",
|
||||||
|
"react-query": "^1.3.3",
|
||||||
"react-redux": "^7.1.3",
|
"react-redux": "^7.1.3",
|
||||||
"react-router-dom": "^5.1.2",
|
"react-router-dom": "^5.1.2",
|
||||||
"react-scrollbars-custom": "^4.0.21",
|
"react-scrollbars-custom": "^4.0.21",
|
||||||
@@ -121,6 +122,7 @@
|
|||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/preset-flow": "^7.9.0",
|
"@babel/preset-flow": "^7.9.0",
|
||||||
"http-proxy-middleware": "^1.0.0",
|
"http-proxy-middleware": "^1.0.0",
|
||||||
|
"react-query-devtools": "^1.1.5",
|
||||||
"redux-devtools": "^3.5.0"
|
"redux-devtools": "^3.5.0"
|
||||||
},
|
},
|
||||||
"jest": {
|
"jest": {
|
||||||
|
|||||||
@@ -8,7 +8,9 @@ import PrivateRoute from 'components/PrivateRoute';
|
|||||||
import Authentication from 'components/Authentication';
|
import Authentication from 'components/Authentication';
|
||||||
import Dashboard from 'components/Dashboard/Dashboard';
|
import Dashboard from 'components/Dashboard/Dashboard';
|
||||||
import { isAuthenticated } from 'store/authentication/authentication.reducer'
|
import { isAuthenticated } from 'store/authentication/authentication.reducer'
|
||||||
import Progress from 'components/NProgress/Progress';
|
import { ReactQueryConfigProvider } from 'react-query';
|
||||||
|
import { ReactQueryDevtools } from "react-query-devtools";
|
||||||
|
|
||||||
import messages from 'lang/en';
|
import messages from 'lang/en';
|
||||||
import 'style/App.scss';
|
import 'style/App.scss';
|
||||||
|
|
||||||
@@ -22,13 +24,22 @@ function App({
|
|||||||
console.log(`new location via ${action}`, location);
|
console.log(`new location via ${action}`, location);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const queryConfig = {
|
||||||
|
refetchAllOnWindowFocus: false,
|
||||||
|
cacheTime: 10000,
|
||||||
|
staleTime: 10000,
|
||||||
|
};
|
||||||
return (
|
return (
|
||||||
<IntlProvider locale={locale} messages={messages}>
|
<IntlProvider locale={locale} messages={messages}>
|
||||||
<div className="App">
|
<div className="App">
|
||||||
|
<ReactQueryConfigProvider config={queryConfig}>
|
||||||
<Router history={history}>
|
<Router history={history}>
|
||||||
<Authentication isAuthenticated={isAuthorized} />
|
<Authentication isAuthenticated={isAuthorized} />
|
||||||
<PrivateRoute isAuthenticated={isAuthorized} component={Dashboard} />
|
<PrivateRoute isAuthenticated={isAuthorized} component={Dashboard} />
|
||||||
</Router>
|
</Router>
|
||||||
|
|
||||||
|
<ReactQueryDevtools />
|
||||||
|
</ReactQueryConfigProvider>
|
||||||
</div>
|
</div>
|
||||||
</IntlProvider>
|
</IntlProvider>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { Redirect, Route, Switch, Link } from 'react-router-dom';
|
|||||||
import BodyClassName from 'react-body-classname';
|
import BodyClassName from 'react-body-classname';
|
||||||
import authenticationRoutes from 'routes/authentication';
|
import authenticationRoutes from 'routes/authentication';
|
||||||
|
|
||||||
export default function({ isAuthenticated =false, ...rest }) {
|
export default function AuthenticationWrapper({ isAuthenticated =false, ...rest }) {
|
||||||
const to = {pathname: '/dashboard/homepage'};
|
const to = {pathname: '/dashboard/homepage'};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import DashboardContent from 'components/Dashboard/DashboardContent';
|
|||||||
import DialogsContainer from 'components/DialogsContainer';
|
import DialogsContainer from 'components/DialogsContainer';
|
||||||
import PreferencesContent from 'components/Preferences/PreferencesContent';
|
import PreferencesContent from 'components/Preferences/PreferencesContent';
|
||||||
import PreferencesSidebar from 'components/Preferences/PreferencesSidebar';
|
import PreferencesSidebar from 'components/Preferences/PreferencesSidebar';
|
||||||
import Search from 'containers/Dashboard/GeneralSearch/Search';
|
import Search from 'containers/GeneralSearch/Search';
|
||||||
|
|
||||||
export default function Dashboard() {
|
export default function Dashboard() {
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import AccountFormDialog from 'containers/Dashboard/Dialogs/AccountFormDialog';
|
import AccountFormDialog from 'containers/Dialogs/AccountFormDialog';
|
||||||
import UserFormDialog from 'containers/Dashboard/Dialogs/UserFormDialog';
|
import UserFormDialog from 'containers/Dialogs/UserFormDialog';
|
||||||
import ItemCategoryDialog from 'containers/Dashboard/Dialogs/ItemCategoryDialog';
|
import ItemCategoryDialog from 'containers/Dialogs/ItemCategoryDialog';
|
||||||
import CurrencyDialog from 'containers/Dashboard/Dialogs/CurrencyDialog';
|
import CurrencyDialog from 'containers/Dialogs/CurrencyDialog';
|
||||||
import InviteUserDialog from 'containers/Dashboard/Dialogs/InviteUserDialog';
|
import InviteUserDialog from 'containers/Dialogs/InviteUserDialog';
|
||||||
|
|
||||||
export default function DialogsContainer() {
|
export default function DialogsContainer() {
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -1,13 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
import { Button, AnchorButton, Classes, Icon } from '@blueprintjs/core';
|
|
||||||
|
|
||||||
const ItemsActionsBar = () => {
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className='dashob'>
|
|
||||||
ItemsActionsBar 22
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default ItemsActionsBar;
|
|
||||||
@@ -1,94 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
import {
|
|
||||||
FormGroup,
|
|
||||||
MenuItem,
|
|
||||||
Intent,
|
|
||||||
InputGroup,
|
|
||||||
Position,
|
|
||||||
Button,
|
|
||||||
} from '@blueprintjs/core';
|
|
||||||
import { DateInput, TimePrecision } from "@blueprintjs/datetime";
|
|
||||||
import {
|
|
||||||
GridComponent,
|
|
||||||
ColumnsDirective,
|
|
||||||
ColumnDirective,
|
|
||||||
Inject,
|
|
||||||
Sort,
|
|
||||||
} from '@syncfusion/ej2-react-grids';
|
|
||||||
import {momentFormatter} from 'utils';
|
|
||||||
|
|
||||||
export default function MakeJournalEntry({
|
|
||||||
accounts,
|
|
||||||
currencies,
|
|
||||||
}) {
|
|
||||||
|
|
||||||
const handleDateChange = () => {
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleClose = () => {
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
const columns = [
|
|
||||||
{
|
|
||||||
headerText: 'Account',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
headerText: 'Description',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
headerText: 'Account',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
headerText: 'Debit',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
headerText: 'Credit',
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div class="make-journal-entry">
|
|
||||||
<div class="make-journal-entry__details">
|
|
||||||
<FormGroup
|
|
||||||
label={'Date'}
|
|
||||||
inline={true}>
|
|
||||||
|
|
||||||
<DateInput
|
|
||||||
{...momentFormatter('MM/DD/YYYY')}
|
|
||||||
defaultValue={new Date()}
|
|
||||||
onChange={handleDateChange}
|
|
||||||
popoverProps={{ position: Position.BOTTOM }}
|
|
||||||
/>
|
|
||||||
</FormGroup>
|
|
||||||
|
|
||||||
<GridComponent>
|
|
||||||
<ColumnsDirective>
|
|
||||||
{columns.map((column) => {
|
|
||||||
return (<ColumnDirective
|
|
||||||
field={column.field}
|
|
||||||
headerText={column.headerText}
|
|
||||||
template={column.template}
|
|
||||||
allowSorting={true}
|
|
||||||
customAttributes={column.customAttributes}
|
|
||||||
/>);
|
|
||||||
})}
|
|
||||||
</ColumnsDirective>
|
|
||||||
</GridComponent>
|
|
||||||
|
|
||||||
<div class="form__floating-footer">
|
|
||||||
<Button onClick={handleClose}>Close</Button>
|
|
||||||
<Button intent={Intent.PRIMARY} type="submit">
|
|
||||||
{ 'Save and Publish' }
|
|
||||||
</Button>
|
|
||||||
|
|
||||||
<Button intent={Intent.PRIMARY} type="submit">
|
|
||||||
{ 'Save as Draft' }
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@@ -1,18 +1,16 @@
|
|||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import {connect} from 'react-redux';
|
|
||||||
import Progress from './Progress';
|
import Progress from './Progress';
|
||||||
|
import {queryCache, useIsFetching} from 'react-query';
|
||||||
|
|
||||||
function AppProgress({
|
function AppProgress({
|
||||||
isAnimating,
|
|
||||||
}) {
|
}) {
|
||||||
|
const isFetching = useIsFetching();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Progress isAnimating={isAnimating} />
|
<Progress isAnimating={isFetching} />
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const mapStateToProps = (state) => ({
|
export default AppProgress;
|
||||||
isAnimating: state.dashboard.requestsLoading > 0,
|
|
||||||
});
|
|
||||||
|
|
||||||
export default connect(mapStateToProps)(AppProgress);
|
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Route, Switch } from 'react-router-dom';
|
import { Route, Switch } from 'react-router-dom';
|
||||||
import DashboardTopbarUser from 'components/Dashboard/TopbarUser';
|
import DashboardTopbarUser from 'components/Dashboard/TopbarUser';
|
||||||
import UsersActions from 'containers/Dashboard/Preferences/UsersActions';
|
import UsersActions from 'containers/Preferences/UsersActions';
|
||||||
import CurrenciesActions from 'containers/Dashboard/Preferences/CurrenciesActions';
|
import CurrenciesActions from 'containers/Preferences/CurrenciesActions';
|
||||||
|
|
||||||
|
|
||||||
export default function PreferencesTopbar() {
|
export default function PreferencesTopbar() {
|
||||||
|
|||||||
33
client/src/components/Utils/Choose.js
Normal file
33
client/src/components/Utils/Choose.js
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import If from './if';
|
||||||
|
|
||||||
|
const Choose = props => {
|
||||||
|
let when = null;
|
||||||
|
let otherwise = null;
|
||||||
|
|
||||||
|
React.Children.forEach(props.children, children => {
|
||||||
|
if (children.props.condition === undefined) {
|
||||||
|
otherwise = children;
|
||||||
|
} else if (!when && children.props.condition === true) {
|
||||||
|
when = children;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return when || otherwise;
|
||||||
|
};
|
||||||
|
|
||||||
|
Choose.propTypes = {
|
||||||
|
children: PropTypes.node
|
||||||
|
};
|
||||||
|
|
||||||
|
Choose.When = If;
|
||||||
|
|
||||||
|
Choose.Otherwise = ({render, children}) => render ? render() : children;
|
||||||
|
|
||||||
|
Choose.Otherwise.propTypes = {
|
||||||
|
children: PropTypes.node,
|
||||||
|
render: PropTypes.func
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Choose;
|
||||||
11
client/src/components/Utils/For.js
Normal file
11
client/src/components/Utils/For.js
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
|
||||||
|
const For = ({render, of}) => of.map((item, index) => render(item, index));
|
||||||
|
|
||||||
|
For.propTypes = {
|
||||||
|
of: PropTypes.array.isRequired,
|
||||||
|
render: PropTypes.func.isRequired
|
||||||
|
};
|
||||||
|
|
||||||
|
export default For;
|
||||||
13
client/src/components/Utils/If.js
Normal file
13
client/src/components/Utils/If.js
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
|
||||||
|
const If = props => props.condition
|
||||||
|
? (props.render ? props.render() : props.children) : null;
|
||||||
|
|
||||||
|
If.propTypes = {
|
||||||
|
condition: PropTypes.bool.isRequired,
|
||||||
|
children: PropTypes.node,
|
||||||
|
render: PropTypes.func
|
||||||
|
};
|
||||||
|
|
||||||
|
export default If;
|
||||||
9
client/src/components/index.js
Normal file
9
client/src/components/index.js
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
import If from './Utils/If';
|
||||||
|
// import Choose from './Utils/Choose';
|
||||||
|
// import For from './Utils/For';
|
||||||
|
|
||||||
|
export {
|
||||||
|
If,
|
||||||
|
// Choose,
|
||||||
|
// For,
|
||||||
|
};
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
import {connect} from 'react-redux';
|
|
||||||
import {getDialogPayload} from 'store/dashboard/dashboard.reducer';
|
|
||||||
|
|
||||||
export const mapStateToProps = (state, props) => {
|
|
||||||
const dialogPayload = getDialogPayload(state, 'account-form');
|
|
||||||
|
|
||||||
return {
|
|
||||||
name: 'account-form',
|
|
||||||
payload: {action: 'new', id: null, ...dialogPayload},
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
export default connect(mapStateToProps);
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
import { connect } from 'react-redux';
|
|
||||||
import {
|
|
||||||
fetchResourceViews,
|
|
||||||
} from 'store/customViews/customViews.actions';
|
|
||||||
|
|
||||||
const mapStateToProps = (state) => ({
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
const mapActionsToProps = (dispatch) => ({
|
|
||||||
fetchResourceViews: (resourceSlug) => dispatch(fetchResourceViews({ resourceSlug })),
|
|
||||||
});
|
|
||||||
|
|
||||||
export default connect(mapStateToProps, mapActionsToProps);
|
|
||||||
@@ -2,10 +2,6 @@
|
|||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import t from 'store/types';
|
import t from 'store/types';
|
||||||
|
|
||||||
const mapStateToProps = (state) => ({
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
const mapActionsToProps = (dispatch) => ({
|
const mapActionsToProps = (dispatch) => ({
|
||||||
changePageTitle: (pageTitle) => dispatch({
|
changePageTitle: (pageTitle) => dispatch({
|
||||||
type: t.CHANGE_DASHBOARD_PAGE_TITLE, pageTitle
|
type: t.CHANGE_DASHBOARD_PAGE_TITLE, pageTitle
|
||||||
@@ -28,4 +24,4 @@ const mapActionsToProps = (dispatch) => ({
|
|||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
|
||||||
export default connect(mapStateToProps, mapActionsToProps);
|
export default connect(null, mapActionsToProps);
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
import { connect } from 'react-redux';
|
|
||||||
import { submitRegister } from 'store/registers/register.action';
|
|
||||||
|
|
||||||
export const mapStateToProps = (state, props) => {
|
|
||||||
return {};
|
|
||||||
};
|
|
||||||
|
|
||||||
export const mapDispatchToProps = (dispatch) => ({
|
|
||||||
requestSubmitRegister: (form) => dispatch(submitRegister({ form })),
|
|
||||||
});
|
|
||||||
|
|
||||||
export default connect(mapStateToProps, mapDispatchToProps);
|
|
||||||
@@ -8,19 +8,21 @@ import {
|
|||||||
getResourceFields,
|
getResourceFields,
|
||||||
getResourceColumn,
|
getResourceColumn,
|
||||||
getResourceField,
|
getResourceField,
|
||||||
|
getResourceMetadata,
|
||||||
} from 'store/resources/resources.reducer';
|
} from 'store/resources/resources.reducer';
|
||||||
|
|
||||||
export const mapStateToProps = (state, props) => ({
|
export const mapStateToProps = (state, props) => ({
|
||||||
getResourceColumns: (resourceSlug) => getResourceColumns(state, resourceSlug),
|
// getResourceColumns: (resourceSlug) => getResourceColumns(state, resourceSlug),
|
||||||
getResourceFields: (resourceSlug) => getResourceFields(state, resourceSlug),
|
// getResourceFields: (resourceSlug) => getResourceFields(state, resourceSlug),
|
||||||
|
// getResourceMetadata: (resourceSlug) => getResourceMetadata(state, resourceSlug),
|
||||||
|
|
||||||
getResourceColumn: (columnId) => getResourceColumn(state, columnId),
|
// getResourceColumn: (columnId) => getResourceColumn(state, columnId),
|
||||||
getResourceField: (fieldId) => getResourceField(state, fieldId),
|
// getResourceField: (fieldId) => getResourceField(state, fieldId),
|
||||||
});
|
});
|
||||||
|
|
||||||
export const mapDispatchToProps = (dispatch) => ({
|
export const mapDispatchToProps = (dispatch) => ({
|
||||||
fetchResourceFields: (resourceSlug) => dispatch(fetchResourceFields({ resourceSlug })),
|
requestFetchResourceFields: (resourceSlug) => dispatch(fetchResourceFields({ resourceSlug })),
|
||||||
fetchResourceColumns: (resourceSlug) => dispatch(fetchResourceColumns({ resourceSlug })),
|
requestFetchResourceColumns: (resourceSlug) => dispatch(fetchResourceColumns({ resourceSlug })),
|
||||||
});
|
});
|
||||||
|
|
||||||
export default connect(mapStateToProps, mapDispatchToProps);
|
export default connect(mapStateToProps, mapDispatchToProps);
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
import {connect} from 'react-redux';
|
|
||||||
import {
|
|
||||||
fetchView,
|
|
||||||
submitView,
|
|
||||||
deleteView,
|
|
||||||
editView,
|
|
||||||
} from 'store/customViews/customViews.actions';
|
|
||||||
import {
|
|
||||||
getViewMeta,
|
|
||||||
getViewItem,
|
|
||||||
} from 'store/customViews/customViews.selectors';
|
|
||||||
|
|
||||||
export const mapStateToProps = (state) => ({
|
|
||||||
getViewMeta: (viewId) => getViewMeta(state, viewId),
|
|
||||||
getViewItem: (viewId) => getViewItem(state, viewId),
|
|
||||||
});
|
|
||||||
|
|
||||||
export const mapDispatchToProps = (dispatch) => ({
|
|
||||||
fetchView: (id) => dispatch(fetchView({ id })),
|
|
||||||
submitView: (form) => dispatch(submitView({ form })),
|
|
||||||
editView: (id, form) => dispatch(editView({ id, form })),
|
|
||||||
deleteView: (id) => dispatch(deleteView({ id })),
|
|
||||||
});
|
|
||||||
|
|
||||||
export default connect(mapStateToProps, mapDispatchToProps);
|
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
import {connect} from 'react-redux';
|
|
||||||
import {
|
|
||||||
fetchResourceColumns,
|
|
||||||
fetchResourceFields,
|
|
||||||
} from 'store/resources/resources.actions';
|
|
||||||
import {
|
|
||||||
getResourceColumns,
|
|
||||||
getResourceFields,
|
|
||||||
} from 'store/resources/resources.reducer';
|
|
||||||
import {
|
|
||||||
fetchView,
|
|
||||||
submitView,
|
|
||||||
editView,
|
|
||||||
} from 'store/customViews/customViews.actions';
|
|
||||||
import t from 'store/types';
|
|
||||||
|
|
||||||
export const mapStateToProps = (state, props) => {
|
|
||||||
return {
|
|
||||||
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
export const mapDispatchToProps = (dispatch) => ({
|
|
||||||
fetchView: (id) => dispatch(fetchView({ id })),
|
|
||||||
submitView: (form) => dispatch(submitView({ form })),
|
|
||||||
editView: (id, form) => dispatch(editView({ id, form })),
|
|
||||||
});
|
|
||||||
|
|
||||||
export default connect(mapStateToProps, mapDispatchToProps);
|
|
||||||
@@ -1,39 +1,41 @@
|
|||||||
import React, {useMemo, useState, useEffect, useRef, useCallback} from 'react';
|
import React, {useMemo, useState, useEffect, useRef, useCallback} from 'react';
|
||||||
import * as Yup from 'yup';
|
import * as Yup from 'yup';
|
||||||
import {
|
import {useFormik} from "formik";
|
||||||
ProgressBar,
|
import moment from 'moment';
|
||||||
Classes,
|
import { Intent } from '@blueprintjs/core';
|
||||||
Intent,
|
|
||||||
} from '@blueprintjs/core';
|
|
||||||
import MakeJournalEntriesHeader from './MakeJournalEntriesHeader';
|
import MakeJournalEntriesHeader from './MakeJournalEntriesHeader';
|
||||||
import MakeJournalEntriesFooter from './MakeJournalEntriesFooter';
|
import MakeJournalEntriesFooter from './MakeJournalEntriesFooter';
|
||||||
import MakeJournalEntriesTable from './MakeJournalEntriesTable';
|
import MakeJournalEntriesTable from './MakeJournalEntriesTable';
|
||||||
import {useFormik} from "formik";
|
|
||||||
import MakeJournalEntriesConnect from 'connectors/MakeJournalEntries.connect';
|
import withJournalsActions from 'containers/Accounting/withJournalsActions';
|
||||||
import AccountsConnect from 'connectors/Accounts.connector';
|
import withManualJournalDetail from 'containers/Accounting/withManualJournalDetail';
|
||||||
import DashboardConnect from 'connectors/Dashboard.connector';
|
import withAccountsActions from 'containers/Accounts/withAccountsActions';
|
||||||
import {compose, saveFilesInAsync} from 'utils';
|
import withDashboardActions from 'containers/Dashboard/withDashboard';
|
||||||
import moment from 'moment';
|
|
||||||
import AppToaster from 'components/AppToaster';
|
import AppToaster from 'components/AppToaster';
|
||||||
import {pick} from 'lodash';
|
import {pick} from 'lodash';
|
||||||
import Dragzone from 'components/Dragzone';
|
import Dragzone from 'components/Dragzone';
|
||||||
import MediaConnect from 'connectors/Media.connect';
|
import MediaConnect from 'connectors/Media.connect';
|
||||||
import classNames from 'classnames';
|
|
||||||
import ManualJournalsConnect from 'connectors/ManualJournals.connect';
|
|
||||||
import useMedia from 'hooks/useMedia';
|
import useMedia from 'hooks/useMedia';
|
||||||
|
import {compose} from 'utils';
|
||||||
|
|
||||||
|
|
||||||
function MakeJournalEntriesForm({
|
function MakeJournalEntriesForm({
|
||||||
requestSubmitMedia,
|
requestSubmitMedia,
|
||||||
|
requestDeleteMedia,
|
||||||
|
|
||||||
requestMakeJournalEntries,
|
requestMakeJournalEntries,
|
||||||
requestEditManualJournal,
|
requestEditManualJournal,
|
||||||
|
|
||||||
changePageTitle,
|
changePageTitle,
|
||||||
changePageSubtitle,
|
changePageSubtitle,
|
||||||
editJournal,
|
|
||||||
|
manualJournalId,
|
||||||
|
manualJournal,
|
||||||
onFormSubmit,
|
onFormSubmit,
|
||||||
onCancelForm,
|
onCancelForm,
|
||||||
|
|
||||||
requestDeleteMedia,
|
|
||||||
manualJournalsItems
|
|
||||||
}) {
|
}) {
|
||||||
const { setFiles, saveMedia, deletedFiles, setDeletedFiles, deleteMedia } = useMedia({
|
const { setFiles, saveMedia, deletedFiles, setDeletedFiles, deleteMedia } = useMedia({
|
||||||
saveCallback: requestSubmitMedia,
|
saveCallback: requestSubmitMedia,
|
||||||
@@ -47,13 +49,13 @@ function MakeJournalEntriesForm({
|
|||||||
const clearSavedMediaIds = () => { savedMediaIds.current = []; }
|
const clearSavedMediaIds = () => { savedMediaIds.current = []; }
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (editJournal && editJournal.id) {
|
if (manualJournal && manualJournal.id) {
|
||||||
changePageTitle('Edit Journal');
|
changePageTitle('Edit Journal');
|
||||||
changePageSubtitle(`No. ${editJournal.journal_number}`);
|
changePageSubtitle(`No. ${manualJournal.journal_number}`);
|
||||||
} else {
|
} else {
|
||||||
changePageTitle('New Journal');
|
changePageTitle('New Journal');
|
||||||
}
|
}
|
||||||
}, [changePageTitle, changePageSubtitle, editJournal]);
|
}, [changePageTitle, changePageSubtitle, manualJournal]);
|
||||||
|
|
||||||
const validationSchema = Yup.object().shape({
|
const validationSchema = Yup.object().shape({
|
||||||
journal_number: Yup.string().required(),
|
journal_number: Yup.string().required(),
|
||||||
@@ -100,24 +102,24 @@ function MakeJournalEntriesForm({
|
|||||||
}), [defaultEntry]);
|
}), [defaultEntry]);
|
||||||
|
|
||||||
const initialValues = useMemo(() => ({
|
const initialValues = useMemo(() => ({
|
||||||
...(editJournal) ? {
|
...(manualJournal) ? {
|
||||||
...pick(editJournal, Object.keys(defaultInitialValues)),
|
...pick(manualJournal, Object.keys(defaultInitialValues)),
|
||||||
entries: editJournal.entries.map((entry) => ({
|
entries: manualJournal.entries.map((entry) => ({
|
||||||
...pick(entry, Object.keys(defaultEntry)),
|
...pick(entry, Object.keys(defaultEntry)),
|
||||||
})),
|
})),
|
||||||
} : {
|
} : {
|
||||||
...defaultInitialValues,
|
...defaultInitialValues,
|
||||||
}
|
}
|
||||||
}), [editJournal, defaultInitialValues, defaultEntry]);
|
}), [manualJournal, defaultInitialValues, defaultEntry]);
|
||||||
|
|
||||||
const initialAttachmentFiles = useMemo(() => {
|
const initialAttachmentFiles = useMemo(() => {
|
||||||
return editJournal && editJournal.media
|
return manualJournal && manualJournal.media
|
||||||
? editJournal.media.map((attach) => ({
|
? manualJournal.media.map((attach) => ({
|
||||||
preview: attach.attachment_file,
|
preview: attach.attachment_file,
|
||||||
uploaded: true,
|
uploaded: true,
|
||||||
metadata: { ...attach },
|
metadata: { ...attach },
|
||||||
})) : [];
|
})) : [];
|
||||||
}, [editJournal]);
|
}, [manualJournal]);
|
||||||
|
|
||||||
const formik = useFormik({
|
const formik = useFormik({
|
||||||
enableReinitialize: true,
|
enableReinitialize: true,
|
||||||
@@ -125,7 +127,7 @@ function MakeJournalEntriesForm({
|
|||||||
initialValues: {
|
initialValues: {
|
||||||
...initialValues,
|
...initialValues,
|
||||||
},
|
},
|
||||||
onSubmit: async (values, actions) => {
|
onSubmit: async (values, { setErrors, setSubmitting }) => {
|
||||||
const entries = values.entries.filter((entry) => (
|
const entries = values.entries.filter((entry) => (
|
||||||
(entry.credit || entry.debit)
|
(entry.credit || entry.debit)
|
||||||
));
|
));
|
||||||
@@ -142,7 +144,7 @@ function MakeJournalEntriesForm({
|
|||||||
AppToaster.show({
|
AppToaster.show({
|
||||||
message: 'credit_and_debit_not_equal',
|
message: 'credit_and_debit_not_equal',
|
||||||
});
|
});
|
||||||
actions.setSubmitting(false);
|
setSubmitting(false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const form = { ...values, status: payload.publish, entries };
|
const form = { ...values, status: payload.publish, entries };
|
||||||
@@ -150,33 +152,43 @@ function MakeJournalEntriesForm({
|
|||||||
const saveJournal = (mediaIds) => new Promise((resolve, reject) => {
|
const saveJournal = (mediaIds) => new Promise((resolve, reject) => {
|
||||||
const requestForm = { ...form, media_ids: mediaIds };
|
const requestForm = { ...form, media_ids: mediaIds };
|
||||||
|
|
||||||
if (editJournal && editJournal.id) {
|
if (manualJournal && manualJournal.id) {
|
||||||
requestEditManualJournal(editJournal.id, requestForm)
|
requestEditManualJournal(manualJournal.id, requestForm)
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
AppToaster.show({
|
AppToaster.show({
|
||||||
message: 'manual_journal_has_been_edited',
|
message: 'manual_journal_has_been_edited',
|
||||||
|
intent: Intent.SUCCESS,
|
||||||
});
|
});
|
||||||
actions.setSubmitting(false);
|
setSubmitting(false);
|
||||||
saveInvokeSubmit({ action: 'update', ...payload });
|
saveInvokeSubmit({ action: 'update', ...payload });
|
||||||
clearSavedMediaIds([]);
|
clearSavedMediaIds([]);
|
||||||
resolve(response);
|
resolve(response);
|
||||||
}).catch((error) => {
|
}).catch((errors) => {
|
||||||
actions.setSubmitting(false);
|
if (errors.find(e => e.type === 'JOURNAL.NUMBER.ALREADY.EXISTS')) {
|
||||||
reject(error);
|
setErrors({
|
||||||
|
journal_number: 'Journal number is already used.',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
setSubmitting(false);
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
requestMakeJournalEntries(requestForm)
|
requestMakeJournalEntries(requestForm)
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
AppToaster.show({
|
AppToaster.show({
|
||||||
message: 'manual_journal_has_been_submit',
|
message: 'manual_journal_has_been_submit',
|
||||||
|
intent: Intent.SUCCESS,
|
||||||
});
|
});
|
||||||
actions.setSubmitting(false);
|
setSubmitting(false);
|
||||||
saveInvokeSubmit({ action: 'new', ...payload });
|
saveInvokeSubmit({ action: 'new', ...payload });
|
||||||
clearSavedMediaIds();
|
clearSavedMediaIds();
|
||||||
resolve(response);
|
resolve(response);
|
||||||
}).catch((error) => {
|
}).catch((errors) => {
|
||||||
actions.setSubmitting(false);
|
if (errors.find(e => e.type === 'JOURNAL.NUMBER.ALREADY.EXISTS')) {
|
||||||
reject(error);
|
setErrors({
|
||||||
|
journal_number: 'Journal number is already used.',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
setSubmitting(false);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -240,9 +252,11 @@ function MakeJournalEntriesForm({
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default compose(
|
export default compose(
|
||||||
ManualJournalsConnect,
|
// ManualJournalsConnect,
|
||||||
MakeJournalEntriesConnect,
|
// MakeJournalEntriesConnect,
|
||||||
AccountsConnect,
|
withJournalsActions,
|
||||||
DashboardConnect,
|
withManualJournalDetail,
|
||||||
|
withAccountsActions,
|
||||||
|
withDashboardActions,
|
||||||
MediaConnect,
|
MediaConnect,
|
||||||
)(MakeJournalEntriesForm);
|
)(MakeJournalEntriesForm);
|
||||||
@@ -1,31 +1,28 @@
|
|||||||
import React, {useMemo, useCallback} from 'react';
|
import React, { useCallback } from 'react';
|
||||||
import { useParams, useHistory } from 'react-router-dom';
|
import { useParams, useHistory } from 'react-router-dom';
|
||||||
import useAsync from 'hooks/async';
|
import { useQuery } from 'react-query';
|
||||||
import MakeJournalEntriesForm from './MakeJournalEntriesForm';
|
import MakeJournalEntriesForm from './MakeJournalEntriesForm';
|
||||||
import DashboardInsider from 'components/Dashboard/DashboardInsider';
|
import DashboardInsider from 'components/Dashboard/DashboardInsider';
|
||||||
import DashboardConnect from 'connectors/Dashboard.connector';
|
|
||||||
|
import withAccountsActions from 'containers/Accounts/withAccountsActions';
|
||||||
|
import withManualJournalsActions from 'containers/Accounting/withManualJournalsActions';
|
||||||
|
|
||||||
import {compose} from 'utils';
|
import {compose} from 'utils';
|
||||||
import MakeJournalEntriesConnect from 'connectors/MakeJournalEntries.connect';
|
|
||||||
import AccountsConnect from 'connectors/Accounts.connector';
|
|
||||||
|
|
||||||
function MakeJournalEntriesPage({
|
function MakeJournalEntriesPage({
|
||||||
fetchManualJournal,
|
requestFetchManualJournal,
|
||||||
getManualJournal,
|
|
||||||
requestFetchAccounts,
|
requestFetchAccounts,
|
||||||
}) {
|
}) {
|
||||||
const history = useHistory();
|
const history = useHistory();
|
||||||
const { id } = useParams();
|
const { id } = useParams();
|
||||||
|
|
||||||
const fetchJournal = useAsync(() => {
|
const fetchAccounts = useQuery('accounts-list',
|
||||||
return Promise.all([
|
(key) => requestFetchAccounts());
|
||||||
requestFetchAccounts(),
|
|
||||||
(id) && fetchManualJournal(id),
|
|
||||||
]);
|
|
||||||
});
|
|
||||||
|
|
||||||
const editJournal = useMemo(() =>
|
const fetchJournal = useQuery(
|
||||||
getManualJournal(id) || null,
|
id && ['manual-journal', id],
|
||||||
[getManualJournal, id]);
|
(key, journalId) => requestFetchManualJournal(journalId));
|
||||||
|
|
||||||
const handleFormSubmit = useCallback((payload) => {
|
const handleFormSubmit = useCallback((payload) => {
|
||||||
payload.redirect &&
|
payload.redirect &&
|
||||||
@@ -37,17 +34,19 @@ function MakeJournalEntriesPage({
|
|||||||
}, [history]);
|
}, [history]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<DashboardInsider loading={fetchJournal.pending} name={'make-journal-page'}>
|
<DashboardInsider
|
||||||
|
loading={fetchJournal.isFetching || fetchAccounts.isFetching}
|
||||||
|
name={'make-journal-page'}>
|
||||||
<MakeJournalEntriesForm
|
<MakeJournalEntriesForm
|
||||||
onFormSubmit={handleFormSubmit}
|
onFormSubmit={handleFormSubmit}
|
||||||
editJournal={editJournal}
|
manualJournalId={id}
|
||||||
onCancelForm={handleCancel} />
|
onCancelForm={handleCancel} />
|
||||||
</DashboardInsider>
|
</DashboardInsider>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default compose(
|
export default compose(
|
||||||
DashboardConnect,
|
// DashboardConnect,
|
||||||
AccountsConnect,
|
withAccountsActions,
|
||||||
MakeJournalEntriesConnect,
|
withManualJournalsActions,
|
||||||
)(MakeJournalEntriesPage);
|
)(MakeJournalEntriesPage);
|
||||||
@@ -5,7 +5,6 @@ import {
|
|||||||
} from '@blueprintjs/core';
|
} from '@blueprintjs/core';
|
||||||
import DataTable from 'components/DataTable';
|
import DataTable from 'components/DataTable';
|
||||||
import Icon from 'components/Icon';
|
import Icon from 'components/Icon';
|
||||||
import AccountsConnect from 'connectors/Accounts.connector.js';
|
|
||||||
import { compose, formattedAmount} from 'utils';
|
import { compose, formattedAmount} from 'utils';
|
||||||
import {
|
import {
|
||||||
AccountsListFieldCell,
|
AccountsListFieldCell,
|
||||||
@@ -14,6 +13,9 @@ import {
|
|||||||
} from 'components/DataTableCells';
|
} from 'components/DataTableCells';
|
||||||
import { omit } from 'lodash';
|
import { omit } from 'lodash';
|
||||||
|
|
||||||
|
import withAccounts from 'containers/Accounts/withAccounts';
|
||||||
|
|
||||||
|
|
||||||
// Actions cell renderer.
|
// Actions cell renderer.
|
||||||
const ActionsCellRenderer = ({
|
const ActionsCellRenderer = ({
|
||||||
row: { index },
|
row: { index },
|
||||||
@@ -70,6 +72,7 @@ const NoteCellRenderer = (chainedComponent) => (props) => {
|
|||||||
return chainedComponent(props);
|
return chainedComponent(props);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Make journal entries table component.
|
* Make journal entries table component.
|
||||||
*/
|
*/
|
||||||
@@ -223,5 +226,5 @@ function MakeJournalEntriesTable({
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default compose(
|
export default compose(
|
||||||
AccountsConnect,
|
withAccounts,
|
||||||
)(MakeJournalEntriesTable);
|
)(MakeJournalEntriesTable);
|
||||||
@@ -18,13 +18,19 @@ import DashboardActionsBar from 'components/Dashboard/DashboardActionsBar';
|
|||||||
import DialogConnect from 'connectors/Dialog.connector';
|
import DialogConnect from 'connectors/Dialog.connector';
|
||||||
import { compose } from 'utils';
|
import { compose } from 'utils';
|
||||||
import FilterDropdown from 'components/FilterDropdown';
|
import FilterDropdown from 'components/FilterDropdown';
|
||||||
import ManualJournalsConnect from 'connectors/ManualJournals.connect';
|
|
||||||
import ResourceConnect from 'connectors/Resource.connector';
|
import withResourceDetail from 'containers/Resources/withResourceDetails';
|
||||||
|
import withManualJournals from 'containers/Accounting/withManualJournals';
|
||||||
|
import withManualJournalsActions from 'containers/Accounting/withManualJournalsActions';
|
||||||
|
|
||||||
|
|
||||||
function ManualJournalActionsBar({
|
function ManualJournalActionsBar({
|
||||||
views,
|
resourceName = 'manual_journal',
|
||||||
getResourceFields,
|
resourceFields,
|
||||||
|
|
||||||
|
manualJournalsViews,
|
||||||
addManualJournalsTableQueries,
|
addManualJournalsTableQueries,
|
||||||
|
|
||||||
onFilterChanged,
|
onFilterChanged,
|
||||||
selectedRows,
|
selectedRows,
|
||||||
onBulkDelete
|
onBulkDelete
|
||||||
@@ -32,8 +38,7 @@ function ManualJournalActionsBar({
|
|||||||
const { path } = useRouteMatch();
|
const { path } = useRouteMatch();
|
||||||
const history = useHistory();
|
const history = useHistory();
|
||||||
|
|
||||||
const manualJournalFields = getResourceFields('manual_journals');
|
const viewsMenuItems = manualJournalsViews.map(view => {
|
||||||
const viewsMenuItems = views.map(view => {
|
|
||||||
return (
|
return (
|
||||||
<MenuItem href={`${path}/${view.id}/custom_view`} text={view.name} />
|
<MenuItem href={`${path}/${view.id}/custom_view`} text={view.name} />
|
||||||
);
|
);
|
||||||
@@ -44,7 +49,7 @@ function ManualJournalActionsBar({
|
|||||||
}, [history]);
|
}, [history]);
|
||||||
|
|
||||||
const filterDropdown = FilterDropdown({
|
const filterDropdown = FilterDropdown({
|
||||||
fields: manualJournalFields,
|
fields: resourceFields,
|
||||||
onFilterChange: filterConditions => {
|
onFilterChange: filterConditions => {
|
||||||
addManualJournalsTableQueries({
|
addManualJournalsTableQueries({
|
||||||
filter_roles: filterConditions || ''
|
filter_roles: filterConditions || ''
|
||||||
@@ -120,6 +125,7 @@ function ManualJournalActionsBar({
|
|||||||
|
|
||||||
export default compose(
|
export default compose(
|
||||||
DialogConnect,
|
DialogConnect,
|
||||||
ManualJournalsConnect,
|
withResourceDetail,
|
||||||
ResourceConnect
|
withManualJournals,
|
||||||
|
withManualJournalsActions,
|
||||||
)(ManualJournalActionsBar);
|
)(ManualJournalActionsBar);
|
||||||
@@ -12,21 +12,33 @@ import { useParams } from 'react-router-dom';
|
|||||||
import Icon from 'components/Icon';
|
import Icon from 'components/Icon';
|
||||||
import { compose } from 'utils';
|
import { compose } from 'utils';
|
||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
import ManualJournalsConnect from 'connectors/ManualJournals.connect';
|
|
||||||
|
import LoadingIndicator from 'components/LoadingIndicator';
|
||||||
import DialogConnect from 'connectors/Dialog.connector';
|
import DialogConnect from 'connectors/Dialog.connector';
|
||||||
import DashboardConnect from 'connectors/Dashboard.connector';
|
|
||||||
import ViewConnect from 'connectors/View.connector';
|
|
||||||
import { useUpdateEffect } from 'hooks';
|
import { useUpdateEffect } from 'hooks';
|
||||||
import DataTable from 'components/DataTable';
|
import DataTable from 'components/DataTable';
|
||||||
import Money from 'components/Money';
|
import Money from 'components/Money';
|
||||||
|
|
||||||
|
import withDashboardActions from 'containers/Dashboard/withDashboard';
|
||||||
|
import withViewDetails from 'containers/Views/withViewDetails';
|
||||||
|
import withManualJournals from 'containers/Accounting/withManualJournals';
|
||||||
|
import withManualJournalsActions from 'containers/Accounting/withManualJournalsActions';
|
||||||
|
|
||||||
|
|
||||||
function ManualJournalsDataTable({
|
function ManualJournalsDataTable({
|
||||||
|
loading,
|
||||||
|
|
||||||
manualJournals,
|
manualJournals,
|
||||||
|
manualJournalsLoading,
|
||||||
|
|
||||||
changeCurrentView,
|
changeCurrentView,
|
||||||
changePageSubtitle,
|
changePageSubtitle,
|
||||||
getViewItem,
|
|
||||||
|
viewId,
|
||||||
|
viewMeta,
|
||||||
setTopbarEditView,
|
setTopbarEditView,
|
||||||
manualJournalsLoading,
|
|
||||||
onFetchData,
|
onFetchData,
|
||||||
onEditJournal,
|
onEditJournal,
|
||||||
onDeleteJournal,
|
onDeleteJournal,
|
||||||
@@ -43,8 +55,6 @@ function ManualJournalsDataTable({
|
|||||||
}, [manualJournalsLoading, setInitialMount]);
|
}, [manualJournalsLoading, setInitialMount]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const viewMeta = getViewItem(customViewId);
|
|
||||||
|
|
||||||
if (customViewId) {
|
if (customViewId) {
|
||||||
changeCurrentView(customViewId);
|
changeCurrentView(customViewId);
|
||||||
setTopbarEditView(customViewId);
|
setTopbarEditView(customViewId);
|
||||||
@@ -55,7 +65,7 @@ function ManualJournalsDataTable({
|
|||||||
changeCurrentView,
|
changeCurrentView,
|
||||||
changePageSubtitle,
|
changePageSubtitle,
|
||||||
setTopbarEditView,
|
setTopbarEditView,
|
||||||
getViewItem,
|
viewMeta,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const handlePublishJournal = useCallback((journal) => () => {
|
const handlePublishJournal = useCallback((journal) => () => {
|
||||||
@@ -172,6 +182,7 @@ function ManualJournalsDataTable({
|
|||||||
}, [onSelectedRowsChange]);
|
}, [onSelectedRowsChange]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
<LoadingIndicator loading={loading} mount={false}>
|
||||||
<DataTable
|
<DataTable
|
||||||
columns={columns}
|
columns={columns}
|
||||||
data={manualJournals}
|
data={manualJournals}
|
||||||
@@ -182,12 +193,15 @@ function ManualJournalsDataTable({
|
|||||||
loading={manualJournalsLoading && !initialMount}
|
loading={manualJournalsLoading && !initialMount}
|
||||||
onSelectedRowsChange={handleSelectedRowsChange}
|
onSelectedRowsChange={handleSelectedRowsChange}
|
||||||
/>
|
/>
|
||||||
|
</LoadingIndicator>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default compose(
|
export default compose(
|
||||||
DialogConnect,
|
DialogConnect,
|
||||||
DashboardConnect,
|
withDashboardActions,
|
||||||
ViewConnect,
|
// withViewsActions,
|
||||||
ManualJournalsConnect,
|
withManualJournalsActions,
|
||||||
|
withManualJournals,
|
||||||
|
withViewDetails,
|
||||||
)(ManualJournalsDataTable);
|
)(ManualJournalsDataTable);
|
||||||
@@ -1,45 +1,49 @@
|
|||||||
import React, { useEffect, useState, useCallback } from 'react';
|
import React, { useEffect, useState, useCallback } from 'react';
|
||||||
import { Route, Switch, useHistory } from 'react-router-dom';
|
import { Route, Switch, useHistory } from 'react-router-dom';
|
||||||
import useAsync from 'hooks/async';
|
import { useQuery } from 'react-query';
|
||||||
import { Alert, Intent } from '@blueprintjs/core';
|
import { Alert, Intent } from '@blueprintjs/core';
|
||||||
import AppToaster from 'components/AppToaster';
|
import AppToaster from 'components/AppToaster';
|
||||||
|
|
||||||
import DashboardPageContent from 'components/Dashboard/DashboardPageContent';
|
import DashboardPageContent from 'components/Dashboard/DashboardPageContent';
|
||||||
import DashboardInsider from 'components/Dashboard/DashboardInsider';
|
import DashboardInsider from 'components/Dashboard/DashboardInsider';
|
||||||
import ManualJournalsViewTabs from 'components/JournalEntry/ManualJournalsViewTabs';
|
|
||||||
import ManualJournalsDataTable from 'components/JournalEntry/ManualJournalsDataTable';
|
import ManualJournalsViewTabs from 'containers/Accounting/ManualJournalsViewTabs';
|
||||||
import ManualJournalsActionsBar from 'components/JournalEntry/ManualJournalActionsBar';
|
import ManualJournalsDataTable from 'containers/Accounting/ManualJournalsDataTable';
|
||||||
import ManualJournalsConnect from 'connectors/ManualJournals.connect';
|
import ManualJournalsActionsBar from 'containers/Accounting/ManualJournalActionsBar';
|
||||||
import DashboardConnect from 'connectors/Dashboard.connector';
|
|
||||||
import CustomViewConnect from 'connectors/CustomView.connector';
|
import withDashboardActions from 'containers/Dashboard/withDashboard';
|
||||||
import ResourceConnect from 'connectors/Resource.connector';
|
import withManualJournalsActions from 'containers/Accounting/withManualJournalsActions';
|
||||||
|
import withViewsActions from 'containers/Views/withViewsActions';
|
||||||
|
|
||||||
import { compose } from 'utils';
|
import { compose } from 'utils';
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Manual journals table.
|
||||||
|
*/
|
||||||
function ManualJournalsTable({
|
function ManualJournalsTable({
|
||||||
changePageTitle,
|
changePageTitle,
|
||||||
|
|
||||||
fetchResourceViews,
|
requestFetchResourceViews,
|
||||||
fetchManualJournalsTable,
|
|
||||||
|
|
||||||
|
requestFetchManualJournalsTable,
|
||||||
requestDeleteManualJournal,
|
requestDeleteManualJournal,
|
||||||
requestPublishManualJournal,
|
requestPublishManualJournal,
|
||||||
requestDeleteBulkManualJournals,
|
requestDeleteBulkManualJournals,
|
||||||
|
|
||||||
addManualJournalsTableQueries
|
addManualJournalsTableQueries,
|
||||||
}) {
|
}) {
|
||||||
const history = useHistory();
|
const history = useHistory();
|
||||||
const [deleteManualJournal, setDeleteManualJournal] = useState(false);
|
const [deleteManualJournal, setDeleteManualJournal] = useState(false);
|
||||||
const [selectedRows, setSelectedRows] = useState([]);
|
const [selectedRows, setSelectedRows] = useState([]);
|
||||||
const [bulkDelete, setBulkDelete] = useState(false);
|
const [bulkDelete, setBulkDelete] = useState(false);
|
||||||
|
|
||||||
const fetchHook = useAsync(async () => {
|
const fetchViews = useQuery('journals-resource-views', () => {
|
||||||
await Promise.all([
|
return requestFetchResourceViews('manual_journals');
|
||||||
fetchResourceViews('manual_journals'),
|
|
||||||
]);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const fetchManualJournalsHook = useAsync(async () => {
|
const fetchManualJournals = useQuery('manual-journals-table', () =>
|
||||||
return fetchManualJournalsTable();
|
requestFetchManualJournalsTable());
|
||||||
});
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
changePageTitle('Manual Journals');
|
changePageTitle('Manual Journals');
|
||||||
@@ -89,13 +93,13 @@ function ManualJournalsTable({
|
|||||||
|
|
||||||
// Handle filter change to re-fetch data-table.
|
// Handle filter change to re-fetch data-table.
|
||||||
const handleFilterChanged = useCallback(() => {
|
const handleFilterChanged = useCallback(() => {
|
||||||
fetchManualJournalsHook.execute();
|
fetchManualJournals.refetch();
|
||||||
}, [fetchManualJournalsHook]);
|
}, [fetchManualJournals]);
|
||||||
|
|
||||||
// Handle view change to re-fetch data table.
|
// Handle view change to re-fetch data table.
|
||||||
const handleViewChanged = useCallback(() => {
|
const handleViewChanged = useCallback(() => {
|
||||||
fetchManualJournalsHook.execute();
|
fetchManualJournals.refetch();
|
||||||
}, [fetchManualJournalsHook]);
|
}, [fetchManualJournals]);
|
||||||
|
|
||||||
// Handle fetch data of manual jouranls datatable.
|
// Handle fetch data of manual jouranls datatable.
|
||||||
const handleFetchData = useCallback(({ pageIndex, pageSize, sortBy }) => {
|
const handleFetchData = useCallback(({ pageIndex, pageSize, sortBy }) => {
|
||||||
@@ -105,9 +109,7 @@ function ManualJournalsTable({
|
|||||||
sort_order: sortBy[0].desc ? 'desc' : 'asc',
|
sort_order: sortBy[0].desc ? 'desc' : 'asc',
|
||||||
} : {},
|
} : {},
|
||||||
});
|
});
|
||||||
fetchManualJournalsHook.execute();
|
|
||||||
}, [
|
}, [
|
||||||
fetchManualJournalsHook,
|
|
||||||
addManualJournalsTableQueries,
|
addManualJournalsTableQueries,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
@@ -123,7 +125,9 @@ function ManualJournalsTable({
|
|||||||
}, [setSelectedRows]);
|
}, [setSelectedRows]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<DashboardInsider loading={fetchHook.pending} name={'manual-journals'}>
|
<DashboardInsider
|
||||||
|
loading={fetchViews.isFetching || fetchManualJournals.isFetching}
|
||||||
|
name={'manual-journals'}>
|
||||||
<ManualJournalsActionsBar
|
<ManualJournalsActionsBar
|
||||||
onBulkDelete={handleBulkDelete}
|
onBulkDelete={handleBulkDelete}
|
||||||
selectedRows={selectedRows}
|
selectedRows={selectedRows}
|
||||||
@@ -184,8 +188,7 @@ function ManualJournalsTable({
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default compose(
|
export default compose(
|
||||||
ManualJournalsConnect,
|
withDashboardActions,
|
||||||
CustomViewConnect,
|
withManualJournalsActions,
|
||||||
ResourceConnect,
|
withViewsActions,
|
||||||
DashboardConnect
|
|
||||||
)(ManualJournalsTable);
|
)(ManualJournalsTable);
|
||||||
10
client/src/containers/Accounting/withJournalDetail.js
Normal file
10
client/src/containers/Accounting/withJournalDetail.js
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
import {connect} from 'react-redux';
|
||||||
|
import {
|
||||||
|
getManualJournal,
|
||||||
|
} from 'store/manualJournals/manualJournals.reducers';
|
||||||
|
|
||||||
|
export const mapStateToProps = (state, props) => ({
|
||||||
|
manualJournal: getManualJournal(state, props.manualJournalId),
|
||||||
|
});
|
||||||
|
|
||||||
|
export default connect(mapStateToProps);
|
||||||
14
client/src/containers/Accounting/withJournalsActions.js
Normal file
14
client/src/containers/Accounting/withJournalsActions.js
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
import {connect} from 'react-redux';
|
||||||
|
import {
|
||||||
|
makeJournalEntries,
|
||||||
|
fetchManualJournal,
|
||||||
|
editManualJournal,
|
||||||
|
} from 'store/manualJournals/manualJournals.actions';
|
||||||
|
|
||||||
|
export const mapDispatchToProps = (dispatch) => ({
|
||||||
|
requestMakeJournalEntries: (form) => dispatch(makeJournalEntries({ form })),
|
||||||
|
requestFetchManualJournal: (id) => dispatch(fetchManualJournal({ id })),
|
||||||
|
requestEditManualJournal: (id, form) => dispatch(editManualJournal({ id, form }))
|
||||||
|
});
|
||||||
|
|
||||||
|
export default connect(null, mapDispatchToProps);
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
import { connect } from 'react-redux';
|
||||||
|
import t from 'store/types';
|
||||||
|
import { getManualJournal } from 'store/manualJournals/manualJournals.reducers';
|
||||||
|
|
||||||
|
const mapStateToProps = (state, props) => ({
|
||||||
|
manualJournal: getManualJournal(state, props.manualJournalId),
|
||||||
|
});
|
||||||
|
|
||||||
|
export default connect(mapStateToProps);
|
||||||
16
client/src/containers/Accounting/withManualJournals.js
Normal file
16
client/src/containers/Accounting/withManualJournals.js
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
import { connect } from 'react-redux';
|
||||||
|
import { getResourceViews } from 'store/customViews/customViews.selectors';
|
||||||
|
import {
|
||||||
|
getManualJournalsItems,
|
||||||
|
} from 'store/manualJournals/manualJournals.selectors'
|
||||||
|
|
||||||
|
|
||||||
|
const mapStateToProps = (state, props) => ({
|
||||||
|
manualJournals: getManualJournalsItems(state, state.manualJournals.currentViewId),
|
||||||
|
manualJournalsViews: getResourceViews(state, 'manual_journals'),
|
||||||
|
manualJournalsItems: state.manualJournals.items,
|
||||||
|
manualJournalsTableQuery: state.manualJournals.tableQuery,
|
||||||
|
manualJournalsLoading: state.manualJournals.loading,
|
||||||
|
});
|
||||||
|
|
||||||
|
export default connect(mapStateToProps);
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
import { connect } from 'react-redux';
|
||||||
|
import t from 'store/types';
|
||||||
|
import {
|
||||||
|
deleteManualJournal,
|
||||||
|
fetchManualJournalsTable,
|
||||||
|
publishManualJournal,
|
||||||
|
deleteBulkManualJournals,
|
||||||
|
fetchManualJournal,
|
||||||
|
} from 'store/manualJournals/manualJournals.actions';
|
||||||
|
|
||||||
|
const mapActionsToProps = (dispatch) => ({
|
||||||
|
requestDeleteManualJournal: (id) => dispatch(deleteManualJournal({ id })),
|
||||||
|
requestFetchManualJournalsTable: (query = {}) => dispatch(fetchManualJournalsTable({ query: { ...query } })),
|
||||||
|
requestFetchManualJournal: (id) => dispatch(fetchManualJournal({ id })),
|
||||||
|
requestPublishManualJournal: (id) => dispatch(publishManualJournal({ id })),
|
||||||
|
requestDeleteBulkManualJournals: (ids) => dispatch(deleteBulkManualJournals({ ids })),
|
||||||
|
|
||||||
|
changeCurrentView: (id) => dispatch({
|
||||||
|
type: t.MANUAL_JOURNALS_SET_CURRENT_VIEW,
|
||||||
|
currentViewId: parseInt(id, 10),
|
||||||
|
}),
|
||||||
|
addManualJournalsTableQueries: (queries) => dispatch({
|
||||||
|
type: t.MANUAL_JOURNALS_TABLE_QUERIES_ADD,
|
||||||
|
queries,
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
export default connect(null, mapActionsToProps);
|
||||||
@@ -13,45 +13,50 @@ import {
|
|||||||
Intent,
|
Intent,
|
||||||
} from '@blueprintjs/core';
|
} from '@blueprintjs/core';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
|
import { useHistory } from 'react-router-dom';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import { useRouteMatch, useHistory } from 'react-router-dom';
|
import { If } from 'components';
|
||||||
|
|
||||||
import DashboardActionsBar from 'components/Dashboard/DashboardActionsBar';
|
import DashboardActionsBar from 'components/Dashboard/DashboardActionsBar';
|
||||||
import DialogConnect from 'connectors/Dialog.connector';
|
import DialogConnect from 'connectors/Dialog.connector';
|
||||||
import AccountsConnect from 'connectors/Accounts.connector';
|
|
||||||
import {compose} from 'utils';
|
|
||||||
import FilterDropdown from 'components/FilterDropdown';
|
import FilterDropdown from 'components/FilterDropdown';
|
||||||
import ResourceConnect from 'connectors/Resource.connector';
|
|
||||||
|
import withResourceDetail from 'containers/Resources/withResourceDetails';
|
||||||
|
import withAccountsTableActions from 'containers/Accounts/withAccountsTableActions';
|
||||||
|
import withAccounts from 'containers/Accounts/withAccounts';
|
||||||
|
|
||||||
|
import {compose} from 'utils';
|
||||||
|
|
||||||
|
|
||||||
function AccountsActionsBar({
|
function AccountsActionsBar({
|
||||||
openDialog,
|
openDialog,
|
||||||
views,
|
accountsViews,
|
||||||
selectedRows = [],
|
|
||||||
getResourceFields,
|
resourceFields,
|
||||||
addAccountsTableQueries,
|
addAccountsTableQueries,
|
||||||
|
|
||||||
|
selectedRows = [],
|
||||||
onFilterChanged,
|
onFilterChanged,
|
||||||
onBulkDelete,
|
onBulkDelete,
|
||||||
onBulkArchive,
|
onBulkArchive,
|
||||||
}) {
|
}) {
|
||||||
const history = useHistory();
|
const history = useHistory();
|
||||||
|
|
||||||
const onClickNewAccount = () => { openDialog('account-form', {}); };
|
|
||||||
|
|
||||||
const accountsFields = getResourceFields('accounts');
|
|
||||||
const [filterCount, setFilterCount] = useState(0);
|
const [filterCount, setFilterCount] = useState(0);
|
||||||
|
|
||||||
|
const onClickNewAccount = () => { openDialog('account-form', {}); };
|
||||||
const onClickViewItem = (view) => {
|
const onClickViewItem = (view) => {
|
||||||
history.push(view
|
history.push(view
|
||||||
? `/dashboard/accounts/${view.id}/custom_view` :
|
? `/dashboard/accounts/${view.id}/custom_view` : '/dashboard/accounts');
|
||||||
'/dashboard/accounts');
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const viewsMenuItems = views.map((view) => {
|
const viewsMenuItems = accountsViews.map((view) => {
|
||||||
return (<MenuItem onClick={() => onClickViewItem(view)} text={view.name} />);
|
return (<MenuItem onClick={() => onClickViewItem(view)} text={view.name} />);
|
||||||
});
|
});
|
||||||
const hasSelectedRows = useMemo(() => selectedRows.length > 0, [selectedRows]);
|
const hasSelectedRows = useMemo(() => selectedRows.length > 0, [selectedRows]);
|
||||||
|
|
||||||
const filterDropdown = FilterDropdown({
|
const filterDropdown = FilterDropdown({
|
||||||
fields: accountsFields,
|
fields: resourceFields,
|
||||||
onFilterChange: (filterConditions) => {
|
onFilterChange: (filterConditions) => {
|
||||||
setFilterCount(filterConditions.length || 0);
|
setFilterCount(filterConditions.length || 0);
|
||||||
addAccountsTableQueries({
|
addAccountsTableQueries({
|
||||||
@@ -106,15 +111,13 @@ function AccountsActionsBar({
|
|||||||
icon={ <Icon icon="filter" /> }/>
|
icon={ <Icon icon="filter" /> }/>
|
||||||
</Popover>
|
</Popover>
|
||||||
|
|
||||||
{hasSelectedRows && (
|
<If condition={hasSelectedRows}>
|
||||||
<Button
|
<Button
|
||||||
className={Classes.MINIMAL}
|
className={Classes.MINIMAL}
|
||||||
icon={<Icon icon='archive' iconSize={15} />}
|
icon={<Icon icon='archive' iconSize={15} />}
|
||||||
text='Archive'
|
text='Archive'
|
||||||
onClick={handleBulkArchive}
|
onClick={handleBulkArchive}
|
||||||
/>
|
/>
|
||||||
)}
|
|
||||||
{hasSelectedRows && (
|
|
||||||
<Button
|
<Button
|
||||||
className={Classes.MINIMAL}
|
className={Classes.MINIMAL}
|
||||||
icon={<Icon icon='trash' iconSize={15} />}
|
icon={<Icon icon='trash' iconSize={15} />}
|
||||||
@@ -122,7 +125,8 @@ function AccountsActionsBar({
|
|||||||
intent={Intent.DANGER}
|
intent={Intent.DANGER}
|
||||||
onClick={handleBulkDelete}
|
onClick={handleBulkDelete}
|
||||||
/>
|
/>
|
||||||
)}
|
</If>
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
className={Classes.MINIMAL}
|
className={Classes.MINIMAL}
|
||||||
icon={<Icon icon='file-import' />}
|
icon={<Icon icon='file-import' />}
|
||||||
@@ -138,8 +142,16 @@ function AccountsActionsBar({
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const mapStateToProps = (state, props) => ({
|
||||||
|
resourceName: 'accounts',
|
||||||
|
});
|
||||||
|
|
||||||
|
const withAccountsActionsBar = connect(mapStateToProps);
|
||||||
|
|
||||||
export default compose(
|
export default compose(
|
||||||
|
withAccountsActionsBar,
|
||||||
DialogConnect,
|
DialogConnect,
|
||||||
AccountsConnect,
|
withAccounts,
|
||||||
ResourceConnect,
|
withResourceDetail,
|
||||||
|
withAccountsTableActions,
|
||||||
)(AccountsActionsBar);
|
)(AccountsActionsBar);
|
||||||
@@ -3,29 +3,49 @@ import {
|
|||||||
Route,
|
Route,
|
||||||
Switch,
|
Switch,
|
||||||
} from 'react-router-dom';
|
} from 'react-router-dom';
|
||||||
import useAsync from 'hooks/async';
|
|
||||||
import { Alert, Intent } from '@blueprintjs/core';
|
import { Alert, Intent } from '@blueprintjs/core';
|
||||||
|
import { useQuery } from 'react-query'
|
||||||
|
|
||||||
import AppToaster from 'components/AppToaster';
|
import AppToaster from 'components/AppToaster';
|
||||||
|
|
||||||
import DashboardPageContent from 'components/Dashboard/DashboardPageContent';
|
import DashboardPageContent from 'components/Dashboard/DashboardPageContent';
|
||||||
import DashboardInsider from 'components/Dashboard/DashboardInsider';
|
import DashboardInsider from 'components/Dashboard/DashboardInsider';
|
||||||
import AccountsViewsTabs from 'components/Accounts/AccountsViewsTabs';
|
import AccountsViewsTabs from 'containers/Accounts/AccountsViewsTabs';
|
||||||
import AccountsDataTable from 'components/Accounts/AccountsDataTable';
|
import AccountsDataTable from 'containers/Accounts/AccountsDataTable';
|
||||||
import DashboardActionsBar from 'components/Accounts/AccountsActionsBar';
|
import DashboardActionsBar from 'containers/Accounts/AccountsActionsBar';
|
||||||
import AccountsConnect from 'connectors/Accounts.connector';
|
|
||||||
import DashboardConnect from 'connectors/Dashboard.connector';
|
import withDashboardActions from 'containers/Dashboard/withDashboard';
|
||||||
import CustomViewConnect from 'connectors/CustomView.connector';
|
import withResourceActions from 'containers/Resources/withResourcesActions';
|
||||||
import ResourceConnect from 'connectors/Resource.connector';
|
import withAccountsActions from 'containers/Accounts/withAccountsActions';
|
||||||
|
import withAccountsTableActions from 'containers/Accounts/withAccountsTableActions';
|
||||||
|
import withViewsActions from 'containers/Views/withViewsActions';
|
||||||
|
import withAccounts from 'containers/Accounts/withAccounts';
|
||||||
|
|
||||||
import { compose } from 'utils';
|
import { compose } from 'utils';
|
||||||
|
|
||||||
|
|
||||||
function AccountsChart({
|
function AccountsChart({
|
||||||
|
|
||||||
|
// #withDashboard
|
||||||
changePageTitle,
|
changePageTitle,
|
||||||
|
|
||||||
|
// #withAccountsActions
|
||||||
requestDeleteAccount,
|
requestDeleteAccount,
|
||||||
requestInactiveAccount,
|
requestInactiveAccount,
|
||||||
fetchResourceViews,
|
|
||||||
fetchResourceFields,
|
// #withViewsActions
|
||||||
|
requestFetchResourceViews,
|
||||||
|
|
||||||
|
// #withResourceActions
|
||||||
|
requestFetchResourceFields,
|
||||||
|
|
||||||
|
// #withAccountsTableActions
|
||||||
requestFetchAccountsTable,
|
requestFetchAccountsTable,
|
||||||
addAccountsTableQueries,
|
|
||||||
requestDeleteBulkAccounts,
|
requestDeleteBulkAccounts,
|
||||||
|
addAccountsTableQueries,
|
||||||
|
|
||||||
|
// #withAccounts
|
||||||
|
accountsTableQuery,
|
||||||
}) {
|
}) {
|
||||||
const [deleteAccount, setDeleteAccount] = useState(false);
|
const [deleteAccount, setDeleteAccount] = useState(false);
|
||||||
const [inactiveAccount, setInactiveAccount] = useState(false);
|
const [inactiveAccount, setInactiveAccount] = useState(false);
|
||||||
@@ -35,19 +55,16 @@ function AccountsChart({
|
|||||||
const [tableLoading, setTableLoading] = useState(false);
|
const [tableLoading, setTableLoading] = useState(false);
|
||||||
|
|
||||||
// Fetch accounts resource views and fields.
|
// Fetch accounts resource views and fields.
|
||||||
const fetchHook = useAsync(async () => {
|
const fetchHook = useQuery('resource-accounts', () => {
|
||||||
await Promise.all([
|
return Promise.all([
|
||||||
fetchResourceViews('accounts'),
|
requestFetchResourceViews('accounts'),
|
||||||
fetchResourceFields('accounts'),
|
requestFetchResourceFields('accounts'),
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Fetch accounts list according to the given custom view id.
|
// Fetch accounts list according to the given custom view id.
|
||||||
const fetchAccountsHook = useAsync(async () => {
|
const fetchAccountsHook = useQuery(['accounts-table', accountsTableQuery],
|
||||||
await Promise.all([
|
() => requestFetchAccountsTable());
|
||||||
requestFetchAccountsTable(),
|
|
||||||
]);
|
|
||||||
}, false);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
changePageTitle('Chart of Accounts');
|
changePageTitle('Chart of Accounts');
|
||||||
@@ -136,18 +153,20 @@ function AccountsChart({
|
|||||||
|
|
||||||
// Refetches accounts data table when current custom view changed.
|
// Refetches accounts data table when current custom view changed.
|
||||||
const handleFilterChanged = useCallback(() => {
|
const handleFilterChanged = useCallback(() => {
|
||||||
fetchAccountsHook.execute();
|
fetchAccountsHook.refetch();
|
||||||
}, [fetchAccountsHook]);
|
}, [fetchAccountsHook]);
|
||||||
|
|
||||||
// Refetch accounts data table when current custom view changed.
|
// Refetch accounts data table when current custom view changed.
|
||||||
const handleViewChanged = useCallback(() => {
|
const handleViewChanged = useCallback(async () => {
|
||||||
setTableLoading(true);
|
setTableLoading(true);
|
||||||
|
|
||||||
fetchAccountsHook.execute().finally(() => {
|
|
||||||
setTableLoading(false);
|
|
||||||
});
|
|
||||||
}, [fetchAccountsHook]);
|
}, [fetchAccountsHook]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (tableLoading && !fetchAccountsHook.isFetching) {
|
||||||
|
setTableLoading(false);
|
||||||
|
}
|
||||||
|
}, [tableLoading, fetchAccountsHook.isFetching]);
|
||||||
|
|
||||||
// Handle fetch data of accounts datatable.
|
// Handle fetch data of accounts datatable.
|
||||||
const handleFetchData = useCallback(({ pageIndex, pageSize, sortBy }) => {
|
const handleFetchData = useCallback(({ pageIndex, pageSize, sortBy }) => {
|
||||||
addAccountsTableQueries({
|
addAccountsTableQueries({
|
||||||
@@ -156,11 +175,13 @@ function AccountsChart({
|
|||||||
sort_order: sortBy[0].desc ? 'desc' : 'asc',
|
sort_order: sortBy[0].desc ? 'desc' : 'asc',
|
||||||
} : {},
|
} : {},
|
||||||
});
|
});
|
||||||
fetchAccountsHook.execute();
|
fetchAccountsHook.refetch();
|
||||||
}, [fetchAccountsHook, addAccountsTableQueries]);
|
}, [fetchAccountsHook, addAccountsTableQueries]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<DashboardInsider loading={fetchHook.pending} name={'accounts-chart'}>
|
<DashboardInsider
|
||||||
|
loading={fetchHook.isFetching}
|
||||||
|
name={'accounts-chart'}>
|
||||||
<DashboardActionsBar
|
<DashboardActionsBar
|
||||||
selectedRows={selectedRows}
|
selectedRows={selectedRows}
|
||||||
onFilterChanged={handleFilterChanged}
|
onFilterChanged={handleFilterChanged}
|
||||||
@@ -236,8 +257,11 @@ function AccountsChart({
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default compose(
|
export default compose(
|
||||||
AccountsConnect,
|
withAccountsActions,
|
||||||
CustomViewConnect,
|
withAccountsTableActions,
|
||||||
ResourceConnect,
|
withViewsActions,
|
||||||
DashboardConnect,
|
withResourceActions,
|
||||||
|
withDashboardActions,
|
||||||
|
|
||||||
|
withAccounts,
|
||||||
)(AccountsChart);
|
)(AccountsChart);
|
||||||
@@ -9,32 +9,34 @@ import {
|
|||||||
Classes,
|
Classes,
|
||||||
Tooltip,
|
Tooltip,
|
||||||
} from '@blueprintjs/core';
|
} from '@blueprintjs/core';
|
||||||
import { useParams } from 'react-router-dom';
|
|
||||||
import Icon from 'components/Icon';
|
import Icon from 'components/Icon';
|
||||||
import { compose } from 'utils';
|
import { compose } from 'utils';
|
||||||
import AccountsConnect from 'connectors/Accounts.connector';
|
|
||||||
import DialogConnect from 'connectors/Dialog.connector';
|
import DialogConnect from 'connectors/Dialog.connector';
|
||||||
import DashboardConnect from 'connectors/Dashboard.connector';
|
|
||||||
import ViewConnect from 'connectors/View.connector';
|
|
||||||
import LoadingIndicator from 'components/LoadingIndicator';
|
import LoadingIndicator from 'components/LoadingIndicator';
|
||||||
import DataTable from 'components/DataTable';
|
import DataTable from 'components/DataTable';
|
||||||
import Money from 'components/Money';
|
import Money from 'components/Money';
|
||||||
import { useUpdateEffect } from 'hooks';
|
import { useUpdateEffect } from 'hooks';
|
||||||
|
|
||||||
|
import withDashboardActions from 'containers/Dashboard/withDashboard';
|
||||||
|
import withAccountsActions from 'containers/Accounts/withAccountsActions';
|
||||||
|
import withAccounts from 'containers/Accounts/withAccounts';
|
||||||
|
|
||||||
|
import {If} from 'components';
|
||||||
|
|
||||||
function AccountsDataTable({
|
function AccountsDataTable({
|
||||||
loading,
|
// # withAccounts
|
||||||
accounts,
|
accounts,
|
||||||
|
accountsLoading,
|
||||||
|
|
||||||
|
// # withDialog.
|
||||||
|
openDialog,
|
||||||
|
|
||||||
|
// own properties
|
||||||
|
loading,
|
||||||
|
onFetchData,
|
||||||
|
onSelectedRowsChange,
|
||||||
onDeleteAccount,
|
onDeleteAccount,
|
||||||
onInactiveAccount,
|
onInactiveAccount,
|
||||||
openDialog,
|
|
||||||
changeCurrentView,
|
|
||||||
changePageSubtitle,
|
|
||||||
getViewItem,
|
|
||||||
setTopbarEditView,
|
|
||||||
accountsLoading,
|
|
||||||
onFetchData,
|
|
||||||
onSelectedRowsChange
|
|
||||||
}) {
|
}) {
|
||||||
const [initialMount, setInitialMount] = useState(false);
|
const [initialMount, setInitialMount] = useState(false);
|
||||||
|
|
||||||
@@ -147,9 +149,9 @@ function AccountsDataTable({
|
|||||||
], [actionMenuList]);
|
], [actionMenuList]);
|
||||||
|
|
||||||
const selectionColumn = useMemo(() => ({
|
const selectionColumn = useMemo(() => ({
|
||||||
minWidth: 42,
|
minWidth: 50,
|
||||||
width: 42,
|
width: 50,
|
||||||
maxWidth: 42,
|
maxWidth: 50,
|
||||||
}), [])
|
}), [])
|
||||||
|
|
||||||
const handleDatatableFetchData = useCallback((...params) => {
|
const handleDatatableFetchData = useCallback((...params) => {
|
||||||
@@ -162,7 +164,11 @@ function AccountsDataTable({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<LoadingIndicator loading={loading} mount={false}>
|
<LoadingIndicator loading={loading} mount={false}>
|
||||||
|
<If condition={loading}>
|
||||||
|
asdasdsadsa
|
||||||
|
</If>
|
||||||
<DataTable
|
<DataTable
|
||||||
|
noInitialFetch={true}
|
||||||
columns={columns}
|
columns={columns}
|
||||||
data={accounts}
|
data={accounts}
|
||||||
onFetchData={handleDatatableFetchData}
|
onFetchData={handleDatatableFetchData}
|
||||||
@@ -178,8 +184,8 @@ function AccountsDataTable({
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default compose(
|
export default compose(
|
||||||
AccountsConnect,
|
|
||||||
DialogConnect,
|
DialogConnect,
|
||||||
DashboardConnect,
|
withDashboardActions,
|
||||||
ViewConnect
|
withAccountsActions,
|
||||||
|
withAccounts,
|
||||||
)(AccountsDataTable);
|
)(AccountsDataTable);
|
||||||
@@ -9,38 +9,60 @@ import {
|
|||||||
Tab,
|
Tab,
|
||||||
Button
|
Button
|
||||||
} from '@blueprintjs/core';
|
} from '@blueprintjs/core';
|
||||||
import { useParams } from 'react-router-dom';
|
import { useParams, withRouter } from 'react-router-dom';
|
||||||
import Icon from 'components/Icon';
|
import Icon from 'components/Icon';
|
||||||
import { Link } from 'react-router-dom';
|
import { Link } from 'react-router-dom';
|
||||||
import { compose } from 'utils';
|
import { compose } from 'utils';
|
||||||
import AccountsConnect from 'connectors/Accounts.connector';
|
|
||||||
import DashboardConnect from 'connectors/Dashboard.connector';
|
|
||||||
import {useUpdateEffect} from 'hooks';
|
import {useUpdateEffect} from 'hooks';
|
||||||
import ViewConnect from 'connectors/View.connector';
|
|
||||||
|
import withDashboard from 'containers/Dashboard/withDashboard';
|
||||||
|
import withAccounts from 'containers/Accounts/withAccounts';
|
||||||
|
import withAccountsTableActions from 'containers/Accounts/withAccountsTableActions';
|
||||||
|
import withViewDetail from 'containers/Views/withViewDetails';
|
||||||
|
|
||||||
function AccountsViewsTabs({
|
function AccountsViewsTabs({
|
||||||
views,
|
// #withViewDetail
|
||||||
setTopbarEditView,
|
viewId,
|
||||||
customViewChanged,
|
viewItem,
|
||||||
|
|
||||||
|
// #withAccounts
|
||||||
|
accountsViews,
|
||||||
|
|
||||||
|
// #withAccountsTableActions
|
||||||
addAccountsTableQueries,
|
addAccountsTableQueries,
|
||||||
onViewChanged,
|
changeAccountsCurrentView,
|
||||||
getViewItem,
|
|
||||||
changeCurrentView,
|
// #withDashboard
|
||||||
|
setTopbarEditView,
|
||||||
changePageSubtitle,
|
changePageSubtitle,
|
||||||
|
|
||||||
|
// props
|
||||||
|
customViewChanged,
|
||||||
|
onViewChanged,
|
||||||
}) {
|
}) {
|
||||||
const history = useHistory();
|
const history = useHistory();
|
||||||
const { custom_view_id: customViewId = null } = useParams();
|
const { custom_view_id: customViewId = null } = useParams();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const viewMeta = getViewItem(customViewId);
|
changeAccountsCurrentView(customViewId || -1);
|
||||||
|
|
||||||
changeCurrentView(customViewId || -1);
|
|
||||||
setTopbarEditView(customViewId);
|
setTopbarEditView(customViewId);
|
||||||
changePageSubtitle((customViewId && viewMeta) ? viewMeta.name : '');
|
changePageSubtitle((customViewId && viewItem) ? viewItem.name : '');
|
||||||
|
|
||||||
|
addAccountsTableQueries({
|
||||||
|
custom_view_id: customViewId,
|
||||||
|
});
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
setTopbarEditView(null);
|
||||||
|
changePageSubtitle('');
|
||||||
|
changeAccountsCurrentView(null)
|
||||||
|
};
|
||||||
|
}, [customViewId]);
|
||||||
|
|
||||||
|
useUpdateEffect(() => {
|
||||||
|
onViewChanged && onViewChanged(customViewId);
|
||||||
}, [customViewId]);
|
}, [customViewId]);
|
||||||
|
|
||||||
// Clear page subtitle when unmount the page.
|
|
||||||
useEffect(() => () => { changePageSubtitle(''); }, []);
|
|
||||||
|
|
||||||
// Handle click a new view tab.
|
// Handle click a new view tab.
|
||||||
const handleClickNewView = () => {
|
const handleClickNewView = () => {
|
||||||
@@ -53,19 +75,7 @@ function AccountsViewsTabs({
|
|||||||
setTopbarEditView(customViewId);
|
setTopbarEditView(customViewId);
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
const tabs = accountsViews.map((view) => {
|
||||||
customViewChanged && customViewChanged(customViewId);
|
|
||||||
|
|
||||||
addAccountsTableQueries({
|
|
||||||
custom_view_id: customViewId,
|
|
||||||
});
|
|
||||||
}, [customViewId]);
|
|
||||||
|
|
||||||
useUpdateEffect(() => {
|
|
||||||
onViewChanged && onViewChanged(customViewId);
|
|
||||||
}, [customViewId]);
|
|
||||||
|
|
||||||
const tabs = views.map((view) => {
|
|
||||||
const baseUrl = '/dashboard/accounts';
|
const baseUrl = '/dashboard/accounts';
|
||||||
|
|
||||||
const link = (
|
const link = (
|
||||||
@@ -104,8 +114,19 @@ function AccountsViewsTabs({
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const mapStateToProps = (state, ownProps) => ({
|
||||||
|
|
||||||
|
// Mapping view id from matched route params.
|
||||||
|
viewId: ownProps.match.params.custom_view_id,
|
||||||
|
});
|
||||||
|
|
||||||
|
const withAccountsViewsTabs = connect(mapStateToProps);
|
||||||
|
|
||||||
export default compose(
|
export default compose(
|
||||||
AccountsConnect,
|
withRouter,
|
||||||
DashboardConnect,
|
withAccountsViewsTabs,
|
||||||
ViewConnect,
|
withDashboard,
|
||||||
|
withAccounts,
|
||||||
|
withAccountsTableActions,
|
||||||
|
withViewDetail
|
||||||
)(AccountsViewsTabs);
|
)(AccountsViewsTabs);
|
||||||
10
client/src/containers/Accounts/withAccountDetail.js
Normal file
10
client/src/containers/Accounts/withAccountDetail.js
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
import { connect } from 'react-redux';
|
||||||
|
import {
|
||||||
|
getItemById
|
||||||
|
} from 'store/selectors';
|
||||||
|
|
||||||
|
const mapStateToProps = (state, props) => ({
|
||||||
|
account: getItemById(state.accounts.items, props.accountId),
|
||||||
|
});
|
||||||
|
|
||||||
|
export default connect(mapStateToProps);
|
||||||
19
client/src/containers/Accounts/withAccounts.js
Normal file
19
client/src/containers/Accounts/withAccounts.js
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
import { connect } from 'react-redux';
|
||||||
|
import {
|
||||||
|
getAccountsItems,
|
||||||
|
} from 'store/accounts/accounts.selectors';
|
||||||
|
import {
|
||||||
|
getResourceViews,
|
||||||
|
} from 'store/customViews/customViews.selectors';
|
||||||
|
|
||||||
|
const mapStateToProps = (state, props) => ({
|
||||||
|
accountsViews: getResourceViews(state, 'accounts'),
|
||||||
|
accounts: getAccountsItems(state, state.accounts.currentViewId),
|
||||||
|
accountsTypes: state.accounts.accountsTypes,
|
||||||
|
|
||||||
|
accountsTableQuery: state.accounts.tableQuery,
|
||||||
|
accountsLoading: state.accounts.loading,
|
||||||
|
accountErrors: state.accounts.errors,
|
||||||
|
});
|
||||||
|
|
||||||
|
export default connect(mapStateToProps);
|
||||||
22
client/src/containers/Accounts/withAccountsActions.js
Normal file
22
client/src/containers/Accounts/withAccountsActions.js
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
import { connect } from 'react-redux';
|
||||||
|
import {
|
||||||
|
fetchAccountTypes,
|
||||||
|
fetchAccountsList,
|
||||||
|
deleteAccount,
|
||||||
|
inactiveAccount,
|
||||||
|
submitAccount,
|
||||||
|
fetchAccount,
|
||||||
|
deleteBulkAccounts,
|
||||||
|
} from 'store/accounts/accounts.actions';
|
||||||
|
|
||||||
|
const mapActionsToProps = (dispatch) => ({
|
||||||
|
requestFetchAccounts: (query) => dispatch(fetchAccountsList({ query })),
|
||||||
|
requestFetchAccountTypes: () => dispatch(fetchAccountTypes()),
|
||||||
|
requestSubmitAccount: ({ form }) => dispatch(submitAccount({ form })),
|
||||||
|
requestDeleteAccount: (id) => dispatch(deleteAccount({ id })),
|
||||||
|
requestInactiveAccount: (id) => dispatch(inactiveAccount({ id })),
|
||||||
|
requestFetchAccount: (id) => dispatch(fetchAccount({ id })),
|
||||||
|
requestDeleteBulkAccounts: (ids) => dispatch(deleteBulkAccounts({ ids })),
|
||||||
|
});
|
||||||
|
|
||||||
|
export default connect(null, mapActionsToProps);
|
||||||
24
client/src/containers/Accounts/withAccountsTableActions.js
Normal file
24
client/src/containers/Accounts/withAccountsTableActions.js
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
import { connect } from 'react-redux';
|
||||||
|
import t from 'store/types';
|
||||||
|
import {
|
||||||
|
fetchAccountsTable,
|
||||||
|
} from 'store/accounts/accounts.actions';
|
||||||
|
|
||||||
|
const mapActionsToProps = (dispatch) => ({
|
||||||
|
requestFetchAccountsTable: (query = {}) => dispatch(fetchAccountsTable({ query: { ...query } })),
|
||||||
|
changeAccountsCurrentView: (id) => dispatch({
|
||||||
|
type: t.ACCOUNTS_SET_CURRENT_VIEW,
|
||||||
|
currentViewId: parseInt(id, 10),
|
||||||
|
}),
|
||||||
|
setAccountsTableQuery: (key, value) => dispatch({
|
||||||
|
type: 'ACCOUNTS_TABLE_QUERY_SET', key, value,
|
||||||
|
}),
|
||||||
|
addAccountsTableQueries: (queries) => dispatch({
|
||||||
|
type: 'ACCOUNTS_TABLE_QUERIES_ADD', queries,
|
||||||
|
}),
|
||||||
|
setSelectedRowsAccounts: (ids) => dispatch({
|
||||||
|
type: t.ACCOUNTS_SELECTED_ROWS_SET, payload: { ids },
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
export default connect(null, mapActionsToProps);
|
||||||
@@ -5,7 +5,7 @@ import { useIntl } from 'react-intl';
|
|||||||
import ErrorMessage from 'components/ErrorMessage';
|
import ErrorMessage from 'components/ErrorMessage';
|
||||||
import AppToaster from 'components/AppToaster';
|
import AppToaster from 'components/AppToaster';
|
||||||
import { compose } from 'utils';
|
import { compose } from 'utils';
|
||||||
import AuthenticationConnect from 'connectors/Authentication.connect';
|
import withAuthenticationActions from './withAuthenticationActions';
|
||||||
import { useParams } from 'react-router-dom';
|
import { useParams } from 'react-router-dom';
|
||||||
import {
|
import {
|
||||||
Button,
|
Button,
|
||||||
@@ -21,6 +21,7 @@ import AuthInsider from 'containers/Authentication/AuthInsider';
|
|||||||
import { Link, useHistory } from 'react-router-dom';
|
import { Link, useHistory } from 'react-router-dom';
|
||||||
import useAsync from 'hooks/async';
|
import useAsync from 'hooks/async';
|
||||||
|
|
||||||
|
|
||||||
function Invite({
|
function Invite({
|
||||||
requestInviteAccept,
|
requestInviteAccept,
|
||||||
requestInviteMetaByToken,
|
requestInviteMetaByToken,
|
||||||
@@ -224,4 +225,6 @@ function Invite({
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default compose(AuthenticationConnect)(Invite);
|
export default compose(
|
||||||
|
withAuthenticationActions,
|
||||||
|
)(Invite);
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
import React, { useEffect, useMemo, useState } from 'react';
|
import React, { useMemo, useState } from 'react';
|
||||||
import { Link, useHistory } from 'react-router-dom';
|
import { Link, useHistory } from 'react-router-dom';
|
||||||
import * as Yup from 'yup';
|
import * as Yup from 'yup';
|
||||||
import { useFormik } from 'formik';
|
import { useFormik } from 'formik';
|
||||||
import { connect } from 'react-redux';
|
|
||||||
import { useIntl } from 'react-intl';
|
import { useIntl } from 'react-intl';
|
||||||
import {
|
import {
|
||||||
Button,
|
Button,
|
||||||
@@ -10,13 +9,12 @@ import {
|
|||||||
Intent,
|
Intent,
|
||||||
FormGroup,
|
FormGroup,
|
||||||
Checkbox,
|
Checkbox,
|
||||||
Position,
|
|
||||||
} from '@blueprintjs/core';
|
} from '@blueprintjs/core';
|
||||||
import Toaster from 'components/AppToaster';
|
import Toaster from 'components/AppToaster';
|
||||||
import ErrorMessage from 'components/ErrorMessage';
|
import ErrorMessage from 'components/ErrorMessage';
|
||||||
import AuthInsider from 'containers/Authentication/AuthInsider';
|
import AuthInsider from 'containers/Authentication/AuthInsider';
|
||||||
import Icon from 'components/Icon';
|
import Icon from 'components/Icon';
|
||||||
import AuthenticationConnect from 'connectors/Authentication.connect';
|
import withAuthenticationActions from './withAuthenticationActions';
|
||||||
import { compose } from 'utils';
|
import { compose } from 'utils';
|
||||||
|
|
||||||
const ERRORS_TYPES = {
|
const ERRORS_TYPES = {
|
||||||
@@ -162,5 +160,5 @@ function Login({
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default compose(
|
export default compose(
|
||||||
AuthenticationConnect
|
withAuthenticationActions,
|
||||||
)(Login);
|
)(Login);
|
||||||
@@ -11,12 +11,13 @@ import {
|
|||||||
} from '@blueprintjs/core';
|
} from '@blueprintjs/core';
|
||||||
import { Row, Col } from 'react-grid-system';
|
import { Row, Col } from 'react-grid-system';
|
||||||
import { Link, useHistory } from 'react-router-dom';
|
import { Link, useHistory } from 'react-router-dom';
|
||||||
import AuthenticationConnect from 'connectors/Authentication.connect';
|
import withAuthenticationActions from './withAuthenticationActions';
|
||||||
import ErrorMessage from 'components/ErrorMessage';
|
import ErrorMessage from 'components/ErrorMessage';
|
||||||
import AppToaster from 'components/AppToaster';
|
import AppToaster from 'components/AppToaster';
|
||||||
import AuthInsider from 'containers/Authentication/AuthInsider';
|
import AuthInsider from 'containers/Authentication/AuthInsider';
|
||||||
import { compose } from 'utils';
|
import { compose } from 'utils';
|
||||||
import Icon from 'components/Icon';
|
import Icon from 'components/Icon';
|
||||||
|
import { If } from 'components';
|
||||||
|
|
||||||
function Register({
|
function Register({
|
||||||
requestRegister,
|
requestRegister,
|
||||||
@@ -212,16 +213,16 @@ function Register({
|
|||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
{ isSubmitting && (
|
<If condition={isSubmitting}>
|
||||||
<div class="authentication-page__loading-overlay">
|
<div class="authentication-page__loading-overlay">
|
||||||
<Spinner size={50} />
|
<Spinner size={50} />
|
||||||
</div>
|
</div>
|
||||||
)}
|
</If>
|
||||||
</div>
|
</div>
|
||||||
</AuthInsider>
|
</AuthInsider>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default compose(
|
export default compose(
|
||||||
AuthenticationConnect,
|
withAuthenticationActions,
|
||||||
)(Register);
|
)(Register);
|
||||||
|
|||||||
@@ -13,9 +13,10 @@ import { Link, useParams, useHistory } from 'react-router-dom';
|
|||||||
import ErrorMessage from 'components/ErrorMessage';
|
import ErrorMessage from 'components/ErrorMessage';
|
||||||
import AppToaster from 'components/AppToaster';
|
import AppToaster from 'components/AppToaster';
|
||||||
import { compose } from 'utils';
|
import { compose } from 'utils';
|
||||||
import AuthenticationConnect from 'connectors/Authentication.connect';
|
import withAuthenticationActions from './withAuthenticationActions';
|
||||||
import AuthInsider from 'containers/Authentication/AuthInsider';
|
import AuthInsider from 'containers/Authentication/AuthInsider';
|
||||||
|
|
||||||
|
|
||||||
function ResetPassword({
|
function ResetPassword({
|
||||||
requestResetPassword,
|
requestResetPassword,
|
||||||
}) {
|
}) {
|
||||||
@@ -131,5 +132,5 @@ function ResetPassword({
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default compose(
|
export default compose(
|
||||||
AuthenticationConnect,
|
withAuthenticationActions,
|
||||||
)(ResetPassword);
|
)(ResetPassword);
|
||||||
|
|||||||
@@ -5,12 +5,17 @@ import { useIntl } from 'react-intl';
|
|||||||
import { Link, useHistory } from 'react-router-dom';
|
import { Link, useHistory } from 'react-router-dom';
|
||||||
import { Button, InputGroup, Intent, FormGroup } from '@blueprintjs/core';
|
import { Button, InputGroup, Intent, FormGroup } from '@blueprintjs/core';
|
||||||
import { FormattedMessage } from 'react-intl';
|
import { FormattedMessage } from 'react-intl';
|
||||||
import ErrorMessage from 'components/ErrorMessage';
|
|
||||||
import AuthenticationConnect from 'connectors/Authentication.connect';
|
|
||||||
import { compose } from 'utils';
|
import { compose } from 'utils';
|
||||||
|
|
||||||
import AppToaster from 'components/AppToaster';
|
import AppToaster from 'components/AppToaster';
|
||||||
|
import ErrorMessage from 'components/ErrorMessage';
|
||||||
|
|
||||||
import AuthInsider from 'containers/Authentication/AuthInsider';
|
import AuthInsider from 'containers/Authentication/AuthInsider';
|
||||||
|
|
||||||
|
import withAuthenticationActions from './withAuthenticationActions';
|
||||||
|
|
||||||
|
|
||||||
function SendResetPassword({
|
function SendResetPassword({
|
||||||
requestSendResetPassword,
|
requestSendResetPassword,
|
||||||
}) {
|
}) {
|
||||||
@@ -65,6 +70,7 @@ function SendResetPassword({
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<AuthInsider>
|
<AuthInsider>
|
||||||
<div class='reset-form'>
|
<div class='reset-form'>
|
||||||
@@ -110,5 +116,5 @@ function SendResetPassword({
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default compose(
|
export default compose(
|
||||||
AuthenticationConnect,
|
withAuthenticationActions,
|
||||||
)(SendResetPassword);
|
)(SendResetPassword);
|
||||||
|
|||||||
@@ -8,9 +8,6 @@ import {
|
|||||||
} from 'store/authentication/authentication.actions';
|
} from 'store/authentication/authentication.actions';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
|
|
||||||
const mapStateToProps = (state) => ({
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
const mapDispatchToProps = (dispatch) => ({
|
const mapDispatchToProps = (dispatch) => ({
|
||||||
requestLogin: (form) => dispatch(login({ form })),
|
requestLogin: (form) => dispatch(login({ form })),
|
||||||
@@ -21,4 +18,4 @@ const mapDispatchToProps = (dispatch) => ({
|
|||||||
requestInviteMetaByToken: (token) => dispatch(inviteMetaByToken({ token })),
|
requestInviteMetaByToken: (token) => dispatch(inviteMetaByToken({ token })),
|
||||||
});
|
});
|
||||||
|
|
||||||
export default connect(mapStateToProps, mapDispatchToProps);
|
export default connect(null, mapDispatchToProps);
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
import React, { useEffect } from 'react';
|
|
||||||
import { useParams } from 'react-router-dom';
|
|
||||||
import DashboardConnect from 'connectors/Dashboard.connector';
|
|
||||||
import DashboardInsider from 'components/Dashboard/DashboardInsider';
|
|
||||||
import CategoryList from 'components/Items/categoryList';
|
|
||||||
import ItemFormDialog from 'connectors/ItemFormDialog.connect';
|
|
||||||
import { compose } from 'utils';
|
|
||||||
|
|
||||||
const ItemCategoryList = ({ changePageTitle }) => {
|
|
||||||
const { id } = useParams();
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
id
|
|
||||||
? changePageTitle('Edit Category Details')
|
|
||||||
: changePageTitle('Category List');
|
|
||||||
}, []);
|
|
||||||
return (
|
|
||||||
<DashboardInsider isLoading={null} name={'item-category-list'}>
|
|
||||||
<CategoryList />
|
|
||||||
</DashboardInsider>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default compose(DashboardConnect, ItemFormDialog)(ItemCategoryList);
|
|
||||||
@@ -1,43 +0,0 @@
|
|||||||
import React, { useEffect } from 'react';
|
|
||||||
import { useParams } from 'react-router-dom';
|
|
||||||
import { useAsync } from 'react-use';
|
|
||||||
import DashboardConnect from 'connectors/Dashboard.connector';
|
|
||||||
import ItemForm from 'components/Items/ItemForm';
|
|
||||||
import DashboardInsider from 'components/Dashboard/DashboardInsider';
|
|
||||||
import ItemsConnect from 'connectors/Items.connect';
|
|
||||||
import AccountsConnect from 'connectors/Accounts.connector';
|
|
||||||
import ItemCategoryConnect from 'connectors/ItemsCategory.connect';
|
|
||||||
import { compose } from 'utils';
|
|
||||||
|
|
||||||
const ItemFormContainer = ({
|
|
||||||
changePageTitle,
|
|
||||||
requestFetchAccounts,
|
|
||||||
requestFetchItemCategories,
|
|
||||||
}) => {
|
|
||||||
const { id } = useParams();
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
id ?
|
|
||||||
changePageTitle('Edit Item Details') :
|
|
||||||
changePageTitle('New Item');
|
|
||||||
}, [id, changePageTitle]);
|
|
||||||
|
|
||||||
const fetchHook = useAsync(async () => {
|
|
||||||
await Promise.all([
|
|
||||||
requestFetchAccounts(),
|
|
||||||
requestFetchItemCategories(),
|
|
||||||
]);
|
|
||||||
});
|
|
||||||
return (
|
|
||||||
<DashboardInsider loading={fetchHook.loading} name={'item-form'}>
|
|
||||||
<ItemForm />
|
|
||||||
</DashboardInsider>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default compose(
|
|
||||||
DashboardConnect,
|
|
||||||
ItemsConnect,
|
|
||||||
AccountsConnect,
|
|
||||||
ItemCategoryConnect,
|
|
||||||
)(ItemFormContainer);
|
|
||||||
@@ -1,102 +0,0 @@
|
|||||||
import React, { useEffect, useState, useCallback } from 'react';
|
|
||||||
import DashboardInsider from 'components/Dashboard/DashboardInsider';
|
|
||||||
import useAsync from 'hooks/async';
|
|
||||||
import { useParams } from 'react-router-dom';
|
|
||||||
import DashboardConnect from 'connectors/Dashboard.connector';
|
|
||||||
import ItemsCategoryConnect from 'connectors/ItemsCategory.connect';
|
|
||||||
import { compose } from 'utils';
|
|
||||||
import ItemsCategoryList from 'components/Items/ItemsCategoryList';
|
|
||||||
import ItemsCategoryActionsBar from './ItemsCategoryActionsBar';
|
|
||||||
import { Alert, Intent } from '@blueprintjs/core';
|
|
||||||
import AppToaster from 'components/AppToaster';
|
|
||||||
import DashboardPageContent from 'components/Dashboard/DashboardPageContent';
|
|
||||||
|
|
||||||
const ItemCategoriesList = ({
|
|
||||||
changePageTitle,
|
|
||||||
views,
|
|
||||||
requestFetchItemCategories,
|
|
||||||
requestEditItemCategory,
|
|
||||||
requestDeleteItemCategory,
|
|
||||||
}) => {
|
|
||||||
const { id } = useParams();
|
|
||||||
const [deleteCategory, setDeleteCategory] = useState(false);
|
|
||||||
const [selectedRows, setSelectedRows] = useState([]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
id
|
|
||||||
? changePageTitle('Edit Item Details')
|
|
||||||
: changePageTitle('Categories List');
|
|
||||||
}, [id, changePageTitle]);
|
|
||||||
|
|
||||||
const fetchHook = useAsync(async () => {
|
|
||||||
await Promise.all([
|
|
||||||
requestFetchItemCategories(),
|
|
||||||
]);
|
|
||||||
}, false);
|
|
||||||
|
|
||||||
const handelDeleteCategory = useCallback((category) => {
|
|
||||||
setDeleteCategory(category);
|
|
||||||
}, [setDeleteCategory]);
|
|
||||||
|
|
||||||
const handelEditCategory = category => {};
|
|
||||||
|
|
||||||
const handelCancelCategoryDelete = useCallback(() => {
|
|
||||||
setDeleteCategory(false);
|
|
||||||
}, [setDeleteCategory]);
|
|
||||||
|
|
||||||
const handelConfirmCategoryDelete = useCallback(() => {
|
|
||||||
requestDeleteItemCategory(deleteCategory.id).then(() => {
|
|
||||||
setDeleteCategory(false);
|
|
||||||
AppToaster.show({
|
|
||||||
message: 'the_category_has_been_delete'
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}, [deleteCategory, requestDeleteItemCategory, setDeleteCategory]);
|
|
||||||
|
|
||||||
const handleFetchData = useCallback(() => {
|
|
||||||
fetchHook.execute();
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
// Handle selected rows change.
|
|
||||||
const handleSelectedRowsChange = useCallback((accounts) => {
|
|
||||||
setSelectedRows(accounts);
|
|
||||||
}, [setSelectedRows]);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<DashboardInsider loading={fetchHook.pending} name="items-categories">
|
|
||||||
<ItemsCategoryActionsBar
|
|
||||||
views={views}
|
|
||||||
onDeleteCategory={handelDeleteCategory}
|
|
||||||
selectedRows={selectedRows}
|
|
||||||
/>
|
|
||||||
<DashboardPageContent>
|
|
||||||
<ItemsCategoryList
|
|
||||||
onDeleteCategory={handelDeleteCategory}
|
|
||||||
onFetchData={handleFetchData}
|
|
||||||
onEditCategory={handelEditCategory}
|
|
||||||
onSelectedRowsChange={handleSelectedRowsChange}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<Alert
|
|
||||||
cancelButtonText='Cancel'
|
|
||||||
confirmButtonText='Move to Trash'
|
|
||||||
icon='trash'
|
|
||||||
intent={Intent.DANGER}
|
|
||||||
isOpen={deleteCategory}
|
|
||||||
onCancel={handelCancelCategoryDelete}
|
|
||||||
onConfirm={handelConfirmCategoryDelete}
|
|
||||||
>
|
|
||||||
<p>
|
|
||||||
Are you sure you want to move <b>filename</b> to Trash? You will be
|
|
||||||
able to restore it later, but it will become private to you.
|
|
||||||
</p>
|
|
||||||
</Alert>
|
|
||||||
</DashboardPageContent>
|
|
||||||
</DashboardInsider>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default compose(
|
|
||||||
DashboardConnect,
|
|
||||||
ItemsCategoryConnect
|
|
||||||
)(ItemCategoriesList);
|
|
||||||
@@ -1,101 +0,0 @@
|
|||||||
import React, {useEffect, useState, useCallback} from 'react';
|
|
||||||
import { useAsync } from 'react-use';
|
|
||||||
import { useParams, useHistory } from 'react-router-dom';
|
|
||||||
import { Intent, Alert } from '@blueprintjs/core';
|
|
||||||
import DashboardInsider from 'components/Dashboard/DashboardInsider';
|
|
||||||
import DashboardPageContent from 'components/Dashboard/DashboardPageContent';
|
|
||||||
import ViewForm from 'components/Views/ViewForm';
|
|
||||||
import DashboardConnect from 'connectors/Dashboard.connector';
|
|
||||||
import ResourceConnect from 'connectors/Resource.connector';
|
|
||||||
import ViewConnect from 'connectors/View.connector';
|
|
||||||
import {compose} from 'utils';
|
|
||||||
import AppToaster from 'components/AppToaster';
|
|
||||||
|
|
||||||
function ViewFormPage({
|
|
||||||
changePageTitle,
|
|
||||||
fetchResourceFields,
|
|
||||||
fetchResourceColumns,
|
|
||||||
fetchView,
|
|
||||||
getResourceColumns,
|
|
||||||
getResourceFields,
|
|
||||||
submitView,
|
|
||||||
getViewMeta,
|
|
||||||
deleteView,
|
|
||||||
}) {
|
|
||||||
const { resource_slug: resourceSlug, view_id: viewId } = useParams();
|
|
||||||
|
|
||||||
const columns = getResourceColumns('accounts');
|
|
||||||
const fields = getResourceFields('accounts');
|
|
||||||
|
|
||||||
const viewForm = (viewId) ? getViewMeta(viewId) : null;
|
|
||||||
|
|
||||||
const [stateDeleteView, setStateDeleteView] = useState(null);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (viewId) {
|
|
||||||
changePageTitle('Edit Custom View');
|
|
||||||
} else {
|
|
||||||
changePageTitle('New Custom View');
|
|
||||||
}
|
|
||||||
}, [viewId, changePageTitle]);
|
|
||||||
|
|
||||||
const fetchHook = useAsync(async () => {
|
|
||||||
await Promise.all([
|
|
||||||
fetchResourceColumns('accounts'),
|
|
||||||
fetchResourceFields('accounts'),
|
|
||||||
...(viewId) ? [
|
|
||||||
fetchView(viewId),
|
|
||||||
] : [],
|
|
||||||
]);
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const handleDeleteView = useCallback((view) => {
|
|
||||||
setStateDeleteView(view);
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const handleCancelDeleteView = useCallback(() => {
|
|
||||||
setStateDeleteView(null);
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const handleConfirmDeleteView = useCallback(() => {
|
|
||||||
deleteView(stateDeleteView.id).then((response) => {
|
|
||||||
setStateDeleteView(null);
|
|
||||||
AppToaster.show({
|
|
||||||
message: 'the_custom_view_has_been_deleted',
|
|
||||||
});
|
|
||||||
})
|
|
||||||
}, [deleteView, stateDeleteView]);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<DashboardInsider name={'view-form'} loading={fetchHook.loading} mount={false}>
|
|
||||||
<DashboardPageContent>
|
|
||||||
<ViewForm
|
|
||||||
resourceName={resourceSlug}
|
|
||||||
columns={columns}
|
|
||||||
fields={fields}
|
|
||||||
viewForm={viewForm}
|
|
||||||
onDelete={handleDeleteView} />
|
|
||||||
|
|
||||||
<Alert
|
|
||||||
cancelButtonText="Cancel"
|
|
||||||
confirmButtonText="Move to Trash"
|
|
||||||
icon="trash"
|
|
||||||
intent={Intent.DANGER}
|
|
||||||
isOpen={stateDeleteView}
|
|
||||||
onCancel={handleCancelDeleteView}
|
|
||||||
onConfirm={handleConfirmDeleteView}>
|
|
||||||
<p>
|
|
||||||
Are you sure you want to move <b>filename</b> to Trash? You will be able to restore it later,
|
|
||||||
but it will become private to you.
|
|
||||||
</p>
|
|
||||||
</Alert>
|
|
||||||
</DashboardPageContent>
|
|
||||||
</DashboardInsider>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default compose(
|
|
||||||
DashboardConnect,
|
|
||||||
ResourceConnect,
|
|
||||||
ViewConnect,
|
|
||||||
)(ViewFormPage);
|
|
||||||
27
client/src/containers/Dashboard/withDashboard.js
Normal file
27
client/src/containers/Dashboard/withDashboard.js
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
|
||||||
|
import { connect } from 'react-redux';
|
||||||
|
import t from 'store/types';
|
||||||
|
|
||||||
|
const mapActionsToProps = (dispatch) => ({
|
||||||
|
changePageTitle: (pageTitle) => dispatch({
|
||||||
|
type: t.CHANGE_DASHBOARD_PAGE_TITLE, pageTitle
|
||||||
|
}),
|
||||||
|
|
||||||
|
changePageSubtitle: (pageSubtitle) => dispatch({
|
||||||
|
type: t.ALTER_DASHBOARD_PAGE_SUBTITLE, pageSubtitle,
|
||||||
|
}),
|
||||||
|
|
||||||
|
setTopbarEditView: (id) => dispatch({
|
||||||
|
type: t.SET_TOPBAR_EDIT_VIEW, id,
|
||||||
|
}),
|
||||||
|
|
||||||
|
setDashboardRequestLoading: () => dispatch({
|
||||||
|
type: t.SET_DASHBOARD_REQUEST_LOADING,
|
||||||
|
}),
|
||||||
|
|
||||||
|
setDashboardRequestCompleted: () => dispatch({
|
||||||
|
type: t.SET_DASHBOARD_REQUEST_COMPLETED,
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
export default connect(null, mapActionsToProps);
|
||||||
30
client/src/containers/Dialogs/AccountFormDialog.container.js
Normal file
30
client/src/containers/Dialogs/AccountFormDialog.container.js
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
import {connect} from 'react-redux';
|
||||||
|
import { compose } from 'utils';
|
||||||
|
import DialogConnect from 'connectors/Dialog.connector';
|
||||||
|
import DialogReduxConnect from 'components/DialogReduxConnect';
|
||||||
|
import {getDialogPayload} from 'store/dashboard/dashboard.reducer';
|
||||||
|
import withAccountsActions from 'containers/Accounts/withAccountsActions';
|
||||||
|
import withAccountDetail from 'containers/Accounts/withAccountDetail';
|
||||||
|
import withAccounts from 'containers/Accounts/withAccounts';
|
||||||
|
|
||||||
|
export const mapStateToProps = (state, props) => {
|
||||||
|
const dialogPayload = getDialogPayload(state, 'account-form');
|
||||||
|
|
||||||
|
return {
|
||||||
|
name: 'account-form',
|
||||||
|
payload: {action: 'new', id: null, ...dialogPayload},
|
||||||
|
|
||||||
|
accountId: dialogPayload?.id || null,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const AccountFormDialogConnect = connect(mapStateToProps);
|
||||||
|
|
||||||
|
export default compose(
|
||||||
|
AccountFormDialogConnect,
|
||||||
|
withAccountsActions,
|
||||||
|
withAccountDetail,
|
||||||
|
withAccounts,
|
||||||
|
DialogReduxConnect,
|
||||||
|
DialogConnect,
|
||||||
|
);
|
||||||
@@ -15,31 +15,40 @@ import * as Yup from 'yup';
|
|||||||
import { useFormik } from 'formik';
|
import { useFormik } from 'formik';
|
||||||
import { useIntl } from 'react-intl';
|
import { useIntl } from 'react-intl';
|
||||||
import { omit } from 'lodash';
|
import { omit } from 'lodash';
|
||||||
import { compose } from 'utils';
|
import { useQuery, queryCache } from 'react-query';
|
||||||
import useAsync from 'hooks/async';
|
|
||||||
import Dialog from 'components/Dialog';
|
import Dialog from 'components/Dialog';
|
||||||
import AppToaster from 'components/AppToaster';
|
import AppToaster from 'components/AppToaster';
|
||||||
import DialogConnect from 'connectors/Dialog.connector';
|
|
||||||
import DialogReduxConnect from 'components/DialogReduxConnect';
|
import AccountFormDialogContainer from 'containers/Dialogs/AccountFormDialog.container';
|
||||||
import AccountFormDialogConnect from 'connectors/AccountFormDialog.connector';
|
|
||||||
import AccountsConnect from 'connectors/Accounts.connector';
|
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import Icon from 'components/Icon';
|
import Icon from 'components/Icon';
|
||||||
import ErrorMessage from 'components/ErrorMessage';
|
import ErrorMessage from 'components/ErrorMessage';
|
||||||
|
import { fetchAccountTypes } from 'store/accounts/accounts.actions';
|
||||||
|
|
||||||
|
|
||||||
function AccountFormDialog({
|
function AccountFormDialog({
|
||||||
name,
|
name,
|
||||||
payload,
|
payload,
|
||||||
isOpen,
|
isOpen,
|
||||||
|
|
||||||
|
// #withAccounts
|
||||||
accountsTypes,
|
accountsTypes,
|
||||||
accounts,
|
accounts,
|
||||||
|
|
||||||
|
// #withAccountDetail
|
||||||
|
account,
|
||||||
|
|
||||||
|
// #withAccountsActions
|
||||||
requestFetchAccounts,
|
requestFetchAccounts,
|
||||||
requestFetchAccountTypes,
|
requestFetchAccountTypes,
|
||||||
requestFetchAccount,
|
requestFetchAccount,
|
||||||
closeDialog,
|
|
||||||
requestSubmitAccount,
|
requestSubmitAccount,
|
||||||
requestEditAccount,
|
requestEditAccount,
|
||||||
getAccountById,
|
|
||||||
|
// #withDialog
|
||||||
|
closeDialog,
|
||||||
}) {
|
}) {
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
const accountFormValidationSchema = Yup.object().shape({
|
const accountFormValidationSchema = Yup.object().shape({
|
||||||
@@ -63,10 +72,6 @@ function AccountFormDialog({
|
|||||||
accounts.find(a => a.id === payload.id) : null,
|
accounts.find(a => a.id === payload.id) : null,
|
||||||
);
|
);
|
||||||
|
|
||||||
const editAccount = useMemo(() =>
|
|
||||||
payload.action === 'edit' ? getAccountById(payload.id) : null,
|
|
||||||
[payload, getAccountById]);
|
|
||||||
|
|
||||||
const transformApiErrors = (errors) => {
|
const transformApiErrors = (errors) => {
|
||||||
const fields = {};
|
const fields = {};
|
||||||
if (errors.find(e => e.type === 'NOT_UNIQUE_CODE')) {
|
if (errors.find(e => e.type === 'NOT_UNIQUE_CODE')) {
|
||||||
@@ -79,8 +84,7 @@ function AccountFormDialog({
|
|||||||
const formik = useFormik({
|
const formik = useFormik({
|
||||||
enableReinitialize: true,
|
enableReinitialize: true,
|
||||||
initialValues: {
|
initialValues: {
|
||||||
...(payload.action === 'edit' && editAccount)
|
...(payload.action === 'edit' && account) ? account : initialValues,
|
||||||
? editAccount : initialValues,
|
|
||||||
},
|
},
|
||||||
validationSchema: accountFormValidationSchema,
|
validationSchema: accountFormValidationSchema,
|
||||||
onSubmit: (values, { setSubmitting, setErrors }) => {
|
onSubmit: (values, { setSubmitting, setErrors }) => {
|
||||||
@@ -97,12 +101,13 @@ function AccountFormDialog({
|
|||||||
intent: Intent.SUCCESS,
|
intent: Intent.SUCCESS,
|
||||||
});
|
});
|
||||||
setSubmitting(false);
|
setSubmitting(false);
|
||||||
|
queryCache.refetchQueries('accounts-table', { force: true });
|
||||||
}).catch((errors) => {
|
}).catch((errors) => {
|
||||||
setSubmitting(false);
|
setSubmitting(false);
|
||||||
setErrors(transformApiErrors(errors));
|
setErrors(transformApiErrors(errors));
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
requestSubmitAccount({ form: { ...omit(values, exclude) } }).then(response => {
|
requestSubmitAccount({ form: { ...omit(values, exclude) } }).then((response) => {
|
||||||
closeDialog(name);
|
closeDialog(name);
|
||||||
AppToaster.show({
|
AppToaster.show({
|
||||||
message: 'the_account_has_been_submit',
|
message: 'the_account_has_been_submit',
|
||||||
@@ -110,6 +115,7 @@ function AccountFormDialog({
|
|||||||
position: Position.BOTTOM,
|
position: Position.BOTTOM,
|
||||||
});
|
});
|
||||||
setSubmitting(false);
|
setSubmitting(false);
|
||||||
|
queryCache.refetchQueries('accounts-table', { force: true });
|
||||||
}).catch((errors) => {
|
}).catch((errors) => {
|
||||||
setSubmitting(false);
|
setSubmitting(false);
|
||||||
setErrors(transformApiErrors(errors));
|
setErrors(transformApiErrors(errors));
|
||||||
@@ -121,13 +127,13 @@ function AccountFormDialog({
|
|||||||
|
|
||||||
// Set default account type.
|
// Set default account type.
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (editAccount && editAccount.account_type_id) {
|
if (account && account.account_type_id) {
|
||||||
const defaultType = accountsTypes.find((t) =>
|
const defaultType = accountsTypes.find((t) =>
|
||||||
t.id === editAccount.account_type_id);
|
t.id === account.account_type_id);
|
||||||
|
|
||||||
defaultType && setSelectedAccountType(defaultType);
|
defaultType && setSelectedAccountType(defaultType);
|
||||||
}
|
}
|
||||||
}, [editAccount, accountsTypes]);
|
}, [account, accountsTypes]);
|
||||||
|
|
||||||
// Filters accounts types items.
|
// Filters accounts types items.
|
||||||
const filterAccountTypeItems = (query, accountType, _index, exactMatch) => {
|
const filterAccountTypeItems = (query, accountType, _index, exactMatch) => {
|
||||||
@@ -168,18 +174,32 @@ function AccountFormDialog({
|
|||||||
// Handles dialog close.
|
// Handles dialog close.
|
||||||
const handleClose = useCallback(() => { closeDialog(name); }, [closeDialog, name]);
|
const handleClose = useCallback(() => { closeDialog(name); }, [closeDialog, name]);
|
||||||
|
|
||||||
const fetchHook = useAsync(async () => {
|
// Fetches accounts list.
|
||||||
await Promise.all([
|
const fetchAccountsList = useQuery('accounts-list',
|
||||||
requestFetchAccounts(),
|
() => requestFetchAccounts(), { manual: true });
|
||||||
requestFetchAccountTypes(),
|
|
||||||
// Fetch the target in case edit mode.
|
// Fetches accounts types.
|
||||||
...(payload.action === 'edit' ?
|
const fetchAccountsTypes = useQuery('accounts-types-list', async () => {
|
||||||
[requestFetchAccount(payload.id)] : [])
|
await requestFetchAccountTypes();
|
||||||
]);
|
}, { manual: true });
|
||||||
}, false);
|
|
||||||
|
// Fetch the given account id on edit mode.
|
||||||
|
const fetchAccount = useQuery(
|
||||||
|
payload.action === 'edit' && ['account', payload.id],
|
||||||
|
(key, id) => requestFetchAccount(id),
|
||||||
|
{ manual: true });
|
||||||
|
|
||||||
|
const isFetching = (
|
||||||
|
fetchAccountsList.isFetching ||
|
||||||
|
fetchAccountTypes.isFetching ||
|
||||||
|
fetchAccount.isFetching);
|
||||||
|
|
||||||
// Fetch requests on dialog opening.
|
// Fetch requests on dialog opening.
|
||||||
const onDialogOpening = useCallback(() => { fetchHook.execute(); }, [fetchHook]);
|
const onDialogOpening = useCallback(() => {
|
||||||
|
fetchAccountsList.refetch();
|
||||||
|
fetchAccountsTypes.refetch();
|
||||||
|
fetchAccount.refetch();
|
||||||
|
}, []);
|
||||||
|
|
||||||
const onChangeAccountType = useCallback((accountType) => {
|
const onChangeAccountType = useCallback((accountType) => {
|
||||||
setSelectedAccountType(accountType);
|
setSelectedAccountType(accountType);
|
||||||
@@ -211,7 +231,7 @@ function AccountFormDialog({
|
|||||||
name={name}
|
name={name}
|
||||||
title={payload.action === 'edit' ? 'Edit Account' : 'New Account'}
|
title={payload.action === 'edit' ? 'Edit Account' : 'New Account'}
|
||||||
className={{
|
className={{
|
||||||
'dialog--loading': fetchHook.pending,
|
'dialog--loading': isFetching,
|
||||||
'dialog--account-form': true
|
'dialog--account-form': true
|
||||||
}}
|
}}
|
||||||
autoFocus={true}
|
autoFocus={true}
|
||||||
@@ -219,7 +239,7 @@ function AccountFormDialog({
|
|||||||
onClosed={onDialogClosed}
|
onClosed={onDialogClosed}
|
||||||
onOpening={onDialogOpening}
|
onOpening={onDialogOpening}
|
||||||
isOpen={isOpen}
|
isOpen={isOpen}
|
||||||
isLoading={fetchHook.pending}
|
isLoading={isFetching}
|
||||||
onClose={handleClose}
|
onClose={handleClose}
|
||||||
>
|
>
|
||||||
<form onSubmit={formik.handleSubmit}>
|
<form onSubmit={formik.handleSubmit}>
|
||||||
@@ -350,9 +370,6 @@ function AccountFormDialog({
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default compose(
|
export default AccountFormDialogContainer(
|
||||||
AccountFormDialogConnect,
|
AccountFormDialog,
|
||||||
AccountsConnect,
|
);
|
||||||
DialogReduxConnect,
|
|
||||||
DialogConnect
|
|
||||||
)(AccountFormDialog);
|
|
||||||
@@ -14,31 +14,52 @@ import * as Yup from 'yup';
|
|||||||
import { useIntl } from 'react-intl';
|
import { useIntl } from 'react-intl';
|
||||||
import { useFormik } from 'formik';
|
import { useFormik } from 'formik';
|
||||||
import { compose } from 'utils';
|
import { compose } from 'utils';
|
||||||
import Dialog from 'components/Dialog';
|
import { useQuery } from 'react-query';
|
||||||
import useAsync from 'hooks/async';
|
import classNames from 'classnames';
|
||||||
|
|
||||||
import AppToaster from 'components/AppToaster';
|
import AppToaster from 'components/AppToaster';
|
||||||
|
import ErrorMessage from 'components/ErrorMessage';
|
||||||
|
|
||||||
|
import Dialog from 'components/Dialog';
|
||||||
import DialogConnect from 'connectors/Dialog.connector';
|
import DialogConnect from 'connectors/Dialog.connector';
|
||||||
import DialogReduxConnect from 'components/DialogReduxConnect';
|
import DialogReduxConnect from 'components/DialogReduxConnect';
|
||||||
import ItemsCategoryConnect from 'connectors/ItemsCategory.connect';
|
|
||||||
import ErrorMessage from 'components/ErrorMessage';
|
import {connect} from 'react-redux';
|
||||||
import classNames from 'classnames';
|
import { getDialogPayload } from 'store/dashboard/dashboard.reducer';
|
||||||
|
import withItemCategoryDetail from 'containers/Items/withItemCategoryDetail';
|
||||||
|
import withItemCategories from 'containers/Items/withItemCategories';
|
||||||
|
import withItemCategoriesActions from 'containers/Items/withItemCategoriesActions';
|
||||||
|
|
||||||
import Icon from 'components/Icon';
|
import Icon from 'components/Icon';
|
||||||
|
|
||||||
|
|
||||||
function ItemCategoryDialog({
|
function ItemCategoryDialog({
|
||||||
name,
|
name,
|
||||||
payload,
|
payload,
|
||||||
isOpen,
|
isOpen,
|
||||||
|
|
||||||
|
// #withDialog
|
||||||
openDialog,
|
openDialog,
|
||||||
closeDialog,
|
closeDialog,
|
||||||
categories,
|
|
||||||
|
// #withItemCategoryDetail
|
||||||
|
itemCategoryId,
|
||||||
|
itemCategory,
|
||||||
|
|
||||||
|
// #withItemCategories
|
||||||
|
categoriesList,
|
||||||
|
|
||||||
|
// #withItemCategoriesActions
|
||||||
requestSubmitItemCategory,
|
requestSubmitItemCategory,
|
||||||
requestFetchItemCategories,
|
requestFetchItemCategories,
|
||||||
requestEditItemCategory,
|
requestEditItemCategory,
|
||||||
editItemCategory
|
|
||||||
}) {
|
}) {
|
||||||
const [selectedParentCategory, setParentCategory] = useState(null);
|
const [selectedParentCategory, setParentCategory] = useState(null);
|
||||||
|
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
|
|
||||||
|
const fetchList = useQuery(['items-categories-list'],
|
||||||
|
() => requestFetchItemCategories());
|
||||||
|
|
||||||
const ValidationSchema = Yup.object().shape({
|
const ValidationSchema = Yup.object().shape({
|
||||||
name: Yup.string().required(intl.formatMessage({ id: 'required' })),
|
name: Yup.string().required(intl.formatMessage({ id: 'required' })),
|
||||||
parent_category_id: Yup.string().nullable(),
|
parent_category_id: Yup.string().nullable(),
|
||||||
@@ -56,7 +77,7 @@ function ItemCategoryDialog({
|
|||||||
enableReinitialize: true,
|
enableReinitialize: true,
|
||||||
initialValues: {
|
initialValues: {
|
||||||
...(payload.action === 'edit' &&
|
...(payload.action === 'edit' &&
|
||||||
pick(editItemCategory, Object.keys(initialValues)))
|
pick(itemCategory, Object.keys(initialValues)))
|
||||||
},
|
},
|
||||||
validationSchema: ValidationSchema,
|
validationSchema: ValidationSchema,
|
||||||
onSubmit: (values, { setSubmitting }) => {
|
onSubmit: (values, { setSubmitting }) => {
|
||||||
@@ -104,15 +125,13 @@ function ItemCategoryDialog({
|
|||||||
);
|
);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
// Handle the dialog closing.
|
||||||
const handleClose = useCallback(() => { closeDialog(name); }, [name, closeDialog]);
|
const handleClose = useCallback(() => { closeDialog(name); }, [name, closeDialog]);
|
||||||
|
|
||||||
const fetchHook = useAsync(async () => {
|
// Handle the dialog opening.
|
||||||
await Promise.all([
|
const onDialogOpening = useCallback(() => {
|
||||||
requestFetchItemCategories(),
|
fetchList.refetch();
|
||||||
]);
|
}, [fetchList]);
|
||||||
}, false);
|
|
||||||
|
|
||||||
const onDialogOpening = useCallback(() => { fetchHook.execute(); }, [fetchHook]);
|
|
||||||
|
|
||||||
const onChangeParentCategory = useCallback((parentCategory) => {
|
const onChangeParentCategory = useCallback((parentCategory) => {
|
||||||
setParentCategory(parentCategory);
|
setParentCategory(parentCategory);
|
||||||
@@ -132,14 +151,14 @@ function ItemCategoryDialog({
|
|||||||
name={name}
|
name={name}
|
||||||
title={payload.action === 'edit' ? 'Edit Category' : ' New Category'}
|
title={payload.action === 'edit' ? 'Edit Category' : ' New Category'}
|
||||||
className={classNames({
|
className={classNames({
|
||||||
'dialog--loading': fetchHook.pending,
|
'dialog--loading': fetchList.isFetching,
|
||||||
},
|
},
|
||||||
'dialog--category-form',
|
'dialog--category-form',
|
||||||
)}
|
)}
|
||||||
isOpen={isOpen}
|
isOpen={isOpen}
|
||||||
onClosed={onDialogClosed}
|
onClosed={onDialogClosed}
|
||||||
onOpening={onDialogOpening}
|
onOpening={onDialogOpening}
|
||||||
isLoading={fetchHook.pending}
|
isLoading={fetchList.isFetching}
|
||||||
onClose={handleClose}
|
onClose={handleClose}
|
||||||
>
|
>
|
||||||
<form onSubmit={formik.handleSubmit}>
|
<form onSubmit={formik.handleSubmit}>
|
||||||
@@ -172,7 +191,7 @@ function ItemCategoryDialog({
|
|||||||
intent={(errors.parent_category_id && touched.parent_category_id) && Intent.DANGER}
|
intent={(errors.parent_category_id && touched.parent_category_id) && Intent.DANGER}
|
||||||
>
|
>
|
||||||
<Select
|
<Select
|
||||||
items={Object.values(categories)}
|
items={categoriesList}
|
||||||
noResults={<MenuItem disabled={true} text='No results.' />}
|
noResults={<MenuItem disabled={true} text='No results.' />}
|
||||||
itemRenderer={parentCategoryItem}
|
itemRenderer={parentCategoryItem}
|
||||||
itemPredicate={filterItemCategory}
|
itemPredicate={filterItemCategory}
|
||||||
@@ -214,8 +233,25 @@ function ItemCategoryDialog({
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const mapStateToProps = (state, props) => {
|
||||||
|
const dialogPayload = getDialogPayload(state, 'item-category-form');
|
||||||
|
|
||||||
|
return {
|
||||||
|
name: 'account-form',
|
||||||
|
payload: {action: 'new', id: null, ...dialogPayload},
|
||||||
|
|
||||||
|
itemCategoryId: dialogPayload?.id || null,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const withItemCategoryDialog = connect(mapStateToProps);
|
||||||
|
|
||||||
export default compose(
|
export default compose(
|
||||||
ItemsCategoryConnect,
|
|
||||||
DialogConnect,
|
DialogConnect,
|
||||||
DialogReduxConnect
|
DialogReduxConnect,
|
||||||
|
withItemCategoryDialog,
|
||||||
|
withItemCategoryDetail,
|
||||||
|
withItemCategories,
|
||||||
|
withItemCategoriesActions
|
||||||
)(ItemCategoryDialog);
|
)(ItemCategoryDialog);
|
||||||
@@ -33,7 +33,6 @@ function ExpenseFormContainer({
|
|||||||
return (
|
return (
|
||||||
<DashboardInsider isLoading={fetchHook.loading} name={'expense-form'}>
|
<DashboardInsider isLoading={fetchHook.loading} name={'expense-form'}>
|
||||||
<ExpenseForm {...{submitExpense, editExpense, accounts, currencies} } />
|
<ExpenseForm {...{submitExpense, editExpense, accounts, currencies} } />
|
||||||
|
|
||||||
</DashboardInsider>
|
</DashboardInsider>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
import React, {useMemo, useCallback} from 'react';
|
import React, {useMemo, useCallback} from 'react';
|
||||||
import FinancialStatementHeader from 'containers/Dashboard/FinancialStatements/FinancialStatementHeader';
|
import FinancialStatementHeader from 'containers/FinancialStatements/FinancialStatementHeader';
|
||||||
import {Row, Col} from 'react-grid-system';
|
import {Row, Col} from 'react-grid-system';
|
||||||
import {
|
import {
|
||||||
Button,
|
Button,
|
||||||
@@ -12,7 +12,7 @@ import moment from 'moment';
|
|||||||
import Icon from 'components/Icon';
|
import Icon from 'components/Icon';
|
||||||
import { useFormik } from 'formik';
|
import { useFormik } from 'formik';
|
||||||
import * as Yup from 'yup';
|
import * as Yup from 'yup';
|
||||||
import FinancialStatementDateRange from 'containers/Dashboard/FinancialStatements/FinancialStatementDateRange';
|
import FinancialStatementDateRange from 'containers/FinancialStatements/FinancialStatementDateRange';
|
||||||
import SelectDisplayColumnsBy from '../SelectDisplayColumnsBy';
|
import SelectDisplayColumnsBy from '../SelectDisplayColumnsBy';
|
||||||
import RadiosAccountingBasis from '../RadiosAccountingBasis';
|
import RadiosAccountingBasis from '../RadiosAccountingBasis';
|
||||||
|
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
import React, { useEffect, useCallback, useState, useMemo } from 'react';
|
import React, { useEffect, useCallback, useState, useMemo } from 'react';
|
||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
import GeneralLedgerTable from 'containers/Dashboard/FinancialStatements/GeneralLedger/GeneralLedgerTable';
|
import GeneralLedgerTable from 'containers/FinancialStatements/GeneralLedger/GeneralLedgerTable';
|
||||||
import useAsync from 'hooks/async';
|
import useAsync from 'hooks/async';
|
||||||
import DashboardConnect from 'connectors/Dashboard.connector';
|
import DashboardConnect from 'connectors/Dashboard.connector';
|
||||||
import GeneralLedgerConnect from 'connectors/GeneralLedgerSheet.connect';
|
import GeneralLedgerConnect from 'connectors/GeneralLedgerSheet.connect';
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
import React, {useState, useMemo, useEffect, useCallback} from 'react';
|
import React, {useState, useMemo, useEffect, useCallback} from 'react';
|
||||||
import FinancialStatementHeader from 'containers/Dashboard/FinancialStatements/FinancialStatementHeader';
|
import FinancialStatementHeader from 'containers/FinancialStatements/FinancialStatementHeader';
|
||||||
import {useIntl} from 'react-intl';
|
import {useIntl} from 'react-intl';
|
||||||
import {
|
import {
|
||||||
Button,
|
Button,
|
||||||
@@ -15,7 +15,7 @@ import AccountsConnect from 'connectors/Accounts.connector'
|
|||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import AccountsMultiSelect from 'components/AccountsMultiSelect';
|
import AccountsMultiSelect from 'components/AccountsMultiSelect';
|
||||||
import {useFormik} from 'formik';
|
import {useFormik} from 'formik';
|
||||||
import FinancialStatementDateRange from 'containers/Dashboard/FinancialStatements/FinancialStatementDateRange';
|
import FinancialStatementDateRange from 'containers/FinancialStatements/FinancialStatementDateRange';
|
||||||
import * as Yup from 'yup';
|
import * as Yup from 'yup';
|
||||||
import RadiosAccountingBasis from '../RadiosAccountingBasis';
|
import RadiosAccountingBasis from '../RadiosAccountingBasis';
|
||||||
|
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
import React, {useState, useCallback, useEffect, useMemo} from 'react';
|
import React, {useState, useCallback, useEffect, useMemo} from 'react';
|
||||||
import {compose} from 'utils';
|
import {compose} from 'utils';
|
||||||
import JournalConnect from 'connectors/Journal.connect';
|
import JournalConnect from 'connectors/Journal.connect';
|
||||||
import JournalHeader from 'containers/Dashboard/FinancialStatements/Journal/JournalHeader';
|
import JournalHeader from 'containers/FinancialStatements/Journal/JournalHeader';
|
||||||
import useAsync from 'hooks/async';
|
import useAsync from 'hooks/async';
|
||||||
import {useIntl} from 'react-intl';
|
import {useIntl} from 'react-intl';
|
||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
@@ -8,8 +8,8 @@ import moment from 'moment';
|
|||||||
import {useFormik} from 'formik';
|
import {useFormik} from 'formik';
|
||||||
import {useIntl} from 'react-intl';
|
import {useIntl} from 'react-intl';
|
||||||
import * as Yup from 'yup';
|
import * as Yup from 'yup';
|
||||||
import FinancialStatementDateRange from 'containers/Dashboard/FinancialStatements/FinancialStatementDateRange';
|
import FinancialStatementDateRange from 'containers/FinancialStatements/FinancialStatementDateRange';
|
||||||
import FinancialStatementHeader from 'containers/Dashboard/FinancialStatements/FinancialStatementHeader';
|
import FinancialStatementHeader from 'containers/FinancialStatements/FinancialStatementHeader';
|
||||||
|
|
||||||
|
|
||||||
export default function JournalHeader({
|
export default function JournalHeader({
|
||||||
@@ -7,8 +7,8 @@ import moment from 'moment';
|
|||||||
import {useFormik} from 'formik';
|
import {useFormik} from 'formik';
|
||||||
import {useIntl} from 'react-intl';
|
import {useIntl} from 'react-intl';
|
||||||
import * as Yup from 'yup';
|
import * as Yup from 'yup';
|
||||||
import FinancialStatementDateRange from 'containers/Dashboard/FinancialStatements/FinancialStatementDateRange';
|
import FinancialStatementDateRange from 'containers/FinancialStatements/FinancialStatementDateRange';
|
||||||
import FinancialStatementHeader from 'containers/Dashboard/FinancialStatements/FinancialStatementHeader';
|
import FinancialStatementHeader from 'containers/FinancialStatements/FinancialStatementHeader';
|
||||||
import SelectsListColumnsBy from '../SelectDisplayColumnsBy';
|
import SelectsListColumnsBy from '../SelectDisplayColumnsBy';
|
||||||
import RadiosAccountingBasis from '../RadiosAccountingBasis';
|
import RadiosAccountingBasis from '../RadiosAccountingBasis';
|
||||||
|
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
import React, {useState, useCallback, useMemo} from 'react';
|
import React, {useState, useCallback, useMemo} from 'react';
|
||||||
import FinancialStatementHeader from 'containers/Dashboard/FinancialStatements/FinancialStatementHeader';
|
import FinancialStatementHeader from 'containers/FinancialStatements/FinancialStatementHeader';
|
||||||
import {Row, Col} from 'react-grid-system';
|
import {Row, Col} from 'react-grid-system';
|
||||||
import {
|
import {
|
||||||
Button,
|
Button,
|
||||||
@@ -17,7 +17,7 @@ import {useIntl} from 'react-intl';
|
|||||||
import { useFormik } from 'formik';
|
import { useFormik } from 'formik';
|
||||||
import * as Yup from 'yup';
|
import * as Yup from 'yup';
|
||||||
import Icon from 'components/Icon';
|
import Icon from 'components/Icon';
|
||||||
import FinancialStatementDateRange from 'containers/Dashboard/FinancialStatements/FinancialStatementDateRange';
|
import FinancialStatementDateRange from 'containers/FinancialStatements/FinancialStatementDateRange';
|
||||||
|
|
||||||
export default function TrialBalanceSheetHeader({
|
export default function TrialBalanceSheetHeader({
|
||||||
pageFilter,
|
pageFilter,
|
||||||
@@ -16,4 +16,5 @@ const mapActionsToProps = (dispatch) => ({
|
|||||||
type: t.CHANGE_DASHBOARD_PAGE_TITLE, pageTitle
|
type: t.CHANGE_DASHBOARD_PAGE_TITLE, pageTitle
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
|
||||||
export default connect(null, mapActionsToProps)(DashboardHomepage);
|
export default connect(null, mapActionsToProps)(DashboardHomepage);
|
||||||
53
client/src/containers/Items/ItemCategoriesList.js
Normal file
53
client/src/containers/Items/ItemCategoriesList.js
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
import React, { useEffect, useState, useCallback } from 'react';
|
||||||
|
import { useParams } from 'react-router-dom';
|
||||||
|
import useAsync from 'hooks/async';
|
||||||
|
|
||||||
|
import DashboardInsider from 'components/Dashboard/DashboardInsider';
|
||||||
|
import ItemCategoriesDataTable from 'containers/Items/ItemCategoriesTable';
|
||||||
|
import ItemsCategoryActionsBar from 'containers/Items/ItemsCategoryActionsBar';
|
||||||
|
|
||||||
|
import withDashboardActions from 'containers/Dashboard/withDashboard';
|
||||||
|
import withItemCategoriesActions from 'containers/Items/withItemCategoriesActions';
|
||||||
|
import withItemCategories from 'containers/Items/withItemCategories';
|
||||||
|
import { compose } from 'utils';
|
||||||
|
|
||||||
|
|
||||||
|
const ItemCategoryList = ({
|
||||||
|
changePageTitle,
|
||||||
|
requestFetchItemCategories,
|
||||||
|
}) => {
|
||||||
|
const { id } = useParams();
|
||||||
|
const [selectedRows, setSelectedRows] = useState([]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
id
|
||||||
|
? changePageTitle('Edit Category Details')
|
||||||
|
: changePageTitle('Category List');
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const fetchCategories = useAsync(() => {
|
||||||
|
return Promise.all([
|
||||||
|
requestFetchItemCategories(),
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
const handleFilterChanged = useCallback(() => {
|
||||||
|
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<DashboardInsider name={'item-category-list'}>
|
||||||
|
<ItemsCategoryActionsBar
|
||||||
|
onFilterChanged={handleFilterChanged}
|
||||||
|
selectedRows={selectedRows} />
|
||||||
|
|
||||||
|
<ItemCategoriesDataTable />
|
||||||
|
</DashboardInsider>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default compose(
|
||||||
|
withDashboardActions,
|
||||||
|
withItemCategoriesActions,
|
||||||
|
withItemCategories,
|
||||||
|
)(ItemCategoryList);
|
||||||
@@ -188,6 +188,8 @@ const ItemForm = ({
|
|||||||
});
|
});
|
||||||
}, [setDeletedFiles, deletedFiles]);
|
}, [setDeletedFiles, deletedFiles]);
|
||||||
|
|
||||||
|
const handleCancelClickBtn = () => { history.goBack(); };
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div class='item-form'>
|
<div class='item-form'>
|
||||||
<form onSubmit={handleSubmit}>
|
<form onSubmit={handleSubmit}>
|
||||||
@@ -442,7 +444,7 @@ const ItemForm = ({
|
|||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
<Button className={'ml1'}>Save as Draft</Button>
|
<Button className={'ml1'}>Save as Draft</Button>
|
||||||
<Button className={'ml1'} onClick={'handleClose'}>Close</Button>
|
<Button className={'ml1'} onClick={handleCancelClickBtn}>Close</Button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
52
client/src/containers/Items/ItemFormPage.js
Normal file
52
client/src/containers/Items/ItemFormPage.js
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
import React, { useEffect } from 'react';
|
||||||
|
import { useParams } from 'react-router-dom';
|
||||||
|
import { useQuery } from 'react-query';
|
||||||
|
|
||||||
|
import ItemForm from 'containers/Items/ItemForm';
|
||||||
|
import DashboardInsider from 'components/Dashboard/DashboardInsider';
|
||||||
|
|
||||||
|
import withDashboard from 'containers/Dashboard/withDashboard';
|
||||||
|
import withAccountsActions from 'containers/Accounts/withAccountsActions';
|
||||||
|
import withItemCategoriesActions from 'containers/Items/withItemCategoriesActions';
|
||||||
|
|
||||||
|
import { compose } from 'utils';
|
||||||
|
|
||||||
|
|
||||||
|
const ItemFormContainer = ({
|
||||||
|
// #withDashboard
|
||||||
|
changePageTitle,
|
||||||
|
|
||||||
|
// #withAccountsActions
|
||||||
|
requestFetchAccounts,
|
||||||
|
|
||||||
|
// #withItemCategoriesActions
|
||||||
|
requestFetchItemCategories,
|
||||||
|
}) => {
|
||||||
|
const { id } = useParams();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
id ?
|
||||||
|
changePageTitle('Edit Item Details') :
|
||||||
|
changePageTitle('New Item');
|
||||||
|
}, [id, changePageTitle]);
|
||||||
|
|
||||||
|
const fetchAccounts = useQuery('accounts-list',
|
||||||
|
(key) => requestFetchAccounts());
|
||||||
|
|
||||||
|
const fetchCategories = useQuery('item-categories-list',
|
||||||
|
(key) => requestFetchItemCategories());
|
||||||
|
|
||||||
|
return (
|
||||||
|
<DashboardInsider
|
||||||
|
loading={fetchAccounts.isFetching || fetchCategories.isFetching}
|
||||||
|
name={'item-form'}>
|
||||||
|
<ItemForm />
|
||||||
|
</DashboardInsider>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default compose(
|
||||||
|
withDashboard,
|
||||||
|
withAccountsActions,
|
||||||
|
withItemCategoriesActions,
|
||||||
|
)(ItemFormContainer);
|
||||||
@@ -1,8 +1,6 @@
|
|||||||
import React, { useMemo, useCallback, useState } from 'react';
|
import React, { useMemo, useCallback, useState } from 'react';
|
||||||
import { useRouteMatch, useHistory } from 'react-router-dom';
|
import { useRouteMatch, useHistory } from 'react-router-dom';
|
||||||
import DashboardActionsBar from 'components/Dashboard/DashboardActionsBar';
|
import classNames from 'classnames';
|
||||||
|
|
||||||
import { compose } from 'utils';
|
|
||||||
import {
|
import {
|
||||||
MenuItem,
|
MenuItem,
|
||||||
Popover,
|
Popover,
|
||||||
@@ -15,43 +13,42 @@ import {
|
|||||||
Classes,
|
Classes,
|
||||||
Intent,
|
Intent,
|
||||||
} from '@blueprintjs/core';
|
} from '@blueprintjs/core';
|
||||||
import classNames from 'classnames';
|
import { compose } from 'utils';
|
||||||
|
import DashboardActionsBar from 'components/Dashboard/DashboardActionsBar';
|
||||||
import Icon from 'components/Icon';
|
import Icon from 'components/Icon';
|
||||||
import DashboardConnect from 'connectors/Dashboard.connector';
|
|
||||||
import ResourceConnect from 'connectors/Resource.connector';
|
|
||||||
import FilterDropdown from 'components/FilterDropdown';
|
import FilterDropdown from 'components/FilterDropdown';
|
||||||
import ItemsConnect from 'connectors/Items.connect';
|
|
||||||
import DialogConnect from 'connectors/Dialog.connector';
|
import DialogConnect from 'connectors/Dialog.connector';
|
||||||
|
import withResourceDetail from 'containers/Resources/withResourceDetails';
|
||||||
|
import withItems from 'containers/Items/withItems';
|
||||||
|
import { If } from 'components';
|
||||||
|
|
||||||
const ItemsActionsBar = ({
|
const ItemsActionsBar = ({
|
||||||
openDialog,
|
openDialog,
|
||||||
getResourceFields,
|
|
||||||
getResourceViews,
|
resourceName = 'items',
|
||||||
views,
|
resourceFields,
|
||||||
|
|
||||||
|
itemsViews,
|
||||||
|
|
||||||
onFilterChanged,
|
onFilterChanged,
|
||||||
addItemsTableQueries,
|
|
||||||
selectedRows = [],
|
selectedRows = [],
|
||||||
}) => {
|
}) => {
|
||||||
const { path } = useRouteMatch();
|
const { path } = useRouteMatch();
|
||||||
const history = useHistory();
|
const history = useHistory();
|
||||||
const [filterCount, setFilterCount] = useState(0);
|
const [filterCount, setFilterCount] = useState(0);
|
||||||
|
|
||||||
const viewsMenuItems = views.map(view =>
|
const viewsMenuItems = itemsViews.map(view =>
|
||||||
(<MenuItem href={`${path}/${view.id}/custom_view`} text={view.name} />));
|
(<MenuItem href={`${path}/${view.id}/custom_view`} text={view.name} />));
|
||||||
|
|
||||||
const onClickNewItem = () => {
|
const onClickNewItem = () => {
|
||||||
history.push('/dashboard/items/new');
|
history.push('/dashboard/items/new');
|
||||||
};
|
};
|
||||||
const itemsFields = getResourceFields('items');
|
|
||||||
const hasSelectedRows = useMemo(() => selectedRows.length > 0, [selectedRows]);
|
const hasSelectedRows = useMemo(() => selectedRows.length > 0, [selectedRows]);
|
||||||
|
|
||||||
const filterDropdown = FilterDropdown({
|
const filterDropdown = FilterDropdown({
|
||||||
fields: itemsFields,
|
fields: resourceFields,
|
||||||
onFilterChange: (filterConditions) => {
|
onFilterChange: (filterConditions) => {
|
||||||
setFilterCount(filterConditions.length);
|
setFilterCount(filterConditions.length);
|
||||||
addItemsTableQueries({
|
|
||||||
filter_roles: filterConditions || '',
|
|
||||||
});
|
|
||||||
onFilterChanged && onFilterChanged(filterConditions);
|
onFilterChanged && onFilterChanged(filterConditions);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -105,14 +102,14 @@ const ItemsActionsBar = ({
|
|||||||
/>
|
/>
|
||||||
</Popover>
|
</Popover>
|
||||||
|
|
||||||
{hasSelectedRows && (
|
<If condition={hasSelectedRows}>
|
||||||
<Button
|
<Button
|
||||||
className={Classes.MINIMAL}
|
className={Classes.MINIMAL}
|
||||||
intent={Intent.DANGER}
|
intent={Intent.DANGER}
|
||||||
icon={<Icon icon='trash' />}
|
icon={<Icon icon='trash' />}
|
||||||
text='Delete'
|
text='Delete'
|
||||||
/>
|
/>
|
||||||
)}
|
</If>
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
className={Classes.MINIMAL}
|
className={Classes.MINIMAL}
|
||||||
@@ -131,7 +128,6 @@ const ItemsActionsBar = ({
|
|||||||
|
|
||||||
export default compose(
|
export default compose(
|
||||||
DialogConnect,
|
DialogConnect,
|
||||||
DashboardConnect,
|
withItems,
|
||||||
ResourceConnect,
|
withResourceDetail,
|
||||||
ItemsConnect
|
|
||||||
)(ItemsActionsBar);
|
)(ItemsActionsBar);
|
||||||
@@ -11,19 +11,24 @@ import {
|
|||||||
PopoverInteractionKind,
|
PopoverInteractionKind,
|
||||||
} from '@blueprintjs/core';
|
} from '@blueprintjs/core';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
|
import { connect } from 'react-redux';
|
||||||
|
import { If } from 'components';
|
||||||
|
|
||||||
import Icon from 'components/Icon';
|
import Icon from 'components/Icon';
|
||||||
import DashboardConnect from 'connectors/Dashboard.connector';
|
|
||||||
import ItemsCategoryConnect from 'connectors/ItemsCategory.connect';
|
|
||||||
import DialogConnect from 'connectors/Dialog.connector';
|
import DialogConnect from 'connectors/Dialog.connector';
|
||||||
import FilterDropdown from 'components/FilterDropdown';
|
import FilterDropdown from 'components/FilterDropdown';
|
||||||
import ResourceConnect from 'connectors/Resource.connector';
|
|
||||||
|
import withResourceDetail from 'containers/Resources/withResourceDetails';
|
||||||
|
import withDashboard from 'containers/Dashboard/withDashboard';
|
||||||
|
|
||||||
|
|
||||||
const ItemsCategoryActionsBar = ({
|
const ItemsCategoryActionsBar = ({
|
||||||
|
resourceName = 'item_category',
|
||||||
|
resourceFields,
|
||||||
|
|
||||||
openDialog,
|
openDialog,
|
||||||
onDeleteCategory,
|
onDeleteCategory,
|
||||||
onFilterChanged,
|
onFilterChanged,
|
||||||
getResourceFields,
|
|
||||||
selectedRows,
|
selectedRows,
|
||||||
}) => {
|
}) => {
|
||||||
const onClickNewCategory = useCallback(() => {
|
const onClickNewCategory = useCallback(() => {
|
||||||
@@ -34,11 +39,10 @@ const ItemsCategoryActionsBar = ({
|
|||||||
onDeleteCategory(selectedRows);
|
onDeleteCategory(selectedRows);
|
||||||
}, [selectedRows, onDeleteCategory]);
|
}, [selectedRows, onDeleteCategory]);
|
||||||
|
|
||||||
const categoriesFields = getResourceFields('itemCategories');
|
|
||||||
const hasSelectedRows = useMemo(() => selectedRows.length > 0, [selectedRows]);
|
const hasSelectedRows = useMemo(() => selectedRows.length > 0, [selectedRows]);
|
||||||
|
|
||||||
const filterDropdown = FilterDropdown({
|
const filterDropdown = FilterDropdown({
|
||||||
fields: categoriesFields,
|
fields: resourceFields,
|
||||||
onFilterChange: (filterConditions) => {
|
onFilterChange: (filterConditions) => {
|
||||||
onFilterChanged && onFilterChanged(filterConditions);
|
onFilterChanged && onFilterChanged(filterConditions);
|
||||||
},
|
},
|
||||||
@@ -53,6 +57,7 @@ const ItemsCategoryActionsBar = ({
|
|||||||
onClick={onClickNewCategory}
|
onClick={onClickNewCategory}
|
||||||
/>
|
/>
|
||||||
<Popover
|
<Popover
|
||||||
|
minimal={true}
|
||||||
content={filterDropdown}
|
content={filterDropdown}
|
||||||
interactionKind={PopoverInteractionKind.CLICK}
|
interactionKind={PopoverInteractionKind.CLICK}
|
||||||
position={Position.BOTTOM_LEFT}
|
position={Position.BOTTOM_LEFT}
|
||||||
@@ -64,7 +69,7 @@ const ItemsCategoryActionsBar = ({
|
|||||||
/>
|
/>
|
||||||
</Popover>
|
</Popover>
|
||||||
|
|
||||||
{ hasSelectedRows && (
|
<If condition={hasSelectedRows}>
|
||||||
<Button
|
<Button
|
||||||
className={Classes.MINIMAL}
|
className={Classes.MINIMAL}
|
||||||
icon={<Icon icon='trash' iconSize={15} />}
|
icon={<Icon icon='trash' iconSize={15} />}
|
||||||
@@ -72,7 +77,8 @@ const ItemsCategoryActionsBar = ({
|
|||||||
intent={Intent.DANGER}
|
intent={Intent.DANGER}
|
||||||
onClick={handleDeleteCategory}
|
onClick={handleDeleteCategory}
|
||||||
/>
|
/>
|
||||||
)}
|
</If>
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
className={Classes.MINIMAL}
|
className={Classes.MINIMAL}
|
||||||
icon={<Icon icon='file-import' />}
|
icon={<Icon icon='file-import' />}
|
||||||
@@ -88,9 +94,15 @@ const ItemsCategoryActionsBar = ({
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const mapStateToProps = (state, props) => ({
|
||||||
|
resourceName: 'items_categories',
|
||||||
|
});
|
||||||
|
|
||||||
|
const withItemsCategoriesActionsBar = connect(mapStateToProps);
|
||||||
|
|
||||||
export default compose(
|
export default compose(
|
||||||
|
withItemsCategoriesActionsBar,
|
||||||
DialogConnect,
|
DialogConnect,
|
||||||
DashboardConnect,
|
withDashboard,
|
||||||
ItemsCategoryConnect,
|
withResourceDetail
|
||||||
ResourceConnect
|
|
||||||
)(ItemsCategoryActionsBar);
|
)(ItemsCategoryActionsBar);
|
||||||
@@ -7,17 +7,21 @@ import {
|
|||||||
MenuDivider,
|
MenuDivider,
|
||||||
Position,
|
Position,
|
||||||
} from '@blueprintjs/core'
|
} from '@blueprintjs/core'
|
||||||
import CustomViewConnect from 'connectors/View.connector';
|
|
||||||
import ItemsConnect from 'connectors/Items.connect';
|
|
||||||
import {compose} from 'utils';
|
import {compose} from 'utils';
|
||||||
import DataTable from 'components/DataTable';
|
import DataTable from 'components/DataTable';
|
||||||
import Icon from 'components/Icon';
|
import Icon from 'components/Icon';
|
||||||
import Money from 'components/Money';
|
import Money from 'components/Money';
|
||||||
|
|
||||||
|
import withItems from 'containers/Items/withItems';
|
||||||
|
import LoadingIndicator from 'components/LoadingIndicator';
|
||||||
|
|
||||||
const ItemsDataTable = ({
|
const ItemsDataTable = ({
|
||||||
|
loading,
|
||||||
|
|
||||||
itemsTableLoading,
|
itemsTableLoading,
|
||||||
currentPageItems,
|
itemsCurrentPage,
|
||||||
|
|
||||||
|
// props
|
||||||
onEditItem,
|
onEditItem,
|
||||||
onDeleteItem,
|
onDeleteItem,
|
||||||
onFetchData,
|
onFetchData,
|
||||||
@@ -112,18 +116,19 @@ const ItemsDataTable = ({
|
|||||||
}, [onSelectedRowsChange]);
|
}, [onSelectedRowsChange]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
<LoadingIndicator loading={loading} mount={false}>
|
||||||
<DataTable
|
<DataTable
|
||||||
columns={columns}
|
columns={columns}
|
||||||
data={currentPageItems}
|
data={itemsCurrentPage}
|
||||||
selectionColumn={selectionColumn}
|
selectionColumn={selectionColumn}
|
||||||
onFetchData={handleFetchData}
|
onFetchData={handleFetchData}
|
||||||
loading={itemsTableLoading && !initialMount}
|
loading={itemsTableLoading && !initialMount}
|
||||||
noInitialFetch={true}
|
noInitialFetch={true}
|
||||||
onSelectedRowsChange={handleSelectedRowsChange} />
|
onSelectedRowsChange={handleSelectedRowsChange} />
|
||||||
|
</LoadingIndicator>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default compose(
|
export default compose(
|
||||||
ItemsConnect,
|
withItems,
|
||||||
CustomViewConnect,
|
|
||||||
)(ItemsDataTable);
|
)(ItemsDataTable);
|
||||||
@@ -8,47 +8,58 @@ import {
|
|||||||
Alert,
|
Alert,
|
||||||
} from '@blueprintjs/core';
|
} from '@blueprintjs/core';
|
||||||
import DashboardInsider from 'components/Dashboard/DashboardInsider';
|
import DashboardInsider from 'components/Dashboard/DashboardInsider';
|
||||||
import useAsync from 'hooks/async';
|
import { useQuery } from 'react-query';
|
||||||
import ItemsActionsBar from 'containers/Dashboard/Items/ItemsActionsBar';
|
import ItemsActionsBar from 'containers/Items/ItemsActionsBar';
|
||||||
import { compose } from 'utils';
|
import { compose } from 'utils';
|
||||||
import ItemsDataTable from './ItemsDataTable';
|
import ItemsDataTable from './ItemsDataTable';
|
||||||
import DashboardPageContent from 'components/Dashboard/DashboardPageContent';
|
import DashboardPageContent from 'components/Dashboard/DashboardPageContent';
|
||||||
import ResourceConnect from 'connectors/Resource.connector';
|
|
||||||
import DashboardConnect from 'connectors/Dashboard.connector';
|
import ItemsViewsTabs from 'containers/Items/ItemsViewsTabs';
|
||||||
import ItemsConnect from 'connectors/Items.connect';
|
|
||||||
import CustomViewsConnect from 'connectors/CustomView.connector'
|
|
||||||
import ItemsViewsTabs from 'containers/Dashboard/Items/ItemsViewsTabs';
|
|
||||||
import AppToaster from 'components/AppToaster';
|
import AppToaster from 'components/AppToaster';
|
||||||
|
|
||||||
|
import withItems from 'containers/Items/withItems';
|
||||||
|
import withResourceActions from 'containers/Resources/withResourcesActions';
|
||||||
|
import withDashboardActions from 'containers/Dashboard/withDashboard';
|
||||||
|
import withItemsActions from 'containers/Items/withItemsActions';
|
||||||
|
import withViewsActions from 'containers/Views/withViewsActions';
|
||||||
|
|
||||||
|
|
||||||
function ItemsList({
|
function ItemsList({
|
||||||
|
// #withDashboard
|
||||||
changePageTitle,
|
changePageTitle,
|
||||||
fetchResourceViews,
|
|
||||||
fetchResourceFields,
|
// #withResourceActions
|
||||||
views,
|
requestFetchResourceViews,
|
||||||
|
requestFetchResourceFields,
|
||||||
|
|
||||||
|
// #withItems
|
||||||
|
itemsViews,
|
||||||
|
itemsCurrentPage,
|
||||||
|
itemsTableQuery,
|
||||||
|
|
||||||
|
// #withItemsActions
|
||||||
requestDeleteItem,
|
requestDeleteItem,
|
||||||
requestFetchItems,
|
requestFetchItems,
|
||||||
addItemsTableQueries,
|
addItemsTableQueries,
|
||||||
|
changeItemsCurrentView
|
||||||
}) {
|
}) {
|
||||||
const [deleteItem, setDeleteItem] = useState(false);
|
const [deleteItem, setDeleteItem] = useState(false);
|
||||||
const [selectedRows, setSelectedRows] = useState([]);
|
const [selectedRows, setSelectedRows] = useState([]);
|
||||||
|
const [tableLoading, setTableLoading] = useState(false);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
changePageTitle('Items List');
|
changePageTitle('Items List');
|
||||||
}, [changePageTitle]);
|
}, [changePageTitle]);
|
||||||
|
|
||||||
const fetchHook = useAsync(async () => {
|
const fetchHook = useQuery('items-resource', () => {
|
||||||
await Promise.all([
|
return Promise.all([
|
||||||
fetchResourceViews('items'),
|
requestFetchResourceViews('items'),
|
||||||
fetchResourceFields('items'),
|
requestFetchResourceFields('items'),
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
const fetchItems = useAsync(async () => {
|
const fetchItems = useQuery(['items-table', itemsTableQuery],
|
||||||
await Promise.all([
|
() => requestFetchItems({}));
|
||||||
requestFetchItems({ }),
|
|
||||||
])
|
|
||||||
});
|
|
||||||
|
|
||||||
// Handle click delete item.
|
// Handle click delete item.
|
||||||
const handleDeleteItem = useCallback((item) => {
|
const handleDeleteItem = useCallback((item) => {
|
||||||
@@ -70,6 +81,7 @@ function ItemsList({
|
|||||||
});
|
});
|
||||||
}, [requestDeleteItem, deleteItem]);
|
}, [requestDeleteItem, deleteItem]);
|
||||||
|
|
||||||
|
// Handle fetch data table.
|
||||||
const handleFetchData = useCallback(({ pageIndex, pageSize, sortBy }) => {
|
const handleFetchData = useCallback(({ pageIndex, pageSize, sortBy }) => {
|
||||||
addItemsTableQueries({
|
addItemsTableQueries({
|
||||||
...(sortBy.length > 0) ? {
|
...(sortBy.length > 0) ? {
|
||||||
@@ -77,30 +89,40 @@ function ItemsList({
|
|||||||
sort_order: sortBy[0].desc ? 'desc' : 'asc',
|
sort_order: sortBy[0].desc ? 'desc' : 'asc',
|
||||||
} : {},
|
} : {},
|
||||||
});
|
});
|
||||||
fetchItems.execute();
|
|
||||||
}, [fetchItems, addItemsTableQueries]);
|
}, [fetchItems, addItemsTableQueries]);
|
||||||
|
|
||||||
// Handle filter change to re-fetch the items.
|
// Handle filter change to re-fetch the items.
|
||||||
const handleFilterChanged = useCallback(() => {
|
const handleFilterChanged = useCallback((filterConditions) => {
|
||||||
fetchItems.execute();
|
addItemsTableQueries({
|
||||||
|
filter_roles: filterConditions || '',
|
||||||
|
});
|
||||||
}, [fetchItems]);
|
}, [fetchItems]);
|
||||||
|
|
||||||
// Handle custom view change to re-fetch the items.
|
// Handle custom view change to re-fetch the items.
|
||||||
const handleCustomViewChanged = useCallback(() => {
|
const handleCustomViewChanged = useCallback((customViewId) => {
|
||||||
fetchItems.execute();
|
setTableLoading(true);
|
||||||
}, [fetchItems]);
|
}, [fetchItems]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (tableLoading && !fetchItems.isFetching) {
|
||||||
|
setTableLoading(false);
|
||||||
|
}
|
||||||
|
}, [tableLoading, fetchItems.isFetching]);
|
||||||
|
|
||||||
// Handle selected rows change.
|
// Handle selected rows change.
|
||||||
const handleSelectedRowsChange = useCallback((accounts) => {
|
const handleSelectedRowsChange = useCallback((accounts) => {
|
||||||
setSelectedRows(accounts);
|
setSelectedRows(accounts);
|
||||||
}, [setSelectedRows]);
|
}, [setSelectedRows]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<DashboardInsider isLoading={fetchHook.pending} name={'items-list'}>
|
<DashboardInsider
|
||||||
|
isLoading={fetchHook.isFetching}
|
||||||
|
name={'items-list'}>
|
||||||
|
|
||||||
<ItemsActionsBar
|
<ItemsActionsBar
|
||||||
onFilterChanged={handleFilterChanged}
|
onFilterChanged={handleFilterChanged}
|
||||||
selectedRows={selectedRows}
|
selectedRows={selectedRows}
|
||||||
views={views} />
|
views={itemsViews} />
|
||||||
|
|
||||||
<DashboardPageContent>
|
<DashboardPageContent>
|
||||||
<Switch>
|
<Switch>
|
||||||
@@ -111,9 +133,11 @@ function ItemsList({
|
|||||||
'/dashboard/items'
|
'/dashboard/items'
|
||||||
]}>
|
]}>
|
||||||
<ItemsViewsTabs
|
<ItemsViewsTabs
|
||||||
|
itemsViews={itemsViews}
|
||||||
onViewChanged={handleCustomViewChanged} />
|
onViewChanged={handleCustomViewChanged} />
|
||||||
|
|
||||||
<ItemsDataTable
|
<ItemsDataTable
|
||||||
|
loading={tableLoading}
|
||||||
onDeleteItem={handleDeleteItem}
|
onDeleteItem={handleDeleteItem}
|
||||||
onEditItem={handleEditItem}
|
onEditItem={handleEditItem}
|
||||||
onFetchData={handleFetchData}
|
onFetchData={handleFetchData}
|
||||||
@@ -140,8 +164,9 @@ function ItemsList({
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default compose(
|
export default compose(
|
||||||
DashboardConnect,
|
withItems,
|
||||||
ResourceConnect,
|
withResourceActions,
|
||||||
ItemsConnect,
|
withDashboardActions,
|
||||||
CustomViewsConnect,
|
withItemsActions,
|
||||||
|
withViewsActions,
|
||||||
)(ItemsList);
|
)(ItemsList);
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user