Problems: improve ack dialog (add ack options), closes #942

This commit is contained in:
Alexander Zobnin
2020-05-18 19:41:33 +03:00
parent dfac36a5bb
commit 959cea2061
14 changed files with 295 additions and 204 deletions

View File

@@ -0,0 +1,236 @@
import React, { PureComponent } from 'react';
import ReactDOM from 'react-dom';
import classNames from 'classnames';
import { ZBX_ACK_ACTION_ADD_MESSAGE, ZBX_ACK_ACTION_ACK, ZBX_ACK_ACTION_CHANGE_SEVERITY, ZBX_ACK_ACTION_CLOSE } from '../../datasource-zabbix/constants';
import { Button, Input, VerticalGroup, Spinner } from '@grafana/ui';
import { FAIcon } from './FAIcon';
import * as grafanaUi from '@grafana/ui';
const Checkbox: any = grafanaUi.Forms?.Checkbox || (grafanaUi as any).Checkbox;
const RadioButtonGroup: any = grafanaUi.Forms?.RadioButtonGroup || (grafanaUi as any).RadioButtonGroup;
const KEYBOARD_ENTER_KEY = 13;
const KEYBOARD_ESCAPE_KEY = 27;
interface Props {
canAck?: boolean;
canClose?: boolean;
isOpen?: boolean;
withBackdrop?: boolean;
onSubmit: (data?: AckProblemData) => Promise<any> | any;
onClose?: () => void;
}
interface State {
value: string;
error: boolean;
errorMessage: string;
ackError: string;
acknowledge: boolean;
closeProblem: boolean;
changeSeverity: boolean;
selectedSeverity: number;
loading: boolean;
}
export interface AckProblemData {
message: string;
closeProblem?: boolean;
action?: number;
severity?: number;
}
const severityOptions = [
{value: 0, label: 'Not classified'},
{value: 1, label: 'Information'},
{value: 2, label: 'Warning'},
{value: 3, label: 'Average'},
{value: 4, label: 'High'},
{value: 5, label: 'Disaster'}
];
export class AckModal extends PureComponent<Props, State> {
modalContainer: HTMLElement;
static defaultProps: Partial<Props> = {
withBackdrop: true,
};
constructor(props) {
super(props);
this.state = {
value: '',
error: false,
errorMessage: '',
ackError: '',
acknowledge: false,
closeProblem: false,
changeSeverity: false,
selectedSeverity: 0,
loading: false,
};
this.modalContainer = document.body;
}
handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
this.setState({ value: event.target.value, error: false });
}
handleKeyUp = (event: React.KeyboardEvent<HTMLInputElement>) => {
if (event.which === KEYBOARD_ENTER_KEY || event.key === 'Enter') {
this.submit();
} else if (event.which === KEYBOARD_ESCAPE_KEY || event.key === 'Escape') {
this.dismiss();
}
}
handleBackdropClick = () => {
this.dismiss();
}
onAcknowledgeToggle = () => {
this.setState({ acknowledge: !this.state.acknowledge, error: false });
}
onChangeSeverityToggle = () => {
this.setState({ changeSeverity: !this.state.changeSeverity, error: false });
}
onCloseProblemToggle = () => {
this.setState({ closeProblem: !this.state.closeProblem, error: false });
}
onChangeSelectedSeverity = v => {
this.setState({ selectedSeverity: v });
};
dismiss = () => {
this.setState({ value: '', error: false, errorMessage: '', ackError: '', loading: false });
this.props.onClose();
}
submit = () => {
const { acknowledge, changeSeverity, closeProblem } = this.state;
const actionSelected = acknowledge || changeSeverity || closeProblem;
if (!this.state.value && !actionSelected) {
return this.setState({
error: true,
errorMessage: 'Enter message text or select an action'
});
}
this.setState({ ackError: '', loading: true });
const ackData: AckProblemData = {
message: this.state.value,
};
let action = ZBX_ACK_ACTION_ADD_MESSAGE;
if (this.state.acknowledge) {
action += ZBX_ACK_ACTION_ACK;
}
if (this.state.changeSeverity) {
action += ZBX_ACK_ACTION_CHANGE_SEVERITY;
ackData.severity = this.state.selectedSeverity;
}
if (this.state.closeProblem) {
action += ZBX_ACK_ACTION_CLOSE;
}
ackData.action = action;
this.props.onSubmit(ackData).then((response) => {
this.dismiss();
}).catch(err => {
this.setState({
ackError: err.message || err.data,
loading: false,
});
});
}
render() {
const { canClose, canAck } = this.props;
if (!this.props.isOpen || !this.modalContainer) {
return null;
}
const inputClass = classNames({ 'zbx-ack-error': this.state.error });
const modalNode = (
<div className="modal modal--narrow zbx-ack-modal" key="modal">
<div className="modal-body">
<div className="modal-header">
<h2 className="modal-header-title" style={{ display: 'flex' }}>
{this.state.loading ? <Spinner size={18} /> : <FAIcon icon="reply-all" />}
<span className="p-l-1">Acknowledge Problem</span>
</h2>
<a className="modal-header-close" onClick={this.dismiss}>
<FAIcon icon="remove" />
</a>
</div>
<div className="modal-content">
<div className="gf-form">
<label className="gf-form-hint">
<Input className={inputClass}
type="text"
name="message"
placeholder="Message"
maxLength={64}
autoComplete="off"
autoFocus={true}
value={this.state.value}
onChange={this.handleChange}
onKeyUp={this.handleKeyUp}>
</Input>
<small className="gf-form-hint-text muted">Press Enter to submit</small>
{this.state.error &&
<small className="gf-form-hint-text muted ack-error-message">{this.state.errorMessage}</small>
}
</label>
</div>
<div className="gf-form">
<VerticalGroup>
<Checkbox label="Acknowledge" value={this.state.acknowledge} onChange={this.onAcknowledgeToggle} />
<Checkbox label="Change severity" description="" value={this.state.changeSeverity} onChange={this.onChangeSeverityToggle} />
{this.state.changeSeverity &&
<RadioButtonGroup
size="sm"
options={severityOptions}
value={this.state.selectedSeverity}
onChange={this.onChangeSelectedSeverity}
/>
}
{canClose &&
<Checkbox label="Close problem" disabled={!canClose} value={this.state.closeProblem} onChange={this.onCloseProblemToggle} />
}
</VerticalGroup>
</div>
{this.state.ackError &&
<div className="gf-form ack-request-error">
<span className="ack-error-message">{this.state.ackError}</span>
</div>
}
<div className="gf-form-button-row text-center">
<Button variant="primary" onClick={this.submit}>Update</Button>
<Button variant="secondary" onClick={this.dismiss}>Cancel</Button>
</div>
</div>
</div>
</div>
);
const modalNodeWithBackdrop = [
modalNode,
<div className="modal-backdrop in" key="modal-backdrop" onClick={this.handleBackdropClick}></div>
];
const modal = this.props.withBackdrop ? modalNodeWithBackdrop : modalNode;
return ReactDOM.createPortal(modal, this.modalContainer);
}
}