LearnersGuild/echo

View on GitHub
src/common/containers/App/index.jsx

Summary

Maintainability
A
2 hrs
Test Coverage
/* global window */
import React, {Component, PropTypes} from 'react'
import {Link} from 'react-router'
import {connect} from 'react-redux'
import ProgressBar from 'react-toolbox/lib/progress_bar'
import AppBar from 'react-toolbox/lib/app_bar'
import Avatar from 'react-toolbox/lib/avatar'
import FontIcon from 'react-toolbox/lib/font_icon'
import {IconMenu, MenuItem, MenuDivider} from 'react-toolbox/lib/menu'
import Helmet from 'react-helmet'
 
import ErrorBar from 'src/common/components/ErrorBar'
import {Flex} from 'src/common/components/Layout'
import {dismissError} from 'src/common/actions/app'
import {userCan} from 'src/common/util'
 
import styles from './index.scss'
import theme from './theme.scss'
 
const navItems = [
{
label: 'Work Plans',
permission: 'listWorkPlans',
path: '/work-plans',
},
{
label: 'Phases',
permission: 'listPhases',
path: '/phases',
},
{
label: 'Projects',
permission: 'listProjects',
path: '/projects',
},
{
label: 'Users',
permission: 'listUsers',
path: '/users',
},
{
label: 'Chapters',
permission: 'listChapters',
path: '/chapters',
},
]
 
export class App extends Component {
constructor(props) {
super(props)
this.handleClickProfile = this.handleClickProfile.bind(this)
this.handleClickSignOut = this.handleClickSignOut.bind(this)
this.renderNavigation = this.renderNavigation.bind(this)
this.renderLoading = this.renderLoading.bind(this)
this.renderMain = this.renderMain.bind(this)
this.renderFooter = this.renderFooter.bind(this)
}
 
handleClickProfile() {
const profileUrl = `${process.env.IDM_BASE_URL}/profile`
window.open(profileUrl, '_blank')
}
 
handleClickSignOut() {
window.location = `${process.env.IDM_BASE_URL}/auth/sign-out?redirect=${process.env.APP_BASE_URL}`
}
 
Function `renderNavigation` has 61 lines of code (exceeds 25 allowed). Consider refactoring.
renderNavigation() {
const {auth: {currentUser = {}}} = this.props
const avatar = <Avatar><img src={currentUser.avatarUrl || process.env.LOGO_SHORT_URL}/></Avatar>
return (
<div className={styles.nav}>
<Helmet>
<meta charSet="utf-8"/>
<meta name="description" content="Learners Guild Echo"/>
<meta httpEquiv="X-UA-Compatible" content="IE=edge"/>
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no"/>
</Helmet>
<AppBar theme={theme} className={styles.appbar} flat>
<Flex className={styles.navBar} justifyContent="center" fill>
<Flex className={styles.container} justifyContent="space-between" alignItems="center" fill>
<Link to="/">
<Flex className={styles.navBarLeft} justifyContent="center" alignItems="center">
<Avatar className={theme.avatar}>
<img
src="https://brand.learnersguild.org/android-chrome-48x48.png"
title="Learners Guild"
alt="Learners Guild"
/>
</Avatar>
</Flex>
</Link>
<Flex className={styles.navBarRight} justifyContent="flex-end" alignItems="center">
<nav className={styles.navigation}>
<ul>
{navItems.filter(item => (
userCan(currentUser, item.permission)
)).map((item, i) => (
<li key={i}>
<Link activeClassName={styles.active} to={item.path}>{item.label}</Link>
</li>
))}
</ul>
</nav>
<IconMenu theme={theme} icon={avatar} position="topRight">
<MenuItem>
<a className={styles.menuItem} onClick={this.handleClickProfile}>
<Flex alignItems="center">
<FontIcon className={styles.menuItemIcon} value="account_circle"/>
<span>My Profile</span>
</Flex>
</a>
</MenuItem>
<MenuDivider/>
<MenuItem>
<a className={styles.menuItem} onClick={this.handleClickSignOut}>
<Flex alignItems="center">
<FontIcon className={styles.menuItemIcon} value="exit_to_app"/>
<span>Sign out</span>
</Flex>
</a>
</MenuItem>
</IconMenu>
</Flex>
</Flex>
</Flex>
</AppBar>
</div>
)
}
 
renderLoading() {
return this.props.app.showLoading ? (
<ProgressBar theme={theme} mode="indeterminate"/>
) : null
}
 
renderMain() {
return !this.props.auth.currentUser && this.props.app.isBusy ? null : (
<div className={styles.mainWrapper}>
<Flex className={styles.main} justifyContent="center" alignItems="center">
<div className={styles.container}>
<div className={styles.content}>
{this.props.children}
</div>
</div>
</Flex>
</div>
)
}
 
renderFooter() {
const {dismissError, app: {errors}} = this.props
return (
<div className={styles.footer}>
<div className={styles.container}>
{errors.map((errorMessage, i) => {
const handleDismiss = () => dismissError(i)
return (
<ErrorBar key={i} onDismiss={handleDismiss} message={errorMessage}/>
)
})}
</div>
</div>
)
}
 
render() {
return (
<div className={styles.app}>
{this.renderNavigation()}
{this.renderLoading()}
{this.renderMain()}
{this.renderFooter()}
</div>
)
}
}
 
App.propTypes = {
app: PropTypes.shape({
isBusy: PropTypes.bool.isRequired,
showLoading: PropTypes.bool.isRequired,
errors: PropTypes.array.isRequired,
}),
auth: PropTypes.shape({
currentUser: PropTypes.object.isRequired,
}),
children: PropTypes.any,
dismissError: PropTypes.func.isRequired,
}
 
function mapStateToProps(state) {
return {
app: state.app,
auth: state.auth,
errors: state.errors,
}
}
 
function mapDispatchToProps(dispatch) {
return {
dismissError: error => dispatch(dismissError(error)),
}
}
 
export default connect(mapStateToProps, mapDispatchToProps)(App)