problems panel: ack dialog

This commit is contained in:
Alexander Zobnin
2018-12-05 13:13:47 +03:00
parent 1ffafac026
commit 6861d80530
7 changed files with 231 additions and 25 deletions

View File

@@ -0,0 +1,100 @@
import React, { PureComponent } from 'react';
import ReactDOM from 'react-dom';
interface ModalProps {
isOpen?: boolean;
withBackdrop?: boolean;
onSubmit: (data?: AckProblemData) => void;
onClose?: () => void;
}
interface ModalState {
value: string;
}
export interface AckProblemData {
message: string;
closeProblem?: boolean;
action?: number;
}
export class Modal extends PureComponent<ModalProps, ModalState> {
modalContainer: HTMLElement;
constructor(props) {
super(props);
this.state = { value: '' };
this.modalContainer = document.body;
}
handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
this.setState({ value: event.target.value });
}
dismiss = () => {
console.log('dismiss');
this.setState({ value: '' });
this.props.onClose();
}
submit = () => {
console.log('submit', this.state.value);
this.props.onSubmit({
message: this.state.value
});
}
render() {
if (!this.props.isOpen || !this.modalContainer) {
return null;
}
const modalNode = (
<div className="modal modal--narrow" key="modal">
<div className="modal-body">
<div className="modal-header">
<h2 className="modal-header-title">
<i className="fa fa-reply-all"></i>
<span className="p-l-1">Acknowledge Problem</span>
</h2>
<a className="modal-header-close" onClick={this.dismiss}>
<i className="fa fa-remove"></i>
</a>
</div>
<div className="modal-content">
{/* Message */}
<div className="gf-form">
<label className="gf-form-hint">
<input className="gf-form-input"
type="text"
name="message"
placeholder="Message"
maxLength={64}
autoComplete="off"
autoFocus={true}
value={this.state.value}
onChange={this.handleChange}>
</input>
</label>
</div>
<div className="gf-form-button-row text-center">
<button className="btn btn-success" onClick={this.submit}>Acknowledge</button>
<button className="btn btn-inverse" onClick={this.dismiss}>Cancel</button>
</div>
</div>
</div>
</div>
);
const modalNodeWithBackdrop = [
modalNode,
<div className="modal-backdrop in" key="modal-backdrop"></div>
];
const modal = this.props.withBackdrop ? modalNodeWithBackdrop : modalNode;
return ReactDOM.createPortal(modal, this.modalContainer);
}
}

View File

@@ -2,6 +2,7 @@ import React, { PureComponent } from 'react';
import ReactTable from 'react-table';
import EventTag from './EventTag';
import Tooltip from './Tooltip';
import { Modal, AckProblemData } from './Modal';
import { ProblemsPanelOptions, Trigger, ZBXItem, ZBXAcknowledge, ZBXHost, ZBXGroup } from '../types';
import * as utils from '../../datasource-zabbix/utils';
@@ -200,17 +201,15 @@ interface ProblemGroupsProps {
className?: string;
}
function ProblemGroups(props: ProblemGroupsProps) {
let groups = "";
if (props.groups && props.groups.length) {
groups = props.groups.map(g => g.name).join(', ');
class ProblemGroups extends PureComponent<ProblemGroupsProps> {
render() {
return this.props.groups.map(g => (
<div className={this.props.className || ''} key={g.groupid}>
<FAIcon icon="folder" />
<span>{g.name}</span>
</div>
));
}
return (
<div className={props.className}>
<FAIcon icon="folder" />
<span>{groups}</span>
</div>
);
}
interface ProblemHostsProps {
@@ -218,25 +217,24 @@ interface ProblemHostsProps {
className?: string;
}
function ProblemHosts(props: ProblemHostsProps) {
let hosts = "";
if (props.hosts && props.hosts.length) {
hosts = props.hosts.map(g => g.name).join(', ');
class ProblemHosts extends PureComponent<ProblemHostsProps> {
render() {
return this.props.hosts.map(h => (
<div className={this.props.className || ''} key={h.hostid}>
<FAIcon icon="server" />
<span>{h.name}</span>
</div>
));
}
return (
<div className={props.className}>
<FAIcon icon="server" />
<span>{hosts}</span>
</div>
);
}
interface ProblemStatusBarProps {
problem: Trigger;
className?: string;
}
function ProblemStatusBar(props: ProblemStatusBarProps) {
const { problem } = props;
const { problem, className } = props;
const multiEvent = problem.type === '1';
const link = problem.url && problem.url !== '';
const maintenance = problem.maintenance;
@@ -244,8 +242,9 @@ function ProblemStatusBar(props: ProblemStatusBarProps) {
const error = problem.error && problem.error !== '';
const stateUnknown = problem.state === '1';
const closeByTag = problem.correlation_mode === '1';
return (
<div className="problem-statusbar">
<div className={`problem-statusbar ${className || ''}`}>
<ProblemStatusBarItem icon="wrench" fired={maintenance} tooltip="Host maintenance" />
<ProblemStatusBarItem icon="globe" fired={link} link={link && problem.url} tooltip="External link" />
<ProblemStatusBarItem icon="bullhorn" fired={multiEvent} tooltip="Trigger generates multiple problem events" />
@@ -281,11 +280,47 @@ function ProblemStatusBarItem(props: ProblemStatusBarItemProps) {
return link ? <a href={link} target="_blank">{item}</a> : item;
}
class ProblemDetails extends PureComponent<any, any> {
interface ProblemActionButtonProps {
icon: string;
tooltip?: string;
className?: string;
onClick?: (event?) => void;
}
class ProblemActionButton extends PureComponent<ProblemActionButtonProps> {
handleClick = (event) => {
this.props.onClick(event);
}
render() {
const { icon, tooltip, className } = this.props;
let button = (
<button className={`btn problem-action-button ${className || ''}`} onClick={this.handleClick}>
<FAIcon icon={icon} />
</button>
);
if (tooltip) {
button = (
<Tooltip placement="bottom" content={tooltip}>
{button}
</Tooltip>
);
}
return button;
}
}
interface ProblemDetailsState {
show: boolean;
showAckDialog: boolean;
}
class ProblemDetails extends PureComponent<any, ProblemDetailsState> {
constructor(props) {
super(props);
this.state = {
show: false
show: false,
showAckDialog: false,
};
}
@@ -295,11 +330,25 @@ class ProblemDetails extends PureComponent<any, any> {
});
}
ackProblem = (data: AckProblemData) => {
const problem = this.props.original as Trigger;
console.log(problem.lastEvent && problem.lastEvent.eventid, data);
}
showAckDialog = () => {
this.setState({ showAckDialog: true });
}
closeAckDialog = () => {
this.setState({ showAckDialog: false });
}
render() {
const problem = this.props.original as Trigger;
const rootWidth = this.props.rootWidth;
const displayClass = this.state.show ? 'show' : '';
const wideLayout = rootWidth > 1000;
const compactStatusBar = rootWidth < 800;
return (
<div className={`problem-details-container ${displayClass}`}>
@@ -313,7 +362,13 @@ class ProblemDetails extends PureComponent<any, any> {
</div>
{problem.items && <ProblemItems items={problem.items} />}
</div>
<ProblemStatusBar problem={problem} />
<ProblemStatusBar problem={problem} className={compactStatusBar && 'compact'} />
<div className="problem-actions">
<ProblemActionButton className="navbar-button navbar-button--settings"
icon="reply-all"
tooltip="Acknowledge problem"
onClick={this.showAckDialog} />
</div>
</div>
{problem.comments &&
<div className="problem-description">
@@ -353,6 +408,10 @@ class ProblemDetails extends PureComponent<any, any> {
{problem.groups && <ProblemGroups groups={problem.groups} className="problem-details-right-item" />}
{problem.hosts && <ProblemHosts hosts={problem.hosts} className="problem-details-right-item" />}
</div>
<Modal withBackdrop={true}
isOpen={this.state.showAckDialog}
onSubmit={this.ackProblem}
onClose={this.closeAckDialog} />
</div>
);
}