diff --git a/src/fireedge/src/client/apps/provision/routes.js b/src/fireedge/src/client/apps/provision/routes.js index 457892594b..2e29a79532 100644 --- a/src/fireedge/src/client/apps/provision/routes.js +++ b/src/fireedge/src/client/apps/provision/routes.js @@ -22,6 +22,8 @@ import { import loadable from '@loadable/component' +import { T } from 'client/constants' + const Dashboard = loadable( () => import('client/containers/Dashboard/Provision'), { ssr: false } @@ -64,48 +66,48 @@ export const PATH = { export const ENDPOINTS = [ { - label: 'Dashboard', + title: T.Dashboard, path: PATH.DASHBOARD, sidebar: true, icon: DashboardIcon, Component: Dashboard, }, { - label: 'Providers', + title: T.Providers, path: PATH.PROVIDERS.LIST, sidebar: true, icon: ProvidersIcon, Component: Providers, }, { - label: 'Create Provider', + title: T.CreateProvider, path: PATH.PROVIDERS.CREATE, Component: CreateProvider, }, { - label: 'Edit Provider template', + title: T.UpdateProvider, path: PATH.PROVIDERS.EDIT, Component: CreateProvider, }, { - label: 'Provisions', + title: T.Provisions, path: PATH.PROVISIONS.LIST, sidebar: true, icon: ProvisionsIcon, Component: Provisions, }, { - label: 'Create Provision', + title: T.CreateProvision, path: PATH.PROVISIONS.CREATE, Component: CreateProvision, }, { - label: 'Edit Provision template', + title: 'Edit Provision template', path: PATH.PROVISIONS.EDIT, Component: CreateProvision, }, { - label: 'Settings', + title: T.Settings, path: PATH.SETTINGS, sidebar: true, icon: SettingsIcon, diff --git a/src/fireedge/src/client/apps/sunstone/routes.js b/src/fireedge/src/client/apps/sunstone/routes.js index e659e1aa9c..a395db8f80 100644 --- a/src/fireedge/src/client/apps/sunstone/routes.js +++ b/src/fireedge/src/client/apps/sunstone/routes.js @@ -13,12 +13,15 @@ * See the License for the specific language governing permissions and * * limitations under the License. * * ------------------------------------------------------------------------- */ -import loadable from '@loadable/component' import { ReportColumns as DashboardIcon, Settings as SettingsIcon, } from 'iconoir-react' +import loadable from '@loadable/component' + +import { T } from 'client/constants' + const Dashboard = loadable( () => import('client/containers/Dashboard/Sunstone'), { ssr: false } @@ -43,7 +46,7 @@ export const PATH = { export const ENDPOINTS = [ { - label: 'Dashboard', + title: T.Dashboard, path: PATH.DASHBOARD, sidebar: true, icon: DashboardIcon, @@ -51,7 +54,7 @@ export const ENDPOINTS = [ Component: Dashboard, }, { - label: 'Settings', + title: T.Settings, path: PATH.SETTINGS, sidebar: true, icon: SettingsIcon, @@ -59,13 +62,13 @@ export const ENDPOINTS = [ Component: Settings, }, { - label: 'Guacamole', + title: 'Guacamole', // no need to translate disableLayout: true, path: PATH.GUACAMOLE, Component: Guacamole, }, { - label: 'WebMKS', + title: 'WebMKS', // no need to translate disableLayout: true, path: PATH.WMKS, Component: WebMKS, diff --git a/src/fireedge/src/client/apps/sunstone/routesOne.js b/src/fireedge/src/client/apps/sunstone/routesOne.js index 06ed4e71d9..4e7dc0070d 100644 --- a/src/fireedge/src/client/apps/sunstone/routesOne.js +++ b/src/fireedge/src/client/apps/sunstone/routesOne.js @@ -204,23 +204,24 @@ export const PATH = { const ENDPOINTS = [ { - label: T.Instances, + title: T.Instances, icon: InstancesIcons, routes: [ { - label: T.VMs, + title: T.VMs, path: PATH.INSTANCE.VMS.LIST, sidebar: true, icon: VmsIcons, Component: VirtualMachines, }, { - label: (params) => [T.VMDetailId, params.id], + title: T.VM, + description: (params) => `#${params?.id}`, path: PATH.INSTANCE.VMS.DETAIL, Component: VirtualMachineDetail, }, { - label: T.VirtualRouters, + title: T.VirtualRouters, path: PATH.INSTANCE.VROUTERS.LIST, sidebar: true, icon: VRoutersIcons, @@ -229,80 +230,85 @@ const ENDPOINTS = [ ], }, { - label: T.Templates, + title: T.Templates, icon: TemplatesIcon, routes: [ { - label: T.VMTemplates, + title: T.VMTemplates, path: PATH.TEMPLATE.VMS.LIST, sidebar: true, icon: TemplateIcon, Component: VmTemplates, }, { - label: T.InstantiateVmTemplate, + title: T.InstantiateVmTemplate, + description: (_, state) => + state?.ID !== undefined && `#${state.ID} ${state.NAME}`, path: PATH.TEMPLATE.VMS.INSTANTIATE, Component: InstantiateVmTemplate, }, { - label: T.CreateVmTemplate, + title: (_, state) => + state?.ID !== undefined ? T.UpdateVmTemplate : T.CreateVmTemplate, + description: (_, state) => + state?.ID !== undefined && `#${state.ID} ${state.NAME}`, path: PATH.TEMPLATE.VMS.CREATE, Component: CreateVmTemplate, }, ], }, { - label: T.Storage, + title: T.Storage, icon: StorageIcon, routes: [ { - label: T.Datastores, + title: T.Datastores, path: PATH.STORAGE.DATASTORES.LIST, sidebar: true, icon: DatastoreIcon, Component: Datastores, }, { - label: T.Images, + title: T.Images, path: PATH.STORAGE.IMAGES.LIST, sidebar: true, icon: ImageIcon, Component: Images, }, { - label: T.Marketplaces, + title: T.Marketplaces, path: PATH.STORAGE.MARKETPLACES.LIST, sidebar: true, icon: MarketplaceIcon, Component: Marketplaces, }, { - label: T.Apps, + title: T.Apps, path: PATH.STORAGE.MARKETPLACE_APPS.LIST, sidebar: true, icon: MarketplaceAppIcon, Component: MarketplaceApps, }, { - label: T.CreateMarketApp, + title: T.CreateMarketApp, path: PATH.STORAGE.MARKETPLACE_APPS.CREATE, Component: CreateMarketplaceApp, }, ], }, { - label: T.Networks, + title: T.Networks, icon: NetworksIcon, routes: [ { - label: T.VirtualNetworks, + title: T.VirtualNetworks, path: PATH.NETWORK.VNETS.LIST, sidebar: true, icon: NetworkIcon, Component: VirtualNetworks, }, { - label: T.NetworkTemplates, + title: T.NetworkTemplates, path: PATH.NETWORK.VN_TEMPLATES.LIST, sidebar: true, icon: NetworkTemplateIcon, @@ -311,40 +317,42 @@ const ENDPOINTS = [ ], }, { - label: T.Infrastructure, + title: T.Infrastructure, icon: InfrastructureIcon, routes: [ { - label: T.Clusters, + title: T.Clusters, path: PATH.INFRASTRUCTURE.CLUSTERS.LIST, sidebar: true, icon: ClusterIcon, Component: Clusters, }, { - label: (params) => [T.ClusterDetailId, params.id], + title: T.Cluster, + description: (params) => `#${params?.id}`, path: PATH.INFRASTRUCTURE.CLUSTERS.DETAIL, Component: ClusterDetail, }, { - label: T.Hosts, + title: T.Hosts, path: PATH.INFRASTRUCTURE.HOSTS.LIST, sidebar: true, icon: HostIcon, Component: Hosts, }, { - label: T.CreateHost, + title: T.CreateHost, path: PATH.INFRASTRUCTURE.HOSTS.CREATE, Component: CreateHost, }, { - label: (params) => [T.HostDetailId, params.id], + title: T.Host, + description: (params) => `#${params?.id}`, path: PATH.INFRASTRUCTURE.HOSTS.DETAIL, Component: HostDetail, }, { - label: T.Zones, + title: T.Zones, path: PATH.INFRASTRUCTURE.ZONES.LIST, sidebar: true, icon: ZoneIcon, @@ -353,30 +361,32 @@ const ENDPOINTS = [ ], }, { - label: T.System, + title: T.System, icon: SystemIcon, routes: [ { - label: T.Users, + title: T.Users, path: PATH.SYSTEM.USERS.LIST, sidebar: true, icon: UserIcon, Component: Users, }, { - label: (params) => [T.UserDetailId, params.id], + title: T.User, + description: (params) => `#${params?.id}`, path: PATH.SYSTEM.USERS.DETAIL, Component: UserDetail, }, { - label: T.Groups, + title: T.Groups, path: PATH.SYSTEM.GROUPS.LIST, sidebar: true, icon: GroupIcon, Component: Groups, }, { - label: (params) => [T.GroupDetailId, params.id], + title: T.Group, + description: (params) => `#${params?.id}`, path: PATH.SYSTEM.GROUPS.DETAIL, Component: GroupDetail, }, diff --git a/src/fireedge/src/client/components/HOC/InternalLayout/index.js b/src/fireedge/src/client/components/HOC/InternalLayout/index.js index 6eac1cd821..a47067be48 100644 --- a/src/fireedge/src/client/components/HOC/InternalLayout/index.js +++ b/src/fireedge/src/client/components/HOC/InternalLayout/index.js @@ -14,30 +14,23 @@ * limitations under the License. * * ------------------------------------------------------------------------- */ /* eslint-disable jsdoc/require-jsdoc */ -import { useRef, useEffect, useMemo } from 'react' +import { useRef, useMemo } from 'react' import PropTypes from 'prop-types' -import { useParams } from 'react-router-dom' import { Box, Container } from '@mui/material' import { CSSTransition } from 'react-transition-group' -import { useGeneral, useGeneralApi } from 'client/features/General' +import { useGeneral } from 'client/features/General' import Header from 'client/components/Header' import Footer from 'client/components/Footer' import internalStyles from 'client/components/HOC/InternalLayout/styles' import { sidebar, footer } from 'client/theme/defaults' -const InternalLayout = ({ title, disableLayout, children }) => { +const InternalLayout = ({ children, ...route }) => { const classes = internalStyles() const container = useRef() const { isFixMenu } = useGeneral() - const { changeTitle } = useGeneralApi() - const params = useParams() - useEffect(() => { - changeTitle(typeof title === 'function' ? title(params) : title) - }, [title]) - - if (disableLayout) { + if (route.disableLayout) { return ( { [isFixMenu] )} > -
+
{ } InternalLayout.propTypes = { - title: PropTypes.oneOfType([PropTypes.string, PropTypes.func]), disableLayout: PropTypes.bool, children: PropTypes.any, } diff --git a/src/fireedge/src/client/components/Header/index.js b/src/fireedge/src/client/components/Header/index.js index 48a4191057..0da7326598 100644 --- a/src/fireedge/src/client/components/Header/index.js +++ b/src/fireedge/src/client/components/Header/index.js @@ -13,9 +13,10 @@ * See the License for the specific language governing permissions and * * limitations under the License. * * ------------------------------------------------------------------------- */ -/* eslint-disable jsdoc/require-jsdoc */ -import { useMemo } from 'react' + +import { memo, useMemo } from 'react' import PropTypes from 'prop-types' +import { useParams, useLocation } from 'react-router-dom' import { AppBar, @@ -37,12 +38,25 @@ import Zone from 'client/components/Header/Zone' import { Translate } from 'client/components/HOC' import { sentenceCase } from 'client/utils' -const Header = () => { +const Header = memo(({ route: { title, description } = {} }) => { const { isOneAdmin } = useAuth() const { fixMenu } = useGeneralApi() - const { appTitle, title, isBeta, withGroupSwitcher } = useGeneral() + const { appTitle, isBeta, withGroupSwitcher } = useGeneral() + + const params = useParams() + const { state } = useLocation() + const appAsSentence = useMemo(() => sentenceCase(appTitle), [appTitle]) + const [ensuredTitle, ensuredDescription] = useMemo(() => { + if (!title) return [] + + const ensure = (label) => + typeof label === 'function' ? label(params, state) : label + + return [ensure(title), ensure(description)] + }, [params, state, title, description]) + return ( @@ -58,10 +72,9 @@ const Header = () => { {'One'} @@ -83,22 +96,35 @@ const Header = () => { )} - {title && ( - - - - )} + + {ensuredTitle && ( + + + + )} + {ensuredDescription && ( + + + + )} + { ) -} +}) Header.propTypes = { + route: PropTypes.object, scrollContainer: PropTypes.object, } +Header.displayName = 'Header' + export default Header diff --git a/src/fireedge/src/client/components/Sidebar/SidebarCollapseItem.js b/src/fireedge/src/client/components/Sidebar/SidebarCollapseItem.js index a46b87085f..fffdba7a06 100644 --- a/src/fireedge/src/client/components/Sidebar/SidebarCollapseItem.js +++ b/src/fireedge/src/client/components/Sidebar/SidebarCollapseItem.js @@ -38,12 +38,12 @@ import { Translate } from 'client/components/HOC' * Renders nested list options for sidebar. * * @param {object} props - Props - * @param {string} props.label - Label + * @param {string} props.title - Title * @param {object[]} props.routes - Nested list of routes * @param {ReactElement} props.icon - Icon * @returns {ReactElement} Sidebar option that includes other list of routes */ -const SidebarCollapseItem = ({ label = '', routes = [], icon: Icon }) => { +const SidebarCollapseItem = ({ title = '', routes = [], icon: Icon }) => { const classes = sidebarStyles() const { pathname } = useLocation() const { isFixMenu } = useGeneral() @@ -75,8 +75,8 @@ const SidebarCollapseItem = ({ label = '', routes = [], icon: Icon }) => { )} } + data-cy={title.toLocaleLowerCase()} + primary={} {...(expanded && { className: 'open' })} primaryTypographyProps={{ variant: 'body1' }} /> @@ -90,10 +90,7 @@ const SidebarCollapseItem = ({ label = '', routes = [], icon: Icon }) => { > {routes - ?.filter( - ({ sidebar = false, ...route }) => - sidebar && typeof route.label === 'string' - ) + ?.filter(({ sidebar = false }) => sidebar) ?.map((subItem, index) => ( ))} @@ -104,7 +101,7 @@ const SidebarCollapseItem = ({ label = '', routes = [], icon: Icon }) => { } SidebarCollapseItem.propTypes = { - label: PropTypes.string.isRequired, + title: PropTypes.string.isRequired, icon: PropTypes.oneOfType([PropTypes.node, PropTypes.object]), routes: PropTypes.arrayOf( PropTypes.shape({ diff --git a/src/fireedge/src/client/components/Sidebar/SidebarLink.js b/src/fireedge/src/client/components/Sidebar/SidebarLink.js index 60ba200179..cac86925d2 100644 --- a/src/fireedge/src/client/components/Sidebar/SidebarLink.js +++ b/src/fireedge/src/client/components/Sidebar/SidebarLink.js @@ -31,7 +31,7 @@ import { Translate } from 'client/components/HOC' const SidebarLink = memo( ({ - label = '', + title = '', path = '/', icon: Icon, devMode = false, @@ -63,7 +63,7 @@ const SidebarLink = memo( )} } + primary={} primaryTypographyProps={{ ...(devMode && { component: DevTypography }), 'data-cy': 'main-menu-item-text', @@ -76,7 +76,7 @@ const SidebarLink = memo( ) SidebarLink.propTypes = { - label: PropTypes.string.isRequired, + title: PropTypes.string.isRequired, path: PropTypes.string.isRequired, icon: PropTypes.any, devMode: PropTypes.bool, diff --git a/src/fireedge/src/client/constants/translates.js b/src/fireedge/src/client/constants/translates.js index 2c8db8360e..bea8c963a9 100644 --- a/src/fireedge/src/client/constants/translates.js +++ b/src/fireedge/src/client/constants/translates.js @@ -57,16 +57,18 @@ module.exports = { Create: 'Create', CreateHost: 'Create Host', CreateMarketApp: 'Create Marketplace App', + CreateProvider: 'Create Provider', + CreateProvision: 'Create Provision', CreateVmTemplate: 'Create VM Template', CurrentGroup: 'Current group: %s', CurrentOwner: 'Current owner: %s', Delete: 'Delete', + DeleteAllImages: 'Delete all images', DeleteDb: 'Delete database', DeleteScheduleAction: 'Delete schedule action: %s', DeleteSeveralTemplates: 'Delete several Templates', - DeleteTemplate: 'Delete Template', DeleteSomething: 'Delete: %s', - DeleteAllImages: 'Delete all images', + DeleteTemplate: 'Delete Template', Deploy: 'Deploy', Detach: 'Detach', DetachSomething: 'Detach: %s', @@ -152,7 +154,9 @@ module.exports = { UnReschedule: 'Un-Reschedule', Unshare: 'Unshare', Update: 'Update', + UpdateProvider: 'Update Provider', UpdateScheduleAction: 'Update schedule action: %s', + UpdateVmTemplate: 'Update VM Template', /* questions */ Yes: 'Yes', @@ -285,10 +289,8 @@ module.exports = { /* sections - system */ User: 'User', - UserDetailId: 'User #%s', Users: 'Users', Group: 'Group', - GroupDetailId: 'Group #%s', Groups: 'Groups', VDC: 'VDC', VDCs: 'VDCs', @@ -297,10 +299,8 @@ module.exports = { /* sections - infrastructure */ Cluster: 'Cluster', - ClusterDetailId: 'Cluster #%s', Clusters: 'Clusters', Host: 'Host', - HostDetailId: 'Host #%s', Hosts: 'Hosts', Infrastructure: 'Infrastructure', Zone: 'Zone', @@ -333,7 +333,6 @@ module.exports = { /* sections - templates & instances */ Instances: 'Instances', VM: 'VM', - VMDetailId: 'VM #%s', VMs: 'VMs', VirtualRouter: 'Virtual Router', VirtualRouters: 'Virtual Routers', diff --git a/src/fireedge/src/client/features/General/actions.js b/src/fireedge/src/client/features/General/actions.js index 1987d66c8c..2125ca9877 100644 --- a/src/fireedge/src/client/features/General/actions.js +++ b/src/fireedge/src/client/features/General/actions.js @@ -18,7 +18,6 @@ import { createAction } from '@reduxjs/toolkit' export const fixMenu = createAction('Fix menu') export const changeZone = createAction('Change zone') export const changeLoading = createAction('Change loading') -export const changeTitle = createAction('Change title') export const changeAppTitle = createAction('Change App title') export const dismissSnackbar = createAction('Dismiss snackbar') diff --git a/src/fireedge/src/client/features/General/hooks.js b/src/fireedge/src/client/features/General/hooks.js index 4ad3d5f343..6948cab549 100644 --- a/src/fireedge/src/client/features/General/hooks.js +++ b/src/fireedge/src/client/features/General/hooks.js @@ -29,7 +29,6 @@ export const useGeneralApi = () => { return { fixMenu: (isFixed) => dispatch(actions.fixMenu(isFixed)), changeLoading: (isLoading) => dispatch(actions.changeLoading(isLoading)), - changeTitle: (title) => dispatch(actions.changeTitle(title)), changeAppTitle: (appTitle) => dispatch(actions.changeAppTitle(appTitle)), changeZone: (zone) => dispatch(actions.changeZone(zone)), diff --git a/src/fireedge/src/client/features/General/slice.js b/src/fireedge/src/client/features/General/slice.js index ced36e7dc8..d9c7be9367 100644 --- a/src/fireedge/src/client/features/General/slice.js +++ b/src/fireedge/src/client/features/General/slice.js @@ -22,7 +22,6 @@ import { APPS_IN_BETA, APPS_WITH_SWITCHER } from 'client/constants' const initial = { zone: 0, - title: null, appTitle: null, isBeta: false, withGroupSwitcher: false, @@ -54,10 +53,6 @@ const { name, reducer } = createSlice({ ...state, isLoading: !!payload, })) - .addCase(actions.changeTitle, (state, { payload }) => ({ - ...state, - title: payload, - })) .addCase(actions.changeAppTitle, (state, { payload: appTitle }) => { const lowerAppTitle = String(appTitle).toLowerCase() const isBeta = APPS_IN_BETA?.includes(lowerAppTitle) diff --git a/src/fireedge/src/client/router/dev.js b/src/fireedge/src/client/router/dev.js index cefb8f01e7..d939ce4638 100644 --- a/src/fireedge/src/client/router/dev.js +++ b/src/fireedge/src/client/router/dev.js @@ -13,8 +13,8 @@ * See the License for the specific language governing permissions and * * limitations under the License. * * ------------------------------------------------------------------------- */ -import loadable from '@loadable/component' import { Code as DevIcon } from 'iconoir-react' +import loadable from '@loadable/component' const TestApi = loadable(() => import('client/containers/TestApi'), { ssr: false, @@ -30,7 +30,7 @@ export const PATH = { export const ENDPOINTS = [ { - label: 'Test API', + title: 'Test API', // no need to translate path: PATH.TEST_API, devMode: true, sidebar: true, @@ -38,7 +38,7 @@ export const ENDPOINTS = [ Component: TestApi, }, { - label: 'Test Form', + title: 'Test Form', // no need to translate path: PATH.TEST_FORM, devMode: true, sidebar: true, diff --git a/src/fireedge/src/client/router/index.js b/src/fireedge/src/client/router/index.js index bc2cd7608e..7f777bb015 100644 --- a/src/fireedge/src/client/router/index.js +++ b/src/fireedge/src/client/router/index.js @@ -28,9 +28,9 @@ import { import { ProtectedRoute, NoAuthRoute } from 'client/components/Route' import { InternalLayout } from 'client/components/HOC' -const renderRoute = ({ Component, label, disableLayout, ...rest }, index) => ( - - +const renderRoute = ({ Component, ...route }) => ( + + } />