diff --git a/src/web/components/bar/menubar.jsx b/src/web/components/bar/menubar.jsx
deleted file mode 100644
index ea33f47a71..0000000000
--- a/src/web/components/bar/menubar.jsx
+++ /dev/null
@@ -1,308 +0,0 @@
-/* Copyright (C) 2016-2022 Greenbone AG
- *
- * SPDX-License-Identifier: AGPL-3.0-or-later
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Affero General Public License
- * as published by the Free Software Foundation, either version 3
- * of the License, or (at your option) any later version.
- *
- * 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 Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see .
- */
-import React from 'react';
-
-import {connect} from 'react-redux';
-
-import styled from 'styled-components';
-
-import _ from 'gmp/locale';
-
-import {isDefined} from 'gmp/utils/identity';
-
-import Layout from 'web/components/layout/layout';
-
-import Menu from 'web/components/menu/menu';
-import MenuEntry from 'web/components/menu/menuentry';
-import MenuHelpEntry from 'web/components/menu/menuhelpentry';
-import MenuSection from 'web/components/menu/menusection';
-
-import {isLoggedIn} from 'web/store/usersettings/selectors';
-
-import compose from 'web/utils/compose';
-import PropTypes from 'web/utils/proptypes';
-import Theme from 'web/utils/theme';
-import withGmp from 'web/utils/withGmp';
-import withCapabilities from 'web/utils/withCapabilities';
-
-const MENU_BAR_HEIGHT = '35px';
-
-const Ul = styled.ul`
- width: 100%;
- display: flex;
- flex-direction: row;
- margin: 0;
- padding: 0;
- list-style: none;
-`;
-
-const Wrapper = styled(Layout)`
- background-color: ${Theme.darkGray};
- height: ${MENU_BAR_HEIGHT};
- position: fixed;
- top: 42px;
- left: 0;
- right: 0;
- z-index: ${Theme.Layers.menu};
-`;
-
-const MenuBarPlaceholder = styled.div`
- height: ${MENU_BAR_HEIGHT};
-`;
-
-// eslint-disable-next-line no-shadow
-const MenuBar = ({isLoggedIn, capabilities}) => {
- if (!isLoggedIn || !isDefined(capabilities)) {
- return null;
- }
-
- const may_op_scans = [
- 'tasks',
- 'reports',
- 'results',
- 'vulns',
- 'overrides',
- 'notes',
- ].reduce((sum, cur) => sum || capabilities.mayAccess(cur), false);
-
- const may_op_configuration = [
- 'targets',
- 'port_lists',
- 'credentials',
- 'scan_configs',
- 'alerts',
- 'schedules',
- 'report_configs',
- 'report_formats',
- 'scanners',
- 'filters',
- 'tags',
- ].reduce((sum, cur) => sum || capabilities.mayAccess(cur), false);
-
- const mayOpNotesOverrides = ['notes', 'overrides'].reduce(
- (sum, cur) => sum || capabilities.mayAccess(cur),
- false,
- );
-
- const mayOpAlertsSchedulesReportFormats = [
- 'alerts',
- 'schedules',
- 'report_formats',
- ].reduce((sum, cur) => sum || capabilities.mayAccess(cur), false);
-
- const mayOpScannersFiltersTags = ['scanners', 'filters', 'tags'].reduce(
- (sum, cur) => sum || capabilities.mayAccess(cur),
- false,
- );
-
- const mayOpResilience = ['tickets', 'policies', 'audits'].reduce(
- (sum, cur) => sum || capabilities.mayAccess(cur),
- false,
- );
-
- const mayOpAssets = ['assets', 'tls_certificates'].reduce(
- (sum, cur) => sum || capabilities.mayAccess(cur),
- false,
- );
-
- return (
-
-
-
-
- {may_op_scans && (
-
- )}
- {mayOpAssets && (
-
- )}
- {mayOpResilience && (
-
- )}
- {capabilities.mayAccess('info') && (
-
- )}
- {may_op_configuration && (
-
- )}
-
-
-
-
-
- );
-};
-
-MenuBar.propTypes = {
- capabilities: PropTypes.capabilities,
- gmp: PropTypes.gmp.isRequired,
- isLoggedIn: PropTypes.bool.isRequired,
-};
-
-const mapStateToProps = rootState => ({
- isLoggedIn: isLoggedIn(rootState),
-});
-
-export default compose(
- withCapabilities,
- withGmp,
- connect(mapStateToProps),
-)(MenuBar);
-
-// vim: set ts=2 sw=2 tw=80:
diff --git a/src/web/components/menu/menu.jsx b/src/web/components/menu/menu.jsx
index 2ef166fc73..19be1eb7a4 100644
--- a/src/web/components/menu/menu.jsx
+++ b/src/web/components/menu/menu.jsx
@@ -1,173 +1,446 @@
-/* Copyright (C) 2016-2022 Greenbone AG
+/* SPDX-FileCopyrightText: 2024 Greenbone AG
*
* SPDX-License-Identifier: AGPL-3.0-or-later
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Affero General Public License
- * as published by the Free Software Foundation, either version 3
- * of the License, or (at your option) any later version.
- *
- * 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 Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see .
*/
import React from 'react';
-import styled, {keyframes} from 'styled-components';
-
-import {isDefined, hasValue} from 'gmp/utils/identity';
+import {AppNavigation} from '@greenbone/opensight-ui-components';
-import PropTypes from 'web/utils/proptypes';
-import Theme from 'web/utils/theme';
+import useTranslation from 'web/hooks/useTranslation';
+import useCapabilities from 'web/utils/useCapabilities';
+import TaskIcon from 'web/components/icon/taskicon';
import Link from 'web/components/link/link';
+import ReportIcon from 'web/components/icon/reporticon';
+import ResultIcon from 'web/components/icon/resulticon';
+import VulnerabilityIcon from 'web/components/icon/vulnerabilityicon';
+import NoteIcon from 'web/components/icon/noteicon';
+import OverrideIcon from 'web/components/icon/overrideicon';
+import HostIcon from 'web/components/icon/hosticon';
+import OperatingSystemIcon from 'web/components/icon/ossvgicon';
+import TlsCertificateIcon from 'web/components/icon/tlscertificateicon';
+import TicketIcon from 'web/components/icon/ticketicon';
+import PolicyIcon from 'web/components/icon/policyicon';
+import AuditIcon from 'web/components/icon/auditicon';
+import NvtIcon from 'web/components/icon/nvticon';
+import CveIcon from 'web/components/icon/cveicon';
+import CpeIcon from 'web/components/icon/cpeicon';
+import CertBundAdvIcon from 'web/components/icon/certbundadvicon';
+import DfnCertAdvIcon from 'web/components/icon/dfncertadvicon';
+import TargetIcon from 'web/components/icon/targeticon';
+import PortListIcon from 'web/components/icon/portlisticon';
+import CvssIcon from 'web/components/icon/cvssicon';
+import CredentialIcon from 'web/components/icon/credentialicon';
+import ScanConfigIcon from 'web/components/icon/scanconfigicon';
+import AlertIcon from 'web/components/icon/alerticon';
+import ScheduleIcon from 'web/components/icon/scheduleicon';
+import ReportFormatIcon from 'web/components/icon/reportformaticon';
+import ScannerIcon from 'web/components/icon/scannericon';
+import FilterIcon from 'web/components/icon/filtericon';
+import TagsSvgIcon from 'web/components/icon/tagssvgicon';
+import UserIcon from 'web/components/icon/usericon';
+import GroupIcon from 'web/components/icon/groupicon';
+import RoleIcon from 'web/components/icon/roleicon';
+import PermissionIcon from 'web/components/icon/permissionicon';
+import PerformanceIcon from 'web/components/icon/performanceicon';
+import TrashcanIcon from 'web/components/icon/trashcanicon';
+import FeedIcon from 'web/components/icon/feedicon';
+import LdapIcon from 'web/components/icon/ldapicon';
+import RadiusIcon from 'web/components/icon/radiusicon';
-import MenuSection from 'web/components/menu/menusection';
-
-const StyledMenu = styled.li`
- flex-grow: 1;
- flex-shrink: 1;
- flex-basis: 0;
- margin: 1px;
- height: 35px;
-
- &:hover {
- border-bottom: 3px solid ${Theme.green};
- }
-
- & a {
- display: flex;
- flex-grow: 1;
- align-items: center ${'' /* center text vertically*/};
- }
-
- & a,
- & a:hover,
- & a:focus,
- & a:link {
- text-decoration: none;
- color: ${Theme.black};
- }
-`;
+const Menu = () => {
+ const [_] = useTranslation();
+ const capabilities = useCapabilities();
-const DefaultEntry = styled.div`
- display: flex;
- justify-content: center;
- flex-grow: 1;
-
- & a,
- & a:hover,
- & a:focus,
- & a:link {
- color: ${Theme.white};
- display: block;
- height: 35px;
- line-height: 35px;
- font-size: 10px;
- font-weight: bold;
- text-align: center;
- }
-`;
-
-export const StyledMenuEntry = styled.li`
- display: flex;
- list-style: none;
- background: ${Theme.white};
- text-indent: 12px;
- text-align: left;
- color: ${Theme.darkGray};
- min-height: 22px;
- font-size: 10px;
- font-weight: bold;
-
- & a {
- line-height: 22px;
- color: ${Theme.darkGray};
- }
-
- & a:hover {
- color: ${Theme.white};
- background: ${Theme.green};
- }
-`;
-
-const MenuList = styled.ul`
- width: 255px;
- z-index: ${Theme.Layers.menu};
- position: absolute;
- display: none;
- background: ${Theme.green};
- border-top: 1px solid ${Theme.mediumGray};
- border-left: 1px solid ${Theme.mediumGray};
- border-right: 1px solid ${Theme.mediumGray};
- border-bottom: 1px solid ${Theme.mediumGray};
- list-style: none;
- padding-left: 0px;
- margin-left: -1px;
+ const mayOpScans = [
+ 'tasks',
+ 'reports',
+ 'results',
+ 'vulns',
+ 'overrides',
+ 'notes',
+ ].reduce((sum, cur) => sum || capabilities.mayAccess(cur), false);
+ const mayOpConfiguration = [
+ 'targets',
+ 'port_lists',
+ 'credentials',
+ 'scan_configs',
+ 'alerts',
+ 'schedules',
+ 'report_configs',
+ 'report_formats',
+ 'scanners',
+ 'filters',
+ 'tags',
+ ].reduce((sum, cur) => sum || capabilities.mayAccess(cur), false);
+ const mayOpResilience = ['tickets', 'policies', 'audits'].reduce(
+ (sum, cur) => sum || capabilities.mayAccess(cur),
+ false,
+ );
+ const mayOpAssets = ['assets', 'tls_certificates'].reduce(
+ (sum, cur) => sum || capabilities.mayAccess(cur),
+ false,
+ );
- ${StyledMenu}:hover & {
- display: block;
- }
- animation: ${keyframes({
- '0%': {
- transform: 'scale(0.9)',
- transformOrigin: 'top',
- opacity: 0,
- translateY: '0px',
+ const menuPoints = [
+ [
+ {
+ icon: () => {},
+ label: _('Dashboards'),
+ to: '/',
+ active: false,
+ defaultOpened: false,
+ isExternal: false,
},
- '100%': {
- transform: 'scale(1.0)',
- transformOrigin: 'top',
- opacity: 1,
- translate: 0,
+ ],
+ [
+ mayOpScans && {
+ icon: () => {},
+ label: _('Scans'),
+ active: false,
+ defaultOpened: false,
+ isExternal: false,
+ subNav: [
+ capabilities.mayAccess('tasks') && {
+ icon: TaskIcon,
+ label: _('Tasks'),
+ to: '/tasks',
+ active: false,
+ isExternal: false,
+ },
+ capabilities.mayAccess('reports') && {
+ icon: ReportIcon,
+ label: _('Reports'),
+ to: '/reports',
+ active: false,
+ isExternal: false,
+ },
+ capabilities.mayAccess('results') && {
+ icon: ResultIcon,
+ label: _('Results'),
+ to: '/results',
+ active: false,
+ isExternal: false,
+ },
+ capabilities.mayAccess('vulns') && {
+ icon: VulnerabilityIcon,
+ label: _('Vulnerabilities'),
+ to: '/vulnerabilities',
+ active: false,
+ isExternal: false,
+ },
+ capabilities.mayAccess('notes') && {
+ icon: NoteIcon,
+ label: _('Notes'),
+ to: '/notes',
+ active: false,
+ isExternal: false,
+ },
+ capabilities.mayAccess('overrides') && {
+ icon: OverrideIcon,
+ label: _('Overrides'),
+ to: '/overrides',
+ active: false,
+ isExternal: false,
+ },
+ ].filter(Boolean),
},
- })}
- 0.1s ease-in;
-`;
-
-const getFirstMenuEntry = child => {
- // return menu entries without the MenuSection
- if (child.type === MenuSection) {
- return React.Children.toArray(child.props.children).find(chil => !!chil);
- }
- return child;
-};
-
-const Menu = ({children, title, to, ...props}) => {
- let link;
- children = React.Children.toArray(children).filter(hasValue);
-
- if (isDefined(to)) {
- link = {title};
- } else if (isDefined(children) && children.length > 0) {
- let [child] = children;
- child = getFirstMenuEntry(child);
- link = React.cloneElement(child, {title});
- }
-
- const menuentries = children.map(child => (
- {child}
- ));
- return (
-
- {link}
- {isDefined(children) && children.length > 0 && (
- {menuentries}
- )}
-
- );
-};
-
-Menu.propTypes = {
- title: PropTypes.string.isRequired,
- to: PropTypes.string,
+ mayOpAssets && {
+ icon: () => {},
+ label: _('Assets'),
+ active: false,
+ defaultOpened: false,
+ isExternal: false,
+ subNav: [
+ capabilities.mayAccess('assets') && {
+ icon: HostIcon,
+ label: _('Hosts'),
+ to: '/hosts',
+ active: false,
+ isExternal: false,
+ },
+ capabilities.mayAccess('assets') && {
+ icon: OperatingSystemIcon,
+ label: _('Operating Systems'),
+ to: '/operatingsystems',
+ active: false,
+ isExternal: false,
+ },
+ capabilities.mayAccess('tls_certificates') && {
+ icon: TlsCertificateIcon,
+ label: _('TLS Certificates'),
+ to: '/tlscertificates',
+ active: false,
+ isExternal: false,
+ },
+ ].filter(Boolean),
+ },
+ mayOpResilience && {
+ icon: () => {},
+ label: _('Resilience'),
+ active: false,
+ defaultOpened: false,
+ isExternal: false,
+ subNav: [
+ capabilities.mayAccess('tickets') && {
+ icon: TicketIcon,
+ label: _('Remediation Tickets'),
+ to: '/tickets',
+ active: false,
+ isExternal: false,
+ },
+ capabilities.mayAccess('policies') && {
+ icon: PolicyIcon,
+ label: _('Compliance Policies'),
+ to: '/policies',
+ active: false,
+ isExternal: false,
+ },
+ capabilities.mayAccess('audits') && {
+ icon: AuditIcon,
+ label: _('Compliance Audits'),
+ to: '/audits',
+ active: false,
+ isExternal: false,
+ },
+ ].filter(Boolean),
+ },
+ capabilities.mayAccess('info') && {
+ icon: () => {},
+ label: _('SecInfo'),
+ active: false,
+ defaultOpened: false,
+ isExternal: false,
+ subNav: [
+ {
+ icon: NvtIcon,
+ label: _('NVTs'),
+ to: '/nvts',
+ active: false,
+ isExternal: false,
+ },
+ {
+ icon: CveIcon,
+ label: _('CVEs'),
+ to: '/cves',
+ active: false,
+ isExternal: false,
+ },
+ {
+ icon: CpeIcon,
+ label: _('CPEs'),
+ to: '/cpes',
+ active: false,
+ isExternal: false,
+ },
+ {
+ icon: CertBundAdvIcon,
+ label: _('CERT-Bund Advisories'),
+ to: '/certbunds',
+ active: false,
+ isExternal: false,
+ },
+ {
+ icon: DfnCertAdvIcon,
+ label: _('DFN-CERT Advisories'),
+ to: '/dfncerts',
+ active: false,
+ isExternal: false,
+ },
+ ],
+ },
+ mayOpConfiguration && {
+ icon: () => {},
+ label: _('Configuration'),
+ active: false,
+ defaultOpened: false,
+ isExternal: false,
+ subNav: [
+ capabilities.mayAccess('targets') && {
+ icon: TargetIcon,
+ label: _('Targets'),
+ to: '/targets',
+ active: false,
+ isExternal: false,
+ },
+ capabilities.mayAccess('port_lists') && {
+ icon: PortListIcon,
+ label: _('Port Lists'),
+ to: '/portlists',
+ active: false,
+ isExternal: false,
+ },
+ capabilities.mayAccess('credentials') && {
+ icon: CredentialIcon,
+ label: _('Credentials'),
+ to: '/credentials',
+ active: false,
+ isExternal: false,
+ },
+ capabilities.mayAccess('configs') && {
+ icon: ScanConfigIcon,
+ label: _('Scan Configs'),
+ to: '/scanconfigs',
+ active: false,
+ isExternal: false,
+ },
+ capabilities.mayAccess('alerts') && {
+ icon: AlertIcon,
+ label: _('Alerts'),
+ to: '/alerts',
+ active: false,
+ isExternal: false,
+ },
+ capabilities.mayAccess('schedules') && {
+ icon: ScheduleIcon,
+ label: _('Schedules'),
+ to: '/schedules',
+ active: false,
+ isExternal: false,
+ },
+ capabilities.mayAccess('report_configs') && {
+ icon: ReportFormatIcon,
+ label: _('Report Configs'),
+ to: '/reportconfigs',
+ active: false,
+ isExternal: false,
+ },
+ capabilities.mayAccess('report_formats') && {
+ icon: ReportFormatIcon,
+ label: _('Report Formats'),
+ to: '/reportformats',
+ active: false,
+ isExternal: false,
+ },
+ capabilities.mayAccess('scanners') && {
+ icon: ScannerIcon,
+ label: _('Scanners'),
+ to: '/scanners',
+ active: false,
+ isExternal: false,
+ },
+ capabilities.mayAccess('filters') && {
+ icon: FilterIcon,
+ label: _('Filters'),
+ to: '/filters',
+ active: false,
+ isExternal: false,
+ },
+ capabilities.mayAccess('tags') && {
+ icon: TagsSvgIcon,
+ label: _('Tags'),
+ to: '/tags',
+ active: false,
+ isExternal: false,
+ },
+ ].filter(Boolean),
+ },
+ {
+ icon: () => {},
+ label: _('Administration'),
+ active: false,
+ defaultOpened: false,
+ isExternal: false,
+ subNav: [
+ capabilities.mayAccess('users') && {
+ icon: UserIcon,
+ label: _('Users'),
+ to: '/users',
+ active: false,
+ isExternal: false,
+ },
+ capabilities.mayAccess('groups') && {
+ icon: GroupIcon,
+ label: _('Groups'),
+ to: '/groups',
+ active: false,
+ isExternal: false,
+ },
+ capabilities.mayAccess('roles') && {
+ icon: RoleIcon,
+ label: _('Roles'),
+ to: '/roles',
+ active: false,
+ isExternal: false,
+ },
+ capabilities.mayAccess('permissions') && {
+ icon: PermissionIcon,
+ label: _('Permissions'),
+ to: '/permissions',
+ active: false,
+ isExternal: false,
+ },
+ capabilities.mayAccess('system_reports') && {
+ icon: PerformanceIcon,
+ label: _('Performance'),
+ to: '/performance',
+ active: false,
+ isExternal: false,
+ },
+ {
+ icon: TrashcanIcon,
+ label: _('Trashcan'),
+ to: '/trashcan',
+ active: false,
+ isExternal: false,
+ },
+ capabilities.mayAccess('feeds') && {
+ icon: FeedIcon,
+ label: _('Feed Status'),
+ to: '/feedstatus',
+ active: false,
+ isExternal: false,
+ },
+ capabilities.mayOp('describe_auth') &&
+ capabilities.mayOp('modify_auth') && {
+ icon: LdapIcon,
+ label: _('LDAP'),
+ to: '/ldap',
+ active: false,
+ isExternal: false,
+ },
+ capabilities.mayOp('describe_auth') &&
+ capabilities.mayOp('modify_auth') && {
+ icon: RadiusIcon,
+ label: _('RADIUS'),
+ to: '/radius',
+ active: false,
+ isExternal: false,
+ },
+ ].filter(Boolean),
+ },
+ {
+ icon: () => {},
+ label: _('Help'),
+ active: false,
+ defaultOpened: false,
+ isExternal: false,
+ subNav: [
+ {
+ label: _('User Manual'),
+ to: 'https://docs.greenbone.net/GSM-Manual/gos-22.04/en/',
+ active: false,
+ isExternal: true,
+ },
+ {
+ icon: CvssIcon,
+ label: _('CVSS Calculator'),
+ to: '/cvsscalculator',
+ active: false,
+ isExternal: false,
+ },
+ {
+ label: _('About'),
+ to: '/about',
+ active: false,
+ isExternal: false,
+ },
+ ],
+ },
+ ].filter(Boolean),
+ ];
+ return ;
};
export default Menu;
-
-// vim: set ts=2 sw=2 tw=80:
diff --git a/src/web/pages/page.jsx b/src/web/pages/page.jsx
index 1d3d371019..0f3fd01f60 100644
--- a/src/web/pages/page.jsx
+++ b/src/web/pages/page.jsx
@@ -18,119 +18,60 @@
import React from 'react';
-import {withRouter} from 'react-router-dom';
+import {useLocation} from 'react-router-dom';
import styled from 'styled-components';
-import _ from 'gmp/locale';
-
-import logger from 'gmp/log';
-
import {isDefined} from 'gmp/utils/identity';
-import Capabilities from 'gmp/capabilities/capabilities';
-
-import MenuBar from 'web/components/bar/menubar';
-
import ErrorBoundary from 'web/components/error/errorboundary';
import Layout from 'web/components/layout/layout';
-import LicenseNotification from 'web/components/notification/licensenotification';
-
import CapabilitiesContext from 'web/components/provider/capabilitiesprovider';
-import LicenseProvider from 'web/components/provider/licenseprovider';
+
+import useLoadCapabilities from 'web/hooks/useLoadCapabilities';
import Footer from 'web/components/structure/footer';
import Header from 'web/components/structure/header';
import Main from 'web/components/structure/main';
-import PropTypes from 'web/utils/proptypes';
-import withGmp from 'web/utils/withGmp';
-import compose from 'web/utils/compose';
+import Menu from 'web/components/menu/menu';
-const log = logger.getLogger('web.page');
+import useTranslation from 'web/hooks/useTranslation';
const StyledLayout = styled(Layout)`
- height: 100%;
+ height: calc(-48px + 100vh);
`;
-class Page extends React.Component {
- constructor(...args) {
- super(...args);
-
- this.handleCloseLicenseNotification =
- this.handleCloseLicenseNotification.bind(this);
+const Page = ({children}) => {
+ const capabilities = useLoadCapabilities();
+ const location = useLocation();
+ const [_] = useTranslation();
- this.state = {};
+ if (!isDefined(capabilities)) {
+ // only show content after caps have been loaded
+ // this avoids ugly re-rendering of parts of the ui (e.g. the menu)
+ return null;
}
- componentDidMount() {
- const {gmp} = this.props;
-
- gmp.user
- .currentCapabilities()
- .then(response => {
- const capabilities = response.data;
- log.debug('User capabilities', capabilities);
- this.setState({capabilities});
- })
- .catch(rejection => {
- log.error('An error occurred during fetching capabilities', rejection);
- // use empty capabilities
- this.setState({capabilities: new Capabilities()});
- });
-
- this.setState({
- notificationClosed: false,
- });
- }
-
- handleCloseLicenseNotification() {
- this.setState({notificationClosed: true});
- }
-
- render() {
- const {children, location} = this.props;
- const {capabilities, notificationClosed} = this.state;
- if (!isDefined(capabilities)) {
- // only show content after caps have been loaded
- // this avoids ugly re-rendering of parts of the ui (e.g. the menu)
- return null;
- }
-
- return (
-
-
-
-
-
- {!notificationClosed && (
-
- )}
-
-
- {children}
-
-
-
-
-
-
- );
- }
-}
-
-Page.propTypes = {
- gmp: PropTypes.gmp.isRequired,
+ return (
+
+
+
+
+
+
+ {children}
+
+
+
+
+
+ );
};
-export default compose(withGmp, withRouter)(Page);
-
-// vim: set ts=2 sw=2 tw=80:
+export default Page;