mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-17 05:10:31 +00:00
chrone: sperate client and server to different repos.
This commit is contained in:
28
src/components/Dashboard/AuthenticatedUser.js
Normal file
28
src/components/Dashboard/AuthenticatedUser.js
Normal file
@@ -0,0 +1,28 @@
|
||||
import React from 'react';
|
||||
import { useUser } from 'hooks/query';
|
||||
import withAuthentication from '../../containers/Authentication/withAuthentication';
|
||||
|
||||
const AuthenticatedUserContext = React.createContext();
|
||||
|
||||
function AuthenticatedUserComponent({ authenticatedUserId, children }) {
|
||||
const { data: user, ...restProps } = useUser(authenticatedUserId);
|
||||
|
||||
return (
|
||||
<AuthenticatedUserContext.Provider
|
||||
value={{
|
||||
user,
|
||||
...restProps,
|
||||
}}
|
||||
children={children}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export const AuthenticatedUser = withAuthentication(
|
||||
({ authenticatedUserId }) => ({
|
||||
authenticatedUserId,
|
||||
}),
|
||||
)(AuthenticatedUserComponent);
|
||||
|
||||
export const useAuthenticatedUser = () =>
|
||||
React.useContext(AuthenticatedUserContext);
|
||||
18
src/components/Dashboard/BigcapitalLoading.js
Normal file
18
src/components/Dashboard/BigcapitalLoading.js
Normal file
@@ -0,0 +1,18 @@
|
||||
import React from 'react';
|
||||
import classNames from 'classnames';
|
||||
import { Icon } from 'components';
|
||||
|
||||
import 'style/components/BigcapitalLoading.scss';
|
||||
|
||||
/**
|
||||
* Bigcapital logo loading.
|
||||
*/
|
||||
export default function BigcapitalLoading({ className }) {
|
||||
return (
|
||||
<div className={classNames('bigcapital-loading', className)}>
|
||||
<div class="center">
|
||||
<Icon icon="bigcapital" height={37} width={228} />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
60
src/components/Dashboard/Dashboard.js
Normal file
60
src/components/Dashboard/Dashboard.js
Normal file
@@ -0,0 +1,60 @@
|
||||
import React from 'react';
|
||||
import { Switch, Route } from 'react-router';
|
||||
|
||||
import 'style/pages/Dashboard/Dashboard.scss';
|
||||
|
||||
import Sidebar from 'components/Sidebar/Sidebar';
|
||||
import DashboardContent from 'components/Dashboard/DashboardContent';
|
||||
import DialogsContainer from 'components/DialogsContainer';
|
||||
import PreferencesPage from 'components/Preferences/PreferencesPage';
|
||||
import DashboardUniversalSearch from 'containers/UniversalSearch/DashboardUniversalSearch';
|
||||
import DashboardSplitPane from 'components/Dashboard/DashboardSplitePane';
|
||||
import GlobalHotkeys from './GlobalHotkeys';
|
||||
import DashboardProvider from './DashboardProvider';
|
||||
import DrawersContainer from 'components/DrawersContainer';
|
||||
import EnsureSubscriptionIsActive from '../Guards/EnsureSubscriptionIsActive';
|
||||
|
||||
/**
|
||||
* Dashboard preferences.
|
||||
*/
|
||||
function DashboardPreferences() {
|
||||
return (
|
||||
<EnsureSubscriptionIsActive>
|
||||
<DashboardSplitPane>
|
||||
<Sidebar />
|
||||
<PreferencesPage />
|
||||
</DashboardSplitPane>
|
||||
</EnsureSubscriptionIsActive>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Dashboard other routes.
|
||||
*/
|
||||
function DashboardAnyPage() {
|
||||
return (
|
||||
<DashboardSplitPane>
|
||||
<Sidebar />
|
||||
<DashboardContent />
|
||||
</DashboardSplitPane>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Dashboard page.
|
||||
*/
|
||||
export default function Dashboard() {
|
||||
return (
|
||||
<DashboardProvider>
|
||||
<Switch>
|
||||
<Route path="/preferences" component={DashboardPreferences} />
|
||||
<Route path="/" component={DashboardAnyPage} />
|
||||
</Switch>
|
||||
|
||||
<DashboardUniversalSearch />
|
||||
<DialogsContainer />
|
||||
<GlobalHotkeys />
|
||||
<DrawersContainer />
|
||||
</DashboardProvider>
|
||||
);
|
||||
}
|
||||
68
src/components/Dashboard/DashboardActionViewsList.js
Normal file
68
src/components/Dashboard/DashboardActionViewsList.js
Normal file
@@ -0,0 +1,68 @@
|
||||
import React, { useMemo, useState } from 'react';
|
||||
import classNames from 'classnames';
|
||||
import {
|
||||
Button,
|
||||
Classes,
|
||||
MenuItem,
|
||||
Menu,
|
||||
Popover,
|
||||
PopoverInteractionKind,
|
||||
Position,
|
||||
Divider,
|
||||
} from '@blueprintjs/core';
|
||||
import { FormattedMessage as T } from 'components';
|
||||
import { Icon } from 'components';
|
||||
|
||||
/**
|
||||
* Dashboard action views list.
|
||||
*/
|
||||
export default function DashboardActionViewsList({
|
||||
resourceName,
|
||||
allMenuItem,
|
||||
allMenuItemText,
|
||||
views,
|
||||
onChange,
|
||||
}) {
|
||||
const handleClickViewItem = (view) => {
|
||||
onChange && onChange(view);
|
||||
};
|
||||
|
||||
const viewsMenuItems = views.map((view) => (
|
||||
<MenuItem onClick={() => handleClickViewItem(view)} text={view.name} />
|
||||
));
|
||||
|
||||
const handleAllTabClick = () => {
|
||||
handleClickViewItem(null);
|
||||
};
|
||||
|
||||
const content = (
|
||||
<Menu>
|
||||
{allMenuItem && (
|
||||
<>
|
||||
<MenuItem
|
||||
onClick={handleAllTabClick}
|
||||
text={allMenuItemText || 'All'}
|
||||
/>
|
||||
<Divider />
|
||||
</>
|
||||
)}
|
||||
{viewsMenuItems}
|
||||
</Menu>
|
||||
);
|
||||
|
||||
return (
|
||||
<Popover
|
||||
content={content}
|
||||
minimal={true}
|
||||
interactionKind={PopoverInteractionKind.CLICK}
|
||||
position={Position.BOTTOM_LEFT}
|
||||
>
|
||||
<Button
|
||||
className={classNames(Classes.MINIMAL, 'button--table-views')}
|
||||
icon={<Icon icon="table-16" iconSize={16} />}
|
||||
text={<T id={'table_views'} />}
|
||||
rightIcon={'caret-down'}
|
||||
/>
|
||||
</Popover>
|
||||
);
|
||||
}
|
||||
16
src/components/Dashboard/DashboardActionsBar.js
Normal file
16
src/components/Dashboard/DashboardActionsBar.js
Normal file
@@ -0,0 +1,16 @@
|
||||
import React from 'react';
|
||||
import classnames from 'classnames';
|
||||
import { Navbar } from '@blueprintjs/core';
|
||||
|
||||
export default function DashboardActionsBar({ children, name }) {
|
||||
return (
|
||||
<div
|
||||
className={classnames({
|
||||
'dashboard__actions-bar': true,
|
||||
[`dashboard__actions-bar--${name}`]: !!name
|
||||
})}
|
||||
>
|
||||
<Navbar className='navbar--dashboard-actions-bar'>{children}</Navbar>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
39
src/components/Dashboard/DashboardBackLink.js
Normal file
39
src/components/Dashboard/DashboardBackLink.js
Normal file
@@ -0,0 +1,39 @@
|
||||
import React from 'react';
|
||||
import withBreadcrumbs from 'react-router-breadcrumbs-hoc';
|
||||
import { useHistory } from 'react-router-dom';
|
||||
import { getDashboardRoutes } from 'routes/dashboard';
|
||||
import { If, Icon } from 'components';
|
||||
import { FormattedMessage as T } from 'components';
|
||||
import withDashboard from 'containers/Dashboard/withDashboard';
|
||||
import { compose } from 'utils';
|
||||
|
||||
function DashboardBackLink({ dashboardBackLink, breadcrumbs }) {
|
||||
const history = useHistory();
|
||||
const crumb = breadcrumbs[breadcrumbs.length - 2];
|
||||
|
||||
const handleClick = (event) => {
|
||||
const url =
|
||||
typeof dashboardBackLink === 'string'
|
||||
? dashboardBackLink
|
||||
: crumb.match.url;
|
||||
history.push(url);
|
||||
event.preventDefault();
|
||||
};
|
||||
|
||||
return (
|
||||
<If condition={dashboardBackLink && crumb}>
|
||||
<div class="dashboard__back-link">
|
||||
<a href="#no-link" onClick={handleClick}>
|
||||
<Icon icon={'arrow-left'} iconSize={18} /> <T id={'back_to_list'} />
|
||||
</a>
|
||||
</div>
|
||||
</If>
|
||||
);
|
||||
}
|
||||
|
||||
export default compose(
|
||||
withBreadcrumbs([]),
|
||||
withDashboard(({ dashboardBackLink }) => ({
|
||||
dashboardBackLink,
|
||||
})),
|
||||
)(DashboardBackLink);
|
||||
76
src/components/Dashboard/DashboardBoot.js
Normal file
76
src/components/Dashboard/DashboardBoot.js
Normal file
@@ -0,0 +1,76 @@
|
||||
import React from 'react';
|
||||
import * as R from 'ramda';
|
||||
|
||||
import { useUser, useCurrentOrganization } from 'hooks/query';
|
||||
import withAuthentication from '../../containers/Authentication/withAuthentication';
|
||||
import withDashboardActions from '../../containers/Dashboard/withDashboardActions';
|
||||
|
||||
import { setCookie, getCookie } from '../../utils';
|
||||
|
||||
/**
|
||||
* Dashboard async booting.
|
||||
*/
|
||||
function DashboardBootJSX({ setAppIsLoading, authenticatedUserId }) {
|
||||
// Fetches the current user's organization.
|
||||
const { isSuccess: isCurrentOrganizationSuccess, data: organization } =
|
||||
useCurrentOrganization();
|
||||
|
||||
// Authenticated user.
|
||||
const { isSuccess: isAuthUserSuccess, data: authUser } =
|
||||
useUser(authenticatedUserId);
|
||||
|
||||
// Initial locale cookie value.
|
||||
const localeCookie = getCookie('locale');
|
||||
|
||||
// Is the dashboard booted.
|
||||
const isBooted = React.useRef(false);
|
||||
|
||||
// Syns the organization language with locale cookie.
|
||||
React.useEffect(() => {
|
||||
if (organization?.metadata?.language) {
|
||||
setCookie('locale', organization.metadata.language);
|
||||
}
|
||||
}, [organization]);
|
||||
|
||||
React.useEffect(() => {
|
||||
// Can't continue if the organization metadata is not loaded yet.
|
||||
if (!organization?.metadata?.language) {
|
||||
return;
|
||||
}
|
||||
// Can't continue if the organization is already booted.
|
||||
if (isBooted.current) {
|
||||
return;
|
||||
}
|
||||
// Reboot the application in case the initial locale not equal
|
||||
// the current organization language.
|
||||
if (localeCookie !== organization.metadata.language) {
|
||||
window.location.reload();
|
||||
}
|
||||
}, [localeCookie, organization]);
|
||||
|
||||
React.useEffect(() => {
|
||||
// Once the all requests complete change the app loading state.
|
||||
if (
|
||||
isAuthUserSuccess &&
|
||||
isCurrentOrganizationSuccess &&
|
||||
localeCookie === organization?.metadata?.language
|
||||
) {
|
||||
setAppIsLoading(false);
|
||||
isBooted.current = true;
|
||||
}
|
||||
}, [
|
||||
isAuthUserSuccess,
|
||||
isCurrentOrganizationSuccess,
|
||||
organization,
|
||||
setAppIsLoading,
|
||||
localeCookie,
|
||||
]);
|
||||
return null;
|
||||
}
|
||||
|
||||
export const DashboardBoot = R.compose(
|
||||
withAuthentication(({ authenticatedUserId }) => ({
|
||||
authenticatedUserId,
|
||||
})),
|
||||
withDashboardActions,
|
||||
)(DashboardBootJSX);
|
||||
34
src/components/Dashboard/DashboardBreadcrumbs.js
Normal file
34
src/components/Dashboard/DashboardBreadcrumbs.js
Normal file
@@ -0,0 +1,34 @@
|
||||
import React from 'react';
|
||||
import {
|
||||
CollapsibleList,
|
||||
MenuItem,
|
||||
Classes,
|
||||
Boundary,
|
||||
} from '@blueprintjs/core';
|
||||
import withBreadcrumbs from 'react-router-breadcrumbs-hoc';
|
||||
import { getDashboardRoutes } from 'routes/dashboard';
|
||||
import { useHistory } from 'react-router-dom';
|
||||
|
||||
function DashboardBreadcrumbs({ breadcrumbs }){
|
||||
const history = useHistory();
|
||||
|
||||
return(
|
||||
<CollapsibleList
|
||||
className={Classes.BREADCRUMBS}
|
||||
dropdownTarget={<span className={Classes.BREADCRUMBS_COLLAPSED} />}
|
||||
collapseFrom={Boundary.START}
|
||||
visibleItemCount={0}>
|
||||
{
|
||||
breadcrumbs.map(({ breadcrumb,match })=>{
|
||||
return (<MenuItem
|
||||
key={match.url}
|
||||
icon={'folder-close'}
|
||||
text={breadcrumb}
|
||||
onClick={() => history.push(match.url) } />)
|
||||
})
|
||||
}
|
||||
</CollapsibleList>
|
||||
)
|
||||
}
|
||||
|
||||
export default withBreadcrumbs([])(DashboardBreadcrumbs)
|
||||
17
src/components/Dashboard/DashboardCard.js
Normal file
17
src/components/Dashboard/DashboardCard.js
Normal file
@@ -0,0 +1,17 @@
|
||||
import React from 'react';
|
||||
import classNames from 'classnames';
|
||||
|
||||
import { CLASSES } from 'common/classes';
|
||||
|
||||
// Dashboard card.
|
||||
export default function DashboardCard({ children, page }) {
|
||||
return (
|
||||
<div
|
||||
className={classNames(CLASSES.DASHBOARD_CARD, {
|
||||
[CLASSES.DASHBOARD_CARD_PAGE]: page,
|
||||
})}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
18
src/components/Dashboard/DashboardContent.js
Normal file
18
src/components/Dashboard/DashboardContent.js
Normal file
@@ -0,0 +1,18 @@
|
||||
import React from 'react';
|
||||
import { ErrorBoundary } from 'react-error-boundary';
|
||||
import DashboardTopbar from 'components/Dashboard/DashboardTopbar';
|
||||
import DashboardContentRoutes from 'components/Dashboard/DashboardContentRoute';
|
||||
import DashboardFooter from 'components/Dashboard/DashboardFooter';
|
||||
import DashboardErrorBoundary from './DashboardErrorBoundary';
|
||||
|
||||
export default React.forwardRef(({}, ref) => {
|
||||
return (
|
||||
<ErrorBoundary FallbackComponent={DashboardErrorBoundary}>
|
||||
<div className="dashboard-content" id="dashboard" ref={ref}>
|
||||
<DashboardTopbar />
|
||||
<DashboardContentRoutes />
|
||||
<DashboardFooter />
|
||||
</div>
|
||||
</ErrorBoundary>
|
||||
);
|
||||
});
|
||||
59
src/components/Dashboard/DashboardContentRoute.js
Normal file
59
src/components/Dashboard/DashboardContentRoute.js
Normal file
@@ -0,0 +1,59 @@
|
||||
import React from 'react';
|
||||
import { Route, Switch } from 'react-router-dom';
|
||||
|
||||
import { getDashboardRoutes } from 'routes/dashboard';
|
||||
import EnsureSubscriptionsIsActive from '../Guards/EnsureSubscriptionsIsActive';
|
||||
import EnsureSubscriptionsIsInactive from '../Guards/EnsureSubscriptionsIsInactive';
|
||||
import DashboardPage from './DashboardPage';
|
||||
|
||||
/**
|
||||
* Dashboard inner route content.
|
||||
*/
|
||||
function DashboardContentRouteContent({ route }) {
|
||||
const content = (
|
||||
<DashboardPage
|
||||
name={route.name}
|
||||
Component={route.component}
|
||||
pageTitle={route.pageTitle}
|
||||
backLink={route.backLink}
|
||||
hint={route.hint}
|
||||
sidebarExpand={route.sidebarExpand}
|
||||
pageType={route.pageType}
|
||||
defaultSearchResource={route.defaultSearchResource}
|
||||
/>
|
||||
);
|
||||
return route.subscriptionActive ? (
|
||||
<EnsureSubscriptionsIsInactive
|
||||
subscriptionTypes={route.subscriptionActive}
|
||||
children={content}
|
||||
redirectTo={'/billing'}
|
||||
/>
|
||||
) : route.subscriptionInactive ? (
|
||||
<EnsureSubscriptionsIsActive
|
||||
subscriptionTypes={route.subscriptionInactive}
|
||||
children={content}
|
||||
redirectTo={'/'}
|
||||
/>
|
||||
) : (
|
||||
content
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Dashboard content route.
|
||||
*/
|
||||
export default function DashboardContentRoute() {
|
||||
const routes = getDashboardRoutes();
|
||||
|
||||
return (
|
||||
<Route pathname="/">
|
||||
<Switch>
|
||||
{routes.map((route, index) => (
|
||||
<Route exact={route.exact} key={index} path={`${route.path}`}>
|
||||
<DashboardContentRouteContent route={route} />
|
||||
</Route>
|
||||
))}
|
||||
</Switch>
|
||||
</Route>
|
||||
);
|
||||
}
|
||||
10
src/components/Dashboard/DashboardContentTable.js
Normal file
10
src/components/Dashboard/DashboardContentTable.js
Normal file
@@ -0,0 +1,10 @@
|
||||
import React from 'react';
|
||||
import classNames from 'classnames';
|
||||
import { CLASSES } from 'common/classes';
|
||||
|
||||
/**
|
||||
* Dashboard content table.
|
||||
*/
|
||||
export default function DashboardContentTable({ children }) {
|
||||
return (<div className={classNames(CLASSES.DASHBOARD_DATATABLE)}>{ children }</div>)
|
||||
}
|
||||
12
src/components/Dashboard/DashboardErrorBoundary.js
Normal file
12
src/components/Dashboard/DashboardErrorBoundary.js
Normal file
@@ -0,0 +1,12 @@
|
||||
import React from 'react';
|
||||
import { FormattedMessage as T, Icon } from 'components';
|
||||
|
||||
export default function DashboardErrorBoundary({}) {
|
||||
return (
|
||||
<div class="dashboard__error-boundary">
|
||||
<h1><T id={'sorry_about_that_something_went_wrong'} /></h1>
|
||||
<p><T id={'if_the_problem_stuck_please_contact_us_as_soon_as_possible'} /></p>
|
||||
<Icon icon="bigcapital" height={30} width={160} />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
26
src/components/Dashboard/DashboardFilterButton.js
Normal file
26
src/components/Dashboard/DashboardFilterButton.js
Normal file
@@ -0,0 +1,26 @@
|
||||
import React from 'react';
|
||||
import classNames from 'classnames';
|
||||
import intl from "react-intl-universal";
|
||||
import { Classes, Button } from '@blueprintjs/core';
|
||||
import { T, Icon } from 'components';
|
||||
|
||||
/**
|
||||
* Dashboard advanced filter button.
|
||||
*/
|
||||
export function DashboardFilterButton({ conditionsCount }) {
|
||||
return (
|
||||
<Button
|
||||
className={classNames(Classes.MINIMAL, 'button--filter', {
|
||||
'has-active-filters': conditionsCount > 0,
|
||||
})}
|
||||
text={
|
||||
conditionsCount > 0 ? (
|
||||
intl.get('count_filters_applied', { count: conditionsCount })
|
||||
) : (
|
||||
<T id={'filter'} />
|
||||
)
|
||||
}
|
||||
icon={<Icon icon="filter-16" iconSize={16} />}
|
||||
/>
|
||||
);
|
||||
}
|
||||
25
src/components/Dashboard/DashboardFooter.js
Normal file
25
src/components/Dashboard/DashboardFooter.js
Normal file
@@ -0,0 +1,25 @@
|
||||
import React from 'react';
|
||||
import { getFooterLinks } from 'config/footerLinks';
|
||||
import { For } from 'components';
|
||||
|
||||
function FooterLinkItem({ title, link }) {
|
||||
return (
|
||||
<div class="">
|
||||
<a href={link} target="_blank" rel="noopener noreferrer">
|
||||
{title}
|
||||
</a>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default function DashboardFooter() {
|
||||
const footerLinks = getFooterLinks();
|
||||
|
||||
return (
|
||||
<div class="dashboard__footer">
|
||||
<div class="footer-links">
|
||||
<For render={FooterLinkItem} of={footerLinks} />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
23
src/components/Dashboard/DashboardInsider.js
Normal file
23
src/components/Dashboard/DashboardInsider.js
Normal file
@@ -0,0 +1,23 @@
|
||||
import React from 'react';
|
||||
import classnames from 'classnames';
|
||||
import LoadingIndicator from 'components/LoadingIndicator';
|
||||
|
||||
export default function DashboardInsider({
|
||||
loading,
|
||||
children,
|
||||
name,
|
||||
mount = false,
|
||||
className,
|
||||
}) {
|
||||
return (
|
||||
<div className={classnames({
|
||||
'dashboard__insider': true,
|
||||
'dashboard__insider--loading': loading,
|
||||
[`dashboard__insider--${name}`]: !!name,
|
||||
}, className)}>
|
||||
<LoadingIndicator loading={loading} mount={mount}>
|
||||
{ children }
|
||||
</LoadingIndicator>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
24
src/components/Dashboard/DashboardLoadingIndicator.js
Normal file
24
src/components/Dashboard/DashboardLoadingIndicator.js
Normal file
@@ -0,0 +1,24 @@
|
||||
import React from 'react';
|
||||
import { Choose } from 'components';
|
||||
import BigcapitalLoading from './BigcapitalLoading';
|
||||
|
||||
/**
|
||||
* Dashboard loading indicator.
|
||||
*/
|
||||
export default function DashboardLoadingIndicator({
|
||||
isLoading = false,
|
||||
className,
|
||||
children,
|
||||
}) {
|
||||
return (
|
||||
<Choose>
|
||||
<Choose.When condition={isLoading}>
|
||||
<BigcapitalLoading />
|
||||
</Choose.When>
|
||||
|
||||
<Choose.Otherwise>
|
||||
{ children }
|
||||
</Choose.Otherwise>
|
||||
</Choose>
|
||||
);
|
||||
}
|
||||
106
src/components/Dashboard/DashboardPage.js
Normal file
106
src/components/Dashboard/DashboardPage.js
Normal file
@@ -0,0 +1,106 @@
|
||||
import React, { useEffect, Suspense } from 'react';
|
||||
// import { isUndefined } from 'lodash';
|
||||
import { CLASSES } from 'common/classes';
|
||||
import withDashboardActions from 'containers/Dashboard/withDashboardActions';
|
||||
import { compose } from 'utils';
|
||||
import { Spinner } from '@blueprintjs/core';
|
||||
|
||||
// import withUniversalSearch from '../../containers/UniversalSearch/withUniversalSearch';
|
||||
import withUniversalSearchActions from '../../containers/UniversalSearch/withUniversalSearchActions';
|
||||
|
||||
/**
|
||||
* Dashboard pages wrapper.
|
||||
*/
|
||||
function DashboardPage({
|
||||
// #ownProps
|
||||
pageTitle,
|
||||
backLink,
|
||||
sidebarExpand = true,
|
||||
Component,
|
||||
name,
|
||||
hint,
|
||||
defaultSearchResource,
|
||||
|
||||
// #withDashboardActions
|
||||
changePageTitle,
|
||||
setDashboardBackLink,
|
||||
changePageHint,
|
||||
toggleSidebarExpand,
|
||||
|
||||
// #withUniversalSearch
|
||||
setResourceTypeUniversalSearch,
|
||||
resetResourceTypeUniversalSearch,
|
||||
}) {
|
||||
// Hydrate the given page title.
|
||||
useEffect(() => {
|
||||
pageTitle && changePageTitle(pageTitle);
|
||||
|
||||
return () => {
|
||||
pageTitle && changePageTitle('');
|
||||
};
|
||||
});
|
||||
|
||||
// Hydrate the given page hint.
|
||||
useEffect(() => {
|
||||
hint && changePageHint(hint);
|
||||
|
||||
return () => {
|
||||
hint && changePageHint('');
|
||||
};
|
||||
}, [hint, changePageHint]);
|
||||
|
||||
// Hydrate the dashboard back link status.
|
||||
useEffect(() => {
|
||||
backLink && setDashboardBackLink(backLink);
|
||||
|
||||
return () => {
|
||||
backLink && setDashboardBackLink(false);
|
||||
};
|
||||
}, [backLink, setDashboardBackLink]);
|
||||
|
||||
useEffect(() => {
|
||||
const className = `page-${name}`;
|
||||
name && document.body.classList.add(className);
|
||||
|
||||
return () => {
|
||||
name && document.body.classList.remove(className);
|
||||
};
|
||||
}, [name]);
|
||||
|
||||
useEffect(() => {
|
||||
toggleSidebarExpand(sidebarExpand);
|
||||
}, [toggleSidebarExpand, sidebarExpand]);
|
||||
|
||||
useEffect(() => {
|
||||
if (defaultSearchResource) {
|
||||
setResourceTypeUniversalSearch(defaultSearchResource);
|
||||
}
|
||||
return () => {
|
||||
resetResourceTypeUniversalSearch();
|
||||
};
|
||||
}, [
|
||||
defaultSearchResource,
|
||||
resetResourceTypeUniversalSearch,
|
||||
setResourceTypeUniversalSearch,
|
||||
]);
|
||||
|
||||
return (
|
||||
<div className={CLASSES.DASHBOARD_PAGE}>
|
||||
<Suspense
|
||||
fallback={
|
||||
<div class="dashboard__fallback-loading">
|
||||
<Spinner size={40} value={null} />
|
||||
</div>
|
||||
}
|
||||
>
|
||||
<Component />
|
||||
</Suspense>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default compose(
|
||||
withDashboardActions,
|
||||
// withUniversalSearch,
|
||||
withUniversalSearchActions,
|
||||
)(DashboardPage);
|
||||
12
src/components/Dashboard/DashboardPageContent.js
Normal file
12
src/components/Dashboard/DashboardPageContent.js
Normal file
@@ -0,0 +1,12 @@
|
||||
import React from 'react';
|
||||
|
||||
/**
|
||||
* Dashboard page content.
|
||||
*/
|
||||
export default function DashboardPageContent({ children }) {
|
||||
return (
|
||||
<div class="dashboard__page-content">
|
||||
{ children }
|
||||
</div>
|
||||
)
|
||||
}
|
||||
8
src/components/Dashboard/DashboardProvider.js
Normal file
8
src/components/Dashboard/DashboardProvider.js
Normal file
@@ -0,0 +1,8 @@
|
||||
import React from 'react';
|
||||
|
||||
/**
|
||||
* Dashboard provider.
|
||||
*/
|
||||
export default function DashboardProvider({ children }) {
|
||||
return children;
|
||||
}
|
||||
36
src/components/Dashboard/DashboardRowsHeightButton.js
Normal file
36
src/components/Dashboard/DashboardRowsHeightButton.js
Normal file
@@ -0,0 +1,36 @@
|
||||
import React from 'react';
|
||||
import {
|
||||
Button,
|
||||
PopoverInteractionKind,
|
||||
Popover,
|
||||
Menu,
|
||||
MenuItem,
|
||||
MenuDivider,
|
||||
Classes
|
||||
} from '@blueprintjs/core';
|
||||
import { Icon } from 'components';
|
||||
|
||||
export function DashboardRowsHeightButton() {
|
||||
return (
|
||||
<Popover
|
||||
minimal={true}
|
||||
content={
|
||||
<Menu>
|
||||
<MenuDivider title={'Rows height'} />
|
||||
<MenuItem text="Compact" />
|
||||
<MenuItem text="Medium" />
|
||||
</Menu>
|
||||
}
|
||||
placement="bottom-start"
|
||||
modifiers={{
|
||||
offset: { offset: '0, 4' },
|
||||
}}
|
||||
interactionKind={PopoverInteractionKind.CLICK}
|
||||
>
|
||||
<Button
|
||||
className={Classes.MINIMAL}
|
||||
icon={<Icon icon="rows-height" iconSize={16} />}
|
||||
/>
|
||||
</Popover>
|
||||
);
|
||||
}
|
||||
44
src/components/Dashboard/DashboardSplitePane.js
Normal file
44
src/components/Dashboard/DashboardSplitePane.js
Normal file
@@ -0,0 +1,44 @@
|
||||
import React, { useState, useRef } from 'react';
|
||||
import SplitPane from 'react-split-pane';
|
||||
import { debounce } from 'lodash';
|
||||
|
||||
import withDashboard from 'containers/Dashboard/withDashboard';
|
||||
import { compose } from 'utils';
|
||||
|
||||
function DashboardSplitPane({
|
||||
sidebarExpended,
|
||||
children
|
||||
}) {
|
||||
const initialSize = 180;
|
||||
|
||||
const [defaultSize, setDefaultSize] = useState(
|
||||
parseInt(localStorage.getItem('dashboard-size'), 10) || initialSize,
|
||||
);
|
||||
const debounceSaveSize = useRef(
|
||||
debounce((size) => {
|
||||
localStorage.setItem('dashboard-size', size);
|
||||
}, 500),
|
||||
);
|
||||
const handleChange = (size) => {
|
||||
debounceSaveSize.current(size);
|
||||
setDefaultSize(size);
|
||||
}
|
||||
return (
|
||||
<SplitPane
|
||||
allowResize={sidebarExpended}
|
||||
split="vertical"
|
||||
minSize={180}
|
||||
maxSize={300}
|
||||
defaultSize={sidebarExpended ? defaultSize : 50}
|
||||
size={sidebarExpended ? defaultSize : 50}
|
||||
onChange={handleChange}
|
||||
className="primary"
|
||||
>
|
||||
{children}
|
||||
</SplitPane>
|
||||
);
|
||||
}
|
||||
|
||||
export default compose(
|
||||
withDashboard(({ sidebarExpended }) => ({ sidebarExpended }))
|
||||
)(DashboardSplitPane);
|
||||
197
src/components/Dashboard/DashboardTopbar.js
Normal file
197
src/components/Dashboard/DashboardTopbar.js
Normal file
@@ -0,0 +1,197 @@
|
||||
import React from 'react';
|
||||
import { useHistory } from 'react-router';
|
||||
import {
|
||||
Navbar,
|
||||
NavbarGroup,
|
||||
NavbarDivider,
|
||||
Button,
|
||||
Classes,
|
||||
Tooltip,
|
||||
Position,
|
||||
} from '@blueprintjs/core';
|
||||
import { FormattedMessage as T } from 'components';
|
||||
|
||||
import DashboardTopbarUser from 'components/Dashboard/TopbarUser';
|
||||
import DashboardBreadcrumbs from 'components/Dashboard/DashboardBreadcrumbs';
|
||||
import DashboardBackLink from 'components/Dashboard/DashboardBackLink';
|
||||
import { Icon, Hint, If } from 'components';
|
||||
|
||||
import withUniversalSearchActions from 'containers/UniversalSearch/withUniversalSearchActions';
|
||||
import withDashboardActions from 'containers/Dashboard/withDashboardActions';
|
||||
import withDashboard from 'containers/Dashboard/withDashboard';
|
||||
|
||||
import QuickNewDropdown from 'containers/QuickNewDropdown/QuickNewDropdown';
|
||||
import { compose } from 'utils';
|
||||
import withSubscriptions from '../../containers/Subscriptions/withSubscriptions';
|
||||
|
||||
function DashboardTopbarSubscriptionMessage() {
|
||||
return (
|
||||
<div class="dashboard__topbar-subscription-msg">
|
||||
<span>
|
||||
<T id={'dashboard.subscription_msg.period_over'} />
|
||||
</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function DashboardHamburgerButton({ ...props }) {
|
||||
return (
|
||||
<Button minimal={true} {...props}>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="20"
|
||||
height="20"
|
||||
viewBox="0 0 20 20"
|
||||
role="img"
|
||||
focusable="false"
|
||||
>
|
||||
<title>
|
||||
<T id={'menu'} />
|
||||
</title>
|
||||
<path
|
||||
stroke="currentColor"
|
||||
stroke-linecap="round"
|
||||
stroke-miterlimit="5"
|
||||
stroke-width="2"
|
||||
d="M4 7h15M4 12h15M4 17h15"
|
||||
></path>
|
||||
</svg>
|
||||
</Button>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Dashboard topbar.
|
||||
*/
|
||||
function DashboardTopbar({
|
||||
// #withDashboard
|
||||
pageTitle,
|
||||
editViewId,
|
||||
pageHint,
|
||||
|
||||
// #withDashboardActions
|
||||
toggleSidebarExpand,
|
||||
|
||||
// #withDashboard
|
||||
sidebarExpended,
|
||||
|
||||
// #withGlobalSearch
|
||||
openGlobalSearch,
|
||||
|
||||
// #withSubscriptions
|
||||
isSubscriptionActive,
|
||||
isSubscriptionInactive,
|
||||
}) {
|
||||
const history = useHistory();
|
||||
|
||||
const handlerClickEditView = () => {
|
||||
history.push(`/custom_views/${editViewId}/edit`);
|
||||
};
|
||||
|
||||
const handleSidebarToggleBtn = () => {
|
||||
toggleSidebarExpand();
|
||||
};
|
||||
|
||||
return (
|
||||
<div class="dashboard__topbar">
|
||||
<div class="dashboard__topbar-left">
|
||||
<div class="dashboard__topbar-sidebar-toggle">
|
||||
<Tooltip
|
||||
content={
|
||||
!sidebarExpended ? (
|
||||
<T id={'open_sidebar'} />
|
||||
) : (
|
||||
<T id={'close_sidebar'} />
|
||||
)
|
||||
}
|
||||
position={Position.RIGHT}
|
||||
>
|
||||
<DashboardHamburgerButton onClick={handleSidebarToggleBtn} />
|
||||
</Tooltip>
|
||||
</div>
|
||||
|
||||
<div class="dashboard__title">
|
||||
<h1>{pageTitle}</h1>
|
||||
|
||||
<If condition={pageHint}>
|
||||
<div class="dashboard__hint">
|
||||
<Hint content={pageHint} />
|
||||
</div>
|
||||
</If>
|
||||
|
||||
<If condition={editViewId}>
|
||||
<Button
|
||||
className={Classes.MINIMAL + ' button--view-edit'}
|
||||
icon={<Icon icon="pen" iconSize={13} />}
|
||||
onClick={handlerClickEditView}
|
||||
/>
|
||||
</If>
|
||||
</div>
|
||||
|
||||
<div class="dashboard__breadcrumbs">
|
||||
<DashboardBreadcrumbs />
|
||||
</div>
|
||||
<DashboardBackLink />
|
||||
</div>
|
||||
|
||||
<div class="dashboard__topbar-right">
|
||||
<If condition={isSubscriptionInactive}>
|
||||
<DashboardTopbarSubscriptionMessage />
|
||||
</If>
|
||||
|
||||
<Navbar class="dashboard__topbar-navbar">
|
||||
<NavbarGroup>
|
||||
<If condition={isSubscriptionActive}>
|
||||
<Button
|
||||
onClick={() => openGlobalSearch(true)}
|
||||
className={Classes.MINIMAL}
|
||||
icon={<Icon icon={'search-24'} iconSize={20} />}
|
||||
text={<T id={'quick_find'} />}
|
||||
/>
|
||||
<QuickNewDropdown />
|
||||
|
||||
<Tooltip
|
||||
content={<T id={'notifications'} />}
|
||||
position={Position.BOTTOM}
|
||||
>
|
||||
<Button
|
||||
className={Classes.MINIMAL}
|
||||
icon={<Icon icon={'notification-24'} iconSize={20} />}
|
||||
/>
|
||||
</Tooltip>
|
||||
</If>
|
||||
|
||||
<Button
|
||||
className={Classes.MINIMAL}
|
||||
icon={<Icon icon={'help-24'} iconSize={20} />}
|
||||
text={<T id={'help'} />}
|
||||
/>
|
||||
<NavbarDivider />
|
||||
</NavbarGroup>
|
||||
</Navbar>
|
||||
|
||||
<div class="dashboard__topbar-user">
|
||||
<DashboardTopbarUser />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default compose(
|
||||
withUniversalSearchActions,
|
||||
withDashboard(({ pageTitle, pageHint, editViewId, sidebarExpended }) => ({
|
||||
pageTitle,
|
||||
editViewId,
|
||||
sidebarExpended,
|
||||
pageHint,
|
||||
})),
|
||||
withDashboardActions,
|
||||
withSubscriptions(
|
||||
({ isSubscriptionActive, isSubscriptionInactive }) => ({
|
||||
isSubscriptionActive,
|
||||
isSubscriptionInactive,
|
||||
}),
|
||||
'main',
|
||||
),
|
||||
)(DashboardTopbar);
|
||||
103
src/components/Dashboard/DashboardViewsTabs.js
Normal file
103
src/components/Dashboard/DashboardViewsTabs.js
Normal file
@@ -0,0 +1,103 @@
|
||||
import React, { useRef, useState, useEffect } from 'react';
|
||||
import { FormattedMessage as T } from 'components';
|
||||
import PropTypes from 'prop-types';
|
||||
import { Button, Tabs, Tab, Tooltip, Position } from '@blueprintjs/core';
|
||||
import { useHistory } from 'react-router';
|
||||
import { debounce } from 'lodash';
|
||||
import { If, Icon } from 'components';
|
||||
import { saveInvoke } from 'utils';
|
||||
|
||||
/**
|
||||
* Dashboard views tabs.
|
||||
*/
|
||||
export default function DashboardViewsTabs({
|
||||
initialViewSlug = 0,
|
||||
currentViewSlug,
|
||||
tabs,
|
||||
defaultTabText = <T id={'all'} />,
|
||||
allTab = true,
|
||||
newViewTab = true,
|
||||
resourceName,
|
||||
onNewViewTabClick,
|
||||
onChange,
|
||||
OnThrottledChange,
|
||||
throttleTime = 250,
|
||||
}) {
|
||||
const history = useHistory();
|
||||
const [currentView, setCurrentView] = useState(initialViewSlug || 0);
|
||||
|
||||
useEffect(() => {
|
||||
if (
|
||||
typeof currentViewSlug !== 'undefined' &&
|
||||
currentViewSlug !== currentView
|
||||
) {
|
||||
setCurrentView(currentViewSlug || 0);
|
||||
}
|
||||
}, [currentView, setCurrentView, currentViewSlug]);
|
||||
|
||||
const throttledOnChange = useRef(
|
||||
debounce((viewId) => saveInvoke(OnThrottledChange, viewId), throttleTime),
|
||||
);
|
||||
|
||||
// Trigger `onChange` and `onThrottledChange` events.
|
||||
const triggerOnChange = (viewSlug) => {
|
||||
const value = viewSlug === 0 ? null : viewSlug;
|
||||
saveInvoke(onChange, value);
|
||||
throttledOnChange.current(value);
|
||||
};
|
||||
|
||||
// Handles click a new view.
|
||||
const handleClickNewView = () => {
|
||||
history.push(`/custom_views/${resourceName}/new`);
|
||||
onNewViewTabClick && onNewViewTabClick();
|
||||
};
|
||||
|
||||
// Handle tabs change.
|
||||
const handleTabsChange = (viewSlug) => {
|
||||
setCurrentView(viewSlug);
|
||||
triggerOnChange(viewSlug);
|
||||
};
|
||||
|
||||
return (
|
||||
<div class="dashboard__views-tabs">
|
||||
<Tabs
|
||||
id="navbar"
|
||||
large={true}
|
||||
selectedTabId={currentView}
|
||||
className="tabs--dashboard-views"
|
||||
onChange={handleTabsChange}
|
||||
animate={false}
|
||||
>
|
||||
{allTab && <Tab id={0} title={defaultTabText} />}
|
||||
|
||||
{tabs.map((tab) => (
|
||||
<Tab id={tab.slug} title={tab.name} />
|
||||
))}
|
||||
<If condition={newViewTab}>
|
||||
<Tooltip
|
||||
content={<T id={'create_a_new_view'} />}
|
||||
position={Position.RIGHT}
|
||||
>
|
||||
<Button
|
||||
className="button--new-view"
|
||||
icon={<Icon icon="plus" />}
|
||||
onClick={handleClickNewView}
|
||||
minimal={true}
|
||||
/>
|
||||
</Tooltip>
|
||||
</If>
|
||||
</Tabs>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
DashboardViewsTabs.propTypes = {
|
||||
tabs: PropTypes.array.isRequired,
|
||||
allTab: PropTypes.bool,
|
||||
newViewTab: PropTypes.bool,
|
||||
|
||||
onNewViewTabClick: PropTypes.func,
|
||||
onChange: PropTypes.func,
|
||||
OnThrottledChange: PropTypes.func,
|
||||
throttleTime: PropTypes.number,
|
||||
};
|
||||
38
src/components/Dashboard/GlobalHotkeys.js
Normal file
38
src/components/Dashboard/GlobalHotkeys.js
Normal file
@@ -0,0 +1,38 @@
|
||||
import React from 'react';
|
||||
import { useHotkeys } from 'react-hotkeys-hook';
|
||||
import { useHistory } from 'react-router-dom';
|
||||
import { getDashboardRoutes } from 'routes/dashboard';
|
||||
import withDashboardActions from 'containers/Dashboard/withDashboardActions';
|
||||
import { compose } from 'utils';
|
||||
|
||||
function GlobalHotkeys({
|
||||
// #withDashboardActions
|
||||
toggleSidebarExpend,
|
||||
}) {
|
||||
const history = useHistory();
|
||||
const routes = getDashboardRoutes();
|
||||
|
||||
const globalHotkeys = routes
|
||||
.filter(({ hotkey }) => hotkey)
|
||||
.map(({ hotkey }) => hotkey)
|
||||
.toString();
|
||||
|
||||
const handleSidebarToggleBtn = () => {
|
||||
toggleSidebarExpend();
|
||||
};
|
||||
useHotkeys(
|
||||
globalHotkeys,
|
||||
(event, handle) => {
|
||||
routes.map(({ path, hotkey }) => {
|
||||
if (handle.key === hotkey) {
|
||||
history.push(path);
|
||||
}
|
||||
});
|
||||
},
|
||||
[history],
|
||||
);
|
||||
useHotkeys('ctrl+/', (event, handle) => handleSidebarToggleBtn());
|
||||
return <div></div>;
|
||||
}
|
||||
|
||||
export default compose(withDashboardActions)(GlobalHotkeys);
|
||||
37
src/components/Dashboard/PrivatePages.js
Normal file
37
src/components/Dashboard/PrivatePages.js
Normal file
@@ -0,0 +1,37 @@
|
||||
import React from 'react';
|
||||
import { Switch, Route } from 'react-router';
|
||||
|
||||
import Dashboard from 'components/Dashboard/Dashboard';
|
||||
import SetupWizardPage from 'containers/Setup/WizardSetupPage';
|
||||
|
||||
import EnsureOrganizationIsReady from 'components/Guards/EnsureOrganizationIsReady';
|
||||
import EnsureOrganizationIsNotReady from 'components/Guards/EnsureOrganizationIsNotReady';
|
||||
import { PrivatePagesProvider } from './PrivatePagesProvider';
|
||||
import { DashboardBoot } from '../../components';
|
||||
|
||||
import 'style/pages/Dashboard/Dashboard.scss';
|
||||
|
||||
/**
|
||||
* Dashboard inner private pages.
|
||||
*/
|
||||
export default function DashboardPrivatePages() {
|
||||
return (
|
||||
<PrivatePagesProvider>
|
||||
<DashboardBoot />
|
||||
|
||||
<Switch>
|
||||
<Route path={'/setup'}>
|
||||
<EnsureOrganizationIsNotReady>
|
||||
<SetupWizardPage />
|
||||
</EnsureOrganizationIsNotReady>
|
||||
</Route>
|
||||
|
||||
<Route path="/">
|
||||
<EnsureOrganizationIsReady>
|
||||
<Dashboard />
|
||||
</EnsureOrganizationIsReady>
|
||||
</Route>
|
||||
</Switch>
|
||||
</PrivatePagesProvider>
|
||||
);
|
||||
}
|
||||
9
src/components/Dashboard/PrivatePagesProvider.js
Normal file
9
src/components/Dashboard/PrivatePagesProvider.js
Normal file
@@ -0,0 +1,9 @@
|
||||
import React from 'react';
|
||||
import { AuthenticatedUser } from './AuthenticatedUser';
|
||||
|
||||
/**
|
||||
* Private pages provider.
|
||||
*/
|
||||
export function PrivatePagesProvider({ children }) {
|
||||
return <AuthenticatedUser>{children}</AuthenticatedUser>;
|
||||
}
|
||||
15
src/components/Dashboard/SplashScreen.js
Normal file
15
src/components/Dashboard/SplashScreen.js
Normal file
@@ -0,0 +1,15 @@
|
||||
import React from 'react';
|
||||
import * as R from 'ramda';
|
||||
import BigcapitalLoading from './BigcapitalLoading';
|
||||
import withDashboard from '../../containers/Dashboard/withDashboard';
|
||||
|
||||
function SplashScreenComponent({ appIsLoading, appIntlIsLoading }) {
|
||||
return appIsLoading || appIntlIsLoading ? <BigcapitalLoading /> : null;
|
||||
}
|
||||
|
||||
export const SplashScreen = R.compose(
|
||||
withDashboard(({ appIsLoading, appIntlIsLoading }) => ({
|
||||
appIsLoading,
|
||||
appIntlIsLoading,
|
||||
})),
|
||||
)(SplashScreenComponent);
|
||||
89
src/components/Dashboard/TopbarUser.js
Normal file
89
src/components/Dashboard/TopbarUser.js
Normal file
@@ -0,0 +1,89 @@
|
||||
import React from 'react';
|
||||
import { useHistory } from 'react-router-dom';
|
||||
import {
|
||||
Menu,
|
||||
MenuItem,
|
||||
MenuDivider,
|
||||
Button,
|
||||
Popover,
|
||||
Position,
|
||||
} from '@blueprintjs/core';
|
||||
import { If, FormattedMessage as T } from 'components';
|
||||
|
||||
import { firstLettersArgs } from 'utils';
|
||||
import { useAuthActions } from 'hooks/state';
|
||||
|
||||
import withDialogActions from 'containers/Dialog/withDialogActions';
|
||||
import { compose } from 'utils';
|
||||
import withSubscriptions from '../../containers/Subscriptions/withSubscriptions';
|
||||
import { useAuthenticatedUser } from './AuthenticatedUser';
|
||||
|
||||
function DashboardTopbarUser({
|
||||
openDialog,
|
||||
|
||||
// #withSubscriptions
|
||||
isSubscriptionActive
|
||||
}) {
|
||||
const history = useHistory();
|
||||
const { setLogout } = useAuthActions();
|
||||
|
||||
// Retrieve authenticated user information.
|
||||
const { user } = useAuthenticatedUser();
|
||||
|
||||
const onClickLogout = () => {
|
||||
setLogout();
|
||||
};
|
||||
|
||||
const onKeyboardShortcut = () => {
|
||||
openDialog('keyboard-shortcuts');
|
||||
};
|
||||
|
||||
return (
|
||||
<Popover
|
||||
content={
|
||||
<Menu className={'menu--logged-user-dropdown'}>
|
||||
<MenuItem
|
||||
multiline={true}
|
||||
className={'menu-item--profile'}
|
||||
text={
|
||||
<div>
|
||||
<div class="person">
|
||||
{user.first_name} {user.last_name}
|
||||
</div>
|
||||
<div class="org">
|
||||
<T id="organization_id" />: {user.tenant_id}
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
/>
|
||||
<MenuDivider />
|
||||
<If condition={isSubscriptionActive}>
|
||||
<MenuItem
|
||||
text={<T id={'keyboard_shortcuts'} />}
|
||||
onClick={onKeyboardShortcut}
|
||||
/>
|
||||
<MenuItem
|
||||
text={<T id={'preferences'} />}
|
||||
onClick={() => history.push('/preferences')}
|
||||
/>
|
||||
</If>
|
||||
<MenuItem text={<T id={'logout'} />} onClick={onClickLogout} />
|
||||
</Menu>
|
||||
}
|
||||
position={Position.BOTTOM}
|
||||
>
|
||||
<Button>
|
||||
<div className="user-text">
|
||||
{firstLettersArgs(user.first_name, user.last_name)}
|
||||
</div>
|
||||
</Button>
|
||||
</Popover>
|
||||
);
|
||||
}
|
||||
export default compose(
|
||||
withDialogActions,
|
||||
withSubscriptions(
|
||||
({ isSubscriptionActive }) => ({ isSubscriptionActive }),
|
||||
'main',
|
||||
),
|
||||
)(DashboardTopbarUser);
|
||||
4
src/components/Dashboard/index.js
Normal file
4
src/components/Dashboard/index.js
Normal file
@@ -0,0 +1,4 @@
|
||||
|
||||
|
||||
export * from './SplashScreen';
|
||||
export * from './DashboardBoot';
|
||||
Reference in New Issue
Block a user