Skip to content
This repository has been archived by the owner on Mar 25, 2020. It is now read-only.

Commit

Permalink
Merge pull request #5 from ks888/improve-incident-error-message
Browse files Browse the repository at this point in the history
Improve incident error message
  • Loading branch information
ks888 authored Dec 20, 2016
2 parents 9928a43 + 25664eb commit 244291e
Show file tree
Hide file tree
Showing 13 changed files with 236 additions and 195 deletions.
4 changes: 3 additions & 1 deletion packages/frontend/src/actions/incidents.js
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,8 @@ export const fetchIncidentsWithUpdates = (callbacks = {}) => {
.then(checkStatus)
.then(response => response.json())
.then(json => {
dispatch(listIncidents(json))

const obj = JSON.parse(json)
if (obj.length !== 0) {
obj.forEach(incident => dispatch(fetchIncidentUpdates(incident.incidentID)))
Expand Down Expand Up @@ -111,7 +113,7 @@ export const fetchIncidentUpdates = (incidentID, callbacks = {}) => {
}
}

export const postIncident = (incidentID, name, incidentStatus, message, components, callbacks = {}) => {
export const postIncident = (name, incidentStatus, message, components, callbacks = {}) => {
const { onLoad, onSuccess, onFailure } = callbacks
return dispatch => {
if (onLoad && typeof onLoad === 'function') onLoad()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import classes from './ComponentDialog.scss'
import Button from 'components/Button'
import ErrorMessage from 'components/ErrorMessage'
import TextField from 'components/TextField'
import { componentStatuses } from 'utils/status'

export const dialogType = {
add: 1,
Expand All @@ -19,21 +20,29 @@ class ComponentDialog extends React.Component {
name: PropTypes.string.isRequired,
description: PropTypes.string.isRequired,
status: PropTypes.string.isRequired
}).isRequired,
}),
dialogType: PropTypes.number.isRequired,
postComponent: PropTypes.func.isRequired,
updateComponent: PropTypes.func.isRequired
}

constructor (props) {
super(props)
this.state = {
name: props.component.name,
description: props.component.description,
status: props.component.status,
isUpdating: false,
message: ''
if (props.component) {
this.state = {
name: props.component.name,
description: props.component.description,
status: props.component.status
}
} else {
this.state = {
name: '',
description: '',
status: componentStatuses[0]
}
}
this.state.isUpdating = false
this.state.message = ''
}

componentDidMount () {
Expand Down Expand Up @@ -107,7 +116,7 @@ class ComponentDialog extends React.Component {
<div className='mdl-dialog__content'>
<ErrorMessage message={this.state.message} />
<TextField label='Name' text={this.state.name} rows={1} onChange={this.handleChangeName} />
<TextField label='Description' text={this.state.description} rows={2} onChange={this.handleChangeDescription} />
<TextField label='Description (optional)' text={this.state.description} rows={2} onChange={this.handleChangeDescription} />
</div>
<div className='mdl-dialog__actions'>
<Button onClick={clickHandler} name={actionName}
Expand Down
10 changes: 2 additions & 8 deletions packages/frontend/src/components/Components/Components.js
Original file line number Diff line number Diff line change
Expand Up @@ -101,14 +101,8 @@ export default class Components extends React.Component {
dialog = null
break
case dialogType.add:
let component = {
componentID: '',
name: '',
description: '',
status: 'Operational'
}
dialog = <ComponentDialog onClosed={this.handleCloseDialog}
component={component} dialogType={componentDialogType.add} />
dialogType={componentDialogType.add} />
break
case dialogType.edit:
dialog = <ComponentDialog onClosed={this.handleCloseDialog}
Expand All @@ -117,7 +111,7 @@ export default class Components extends React.Component {
case dialogType.delete:
dialog = <FoolproofDialog onClosed={this.handleCloseDialog}
name={this.state.component.name} ID={this.state.component.componentID}
deleteComponent={this.props.deleteComponent} />
deleteFunction={this.props.deleteComponent} />
break
default:
console.warn('unknown dialog type: ', this.state.dialogType)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import ErrorMessage from 'components/ErrorMessage'
class FoolproofDialog extends React.Component {
static propTypes = {
onClosed: PropTypes.func.isRequired,
deleteComponent: PropTypes.func.isRequired,
deleteFunction: PropTypes.func.isRequired,
name: PropTypes.string.isRequired,
ID: PropTypes.string.isRequired
}
Expand Down Expand Up @@ -45,7 +45,7 @@ class FoolproofDialog extends React.Component {
}

handleClickDeleteButton = () => {
this.props.deleteComponent(this.props.ID, this.updateCallbacks)
this.props.deleteFunction(this.props.ID, this.updateCallbacks)
}

handleHideDialog = () => {
Expand Down
5 changes: 3 additions & 2 deletions packages/frontend/src/components/History/History.js
Original file line number Diff line number Diff line change
Expand Up @@ -79,10 +79,11 @@ export default class History extends React.Component {
}

render () {
const { incidents, isFetching } = this.props
const { incidents } = this.props
const incidentsByMonth = this.renderIncidentsByMonth(incidents)

return (<div className={classnames(classes.layout, 'mdl-grid')} style={{ opacity: isFetching ? 0.5 : 1 }}>
return (<div className={classnames(classes.layout, 'mdl-grid')}
style={{ opacity: this.state.isFetching ? 0.5 : 1 }}>
<Title service_name='Service' />
<div className='mdl-cell mdl-cell--12-col'>
<h4>Incident History</h4>
Expand Down
168 changes: 126 additions & 42 deletions packages/frontend/src/components/IncidentDialog/IncidentDialog.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import React, { PropTypes } from 'react'
import ReactDOM from 'react-dom'
import classnames from 'classnames'
import moment from 'moment-timezone'
import classes from './IncidentDialog.scss'
Expand All @@ -7,17 +8,81 @@ import Button from 'components/Button'
import RadioButton from 'components/RadioButton'
import TextField from 'components/TextField'
import DropdownList from 'components/DropdownList'
import ErrorMessage from 'components/ErrorMessage'
import { componentStatuses, incidentStatuses } from 'utils/status'

export const dialogType = {
add: 1,
update: 2
}

class IncidentDialog extends React.Component {
static propTypes = {
onClosed: PropTypes.func.isRequired,
incidentID: PropTypes.string,
dialogType: PropTypes.number.isRequired,
incident: PropTypes.shape({
incidentID: PropTypes.string.isRequired,
name: PropTypes.string.isRequired,
status: PropTypes.string.isRequired,
incidentUpdates: PropTypes.arrayOf(PropTypes.shape({
incidentUpdateID: PropTypes.string.isRequired,
incidentStatus: PropTypes.string.isRequired,
message: PropTypes.string.isRequired,
updatedAt: PropTypes.string.isRequired
}).isRequired)
}),
components: PropTypes.arrayOf(PropTypes.shape({
componentID: PropTypes.string.isRequired,
name: PropTypes.string.isRequired,
status: PropTypes.string.isRequired
}).isRequired).isRequired,
fetchComponents: PropTypes.func.isRequired,
fetchIncidentUpdates: PropTypes.func.isRequired,
postIncident: PropTypes.func.isRequired,
updateIncident: PropTypes.func.isRequired
}

constructor (props) {
super(props)
this.state = {
incidentID: props.incident.incidentID,
name: props.incident.name,
components: props.components,
incidentStatus: props.incident.status || incidentStatuses[0],
message: ''
if (props.incident) {
this.state = {
name: props.incident.name,
incidentStatus: props.incident.status
}
} else {
this.state = {
name: '',
incidentStatus: incidentStatuses[0]
}
}
this.state.components = props.components
this.state.isUpdating = false
this.state.incidentMessage = ''
this.state.message = ''
}

componentDidMount () {
const fetchCallbacks = {
onFailure: (msg) => {
this.setState({message: msg})
}
}
if (!this.props.components || this.props.components.length === 0) {
this.props.fetchComponents(fetchCallbacks)
}
if (this.props.incident && !this.props.incident.incidentUpdates) {
this.props.fetchIncidentUpdates(this.props.incident.incidentID, fetchCallbacks)
}

const dialog = ReactDOM.findDOMNode(this.refs.dialog)
if (dialog && !dialog.showModal) {
// dialog polyfill has a limitation that the dialog should have a child of parents without parents.
// Here is a workaround for this limitation.
document.getElementById('dialog-container').appendChild(dialog)

dialogPolyfill.registerDialog(dialog)
dialog.showModal()
}
}

Expand All @@ -27,7 +92,7 @@ class IncidentDialog extends React.Component {

handleChangeComponentStatus = (componentID) => {
return (status) => {
let newComponents = this.state.components.map((component) => {
let newComponents = this.props.components.map((component) => {
if (component.componentID === componentID) {
return Object.assign({}, component, {
status: status
Expand All @@ -43,13 +108,39 @@ class IncidentDialog extends React.Component {
this.setState({incidentStatus: value})
}

handleChangeMessage = (value) => {
this.setState({message: value})
handleChangeIncidentMessage = (value) => {
this.setState({incidentMessage: value})
}

handleClickDoneButton = (e) => {
this.props.onCompleted(this.state.incidentID, this.state.name, this.state.incidentStatus,
this.state.message, this.state.components)
updateCallbacks = {
onLoad: () => { this.setState({isUpdating: true}) },
onSuccess: () => {
this.setState({isUpdating: false})
this.handleHideDialog()
},
onFailure: (msg) => {
this.setState({isUpdating: false, message: msg})
}
}

handleClickAddButton = (e) => {
this.props.postIncident(this.state.name, this.state.incidentStatus,
this.state.incidentMessage, this.state.components, this.updateCallbacks)
}

handleClickUpdateButton = (e) => {
this.props.updateIncident(this.props.incident.incidentID, this.state.name,
this.state.incidentStatus, this.state.incidentMessage, this.state.components,
this.updateCallbacks)
}

handleHideDialog = () => {
const dialog = ReactDOM.findDOMNode(this.refs.dialog)
if (dialog) {
dialog.close()
document.getElementById('inner-dialog-container').appendChild(dialog)
}
this.props.onClosed()
}

renderIncidentStatuses = () => {
Expand Down Expand Up @@ -109,7 +200,7 @@ class IncidentDialog extends React.Component {
}

renderIncidentUpdates = () => {
if (!this.props.incident.incidentUpdates) {
if (!this.props.incident || !this.props.incident.incidentUpdates) {
return
}
const updates = this.props.incident.incidentUpdates.map(this.renderIncidentUpdateItem)
Expand All @@ -126,50 +217,43 @@ class IncidentDialog extends React.Component {
}

render () {
let actionName, clickHandler
switch (this.props.dialogType) {
case dialogType.add:
actionName = 'Add'
clickHandler = this.handleClickAddButton
break
case dialogType.update:
actionName = 'Update'
clickHandler = this.handleClickUpdateButton
break
default:
console.warn('unknown dialog type: ', this.state.dialogType)
}

const incidentStatuses = this.renderIncidentStatuses()
const componentStatuses = this.renderComponentStatuses()
const incidentUpdates = this.renderIncidentUpdates()
return (<dialog className={classnames('mdl-dialog', classes.dialog)}>
return (<dialog className={classnames('mdl-dialog', classes.dialog)} ref='dialog'>
<h4 className={classnames('mdl-dialog__title', classes.title)}>
{this.props.actionName} Incident
{actionName} Incident
</h4>
<div className='mdl-dialog__content'>
<ErrorMessage message={this.state.message} />
<TextField label='Name' text={this.state.name} rows={1} onChange={this.handleChangeName} />
{incidentStatuses}
<TextField label='Message' text={this.state.message} rows={2} onChange={this.handleChangeMessage} />
<TextField label='Message' text={this.state.incidentMessage} rows={2}
onChange={this.handleChangeIncidentMessage} />
{componentStatuses}
{incidentUpdates}
</div>
<div className='mdl-dialog__actions'>
<Button onClick={this.handleClickDoneButton} name={this.props.actionName}
class='mdl-button--accent' disabled={this.props.isUpdating} />
<Button onClick={this.props.onCanceled} name='Cancel' />
<Button onClick={clickHandler} name={actionName}
class='mdl-button--accent' disabled={this.state.isUpdating} />
<Button onClick={this.handleHideDialog} name='Cancel' />
</div>
</dialog>)
}
}

IncidentDialog.propTypes = {
onCompleted: PropTypes.func.isRequired,
onCanceled: PropTypes.func.isRequired,
incident: PropTypes.shape({
incidentID: PropTypes.string.isRequired,
name: PropTypes.string.isRequired,
status: PropTypes.string,
incidentUpdates: PropTypes.arrayOf(PropTypes.shape({
incidentUpdateID: PropTypes.string.isRequired,
incidentStatus: PropTypes.string.isRequired,
message: PropTypes.string.isRequired,
updatedAt: PropTypes.string.isRequired
}).isRequired)
}).isRequired,
components: PropTypes.arrayOf(PropTypes.shape({
componentID: PropTypes.string.isRequired,
name: PropTypes.string.isRequired,
status: PropTypes.string.isRequired
}).isRequired).isRequired,
isUpdating: PropTypes.bool.isRequired,
actionName: PropTypes.string.isRequired
}

export default IncidentDialog
3 changes: 2 additions & 1 deletion packages/frontend/src/components/IncidentDialog/index.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import IncidentDialog from './IncidentDialog'
import IncidentDialog, { dialogType } from './IncidentDialog'

export default IncidentDialog
export const incidentDialogType = dialogType
Loading

0 comments on commit 244291e

Please sign in to comment.