From 6861d805305864766db09832202b4711104aba12 Mon Sep 17 00:00:00 2001 From: Alexander Zobnin Date: Wed, 5 Dec 2018 13:13:47 +0300 Subject: [PATCH] problems panel: ack dialog --- package.json | 1 + src/panel-triggers/components/Modal.tsx | 100 +++++++++++++++++++ src/panel-triggers/components/Problems.tsx | 109 ++++++++++++++++----- src/sass/_panel-problems.scss | 29 ++++++ src/sass/_variables.dark.scss | 1 + src/sass/_variables.light.scss | 1 + yarn.lock | 15 +++ 7 files changed, 231 insertions(+), 25 deletions(-) create mode 100644 src/panel-triggers/components/Modal.tsx diff --git a/package.json b/package.json index 75c9abb..002aa13 100644 --- a/package.json +++ b/package.json @@ -30,6 +30,7 @@ "@types/lodash": "^4.14.104", "@types/moment": "^2.13.0", "@types/react": "^16.4.6", + "@types/react-dom": "^16.0.11", "babel-core": "^6.26.3", "babel-jest": "^23.6.0", "babel-loader": "^7.1.2", diff --git a/src/panel-triggers/components/Modal.tsx b/src/panel-triggers/components/Modal.tsx new file mode 100644 index 0000000..5c63a04 --- /dev/null +++ b/src/panel-triggers/components/Modal.tsx @@ -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 { + modalContainer: HTMLElement; + + constructor(props) { + super(props); + this.state = { value: '' }; + + this.modalContainer = document.body; + } + + handleChange = (event: React.ChangeEvent) => { + 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 = ( +
+
+
+

+ + Acknowledge Problem +

+ + + + +
+
+ {/* Message */} +
+ +
+ +
+ + +
+
+
+
+ ); + + const modalNodeWithBackdrop = [ + modalNode, +
+ ]; + + const modal = this.props.withBackdrop ? modalNodeWithBackdrop : modalNode; + return ReactDOM.createPortal(modal, this.modalContainer); + } +} diff --git a/src/panel-triggers/components/Problems.tsx b/src/panel-triggers/components/Problems.tsx index 8e58353..8661c4f 100644 --- a/src/panel-triggers/components/Problems.tsx +++ b/src/panel-triggers/components/Problems.tsx @@ -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 { + render() { + return this.props.groups.map(g => ( +
+ + {g.name} +
+ )); } - return ( -
- - {groups} -
- ); } 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 { + render() { + return this.props.hosts.map(h => ( +
+ + {h.name} +
+ )); } - return ( -
- - {hosts} -
- ); } 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 ( -
+
@@ -281,11 +280,47 @@ function ProblemStatusBarItem(props: ProblemStatusBarItemProps) { return link ? {item} : item; } -class ProblemDetails extends PureComponent { +interface ProblemActionButtonProps { + icon: string; + tooltip?: string; + className?: string; + onClick?: (event?) => void; +} + +class ProblemActionButton extends PureComponent { + handleClick = (event) => { + this.props.onClick(event); + } + + render() { + const { icon, tooltip, className } = this.props; + let button = ( + + ); + if (tooltip) { + button = ( + + {button} + + ); + } + return button; + } +} + +interface ProblemDetailsState { + show: boolean; + showAckDialog: boolean; +} + +class ProblemDetails extends PureComponent { constructor(props) { super(props); this.state = { - show: false + show: false, + showAckDialog: false, }; } @@ -295,11 +330,25 @@ class ProblemDetails extends PureComponent { }); } + 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 (
@@ -313,7 +362,13 @@ class ProblemDetails extends PureComponent {
{problem.items && }
- + +
+ +
{problem.comments &&
@@ -353,6 +408,10 @@ class ProblemDetails extends PureComponent { {problem.groups && } {problem.hosts && }
+ ); } diff --git a/src/sass/_panel-problems.scss b/src/sass/_panel-problems.scss index 718c828..3f9875c 100644 --- a/src/sass/_panel-problems.scss +++ b/src/sass/_panel-problems.scss @@ -217,6 +217,11 @@ flex-direction: row; // max-width: 13rem; + &.compact .problem-statusbar-item { + width: 2.5rem; + padding: 0.4rem 0.4rem 0.4rem 0.8rem; + } + .problem-statusbar-item { width: 3rem; height: 2rem; @@ -246,6 +251,26 @@ } } + .problem-actions { + margin-left: 1.6rem; + } + + .problem-action-button { + &.btn { + width: 3rem; + height: 2rem; + + background-image: none; + background-color: $action-button-color; + border: 1px solid darken($action-button-color, 6%); + border-radius: 1px; + + &:hover { + background-color: darken($action-button-color, 4%); + } + } + } + .problem-details-middle { flex: 1 0 auto; padding: 0.6rem 1rem; @@ -288,6 +313,10 @@ // background: $dark-4; color: $text-color-muted; + &.compact { + padding: 0.5rem 1rem; + } + .problem-details-right-item { margin-bottom: 0.2rem; } diff --git a/src/sass/_variables.dark.scss b/src/sass/_variables.dark.scss index 2928fcb..c275205 100644 --- a/src/sass/_variables.dark.scss +++ b/src/sass/_variables.dark.scss @@ -44,3 +44,4 @@ $problems-table-stripe: $dark-3; $problem-details-background: $dark-3; $problem-statusbar-background: $dark-2; $problem-statusbar-muted: $dark-3; +$action-button-color: #103e66; diff --git a/src/sass/_variables.light.scss b/src/sass/_variables.light.scss index c14fcc2..eda72db 100644 --- a/src/sass/_variables.light.scss +++ b/src/sass/_variables.light.scss @@ -43,3 +43,4 @@ $problems-table-stripe: $gray-6; $problem-details-background: $gray-6; $problem-statusbar-background: $gray-4; $problem-statusbar-muted: $gray-5; +$action-button-color: #103e66; diff --git a/yarn.lock b/yarn.lock index dd79759..12a0bc1 100644 --- a/yarn.lock +++ b/yarn.lock @@ -146,6 +146,21 @@ resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.5.6.tgz#9c03d3fed70a8d517c191b7734da2879b50ca26c" integrity sha512-ZBFR7TROLVzCkswA3Fmqq+IIJt62/T7aY/Dmz+QkU7CaW2QFqAitCE8Ups7IzmGhcN1YWMBT4Qcoc07jU9hOJQ== +"@types/react-dom@^16.0.11": + version "16.0.11" + resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-16.0.11.tgz#bd10ccb0d9260343f4b9a49d4f7a8330a5c1f081" + integrity sha512-x6zUx9/42B5Kl2Vl9HlopV8JF64wLpX3c+Pst9kc1HgzrsH+mkehe/zmHMQTplIrR48H2gpU7ZqurQolYu8XBA== + dependencies: + "@types/react" "*" + +"@types/react@*": + version "16.7.13" + resolved "https://registry.yarnpkg.com/@types/react/-/react-16.7.13.tgz#d2369ae78377356d42fb54275d30218e84f2247a" + integrity sha512-WhqrQLAE9z65hfcvWqZfR6qUtIazFRyb8LXqHo8440R53dAQqNkt2OlVJ3FXwqOwAXXg4nfYxt0qgBvE18o5XA== + dependencies: + "@types/prop-types" "*" + csstype "^2.2.0" + "@types/react@^16.4.6": version "16.7.7" resolved "https://registry.yarnpkg.com/@types/react/-/react-16.7.7.tgz#1e5e23e7dd922968ed4b484cdec00a5402c9f31b"