mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-15 20:30:33 +00:00
feat(FinancialReports): add loading progress bar.
fix(preformance): Optimize preformance of virtualized list. fix(preformance): Optimize financial reports preformance.
This commit is contained in:
@@ -19,7 +19,7 @@ function App({ locale }) {
|
||||
const queryConfig = {
|
||||
defaultOptions: {
|
||||
queries: {
|
||||
refetchOnWindowFocus: false,
|
||||
refetchOnWindowFocus: true,
|
||||
staleTime: 30000,
|
||||
},
|
||||
},
|
||||
|
||||
@@ -4,8 +4,6 @@ import { useQuery } from 'react-query';
|
||||
|
||||
import 'style/pages/Dashboard/Dashboard.scss';
|
||||
|
||||
import DashboardLoadingIndicator from './DashboardLoadingIndicator';
|
||||
|
||||
import Sidebar from 'components/Sidebar/Sidebar';
|
||||
import DashboardContent from 'components/Dashboard/DashboardContent';
|
||||
import DialogsContainer from 'components/DialogsContainer';
|
||||
@@ -13,22 +11,15 @@ import PreferencesPage from 'components/Preferences/PreferencesPage';
|
||||
import Search from 'containers/GeneralSearch/Search';
|
||||
import DashboardSplitPane from 'components/Dashboard/DashboardSplitePane';
|
||||
import GlobalHotkeys from './GlobalHotkeys';
|
||||
import withSettingsActions from 'containers/Settings/withSettingsActions';
|
||||
import DashboardProvider from './DashboardProvider';
|
||||
import DrawersContainer from 'components/DrawersContainer';
|
||||
|
||||
import { compose } from 'utils';
|
||||
|
||||
/**
|
||||
* Dashboard page.
|
||||
*/
|
||||
function Dashboard({
|
||||
// #withSettings
|
||||
requestFetchOptions,
|
||||
}) {
|
||||
const fetchOptions = useQuery(['options'], () => requestFetchOptions());
|
||||
|
||||
export default function Dashboard() {
|
||||
return (
|
||||
<DashboardLoadingIndicator isLoading={fetchOptions.isFetching}>
|
||||
<DashboardProvider>
|
||||
<Switch>
|
||||
<Route path="/preferences">
|
||||
<DashboardSplitPane>
|
||||
@@ -49,8 +40,6 @@ function Dashboard({
|
||||
<DialogsContainer />
|
||||
<GlobalHotkeys />
|
||||
<DrawersContainer />
|
||||
</DashboardLoadingIndicator>
|
||||
</DashboardProvider>
|
||||
);
|
||||
}
|
||||
|
||||
export default compose(withSettingsActions)(Dashboard);
|
||||
}
|
||||
@@ -10,7 +10,7 @@ export default function DashboardLoadingIndicator({
|
||||
className,
|
||||
children,
|
||||
}) {
|
||||
return (
|
||||
return (
|
||||
<Choose>
|
||||
<Choose.When condition={isLoading}>
|
||||
<BigcapitalLoading />
|
||||
|
||||
16
client/src/components/Dashboard/DashboardProvider.js
Normal file
16
client/src/components/Dashboard/DashboardProvider.js
Normal file
@@ -0,0 +1,16 @@
|
||||
import React from 'react';
|
||||
import DashboardLoadingIndicator from './DashboardLoadingIndicator';
|
||||
import { useSettings } from 'hooks/query';
|
||||
|
||||
/**
|
||||
* Dashboard provider.
|
||||
*/
|
||||
export default function DashboardProvider({ children }) {
|
||||
const { isLoading } = useSettings();
|
||||
|
||||
return (
|
||||
<DashboardLoadingIndicator isLoading={isLoading}>
|
||||
{ children }
|
||||
</DashboardLoadingIndicator>
|
||||
)
|
||||
}
|
||||
@@ -1,46 +1,21 @@
|
||||
import React from 'react';
|
||||
import { Switch, Route } from 'react-router';
|
||||
import { useQuery } from 'react-query';
|
||||
|
||||
import Dashboard from 'components/Dashboard/Dashboard';
|
||||
import SetupWizardPage from 'containers/Setup/WizardSetupPage';
|
||||
import DashboardLoadingIndicator from 'components/Dashboard/DashboardLoadingIndicator';
|
||||
|
||||
import withOrganizationActions from 'containers/Organization/withOrganizationActions';
|
||||
import withSubscriptionsActions from 'containers/Subscriptions/withSubscriptionsActions';
|
||||
|
||||
import EnsureOrganizationIsReady from 'components/Guards/EnsureOrganizationIsReady';
|
||||
import EnsureOrganizationIsNotReady from 'components/Guards/EnsureOrganizationIsNotReady';
|
||||
|
||||
import { compose } from 'utils';
|
||||
import { PrivatePagesProvider } from './PrivatePagesProvider';
|
||||
|
||||
import 'style/pages/Dashboard/Dashboard.scss';
|
||||
|
||||
/**
|
||||
* Dashboard inner private pages.
|
||||
*/
|
||||
function DashboardPrivatePages({
|
||||
// #withOrganizationActions
|
||||
requestAllOrganizations,
|
||||
|
||||
// #withSubscriptionsActions
|
||||
requestFetchSubscriptions,
|
||||
}) {
|
||||
// Fetches all user's organizatins.
|
||||
const fetchOrganizations = useQuery(
|
||||
['organizations'], () => requestAllOrganizations(),
|
||||
);
|
||||
|
||||
// Fetches organization subscriptions.
|
||||
const fetchSuscriptions = useQuery(
|
||||
['susbcriptions'], () => requestFetchSubscriptions(),
|
||||
)
|
||||
|
||||
export default function DashboardPrivatePages() {
|
||||
return (
|
||||
<DashboardLoadingIndicator isLoading={
|
||||
fetchOrganizations.isFetching ||
|
||||
fetchSuscriptions.isFetching
|
||||
}>
|
||||
<PrivatePagesProvider>
|
||||
<Switch>
|
||||
<Route path={'/setup'}>
|
||||
<EnsureOrganizationIsNotReady>
|
||||
@@ -54,11 +29,6 @@ function DashboardPrivatePages({
|
||||
</EnsureOrganizationIsReady>
|
||||
</Route>
|
||||
</Switch>
|
||||
</DashboardLoadingIndicator>
|
||||
</PrivatePagesProvider>
|
||||
);
|
||||
}
|
||||
|
||||
export default compose(
|
||||
withOrganizationActions,
|
||||
withSubscriptionsActions,
|
||||
)(DashboardPrivatePages);
|
||||
}
|
||||
17
client/src/components/Dashboard/PrivatePagesProvider.js
Normal file
17
client/src/components/Dashboard/PrivatePagesProvider.js
Normal file
@@ -0,0 +1,17 @@
|
||||
import React from 'react';
|
||||
import DashboardLoadingIndicator from 'components/Dashboard/DashboardLoadingIndicator';
|
||||
import { useCurrentOrganization } from '../../hooks/query/organization';
|
||||
|
||||
/**
|
||||
* Private pages provider.
|
||||
*/
|
||||
export function PrivatePagesProvider({ children }) {
|
||||
// Fetches the current user's organization.
|
||||
const { isLoading } = useCurrentOrganization();
|
||||
|
||||
return (
|
||||
<DashboardLoadingIndicator isLoading={isLoading}>
|
||||
{children}
|
||||
</DashboardLoadingIndicator>
|
||||
)
|
||||
}
|
||||
@@ -62,7 +62,7 @@ export default function TableCell({
|
||||
'cell-inner',
|
||||
)}
|
||||
style={{
|
||||
'padding-left':
|
||||
paddingLeft:
|
||||
isExpandColumn && expandable
|
||||
? `${depth * expandColumnSpace}rem`
|
||||
: '',
|
||||
|
||||
@@ -66,7 +66,7 @@ function TableHeaderGroup({ headerGroup }) {
|
||||
return (
|
||||
<div {...headerGroup.getHeaderGroupProps()} className="tr">
|
||||
{headerGroup.headers.map((column, index) => (
|
||||
<TableHeaderCell column={column} index={index} />
|
||||
<TableHeaderCell key={index} column={column} index={index} />
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
@@ -87,8 +87,8 @@ export default function TableHeader() {
|
||||
return (
|
||||
<ScrollSyncPane>
|
||||
<div className="thead">
|
||||
{headerGroups.map((headerGroup) => (
|
||||
<TableHeaderGroup headerGroup={headerGroup} />
|
||||
{headerGroups.map((headerGroup, index) => (
|
||||
<TableHeaderGroup key={index} headerGroup={headerGroup} />
|
||||
))}
|
||||
<If condition={progressBarLoading}>
|
||||
<MaterialProgressBar />
|
||||
|
||||
@@ -1,23 +1,18 @@
|
||||
import React, { useContext } from 'react';
|
||||
import React, { useCallback, useContext } from 'react';
|
||||
import { ContextMenu } from 'components';
|
||||
import classNames from 'classnames';
|
||||
import useContextMenu from 'react-use-context-menu';
|
||||
|
||||
import TableContext from './TableContext';
|
||||
import { saveInvoke } from 'utils';
|
||||
import { ContextMenu } from 'components';
|
||||
|
||||
import { saveInvoke, ConditionalWrapper } from 'utils';
|
||||
|
||||
/**
|
||||
* Table row.
|
||||
* Table row context wrapper.
|
||||
*/
|
||||
export default function TableRow({ row, className, style }) {
|
||||
function TableRowContextMenu({ children, row }) {
|
||||
// Table context.
|
||||
const {
|
||||
props: {
|
||||
TableCellRenderer,
|
||||
rowContextMenu,
|
||||
rowClassNames,
|
||||
ContextMenu: ContextMenuContent,
|
||||
},
|
||||
props: { ContextMenu: ContextMenuContent },
|
||||
table,
|
||||
} = useContext(TableContext);
|
||||
|
||||
@@ -32,33 +27,64 @@ export default function TableRow({ row, className, style }) {
|
||||
collect: () => 'Title',
|
||||
});
|
||||
|
||||
const handleClose = useCallback(() => {
|
||||
setVisible(false);
|
||||
}, [setVisible]);
|
||||
|
||||
return (
|
||||
<div
|
||||
{...row.getRowProps({
|
||||
className: classNames(
|
||||
'tr',
|
||||
{
|
||||
'is-expanded': row.isExpanded && row.canExpand,
|
||||
},
|
||||
saveInvoke(rowClassNames, row),
|
||||
className,
|
||||
),
|
||||
style,
|
||||
})}
|
||||
{...bindTrigger}
|
||||
>
|
||||
{row.cells.map((cell, index) => (
|
||||
<TableCellRenderer cell={cell} row={row} index={index + 1} />
|
||||
))}
|
||||
<div class="tr-context" {...bindTrigger}>
|
||||
{children}
|
||||
|
||||
<ContextMenu
|
||||
bindMenu={bindMenu}
|
||||
isOpen={isVisible}
|
||||
coords={coords}
|
||||
onClosed={() => setVisible(false)}
|
||||
onClosed={handleClose}
|
||||
>
|
||||
<ContextMenuContent {...table} row={row} />
|
||||
</ContextMenu>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Table row.
|
||||
*/
|
||||
export default function TableRow({ row, className, style }) {
|
||||
const {
|
||||
props: {
|
||||
TableCellRenderer,
|
||||
rowClassNames,
|
||||
ContextMenu: ContextMenuContent,
|
||||
},
|
||||
} = useContext(TableContext);
|
||||
|
||||
return (
|
||||
<div
|
||||
{...row.getRowProps({
|
||||
className: classNames(
|
||||
'tr',
|
||||
{ 'is-expanded': row.isExpanded && row.canExpand },
|
||||
saveInvoke(rowClassNames, row),
|
||||
className,
|
||||
),
|
||||
style,
|
||||
})}
|
||||
>
|
||||
<ConditionalWrapper
|
||||
condition={ContextMenuContent}
|
||||
wrapper={TableRowContextMenu}
|
||||
row={row}
|
||||
>
|
||||
{row.cells.map((cell, index) => (
|
||||
<TableCellRenderer
|
||||
key={index}
|
||||
cell={cell}
|
||||
row={row}
|
||||
index={index + 1}
|
||||
/>
|
||||
))}
|
||||
</ConditionalWrapper>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -10,8 +10,8 @@ export default function TableRows() {
|
||||
props: { TableRowRenderer, TableCellRenderer },
|
||||
} = useContext(TableContext);
|
||||
|
||||
return page.map((row) => {
|
||||
return page.map((row, index) => {
|
||||
prepareRow(row);
|
||||
return <TableRowRenderer row={row} TableCellRenderer={TableCellRenderer} />;
|
||||
return <TableRowRenderer key={index} row={row} TableCellRenderer={TableCellRenderer} />;
|
||||
});
|
||||
}
|
||||
@@ -3,11 +3,13 @@ import { WindowScroller, AutoSizer, List } from 'react-virtualized';
|
||||
import { CLASSES } from 'common/classes';
|
||||
import TableContext from './TableContext';
|
||||
|
||||
/**
|
||||
* Table virtualized list row.
|
||||
*/
|
||||
function TableVirtualizedListRow({
|
||||
index,
|
||||
isScrolling,
|
||||
isVisible,
|
||||
key,
|
||||
style,
|
||||
}) {
|
||||
const {
|
||||
@@ -18,7 +20,7 @@ function TableVirtualizedListRow({
|
||||
const row = page[index];
|
||||
prepareRow(row);
|
||||
|
||||
return <TableRowRenderer row={row} style={style} />;
|
||||
return (<TableRowRenderer row={row} style={style} />);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -27,39 +29,38 @@ function TableVirtualizedListRow({
|
||||
export default function TableVirtualizedListRows() {
|
||||
const {
|
||||
table: { page },
|
||||
props: { vListrowHeight, vListOverscanRowCount }
|
||||
props: { vListrowHeight, vListOverscanRowCount },
|
||||
} = useContext(TableContext);
|
||||
|
||||
// Dashboard content pane.
|
||||
const dashboardContentPane = document.querySelector(
|
||||
const dashboardContentPane = React.useMemo(()=> document.querySelector(
|
||||
`.${CLASSES.DASHBOARD_CONTENT_PANE}`,
|
||||
);
|
||||
), []);
|
||||
|
||||
const rowRenderer = React.useCallback(({ key, ...args }) => (
|
||||
<TableVirtualizedListRow {...args} key={key} />
|
||||
), []);
|
||||
|
||||
return (
|
||||
<WindowScroller scrollElement={dashboardContentPane}>
|
||||
{({ height, isScrolling, registerChild, onChildScroll, scrollTop }) => (
|
||||
<div className={'WindowScrollerWrapper'}>
|
||||
<AutoSizer disableHeight>
|
||||
{({ width }) => (
|
||||
<div ref={registerChild}>
|
||||
<List
|
||||
autoHeight={true}
|
||||
className={'List'}
|
||||
height={height}
|
||||
isScrolling={isScrolling}
|
||||
onScroll={onChildScroll}
|
||||
overscanRowCount={vListOverscanRowCount}
|
||||
rowCount={page.length}
|
||||
rowHeight={vListrowHeight}
|
||||
rowRenderer={({ ...args }) => {
|
||||
return <TableVirtualizedListRow {...args} />;
|
||||
}}
|
||||
scrollTop={scrollTop}
|
||||
width={width}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</AutoSizer>
|
||||
</div>
|
||||
{({ height, isScrolling, onChildScroll, scrollTop }) => (
|
||||
<AutoSizer disableHeight>
|
||||
{({ width }) => (
|
||||
<List
|
||||
autoHeight={true}
|
||||
className={'List'}
|
||||
height={height}
|
||||
isScrolling={isScrolling}
|
||||
onScroll={onChildScroll}
|
||||
overscanRowCount={vListOverscanRowCount}
|
||||
rowCount={page.length}
|
||||
rowHeight={vListrowHeight}
|
||||
rowRenderer={rowRenderer}
|
||||
scrollTop={scrollTop}
|
||||
width={width}
|
||||
/>
|
||||
)}
|
||||
</AutoSizer>
|
||||
)}
|
||||
</WindowScroller>
|
||||
);
|
||||
|
||||
@@ -7,7 +7,6 @@ import 'style/pages/FinancialStatements/FinancialSheet.scss';
|
||||
|
||||
import { If, LoadingIndicator, MODIFIER } from 'components';
|
||||
|
||||
|
||||
export default function FinancialSheet({
|
||||
companyName,
|
||||
sheetType,
|
||||
@@ -57,49 +56,47 @@ export default function FinancialSheet({
|
||||
'is-full-width': fullWidth,
|
||||
})}
|
||||
>
|
||||
<LoadingIndicator loading={loading} spinnerSize={34} />
|
||||
|
||||
<div
|
||||
className={classnames('financial-sheet__inner', {
|
||||
'is-loading': loading,
|
||||
})}
|
||||
>
|
||||
<If condition={!!companyName}>
|
||||
<h1 class="financial-sheet__title">{companyName}</h1>
|
||||
</If>
|
||||
|
||||
<If condition={!!sheetType}>
|
||||
<h6 class="financial-sheet__sheet-type">{sheetType}</h6>
|
||||
</If>
|
||||
|
||||
<div class="financial-sheet__date">
|
||||
<If condition={asDate}>
|
||||
<T id={'as'} /> {formattedAsDate}
|
||||
{loading ? (
|
||||
<LoadingIndicator loading={loading} spinnerSize={34} />
|
||||
) : (
|
||||
<div className={classnames('financial-sheet__inner')}>
|
||||
<If condition={!!companyName}>
|
||||
<h1 class="financial-sheet__title">{companyName}</h1>
|
||||
</If>
|
||||
|
||||
<If condition={fromDate && toDate}>
|
||||
<T id={'from'} /> {formattedFromDate} | <T id={'to'} />{' '}
|
||||
{formattedToDate}
|
||||
<If condition={!!sheetType}>
|
||||
<h6 class="financial-sheet__sheet-type">{sheetType}</h6>
|
||||
</If>
|
||||
|
||||
<div class="financial-sheet__date">
|
||||
<If condition={asDate}>
|
||||
<T id={'as'} /> {formattedAsDate}
|
||||
</If>
|
||||
|
||||
<If condition={fromDate && toDate}>
|
||||
<T id={'from'} /> {formattedFromDate} | <T id={'to'} />{' '}
|
||||
{formattedToDate}
|
||||
</If>
|
||||
</div>
|
||||
|
||||
<div class="financial-sheet__table">{children}</div>
|
||||
<div class="financial-sheet__accounting-basis">{accountingBasis}</div>
|
||||
|
||||
<div class="financial-sheet__footer">
|
||||
<If condition={basisLabel}>
|
||||
<span class="financial-sheet__basis">
|
||||
<T id={'accounting_basis'} /> {basisLabel}
|
||||
</span>
|
||||
</If>
|
||||
|
||||
<If condition={currentDate}>
|
||||
<span class="financial-sheet__current-date">
|
||||
{moment().format('YYYY MMM DD HH:MM')}
|
||||
</span>
|
||||
</If>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="financial-sheet__table">{children}</div>
|
||||
<div class="financial-sheet__accounting-basis">{accountingBasis}</div>
|
||||
|
||||
<div class="financial-sheet__footer">
|
||||
<If condition={basisLabel}>
|
||||
<span class="financial-sheet__basis">
|
||||
<T id={'accounting_basis'} /> {basisLabel}
|
||||
</span>
|
||||
</If>
|
||||
|
||||
<If condition={currentDate}>
|
||||
<span class="financial-sheet__current-date">
|
||||
{moment().format('YYYY MMM DD HH:MM')}
|
||||
</span>
|
||||
</If>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -5,6 +5,9 @@ import { compose } from 'utils';
|
||||
import withAuthentication from 'containers/Authentication/withAuthentication';
|
||||
import withOrganization from 'containers/Organization/withOrganization';
|
||||
|
||||
/**
|
||||
* Ensures organization is not ready.
|
||||
*/
|
||||
function EnsureOrganizationIsNotReady({
|
||||
children,
|
||||
|
||||
|
||||
@@ -11,11 +11,7 @@ export default function PrivateRoute({ component: Component, ...rest }) {
|
||||
{isAuthenticated ? (
|
||||
<Component />
|
||||
) : (
|
||||
<Redirect
|
||||
to={{
|
||||
pathname: '/auth/login',
|
||||
}}
|
||||
/>
|
||||
<Redirect to={{ pathname: '/auth/login' }} />
|
||||
)}
|
||||
</BodyClassName>
|
||||
);
|
||||
|
||||
@@ -5,7 +5,7 @@ const If = props => props.condition
|
||||
? (props.render ? props.render() : props.children) : null;
|
||||
|
||||
If.propTypes = {
|
||||
condition: PropTypes.bool.isRequired,
|
||||
// condition: PropTypes.bool.isRequired,
|
||||
children: PropTypes.node,
|
||||
render: PropTypes.func
|
||||
};
|
||||
|
||||
@@ -53,6 +53,7 @@ import Drawer from './Drawer/Drawer';
|
||||
import DrawerSuspense from './Drawer/DrawerSuspense';
|
||||
import Postbox from './Postbox';
|
||||
import AccountsSuggestField from './AccountsSuggestField';
|
||||
import MaterialProgressBar from './MaterialProgressBar';
|
||||
|
||||
const Hint = FieldHint;
|
||||
|
||||
@@ -112,5 +113,6 @@ export {
|
||||
Drawer,
|
||||
DrawerSuspense,
|
||||
Postbox,
|
||||
AccountsSuggestField
|
||||
AccountsSuggestField,
|
||||
MaterialProgressBar
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user