diff --git a/src/panel-triggers/components/FAIcon.tsx b/src/panel-triggers/components/FAIcon.tsx new file mode 100644 index 0000000..aca9968 --- /dev/null +++ b/src/panel-triggers/components/FAIcon.tsx @@ -0,0 +1,14 @@ +import React from 'react'; + +interface FAIconProps { + icon: string; + customClass?: string; +} + +export default function FAIcon(props: FAIconProps) { + return ( + + + + ); +} diff --git a/src/panel-triggers/components/ProblemDetails.tsx b/src/panel-triggers/components/ProblemDetails.tsx new file mode 100644 index 0000000..e6b5fc5 --- /dev/null +++ b/src/panel-triggers/components/ProblemDetails.tsx @@ -0,0 +1,308 @@ +import React, { PureComponent } from 'react'; +import * as utils from '../../datasource-zabbix/utils'; +import { Trigger, ZBXItem, ZBXAcknowledge, ZBXHost, ZBXGroup, ZBXEvent, GFTimeRange, RTRow } from '../types'; +import { Modal, AckProblemData } from './Modal'; +import EventTag from './EventTag'; +import Tooltip from './Tooltip'; +import ProblemTimeline from './ProblemTimeline'; +import FAIcon from './FAIcon'; + +interface ProblemDetailsProps extends RTRow { + rootWidth: number; + timeRange: GFTimeRange; + getProblemEvents: (problem: Trigger) => Promise; +} + +interface ProblemDetailsState { + events: ZBXEvent[]; + show: boolean; + showAckDialog: boolean; +} + +export default class ProblemDetails extends PureComponent { + constructor(props) { + super(props); + this.state = { + events: [], + show: false, + showAckDialog: false, + }; + } + + componentDidMount() { + this.fetchProblemEvents(); + requestAnimationFrame(() => { + this.setState({ show: true }); + }); + } + + fetchProblemEvents() { + const problem = this.props.original; + this.props.getProblemEvents(problem) + .then(events => { + console.log(events, this.props.timeRange); + this.setState({ events }); + }); + } + + 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 || problem.acknowledges && wideLayout && rootWidth < 1200; + + return ( +
+
+
+
+
+ + {problem.age} +
+ {problem.items && } +
+ +
+ +
+
+ {problem.comments && +
+ Description:  + {problem.comments} +
+ } + {problem.tags && problem.tags.length > 0 && +
+ {problem.tags && problem.tags.map(tag => + ) + } +
+ } + {this.state.events.length > 0 && + + } + {problem.acknowledges && !wideLayout && +
+
Acknowledges
+ +
+ } +
+ {problem.acknowledges && wideLayout && +
+
Acknowledges
+ +
+ } +
+
+ + {problem.datasource} +
+ {problem.proxy && +
+ + {problem.proxy} +
+ } + {problem.groups && } + {problem.hosts && } +
+ +
+ ); + } +} + +interface ProblemItemProps { + item: ZBXItem; + showName?: boolean; +} + +function ProblemItem(props: ProblemItemProps) { + const { item, showName } = props; + const itemName = utils.expandItemName(item.name, item.key_); + return ( + +
+ + {showName && {item.name}: } + {item.lastvalue} +
+
+ ); +} + +interface ProblemItemsProps { + items: ZBXItem[]; +} + +class ProblemItems extends PureComponent { + render() { + const { items } = this.props; + return (items.length > 1 ? + items.map(item => ) : + + ); + } +} + +interface AcknowledgesListProps { + acknowledges: ZBXAcknowledge[]; +} + +function AcknowledgesList(props: AcknowledgesListProps) { + const { acknowledges } = props; + return ( +
+
+ {acknowledges.map(ack => {ack.time})} +
+
+ {acknowledges.map(ack => {ack.user})} +
+
+ {acknowledges.map(ack => {ack.message})} +
+
+ ); +} + +interface ProblemGroupsProps { + groups: ZBXGroup[]; + className?: string; +} + +class ProblemGroups extends PureComponent { + render() { + return this.props.groups.map(g => ( +
+ + {g.name} +
+ )); + } +} + +interface ProblemHostsProps { + hosts: ZBXHost[]; + className?: string; +} + +class ProblemHosts extends PureComponent { + render() { + return this.props.hosts.map(h => ( +
+ + {h.name} +
+ )); + } +} + +interface ProblemStatusBarProps { + problem: Trigger; + className?: string; +} + +function ProblemStatusBar(props: ProblemStatusBarProps) { + const { problem, className } = props; + const multiEvent = problem.type === '1'; + const link = problem.url && problem.url !== ''; + const maintenance = problem.maintenance; + const manualClose = problem.manual_close === '1'; + const error = problem.error && problem.error !== ''; + const stateUnknown = problem.state === '1'; + const closeByTag = problem.correlation_mode === '1'; + const actions = problem.alerts && problem.alerts.length !== 0; + const actionMessage = problem.alerts ? problem.alerts[0].message : ''; + + return ( +
+ + + + + + + + +
+ ); +} + +interface ProblemStatusBarItemProps { + icon: string; + fired?: boolean; + link?: string; + tooltip?: string; +} + +function ProblemStatusBarItem(props: ProblemStatusBarItemProps) { + const { fired, icon, link, tooltip } = props; + let item = ( +
+ +
+ ); + if (tooltip && fired) { + item = ( + + {item} + + ); + } + return link ? {item} : item; +} + +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; + } +} diff --git a/src/panel-triggers/components/Problems.tsx b/src/panel-triggers/components/Problems.tsx index c914b62..840e68f 100644 --- a/src/panel-triggers/components/Problems.tsx +++ b/src/panel-triggers/components/Problems.tsx @@ -1,11 +1,9 @@ import React, { PureComponent } from 'react'; import ReactTable from 'react-table'; import * as utils from '../../datasource-zabbix/utils'; -import { ProblemsPanelOptions, Trigger, ZBXItem, ZBXAcknowledge, ZBXHost, ZBXGroup, ZBXEvent, GFTimeRange } from '../types'; -import { Modal, AckProblemData } from './Modal'; +import { ProblemsPanelOptions, Trigger, ZBXEvent, GFTimeRange, RTCell } from '../types'; import EventTag from './EventTag'; -import Tooltip from './Tooltip'; -import ProblemTimeline from './ProblemTimeline'; +import ProblemDetails from './ProblemDetails'; export interface ProblemListProps { problems: Trigger[]; @@ -112,12 +110,7 @@ export class ProblemList extends PureComponent) { return (
{props.original.severity} @@ -128,7 +121,7 @@ function SeverityCell(props) { const DEFAULT_OK_COLOR = 'rgb(56, 189, 113)'; const DEFAULT_PROBLEM_COLOR = 'rgb(215, 0, 0)'; -function StatusCell(props, okColor = DEFAULT_OK_COLOR, problemColor = DEFAULT_PROBLEM_COLOR, highlightNewerThan?: string) { +function StatusCell(props: RTCell, okColor = DEFAULT_OK_COLOR, problemColor = DEFAULT_PROBLEM_COLOR, highlightNewerThan?: string) { // console.log(props); const status = props.value === '0' ? 'RESOLVED' : 'PROBLEM'; const color = props.value === '0' ? okColor : problemColor; @@ -141,7 +134,7 @@ function StatusCell(props, okColor = DEFAULT_OK_COLOR, problemColor = DEFAULT_PR ); } -function GroupCell(props) { +function GroupCell(props: RTCell) { let groups = ""; if (props.value && props.value.length) { groups = props.value.map(g => g.name).join(', '); @@ -151,7 +144,7 @@ function GroupCell(props) { ); } -function ProblemCell(props) { +function ProblemCell(props: RTCell) { const comments = props.original.comments; return (
@@ -161,14 +154,14 @@ function ProblemCell(props) { ); } -function TagCell(props) { +function TagCell(props: RTCell) { const tags = props.value || []; return [ tags.map(tag => ) ]; } -function CustomExpander(props) { +function CustomExpander(props: RTCell) { return ( @@ -176,314 +169,6 @@ function CustomExpander(props) { ); } -interface FAIconProps { - icon: string; - customClass?: string; -} - -function FAIcon(props: FAIconProps) { - return ( - - - - ); -} - -interface ProblemItemProps { - item: ZBXItem; - showName?: boolean; -} - -function ProblemItem(props: ProblemItemProps) { - const { item, showName } = props; - const itemName = utils.expandItemName(item.name, item.key_); - return ( - -
- - {showName && {item.name}: } - {item.lastvalue} -
-
- ); -} - -interface ProblemItemsProps { - items: ZBXItem[]; -} - -class ProblemItems extends PureComponent { - render() { - const { items } = this.props; - return (items.length > 1 ? - items.map(item => ) : - - ); - } -} - -interface AcknowledgesListProps { - acknowledges: ZBXAcknowledge[]; -} - -function AcknowledgesList(props: AcknowledgesListProps) { - const { acknowledges } = props; - return ( -
-
- {acknowledges.map(ack => {ack.time})} -
-
- {acknowledges.map(ack => {ack.user})} -
-
- {acknowledges.map(ack => {ack.message})} -
-
- ); -} - -interface ProblemGroupsProps { - groups: ZBXGroup[]; - className?: string; -} - -class ProblemGroups extends PureComponent { - render() { - return this.props.groups.map(g => ( -
- - {g.name} -
- )); - } -} - -interface ProblemHostsProps { - hosts: ZBXHost[]; - className?: string; -} - -class ProblemHosts extends PureComponent { - render() { - return this.props.hosts.map(h => ( -
- - {h.name} -
- )); - } -} - -interface ProblemStatusBarProps { - problem: Trigger; - className?: string; -} - -function ProblemStatusBar(props: ProblemStatusBarProps) { - const { problem, className } = props; - const multiEvent = problem.type === '1'; - const link = problem.url && problem.url !== ''; - const maintenance = problem.maintenance; - const manualClose = problem.manual_close === '1'; - const error = problem.error && problem.error !== ''; - const stateUnknown = problem.state === '1'; - const closeByTag = problem.correlation_mode === '1'; - const actions = problem.alerts && problem.alerts.length !== 0; - const actionMessage = problem.alerts ? problem.alerts[0].message : ''; - - return ( -
- - - - - - - - -
- ); -} - -interface ProblemStatusBarItemProps { - icon: string; - fired?: boolean; - link?: string; - tooltip?: string; -} - -function ProblemStatusBarItem(props: ProblemStatusBarItemProps) { - const { fired, icon, link, tooltip } = props; - let item = ( -
- -
- ); - if (tooltip && fired) { - item = ( - - {item} - - ); - } - return link ? {item} : item; -} - -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 { - events: ZBXEvent[]; - show: boolean; - showAckDialog: boolean; -} - -class ProblemDetails extends PureComponent { - constructor(props) { - super(props); - this.state = { - events: [], - show: false, - showAckDialog: false, - }; - } - - componentDidMount() { - this.fetchProblemEvents(); - requestAnimationFrame(() => { - this.setState({ show: true }); - }); - } - - fetchProblemEvents() { - const problem = this.props.original; - this.props.getProblemEvents(problem) - .then(events => { - console.log(events, this.props.timeRange); - this.setState({ events }); - }); - } - - 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 || problem.acknowledges && wideLayout && rootWidth < 1200; - - return ( -
-
- {/*
Problem Details
*/} -
-
-
- - {problem.age} -
- {problem.items && } -
- -
- -
-
- {problem.comments && -
- Description:  - {problem.comments} -
- } - {problem.tags && problem.tags.length > 0 && -
- {problem.tags && problem.tags.map(tag => - ) - } -
- } - {this.state.events.length > 0 && - - } - {problem.acknowledges && !wideLayout && -
-
Acknowledges
- -
- } -
- {problem.acknowledges && wideLayout && -
-
Acknowledges
- -
- } -
-
- - {problem.datasource} -
- {problem.proxy && -
- - {problem.proxy} -
- } - {problem.groups && } - {problem.hosts && } -
- -
- ); - } -} - function isNewProblem(problem: Trigger, highlightNewerThan: string): boolean { try { const highlightIntervalMs = utils.parseInterval(highlightNewerThan); diff --git a/src/panel-triggers/types.ts b/src/panel-triggers/types.ts index 3abbb30..13feaba 100644 --- a/src/panel-triggers/types.ts +++ b/src/panel-triggers/types.ts @@ -165,3 +165,47 @@ export interface GFTimeRange { timeFrom: number; timeTo: number; } + +export interface RTRow { + /** the materialized row of data */ + row: any; + /** the original row of data */ + original: T; + /** the index of the row in the original array */ + index: number; + /** the index of the row relative to the current view */ + viewIndex: number; + /** the nesting level of this row */ + level: number; + /** the nesting path of this row */ + nestingPath: number[]; + /** true if this row's values were aggregated */ + aggregated?: boolean; + /** true if this row was produced by a pivot */ + groupedByPivot?: boolean; + /** any sub rows defined by the `subRowKey` prop */ + subRows?: boolean; +} + +export interface RTCell extends RTRow { + /** true if this row is expanded */ + isExpanded?: boolean; + /** the materialized value of this cell */ + value: any; + /** the resize information for this cell's column */ + resized: any[]; + /** true if the column is visible */ + show?: boolean; + /** the resolved width of this cell */ + width: number; + /** the resolved maxWidth of this cell */ + maxWidth: number; + /** the resolved tdProps from `getTdProps` for this cell */ + tdProps: any; + /** the resolved column props from 'getProps' for this cell's column */ + columnProps: any; + /** the resolved array of classes for this cell */ + classes: string[]; + /** the resolved styles for this cell */ + styles: any; +}