Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Frontend Error Handling #667

Merged
merged 29 commits into from
May 15, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
1cd775a
console: Extend error library
kschiffer May 9, 2019
04582bb
console: Update error lib references
kschiffer May 9, 2019
feed3cc
console: Add anchor link option to navbar
kschiffer May 13, 2019
7cd9788
console: Add anchor link option to profile dropdown
kschiffer May 13, 2019
81a59fb
console: Add anchor link option to header
kschiffer May 13, 2019
1ae39cd
console: Add header container component
kschiffer May 13, 2019
16e2153
console: Add console error view
kschiffer May 9, 2019
a899300
console,oauth: Update error message component
kschiffer May 9, 2019
2760ef1
console,oauth: Add error boundary component
kschiffer May 9, 2019
547a773
console: Add error boundary to console app
kschiffer May 14, 2019
e7ba963
console: Handle error in application view
kschiffer May 9, 2019
e57ad7b
console: Refactor header styles
kschiffer May 13, 2019
5317d1d
console: Extend error view styles
kschiffer May 13, 2019
7faf442
console: Add sub-view error component
kschiffer May 13, 2019
67b7cc6
console: Add error boundary to api key views
kschiffer May 13, 2019
722bdda
console: Throw errors in application api key views
kschiffer May 13, 2019
c6f9f62
console: Add error boundary to collaborator views
kschiffer May 13, 2019
48f83a8
console: Throw errors in collaborator views
kschiffer May 13, 2019
8001ac1
console: Throw errors in entity views
kschiffer May 13, 2019
e2bcc7b
console: Add error boundary to gateway api key views
kschiffer May 14, 2019
441059c
console: Move router context to header container
kschiffer May 14, 2019
cf8e062
console: Throw errors in gateway api key views
kschiffer May 14, 2019
90727f5
console: Add not found route
kschiffer May 13, 2019
a3d1eb5
oauth: Add full view error
kschiffer May 14, 2019
ed082ae
oauth: Add topmost error boundary to oauth app
kschiffer May 14, 2019
7b01c9f
console: Add handling of page data errors
kschiffer May 14, 2019
3ba8f64
console: Fix api key selectors
kschiffer May 15, 2019
7c9584b
dev: Update locales
kschiffer May 13, 2019
852aa4e
oauth: Add not found route
kschiffer May 15, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions pkg/webui/components/footer/footer.styl
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@
justify-content: space-between
padding: $cs.s
color: $tc-subtle-gray
flex: none
+media-query($bp.s)
background-color: white

.link
text-decoration: underline
Expand Down
3 changes: 3 additions & 0 deletions pkg/webui/components/header/header.styl
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@
display: flex
padding: 0 $ls.s
justify-content: space-between
flex: none
+media-query($bp.s)
background-color: white

.logo
display: flex
Expand Down
15 changes: 13 additions & 2 deletions pkg/webui/components/header/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ const Header = function ({
user,
searchable,
handleSearchRequest = () => null,
anchored = false,
...rest
}) {
const isGuest = !Boolean(user)
Expand All @@ -84,11 +85,19 @@ const Header = function ({
<React.Fragment>
<div className={styles.left}>
<div className={styles.logo}><Logo /></div>
<NavigationBar className={styles.navList} entries={navigationEntries} />
<NavigationBar
className={styles.navList}
entries={navigationEntries}
anchored={anchored}
/>
</div>
<div className={styles.right}>
{ searchable && <Input icon="search" onEnter={handleSearchRequest} /> }
<ProfileDropdown dropdownItems={dropdownItems || defaultDropdownItems} userId={user.ids.user_id} />
<ProfileDropdown
dropdownItems={dropdownItems || defaultDropdownItems}
userId={user.ids.user_id}
anchored={anchored}
/>
</div>
</React.Fragment>
)
Expand Down Expand Up @@ -129,6 +138,8 @@ Header.propTypes = {
action: PropTypes.func,
icon: PropTypes.string,
})),
/** Flag identifying whether links should be rendered as plain anchor link */
anchored: PropTypes.bool,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

apparently, this breaks the header:
Screenshot 2019-05-14 at 11 45 27

/**
* A handler for when the user clicks the logout button
*/
Expand Down
11 changes: 8 additions & 3 deletions pkg/webui/components/navigation/bar/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
import React from 'react'
import classnames from 'classnames'

import NavigationLink from '../link'
import NavigationLink, { NavigationAnchorLink } from '../link'
import Message from '../../../lib/components/message'
import Icon from '../../icon'
import PropTypes from '../../../lib/prop-types'
Expand All @@ -25,6 +25,7 @@ import style from './bar.styl'
const NavigationBar = function ({
className,
entries,
anchored,
}) {
return (
<nav className={classnames(className, style.bar)}>
Expand All @@ -36,8 +37,10 @@ const NavigationBar = function ({
exact = false,
} = entry

const Component = anchored ? NavigationAnchorLink : NavigationLink

return (
<NavigationLink
<Component
key={index}
path={path}
exact={exact}
Expand All @@ -46,7 +49,7 @@ const NavigationBar = function ({
>
{icon && <Icon icon={icon} className={style.icon} />}
<Message content={title} />
</NavigationLink>
</Component>
)
})}
</nav>
Expand All @@ -67,6 +70,8 @@ NavigationBar.propTypes = {
icon: PropTypes.string,
exact: PropTypes.bool,
})),
/** Flag identifying whether links should be rendered as plain anchor link */
anchored: PropTypes.bool,
}

NavigationBar.defaultProps = {
Expand Down
19 changes: 18 additions & 1 deletion pkg/webui/components/navigation/link/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ import classnames from 'classnames'

import { NavLink } from 'react-router-dom'

import Link from '../../link'

import PropTypes from '../../../lib/prop-types'
import style from './link.styl'

Expand All @@ -43,6 +45,21 @@ const NavigationLink = function ({
)
}

const NavigationAnchorLink = function ({
className,
children,
path,
...rest
}) {
return (
<Link.Anchor
href={path}
className={classnames(className, style.link)}
children={children}
/>
)
}

NavigationLink.propTypes = {
/** The path for a link */
path: PropTypes.string.isRequired,
Expand All @@ -55,4 +72,4 @@ NavigationLink.propTypes = {
exact: PropTypes.bool.isRequired,
}

export default NavigationLink
export { NavigationLink as default, NavigationAnchorLink }
29 changes: 21 additions & 8 deletions pkg/webui/components/profile-dropdown/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,10 @@

import React from 'react'
import bind from 'autobind-decorator'
import { Link } from 'react-router-dom'

import Icon from '../icon'
import Message from '../../lib/components/message'
import Link from '../link'
import PropTypes from '../../lib/prop-types'

import styles from './profile-dropdown.styl'
Expand Down Expand Up @@ -80,7 +80,7 @@ export default class ProfileDropdown extends React.PureComponent {
}

render () {
const { userId, dropdownItems, ...rest } = this.props
const { userId, dropdownItems, anchored, ...rest } = this.props

return (
<div
Expand All @@ -95,22 +95,33 @@ export default class ProfileDropdown extends React.PureComponent {
<div className={styles.profilePicture} />
<span className={styles.id}>{userId}</span>
<Icon icon="arrow_drop_down" />
{ this.state.expanded && <Dropdown items={dropdownItems} />}
{ this.state.expanded && <Dropdown items={dropdownItems} anchored={anchored} />}
</div>
)
}
}

const Dropdown = ({ items }) => (
const Dropdown = ({ items, anchored }) => (
<ul className={styles.dropdown}>
{ items.map( function (item) {
const icon = item.icon && <Icon className={styles.icon} icon={item.icon} />
const ItemElement = item.action
? (
<button
onClick={item.action}
onKeyPress={item.action}
role="tab"
tabIndex="0"
>
{icon}
<Message content={item.title} />
</button>)
: anchored
? <Link.Anchor href={item.path}>{icon}<Message content={item.title} /></Link.Anchor>
: <Link to={item.path}>{icon}<Message content={item.title} /></Link>
return (
<li className={styles.dropdownItem} key={item.title.id || item.title}>
{ item.action
? <button onClick={item.action} onKeyPress={item.action} role="tab" tabIndex="0">{icon}<Message content={item.title} /></button>
: <Link to={item.path}>{icon}<Message content={item.title} /></Link>
}
{ ItemElement }
</li>
)
}
Expand All @@ -132,6 +143,8 @@ Dropdown.propTypes = {
path: PropTypes.string,
action: PropTypes.func,
})),
/** Flag identifying whether link should be rendered as plain anchor link */
anchored: PropTypes.bool,
}

export { Dropdown }
2 changes: 2 additions & 0 deletions pkg/webui/components/profile-dropdown/profile-dropdown.styl
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@ ul.dropdown

& > a, & > button
one-liner()
color: $tc-deep-gray
text-decoration: none
padding: $cs.m 0
width: 100%

Expand Down
4 changes: 2 additions & 2 deletions pkg/webui/console/containers/gateway-statistics/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ import Icon from '../../../components/icon'
import DateTime from '../../../lib/components/date-time'
import Message from '../../../lib/components/message'
import Button from '../../../components/button'
import { isNotFoundError, isErrorTranslated } from '../../../lib/errors'
import { isNotFoundError, isTranslated } from '../../../lib/errors/utils'
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

isNotFoundError throws TypeError when creating a new gateway


import {
statisticsSelector,
Expand Down Expand Up @@ -66,7 +66,7 @@ class GatewayStatistic extends React.PureComponent {
} else if (fetching) {
statusIndicator = 'mediocre'
message = sharedMessages.connecting
} else if (error && error.message && isErrorTranslated(error.message)) {
} else if (error && error.message && isTranslated(error.message)) {
statusIndicator = 'unknown'
message = error.message
} else if (statistics) {
Expand Down
98 changes: 98 additions & 0 deletions pkg/webui/console/containers/header/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
// Copyright © 2019 The Things Network Foundation, The Things Industries B.V.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

import React, { Component } from 'react'
import { connect } from 'react-redux'
import { withRouter } from 'react-router-dom'
import bind from 'autobind-decorator'

import { logout } from '../../store/actions/user'
import PropTypes from '../../../lib/prop-types'

import HeaderComponent from '../../../components/header'

@withRouter
@connect(state => ({
user: state.user.user,
}))
@bind
class Header extends Component {

handleLogout () {
const { dispatch } = this.props
dispatch(logout())
}

render () {
const {
user,
dropdownItems,
navigationEntries,
anchored,
handleSearchRequest,
searchable,
} = this.props

return (
<HeaderComponent
user={user}
handleLogout={this.handleLogout}
dropdownItems={dropdownItems}
navigationEntries={navigationEntries}
anchored={anchored}
searchable={searchable}
handleSearchRequest={handleSearchRequest}
/>
)
}
}

Header.propTypes = {
/**
* A list of items for the dropdown
* @param {(string|Object)} title - The title to be displayed
* @param {string} icon - The icon name to be displayed next to the title
* @param {string} path - The path for a navigation tab
* @param {function} action - Alternatively, the function to be called on click
*/
dropdownItems: PropTypes.arrayOf(PropTypes.shape({
title: PropTypes.message.isRequired,
icon: PropTypes.string,
path: PropTypes.string.isRequired,
action: PropTypes.func,
})),
/**
* A list of navigation bar entries.
* @param {(string|Object)} title - The title to be displayed
* @param {string} icon - The icon name to be displayed next to the title
* @param {string} path - The path for a navigation tab
* @param {boolean} exact - Flag identifying whether the path should be matched exactly
*/
navigationEntries: PropTypes.arrayOf(PropTypes.shape({
path: PropTypes.string.isRequired,
title: PropTypes.message.isRequired,
action: PropTypes.func,
icon: PropTypes.string,
})),
/** Flag identifying whether links should be rendered as plain anchor link */
anchored: PropTypes.bool,
/**
* A handler for when the user used the search input
*/
handleSearchRequest: PropTypes.func,
/** A flag identifying whether the header should display the search input */
searchable: PropTypes.bool,
}

export default Header
4 changes: 2 additions & 2 deletions pkg/webui/console/store/selectors/api-keys.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.

const storeSelector = (state, props) => state[props.id]
const storeSelector = (state, props) => state[props.id] || {}

export const apiKeysStoreSelector = entity => (state, props) => (
storeSelector(state.apiKeys[entity], props) || {}
Expand All @@ -21,7 +21,7 @@ export const apiKeysStoreSelector = entity => (state, props) => (
export const apiKeysSelector = entity => function (state, props) {
const store = storeSelector(state.apiKeys[entity], props)

return store ? store.keys : []
return store.keys ? store.keys : []
}

export const apiKeySelector = function (entity) {
Expand Down
6 changes: 0 additions & 6 deletions pkg/webui/console/views/app/app.styl
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,3 @@
.content
overflow: hidden
flex: 1

.header,
.footer
flex: none
+media-query($bp.s)
background-color: white
Loading