mirror of
https://github.com/empayre/fleet.git
synced 2024-11-06 08:55:24 +00:00
Updated 500 page copy and added button to reveal error message. (#1)
Updated the 500 page component to render a "SHOW ERROR" button. This button is only rendered if the errors slice of the state contains an error and the base property exists. Otherwise, the 500 page will not render this button because there is no error message to show the user. Created a errors500 reducer and actions to update the state tree when a 500 error occurs. When 500 error occurs, the errors slice of state is updated with the error object. When the 500 page component unmounts the error object is removed from state. Demo: https://www.loom.com/share/b87c4aee42274e7bb553e703d3f950c6
This commit is contained in:
parent
2e333a4e2e
commit
d604c6a106
6
frontend/interfaces/errors500.js
Normal file
6
frontend/interfaces/errors500.js
Normal file
@ -0,0 +1,6 @@
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
export default PropTypes.shape({
|
||||
http_status: PropTypes.number,
|
||||
base: PropTypes.string,
|
||||
});
|
@ -1,12 +1,72 @@
|
||||
import React, { Component } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { connect } from 'react-redux';
|
||||
import { noop } from 'lodash';
|
||||
import { resetErrors } from 'redux/nodes/errors500/actions';
|
||||
import errorsInterface from 'interfaces/errors500';
|
||||
|
||||
import kolideLogo from '../../../assets/images/kolide-logo-condensed.svg';
|
||||
import gopher from '../../../assets/images/500.svg';
|
||||
|
||||
const baseClass = 'kolide-500';
|
||||
|
||||
class Kolide404 extends Component {
|
||||
class Kolide500 extends Component {
|
||||
static propTypes = {
|
||||
errors: errorsInterface,
|
||||
dispatch: PropTypes.func,
|
||||
};
|
||||
|
||||
static defaultProps = {
|
||||
dispatch: noop,
|
||||
};
|
||||
|
||||
constructor (props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
showErrorMessage: false,
|
||||
};
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
const { dispatch } = this.props;
|
||||
dispatch(resetErrors());
|
||||
}
|
||||
|
||||
onShowErrorMessage = () => {
|
||||
this.setState({ showErrorMessage: true });
|
||||
}
|
||||
|
||||
renderError = () => {
|
||||
const { errors } = this.props;
|
||||
const errorMessage = errors ? errors.base : null;
|
||||
const { showErrorMessage } = this.state;
|
||||
const { onShowErrorMessage } = this;
|
||||
|
||||
if (errorMessage && !showErrorMessage) {
|
||||
// We only show the button when errorMessage exists
|
||||
// and showErrorMessage is set to false
|
||||
return (
|
||||
<button className="button button--muted" onClick={onShowErrorMessage}>SHOW ERROR</button>
|
||||
);
|
||||
}
|
||||
|
||||
if (errorMessage && showErrorMessage) {
|
||||
// We only show the error message when errorMessage exists
|
||||
// and showErrorMessage is set to true
|
||||
return (
|
||||
<div className="error-message-container">
|
||||
<p>{errorMessage}</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
render () {
|
||||
const { renderError } = this;
|
||||
|
||||
return (
|
||||
<div className={baseClass}>
|
||||
<header className="primary-header">
|
||||
@ -18,10 +78,17 @@ class Kolide404 extends Component {
|
||||
<h1>Uh oh!</h1>
|
||||
<h2>Error 500</h2>
|
||||
<p>Something went wrong on our end.</p>
|
||||
<p>We have alerted the engineers and they are working on a solution.</p>
|
||||
{renderError()}
|
||||
<p>Please file an issue if you believe this is a bug.</p>
|
||||
<a
|
||||
href="https://github.com/fleetdm/fleet/issues"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
File an issue
|
||||
</a>
|
||||
<div className="gopher-container">
|
||||
<img src={gopher} alt="" />
|
||||
<p>Need assistance? <a href="https://github.com/kolide/fleet/issues">File an issue</a>.</p>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
@ -29,4 +96,11 @@ class Kolide404 extends Component {
|
||||
}
|
||||
}
|
||||
|
||||
export default Kolide404;
|
||||
const mapStateToProps = (state) => {
|
||||
const { errors } = state.errors500;
|
||||
return {
|
||||
errors,
|
||||
};
|
||||
};
|
||||
|
||||
export default connect(mapStateToProps)(Kolide500);
|
||||
|
@ -36,6 +36,10 @@
|
||||
}
|
||||
}
|
||||
|
||||
.error-message-container {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
main {
|
||||
text-align: center;
|
||||
|
||||
|
7
frontend/redux/nodes/errors500/actions.js
Normal file
7
frontend/redux/nodes/errors500/actions.js
Normal file
@ -0,0 +1,7 @@
|
||||
export const RESET_ERRORS = 'RESET_ERRORS';
|
||||
|
||||
export const resetErrors = () => {
|
||||
return {
|
||||
type: RESET_ERRORS,
|
||||
};
|
||||
};
|
22
frontend/redux/nodes/errors500/reducer.js
Normal file
22
frontend/redux/nodes/errors500/reducer.js
Normal file
@ -0,0 +1,22 @@
|
||||
import {
|
||||
RESET_ERRORS,
|
||||
} from './actions';
|
||||
|
||||
const initialState = {
|
||||
errors: null,
|
||||
};
|
||||
|
||||
const reducer = (state = initialState, { type, payload }) => {
|
||||
if (payload && payload.errors) {
|
||||
return {
|
||||
errors: payload.errors,
|
||||
};
|
||||
} else if (type === RESET_ERRORS) {
|
||||
return {
|
||||
errors: null,
|
||||
};
|
||||
}
|
||||
return state;
|
||||
};
|
||||
|
||||
export default reducer;
|
39
frontend/redux/nodes/errors500/reducer.tests.js
Normal file
39
frontend/redux/nodes/errors500/reducer.tests.js
Normal file
@ -0,0 +1,39 @@
|
||||
import expect from 'expect';
|
||||
|
||||
import reducer from './reducer';
|
||||
|
||||
describe('Errors - reducer', () => {
|
||||
it('Updates state with errors object when an action that has a payload with an errors object is dispatched', () => {
|
||||
const payload = {
|
||||
errors: {
|
||||
base: "inserting pack: Error 1136: Column count doesn't match value count at row 1",
|
||||
http_status: 500,
|
||||
},
|
||||
};
|
||||
const packsCreateFailureAction = { type: 'packs_CREATE_FAILURE', payload };
|
||||
const initialState = {
|
||||
errors: null,
|
||||
};
|
||||
const newState = reducer(initialState, packsCreateFailureAction);
|
||||
|
||||
expect(newState).toEqual({
|
||||
errors: {
|
||||
base: "inserting pack: Error 1136: Column count doesn't match value count at row 1",
|
||||
http_status: 500,
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('Updates state by setting errors to null when the RESET_ERRORS action is dipatched', () => {
|
||||
const errorsState = {
|
||||
errors: {
|
||||
base: "inserting pack: Error 1136: Column count doesn't match value count at row 1",
|
||||
http_status: 500,
|
||||
},
|
||||
};
|
||||
const newState = reducer(errorsState, { type: 'RESET_ERRORS' });
|
||||
expect(newState).toEqual({
|
||||
errors: null,
|
||||
});
|
||||
});
|
||||
});
|
@ -6,6 +6,7 @@ import app from './nodes/app/reducer';
|
||||
import auth from './nodes/auth/reducer';
|
||||
import components from './nodes/components/reducer';
|
||||
import entities from './nodes/entities/reducer';
|
||||
import errors500 from './nodes/errors500/reducer';
|
||||
import notifications from './nodes/notifications/reducer';
|
||||
import persistentFlash from './nodes/persistent_flash/reducer';
|
||||
import redirectLocation from './nodes/redirectLocation/reducer';
|
||||
@ -15,6 +16,7 @@ export default combineReducers({
|
||||
auth,
|
||||
components,
|
||||
entities,
|
||||
errors500,
|
||||
loadingBar: loadingBarReducer,
|
||||
notifications,
|
||||
persistentFlash,
|
||||
|
Loading…
Reference in New Issue
Block a user