Problems: navigate to Explore button, #948
This commit is contained in:
@@ -1,6 +1,8 @@
|
|||||||
import React, { PureComponent } from 'react';
|
import React, { PureComponent } from 'react';
|
||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
import * as utils from '../../../datasource-zabbix/utils';
|
import * as utils from '../../../datasource-zabbix/utils';
|
||||||
|
import { MODE_ITEMID } from '../../../datasource-zabbix/constants';
|
||||||
|
import { ProblemDTO, ZBXHost, ZBXGroup, ZBXEvent, ZBXTag, ZBXAlert } from '../../../datasource-zabbix/types';
|
||||||
import { ZBXItem, ZBXAcknowledge, GFTimeRange, RTRow } from '../../types';
|
import { ZBXItem, ZBXAcknowledge, GFTimeRange, RTRow } from '../../types';
|
||||||
import { Modal, AckProblemData } from '../Modal';
|
import { Modal, AckProblemData } from '../Modal';
|
||||||
import EventTag from '../EventTag';
|
import EventTag from '../EventTag';
|
||||||
@@ -9,12 +11,14 @@ import ProblemStatusBar from './ProblemStatusBar';
|
|||||||
import AcknowledgesList from './AcknowledgesList';
|
import AcknowledgesList from './AcknowledgesList';
|
||||||
import ProblemTimeline from './ProblemTimeline';
|
import ProblemTimeline from './ProblemTimeline';
|
||||||
import FAIcon from '../FAIcon';
|
import FAIcon from '../FAIcon';
|
||||||
import { ProblemDTO, ZBXHost, ZBXGroup, ZBXEvent, ZBXTag, ZBXAlert } from '../../../datasource-zabbix/types';
|
import { renderUrl } from '../../utils';
|
||||||
|
import { getLocationSrv } from '@grafana/runtime';
|
||||||
|
|
||||||
interface ProblemDetailsProps extends RTRow<ProblemDTO> {
|
interface ProblemDetailsProps extends RTRow<ProblemDTO> {
|
||||||
rootWidth: number;
|
rootWidth: number;
|
||||||
timeRange: GFTimeRange;
|
timeRange: GFTimeRange;
|
||||||
showTimeline?: boolean;
|
showTimeline?: boolean;
|
||||||
|
panelId?: number;
|
||||||
getProblemEvents: (problem: ProblemDTO) => Promise<ZBXEvent[]>;
|
getProblemEvents: (problem: ProblemDTO) => Promise<ZBXEvent[]>;
|
||||||
getProblemAlerts: (problem: ProblemDTO) => Promise<ZBXAlert[]>;
|
getProblemAlerts: (problem: ProblemDTO) => Promise<ZBXAlert[]>;
|
||||||
onProblemAck?: (problem: ProblemDTO, data: AckProblemData) => Promise<any> | any;
|
onProblemAck?: (problem: ProblemDTO, data: AckProblemData) => Promise<any> | any;
|
||||||
@@ -89,6 +93,25 @@ export default class ProblemDetails extends PureComponent<ProblemDetailsProps, P
|
|||||||
this.setState({ showAckDialog: false });
|
this.setState({ showAckDialog: false });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
openInExplore = () => {
|
||||||
|
const problem = this.props.original as ProblemDTO;
|
||||||
|
const itemids = problem.items?.map(p => p.itemid).join(',');
|
||||||
|
|
||||||
|
const state: any = {
|
||||||
|
datasource: problem.datasource,
|
||||||
|
context: 'explore',
|
||||||
|
originPanelId: this.props.panelId,
|
||||||
|
queries: [{
|
||||||
|
queryType: MODE_ITEMID,
|
||||||
|
itemids: itemids,
|
||||||
|
}],
|
||||||
|
};
|
||||||
|
|
||||||
|
const exploreState = JSON.stringify(state);
|
||||||
|
const url = renderUrl('/explore', { left: exploreState });
|
||||||
|
getLocationSrv().update({ path: url, query: {} });
|
||||||
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const problem = this.props.original as ProblemDTO;
|
const problem = this.props.original as ProblemDTO;
|
||||||
const alerts = this.state.alerts;
|
const alerts = this.state.alerts;
|
||||||
@@ -110,6 +133,7 @@ export default class ProblemDetails extends PureComponent<ProblemDetailsProps, P
|
|||||||
</div>
|
</div>
|
||||||
{problem.items && <ProblemItems items={problem.items} />}
|
{problem.items && <ProblemItems items={problem.items} />}
|
||||||
</div>
|
</div>
|
||||||
|
<ExploreButton onClick={this.openInExplore} />
|
||||||
<ProblemStatusBar problem={problem} alerts={alerts} className={compactStatusBar && 'compact'} />
|
<ProblemStatusBar problem={problem} alerts={alerts} className={compactStatusBar && 'compact'} />
|
||||||
{problem.showAckButton &&
|
{problem.showAckButton &&
|
||||||
<div className="problem-actions">
|
<div className="problem-actions">
|
||||||
@@ -273,3 +297,17 @@ class ProblemActionButton extends PureComponent<ProblemActionButtonProps> {
|
|||||||
return button;
|
return button;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface ExploreButtonProps {
|
||||||
|
onClick: (event?) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ExploreButton: React.FC<ExploreButtonProps> = ({ onClick }) => {
|
||||||
|
return (
|
||||||
|
<Tooltip placement="bottom" content="Open in Explore">
|
||||||
|
<button className="btn problem-explore-button" onClick={onClick}>
|
||||||
|
<FAIcon icon="compass" /><span>Explore</span>
|
||||||
|
</button>
|
||||||
|
</Tooltip>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ export interface ProblemListProps {
|
|||||||
timeRange?: GFTimeRange;
|
timeRange?: GFTimeRange;
|
||||||
pageSize?: number;
|
pageSize?: number;
|
||||||
fontSize?: number;
|
fontSize?: number;
|
||||||
|
panelId?: number;
|
||||||
getProblemEvents: (problem: ProblemDTO) => Promise<ZBXEvent[]>;
|
getProblemEvents: (problem: ProblemDTO) => Promise<ZBXEvent[]>;
|
||||||
getProblemAlerts: (problem: ProblemDTO) => Promise<ZBXAlert[]>;
|
getProblemAlerts: (problem: ProblemDTO) => Promise<ZBXAlert[]>;
|
||||||
onProblemAck?: (problem: ProblemDTO, data: AckProblemData) => void;
|
onProblemAck?: (problem: ProblemDTO, data: AckProblemData) => void;
|
||||||
@@ -168,6 +169,7 @@ export default class ProblemList extends PureComponent<ProblemListProps, Problem
|
|||||||
rootWidth={this.rootWidth}
|
rootWidth={this.rootWidth}
|
||||||
timeRange={this.props.timeRange}
|
timeRange={this.props.timeRange}
|
||||||
showTimeline={panelOptions.problemTimeline}
|
showTimeline={panelOptions.problemTimeline}
|
||||||
|
panelId={this.props.panelId}
|
||||||
getProblemEvents={this.props.getProblemEvents}
|
getProblemEvents={this.props.getProblemEvents}
|
||||||
getProblemAlerts={this.props.getProblemAlerts}
|
getProblemAlerts={this.props.getProblemAlerts}
|
||||||
onProblemAck={this.handleProblemAck}
|
onProblemAck={this.handleProblemAck}
|
||||||
|
|||||||
@@ -395,6 +395,7 @@ export class TriggerPanelCtrl extends MetricsPanelCtrl {
|
|||||||
loading,
|
loading,
|
||||||
pageSize,
|
pageSize,
|
||||||
fontSize: fontSizeProp,
|
fontSize: fontSizeProp,
|
||||||
|
panelId: ctrl.panel.id,
|
||||||
getProblemEvents: ctrl.getProblemEvents.bind(ctrl),
|
getProblemEvents: ctrl.getProblemEvents.bind(ctrl),
|
||||||
getProblemAlerts: ctrl.getProblemAlerts.bind(ctrl),
|
getProblemAlerts: ctrl.getProblemAlerts.bind(ctrl),
|
||||||
onPageSizeChange: ctrl.handlePageSizeChange.bind(ctrl),
|
onPageSizeChange: ctrl.handlePageSizeChange.bind(ctrl),
|
||||||
|
|||||||
@@ -32,3 +32,73 @@ export const getNextRefIdChar = (queries: DataQuery[]): string => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type UrlQueryMap = Record<string, any>;
|
||||||
|
|
||||||
|
export function renderUrl(path: string, query: UrlQueryMap | undefined): string {
|
||||||
|
if (query && Object.keys(query).length > 0) {
|
||||||
|
path += '?' + toUrlParams(query);
|
||||||
|
}
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
function encodeURIComponentAsAngularJS(val: string, pctEncodeSpaces?: boolean) {
|
||||||
|
return encodeURIComponent(val)
|
||||||
|
.replace(/%40/gi, '@')
|
||||||
|
.replace(/%3A/gi, ':')
|
||||||
|
.replace(/%24/g, '$')
|
||||||
|
.replace(/%2C/gi, ',')
|
||||||
|
.replace(/%3B/gi, ';')
|
||||||
|
.replace(/%20/g, pctEncodeSpaces ? '%20' : '+');
|
||||||
|
}
|
||||||
|
|
||||||
|
function toUrlParams(a: any) {
|
||||||
|
const s: any[] = [];
|
||||||
|
const rbracket = /\[\]$/;
|
||||||
|
|
||||||
|
const isArray = (obj: any) => {
|
||||||
|
return Object.prototype.toString.call(obj) === '[object Array]';
|
||||||
|
};
|
||||||
|
|
||||||
|
const add = (k: string, v: any) => {
|
||||||
|
v = typeof v === 'function' ? v() : v === null ? '' : v === undefined ? '' : v;
|
||||||
|
if (typeof v !== 'boolean') {
|
||||||
|
s[s.length] = encodeURIComponentAsAngularJS(k, true) + '=' + encodeURIComponentAsAngularJS(v, true);
|
||||||
|
} else {
|
||||||
|
s[s.length] = encodeURIComponentAsAngularJS(k, true);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const buildParams = (prefix: string, obj: any) => {
|
||||||
|
let i, len, key;
|
||||||
|
|
||||||
|
if (prefix) {
|
||||||
|
if (isArray(obj)) {
|
||||||
|
for (i = 0, len = obj.length; i < len; i++) {
|
||||||
|
if (rbracket.test(prefix)) {
|
||||||
|
add(prefix, obj[i]);
|
||||||
|
} else {
|
||||||
|
buildParams(prefix, obj[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (obj && String(obj) === '[object Object]') {
|
||||||
|
for (key in obj) {
|
||||||
|
buildParams(prefix + '[' + key + ']', obj[key]);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
add(prefix, obj);
|
||||||
|
}
|
||||||
|
} else if (isArray(obj)) {
|
||||||
|
for (i = 0, len = obj.length; i < len; i++) {
|
||||||
|
add(obj[i].name, obj[i].value);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (key in obj) {
|
||||||
|
buildParams(key, obj[key]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return s;
|
||||||
|
};
|
||||||
|
|
||||||
|
return buildParams('', a).join('&');
|
||||||
|
}
|
||||||
|
|||||||
@@ -339,6 +339,32 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.problem-explore-button {
|
||||||
|
&.btn {
|
||||||
|
width: 6rem;
|
||||||
|
height: 2rem;
|
||||||
|
|
||||||
|
background-image: none;
|
||||||
|
background-color: $action-button-color;
|
||||||
|
border: 1px solid darken($action-button-color, 6%);
|
||||||
|
border-radius: 1px;
|
||||||
|
|
||||||
|
margin-right: 1.6rem;
|
||||||
|
|
||||||
|
span {
|
||||||
|
color: $action-button-text-color;
|
||||||
|
}
|
||||||
|
|
||||||
|
i {
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: darken($action-button-color, 4%);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.problem-details-middle {
|
.problem-details-middle {
|
||||||
flex: 1 0 auto;
|
flex: 1 0 auto;
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
|
|||||||
Reference in New Issue
Block a user