UNPKG

@craftercms/studio-ui

Version:

Services, components, models & utils to build CrafterCMS authoring extensions.

246 lines (244 loc) 9.73 kB
/* * 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;