awx/src/App.jsx
Michael Abashian 52851c57d8
Display details about network errors in alert modal and content error components (#288)
Display details about network errors in alert modal and content error components
2019-06-26 11:40:15 -04:00

191 lines
4.7 KiB
JavaScript

import React, { Component, Fragment } from 'react';
import { global_breakpoint_md } from '@patternfly/react-tokens';
import {
Nav,
NavList,
Page,
PageHeader as PFPageHeader,
PageSidebar,
} from '@patternfly/react-core';
import styled from 'styled-components';
import { t } from '@lingui/macro';
import { withI18n } from '@lingui/react';
import { ConfigAPI, MeAPI, RootAPI } from '@api';
import About from '@components/About';
import AlertModal from '@components/AlertModal';
import NavExpandableGroup from '@components/NavExpandableGroup';
import BrandLogo from '@components/BrandLogo';
import PageHeaderToolbar from '@components/PageHeaderToolbar';
import ErrorDetail from '@components/ErrorDetail';
import { ConfigProvider } from '@contexts/Config';
const PageHeader = styled(PFPageHeader)`
& .pf-c-page__header-brand-link {
color: inherit;
&:hover {
color: inherit;
}
& svg {
height: 76px;
}
}
`;
class App extends Component {
constructor (props) {
super(props);
// initialize with a closed navbar if window size is small
const isNavOpen = typeof window !== 'undefined'
&& window.innerWidth >= parseInt(global_breakpoint_md.value, 10);
this.state = {
ansible_version: null,
custom_virtualenvs: null,
me: null,
version: null,
isAboutModalOpen: false,
isNavOpen,
configError: null
};
this.handleLogout = this.handleLogout.bind(this);
this.handleAboutClose = this.handleAboutClose.bind(this);
this.handleAboutOpen = this.handleAboutOpen.bind(this);
this.handleNavToggle = this.handleNavToggle.bind(this);
this.handleConfigErrorClose = this.handleConfigErrorClose.bind(this);
}
async componentDidMount () {
await this.loadConfig();
}
// eslint-disable-next-line class-methods-use-this
async handleLogout () {
await RootAPI.logout();
window.location.replace('/#/login');
}
handleAboutOpen () {
this.setState({ isAboutModalOpen: true });
}
handleAboutClose () {
this.setState({ isAboutModalOpen: false });
}
handleNavToggle () {
this.setState(({ isNavOpen }) => ({ isNavOpen: !isNavOpen }));
}
handleConfigErrorClose () {
this.setState({
configError: null
});
}
async loadConfig () {
try {
const [configRes, meRes] = await Promise.all([ConfigAPI.read(), MeAPI.read()]);
const { data: { ansible_version, custom_virtualenvs, version } } = configRes;
const { data: { results: [me] } } = meRes;
this.setState({ ansible_version, custom_virtualenvs, version, me });
} catch (err) {
this.setState({ configError: err });
}
}
render () {
const {
ansible_version,
custom_virtualenvs,
isAboutModalOpen,
isNavOpen,
me,
version,
configError
} = this.state;
const {
i18n,
render = () => {},
routeGroups = [],
navLabel = '',
} = this.props;
const header = (
<PageHeader
showNavToggle
onNavToggle={this.handleNavToggle}
logo={<BrandLogo />}
logoProps={{ href: '/' }}
toolbar={(
<PageHeaderToolbar
loggedInUser={me}
isAboutDisabled={!version}
onAboutClick={this.handleAboutOpen}
onLogoutClick={this.handleLogout}
/>
)}
/>
);
const sidebar = (
<PageSidebar
isNavOpen={isNavOpen}
nav={(
<Nav aria-label={navLabel}>
<NavList>
{routeGroups.map(
({ groupId, groupTitle, routes }) => (
<NavExpandableGroup
key={groupId}
groupId={groupId}
groupTitle={groupTitle}
routes={routes}
/>
)
)}
</NavList>
</Nav>
)}
/>
);
return (
<Fragment>
<Page
usecondensed="True"
header={header}
sidebar={sidebar}
>
<ConfigProvider value={{ ansible_version, custom_virtualenvs, me, version }}>
{render({ routeGroups })}
</ConfigProvider>
</Page>
<About
ansible_version={ansible_version}
version={version}
isOpen={isAboutModalOpen}
onClose={this.handleAboutClose}
/>
<AlertModal
isOpen={configError}
variant="danger"
title={i18n._(t`Error!`)}
onClose={this.handleConfigErrorClose}
>
{i18n._(t`Failed to retrieve configuration.`)}
<ErrorDetail error={configError} />
</AlertModal>
</Fragment>
);
}
}
export { App as _App };
export default withI18n()(App);