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'; export interface ProblemListProps { problems: Trigger[]; panelOptions: ProblemsPanelOptions; loading?: boolean; } interface ProblemListState { expanded: any; page: number; } export class ProblemList extends PureComponent { rootWidth: number; rootRef: any; constructor(props) { super(props); this.state = { expanded: {}, page: 0, }; } setRootRef = ref => { this.rootRef = ref; } buildColumns() { const result = []; const options = this.props.panelOptions; const problems = this.props.problems; const timeColWidth = problems && problems.length ? problems[0].lastchange.length * 9 : 160; const highlightNewerThan = options.highlightNewEvents && options.highlightNewerThan; const statusCell = props => StatusCell(props, options.okEventColor, DEFAULT_PROBLEM_COLOR, highlightNewerThan); const columns = [ { Header: 'Host', accessor: 'host', show: options.hostField }, { Header: 'Host (Technical Name)', accessor: 'hostTechName', show: options.hostTechNameField }, { Header: 'Host Groups', accessor: 'groups', show: options.hostGroups, Cell: GroupCell }, { Header: 'Proxy', accessor: 'proxy', show: options.hostProxy }, { Header: 'Severity', show: options.severityField, className: 'problem-severity', width: 120, accessor: problem => problem.priority, id: 'severity', Cell: SeverityCell, }, { Header: 'Status', accessor: 'value', show: options.statusField, width: 100, Cell: statusCell }, { Header: 'Problem', accessor: 'description', minWidth: 200, Cell: ProblemCell}, { Header: 'Tags', accessor: 'tags', show: options.showTags, className: 'problem-tags', Cell: TagCell }, { Header: 'Time', className: 'last-change', width: timeColWidth, accessor: 'lastchangeUnix', id: 'lastchange', Cell: row => row.original.lastchange, }, { Header: 'Details', className: 'custom-expander', width: 60, expander: true, Expander: CustomExpander }, ]; for (const column of columns) { if (column.show || column.show === undefined) { delete column.show; result.push(column); } } return result; } getExpandedPage = (page: number) => { return this.state.expanded[page] || {}; } handleExpandedChange = expanded => { const nextExpanded = {...this.state.expanded}; nextExpanded[this.state.page] = expanded; this.setState({ expanded: nextExpanded }); } render() { // console.log(this.props.problems, this.props.panelOptions); const columns = this.buildColumns(); this.rootWidth = this.rootRef && this.rootRef.clientWidth; return (
} expanded={this.getExpandedPage(this.state.page)} onExpandedChange={this.handleExpandedChange} onPageChange={page => this.setState({ page })} />
); } } // interface CellProps { // row: any; // original: any; // } function SeverityCell(props) { return (
{props.original.severity}
); } 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) { // console.log(props); const status = props.value === '0' ? 'RESOLVED' : 'PROBLEM'; const color = props.value === '0' ? okColor : problemColor; let newProblem = false; if (highlightNewerThan) { newProblem = isNewProblem(props.original, highlightNewerThan); } return ( {status} ); } function GroupCell(props) { let groups = ""; if (props.value && props.value.length) { groups = props.value.map(g => g.name).join(', '); } return ( {groups} ); } function ProblemCell(props) { const comments = props.original.comments; return (
{props.value} {/* {comments && } */}
); } function TagCell(props) { const tags = props.value || []; return [ tags.map(tag => ) ]; } function CustomExpander(props) { return ( ); } 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; 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'; 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 { show: boolean; showAckDialog: boolean; } class ProblemDetails extends PureComponent { constructor(props) { super(props); this.state = { show: false, showAckDialog: false, }; } componentDidMount() { requestAnimationFrame(() => { this.setState({ show: true }); }); } 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 (
{/*
Problem Details
*/}
{problem.age}
{problem.items && }
{problem.comments &&
Description:  {problem.comments}
}
{problem.tags && problem.tags.map(tag => ) }
{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); const durationSec = (Date.now() - problem.lastchangeUnix * 1000); return durationSec < highlightIntervalMs; } catch (e) { return false; } }