From dd7b2565cb14561e041acbae54a66a61f345e0c5 Mon Sep 17 00:00:00 2001 From: Alexander Zobnin Date: Wed, 26 Dec 2018 20:57:38 +0300 Subject: [PATCH] problems timeline: show acknowledges --- .../components/ProblemTimeline.tsx | 189 +++++++++++++++++- src/sass/_panel-problems.scss | 28 ++- 2 files changed, 215 insertions(+), 2 deletions(-) diff --git a/src/panel-triggers/components/ProblemTimeline.tsx b/src/panel-triggers/components/ProblemTimeline.tsx index 193c62b..b8041b1 100644 --- a/src/panel-triggers/components/ProblemTimeline.tsx +++ b/src/panel-triggers/components/ProblemTimeline.tsx @@ -1,7 +1,7 @@ import React, { PureComponent } from 'react'; import _ from 'lodash'; import moment from 'moment'; -import { GFTimeRange, ZBXEvent } from 'panel-triggers/types'; +import { GFTimeRange, ZBXEvent, ZBXAcknowledge } from 'panel-triggers/types'; const DEFAULT_OK_COLOR = 'rgb(56, 189, 113)'; const DEFAULT_PROBLEM_COLOR = 'rgb(215, 0, 0)'; @@ -28,7 +28,9 @@ interface ProblemTimelineState { } interface EventInfo { + timestamp?: number; duration?: number; + message?: string; } export default class ProblemTimeline extends PureComponent { @@ -49,6 +51,7 @@ export default class ProblemTimeline extends PureComponent { + this.setState({ + showEventInfo: true, + eventInfo: { + timestamp: Number(ack.clock), + message: ack.message, + } + }); + } + + handleAckUnHighlight = () => { + this.setState({ showEventInfo: false }); + } + showEventInfo = (event: ZBXEvent) => { this.setState({ highlightedEvent: event, showEventInfo: true }); } @@ -111,6 +128,18 @@ export default class ProblemTimeline extends PureComponent Number(ack.clock)); + } + render() { if (!this.rootRef) { return
; @@ -118,6 +147,7 @@ export default class ProblemTimeline extends PureComponent + + + { ); } + if (eventInfo && eventInfo.timestamp) { + const ts = moment(eventInfo.timestamp * 1000); + const tsFormatted = ts.format('HH:mm:ss'); + infoItems = [ + + Time:  + {tsFormatted} + + ]; + } + const containerStyle: React.CSSProperties = { left }; if (!show) { containerStyle.opacity = 0; @@ -252,6 +303,14 @@ class TimelineInfoContainer extends PureComponent {
{durationItem}
+ {eventInfo && eventInfo.message && +
+ + Message:  + {eventInfo.message} + +
+ }
); } @@ -497,6 +556,134 @@ class TimelinePoint extends PureComponent void; + onUnHighlight?: () => void; +} + +interface TimelineAcksState { + order: number[]; + highlighted: number; +} + +class TimelineAcks extends PureComponent { + constructor(props) { + super(props); + this.state = { order: [], highlighted: null }; + } + + handleHighlight = (index: number) => () => { + if (this.props.onHighlight) { + const ack = this.props.acknowledges[index]; + this.props.onHighlight(ack, index); + } + this.bringToFront(index, true); + } + + handleUnHighlight = () => { + if (this.props.onUnHighlight) { + this.props.onUnHighlight(); + } + const order = this.props.acknowledges.map((v, i) => i); + this.setState({ order, highlighted: null }); + } + + bringToFront = (index: number, highlight = false) => { + const { acknowledges } = this.props; + let order = acknowledges.map((v, i) => i); + order = moveToEnd(order, [index]); + const highlighted = highlight ? index : null; + this.setState({ order, highlighted }); + } + + render() { + const { acknowledges, timeRange, width, size } = this.props; + const { timeFrom, timeTo } = timeRange; + const range = timeTo - timeFrom; + const pointR = size / 2; + const eventsItems = acknowledges.map((ack, i) => { + const ts = Number(ack.clock); + const posLeft = Math.round((ts - timeFrom) / range * width - pointR); + const highlighted = this.state.highlighted === i; + + return ( + + ); + }); + if (this.state.order.length) { + return this.state.order.map(i => eventsItems[i]); + } + return eventsItems; + } +} + +interface TimelineAckProps { + x: number; + r: number; + highlighted?: boolean; + onHighlight?: () => void; + onUnHighlight?: () => void; +} + +interface TimelineAckState { + highlighted?: boolean; +} + +class TimelineAck extends PureComponent { + constructor(props) { + super(props); + this.state = { highlighted: false }; + } + + componentDidUpdate(prevProps: TimelineAckProps) { + // Update component after reordering to make animation working + if (prevProps.highlighted !== this.props.highlighted) { + this.setState({ highlighted: this.props.highlighted }); + } + } + + handleHighlight = () => { + if (this.props.onHighlight) { + this.props.onHighlight(); + } + } + + handleUnHighlight = () => { + if (this.props.onUnHighlight) { + this.props.onUnHighlight(); + } + } + + render() { + const { x } = this.props; + const r = this.state.highlighted ? Math.round(this.props.r * HIGHLIGHTED_POINT_SIZE) : this.props.r; + const cx = x + this.props.r; + const rInner = Math.round(r * INNER_POINT_SIZE); + const className = `problem-event-ack ${this.state.highlighted ? 'highlighted' : ''}`; + return ( + + + + + ); + } +} + function moveToEnd(array: T[], itemsToMove: number[]): T[] { const removed = _.pullAt(array, itemsToMove); removed.reverse(); diff --git a/src/sass/_panel-problems.scss b/src/sass/_panel-problems.scss index 8549d1c..7e443a4 100644 --- a/src/sass/_panel-problems.scss +++ b/src/sass/_panel-problems.scss @@ -399,7 +399,7 @@ .event-timeline { position: relative; - margin: 0.6rem 0; + margin: 0.2rem 0; padding-top: 2.6rem; // margin-top: auto; @@ -474,6 +474,32 @@ } } } + + .problem-event-ack { + circle { + transition: all 0.2s ease-out; + } + + .point-border { + fill: $blue; + } + + .point-core { + fill: $problem-event-core; + } + + &:hover, + &.highlighted { + circle.point-border { + stroke: $problem-event-highlighted; + stroke-width: 1; + filter: url(#glowShadow); + } + .point-core { + fill: $problem-event-core-highlighted; + } + } + } } }