@craftercms/studio-ui
Version:
Services, components, models & utils to build CrafterCMS authoring extensions.
246 lines (244 loc) • 9.73 kB
JavaScript
/*
* Copyright (C) 2007-2022 Crafter Software Corporation. All Rights Reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License version 3 as published by
* the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/*
* Copyright (C) 2007-2022 Crafter Software Corporation. All Rights Reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 3 as published by
* the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import Box from '@mui/material/Box';
import Paper from '@mui/material/Paper';
import React, { useState, lazy, Suspense, useEffect, useMemo } from 'react';
import LauncherGlobalNav from '../LauncherGlobalNav';
import ResizeableDrawer from '../ResizeableDrawer/ResizeableDrawer';
import { useStyles } from './styles';
import {
createHashRouter,
createRoutesFromElements,
Navigate,
Route,
RouterProvider,
useLocation,
Outlet
} from 'react-router-dom';
import SiteManagement from '../SiteManagement';
import { getLauncherSectionLink, urlMapping } from '../LauncherSection/utils';
import EmptyState from '../EmptyState/EmptyState';
import { FormattedMessage, useIntl } from 'react-intl';
import { useGlobalAppState } from './GlobalAppContext';
import Typography from '@mui/material/Typography';
import CrafterCMSLogo from '../../icons/CrafterCMSLogo';
import LoadingState from '../LoadingState/LoadingState';
import LauncherOpenerButton from '../LauncherOpenerButton';
import { useGlobalNavigation } from '../../hooks/useGlobalNavigation';
import GlobalAppToolbar from '../GlobalAppToolbar';
import Skeleton from '@mui/material/Skeleton';
import { globalMenuMessages } from '../../env/i18n-legacy';
import { GlobalRoutes } from '../../env/routes';
const routeWrapper = (module) => ({ ...module, Component: module.default });
// Site management loaded normally above as it is usually where people first land.
const UserManagement = lazy(() => import('../UserManagement/UserManagement'));
const GroupManagement = () => import('../GroupManagement/GroupManagement').then(routeWrapper);
const AuditManagement = () => import('../AuditManagement/AuditManagement').then(routeWrapper);
const LogLevelManagement = () => import('../LogLevelManagement/LogLevelManagement').then(routeWrapper);
const LogConsole = () => import('../LogConsole/LogConsole').then(routeWrapper);
const GlobalConfigManagement = () => import('../GlobalConfigManagement/GlobalConfigManagement').then(routeWrapper);
const EncryptTool = () => import('../EncryptTool/EncryptTool').then(routeWrapper);
const TokenManagement = () => import('../TokenManagement/TokenManagement').then(routeWrapper);
const AboutCrafterCMSView = () => import('../AboutCrafterCMSView/AboutCrafterCMSView').then(routeWrapper);
const AccountManagement = lazy(() => import('../AccountManagement/AccountManagement'));
export function GlobalApp(props) {
const { passwordRequirementsMinComplexity } = props;
const globalNavigation = useGlobalNavigation();
const router = createHashRouter(
createRoutesFromElements(
React.createElement(
Route,
{ path: '/', element: React.createElement(GlobalAppInternal, { ...props }) },
React.createElement(Route, { path: GlobalRoutes.Projects, element: React.createElement(SiteManagement, null) }),
React.createElement(Route, { path: '/sites', element: React.createElement(SiteManagement, null) }),
React.createElement(Route, {
path: GlobalRoutes.Users,
element: React.createElement(UserManagement, {
passwordRequirementsMinComplexity: passwordRequirementsMinComplexity
})
}),
React.createElement(Route, { path: GlobalRoutes.Groups, lazy: GroupManagement }),
React.createElement(Route, { path: GlobalRoutes.Audit, lazy: AuditManagement }),
React.createElement(Route, { path: GlobalRoutes.LogLevel, lazy: LogLevelManagement }),
React.createElement(Route, { path: GlobalRoutes.LogConsole, lazy: LogConsole }),
React.createElement(Route, { path: GlobalRoutes.GlobalConfig, lazy: GlobalConfigManagement }),
React.createElement(Route, { path: GlobalRoutes.EncryptTool, lazy: EncryptTool }),
React.createElement(Route, { path: GlobalRoutes.TokenManagement, lazy: TokenManagement }),
React.createElement(Route, { path: GlobalRoutes.About, lazy: AboutCrafterCMSView }),
React.createElement(Route, {
path: GlobalRoutes.Settings,
element: React.createElement(AccountManagement, {
passwordRequirementsMinComplexity: passwordRequirementsMinComplexity
})
}),
React.createElement(Route, {
path: '/',
element: globalNavigation.items
? React.createElement(Navigate, { to: `${urlMapping[globalNavigation.items[0].id].replace('#', '')}` })
: React.createElement(LoadingState, {
styles: {
root: {
height: '100%',
margin: 0
}
}
})
}),
React.createElement(Route, { path: '*', element: React.createElement(RouteNotFound, null) })
)
)
);
return React.createElement(RouterProvider, { router: router });
}
function RouteNotFound() {
const { pathname } = useLocation();
return React.createElement(
Box,
{ display: 'flex', flexDirection: 'column', height: '100%' },
React.createElement(
Box,
{ component: 'section', sx: { margin: '10px 12px 0 auto' } },
React.createElement(LauncherOpenerButton, null)
),
React.createElement(EmptyState, {
styles: {
root: {
height: '100%',
margin: 0
}
},
title: '404',
subtitle: React.createElement(FormattedMessage, {
id: 'globalApp.routeNotFound',
defaultMessage: 'Route "{pathname}" not found',
values: { pathname }
})
})
);
}
export function GlobalAppInternal(props) {
const { classes } = useStyles();
const { footerHtml } = props;
const [width, setWidth] = useState(240);
const [{ openSidebar }] = useGlobalAppState();
const { items } = useGlobalNavigation();
const { formatMessage } = useIntl();
const location = useLocation();
const idByPathLookup = useMemo(
() =>
items?.reduce((lookup, item) => {
lookup[getLauncherSectionLink(item.id, '').replace(/^#/, '')] = item.id;
return lookup;
}, {}),
[items]
);
useEffect(() => {
const path = location.pathname;
const id = idByPathLookup?.[path];
document.title = `CrafterCMS - ${formatMessage(
globalMenuMessages[id] ?? {
id: 'globalApp.routeNotFound',
defaultMessage: 'Route not found'
}
)}`;
}, [formatMessage, idByPathLookup, location.pathname]);
return React.createElement(
Paper,
{ className: classes.root, elevation: 0 },
React.createElement(
ResizeableDrawer,
{
classes: { drawerPaper: classes.drawerPaper, drawerBody: classes.drawerBody },
open: openSidebar,
width: width,
onWidthChange: setWidth
},
React.createElement(LauncherGlobalNav, {
title: '',
sectionStyles: {
nav: {
maxHeight: '100%',
overflow: 'auto'
}
},
tileStyles: {
tile: {
width: '100%',
height: 'auto',
flexDirection: 'row',
justifyContent: 'left',
margin: '0 0 5px'
},
iconAvatar: {
width: '25px',
height: '25px',
margin: '5px 10px'
},
title: {
textAlign: 'left'
}
}
}),
React.createElement(
'footer',
{ className: classes.footer },
React.createElement(CrafterCMSLogo, { width: 100, className: classes.logo }),
React.createElement(Typography, {
component: 'p',
variant: 'caption',
className: classes.footerDescription,
dangerouslySetInnerHTML: { __html: footerHtml }
})
)
),
React.createElement(
Box,
{ className: classes.wrapper, height: '100%', width: '100%', paddingLeft: openSidebar ? `${width}px` : 0 },
React.createElement(
Suspense,
{
fallback: React.createElement(
React.Fragment,
null,
React.createElement(GlobalAppToolbar, { title: React.createElement(Skeleton, { width: '140px' }) }),
React.createElement(
Box,
{ display: 'flex', sx: { height: '100%' } },
React.createElement(LoadingState, null)
)
)
},
React.createElement(Outlet, null)
)
)
);
}
export default GlobalApp;