Chore: modal refactor
This commit is contained in:
34
src/components/ConfigProvider/ConfigProvider.tsx
Normal file
34
src/components/ConfigProvider/ConfigProvider.tsx
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { config, GrafanaBootConfig } from '@grafana/runtime';
|
||||||
|
import { ThemeContext, getTheme } from '@grafana/ui';
|
||||||
|
import { GrafanaThemeType } from '@grafana/data';
|
||||||
|
|
||||||
|
export const ConfigContext = React.createContext<GrafanaBootConfig>(config);
|
||||||
|
export const ConfigConsumer = ConfigContext.Consumer;
|
||||||
|
|
||||||
|
export const provideConfig = (component: React.ComponentType<any>) => {
|
||||||
|
const ConfigProvider = (props: any) => (
|
||||||
|
<ConfigContext.Provider value={config}>{React.createElement(component, { ...props })}</ConfigContext.Provider>
|
||||||
|
);
|
||||||
|
|
||||||
|
return ConfigProvider;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getCurrentThemeName = () =>
|
||||||
|
config.bootData.user.lightTheme ? GrafanaThemeType.Light : GrafanaThemeType.Dark;
|
||||||
|
|
||||||
|
export const getCurrentTheme = () => getTheme(getCurrentThemeName());
|
||||||
|
|
||||||
|
export const ThemeProvider = ({ children }: { children: React.ReactNode }) => {
|
||||||
|
return (
|
||||||
|
<ConfigConsumer>
|
||||||
|
{config => {
|
||||||
|
return <ThemeContext.Provider value={getCurrentTheme()}>{children}</ThemeContext.Provider>;
|
||||||
|
}}
|
||||||
|
</ConfigConsumer>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const provideTheme = (component: React.ComponentType<any>) => {
|
||||||
|
return provideConfig((props: any) => <ThemeProvider>{React.createElement(component, { ...props })}</ThemeProvider>);
|
||||||
|
};
|
||||||
70
src/components/Modal/ModalController.tsx
Normal file
70
src/components/Modal/ModalController.tsx
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
import React, { FC } from 'react';
|
||||||
|
import ReactDOM from 'react-dom';
|
||||||
|
import { provideTheme } from '../ConfigProvider/ConfigProvider';
|
||||||
|
|
||||||
|
interface ModalWrapperProps {
|
||||||
|
showModal: <T>(component: React.ComponentType<T>, props: T) => void;
|
||||||
|
hideModal: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
type ModalWrapper<T> = FC<ModalWrapperProps>;
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
children: ModalWrapper<any>;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface State {
|
||||||
|
component: React.ComponentType<any> | null;
|
||||||
|
props: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class ModalController extends React.Component<Props, State> {
|
||||||
|
modalRoot = document.body;
|
||||||
|
modalNode = document.createElement('div');
|
||||||
|
|
||||||
|
constructor(props: Props) {
|
||||||
|
super(props);
|
||||||
|
this.state = {
|
||||||
|
component: null,
|
||||||
|
props: {},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
showModal = (component: React.ComponentType<any>, props: any) => {
|
||||||
|
this.setState({
|
||||||
|
component,
|
||||||
|
props
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
hideModal = () => {
|
||||||
|
this.modalRoot.removeChild(this.modalNode);
|
||||||
|
this.setState({
|
||||||
|
component: null,
|
||||||
|
props: {},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
renderModal() {
|
||||||
|
const { component, props } = this.state;
|
||||||
|
if (!component) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.modalRoot.appendChild(this.modalNode);
|
||||||
|
const modal = React.createElement(provideTheme(component), props);
|
||||||
|
return ReactDOM.createPortal(modal, this.modalNode);
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { children } = this.props;
|
||||||
|
const ChildrenComponent = children;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<ChildrenComponent showModal={this.showModal} hideModal={this.hideModal} />
|
||||||
|
{this.renderModal()}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
export { GFHeartIcon } from './GFHeartIcon/GFHeartIcon';
|
export { GFHeartIcon } from './GFHeartIcon/GFHeartIcon';
|
||||||
export { FAIcon } from './FAIcon/FAIcon';
|
export { FAIcon } from './FAIcon/FAIcon';
|
||||||
export { Tooltip } from './Tooltip/Tooltip';
|
export { Tooltip } from './Tooltip/Tooltip';
|
||||||
|
export { ModalController } from './Modal/ModalController';
|
||||||
|
|||||||
@@ -1,25 +1,24 @@
|
|||||||
import React, { PureComponent } from 'react';
|
import React, { PureComponent } from 'react';
|
||||||
import ReactDOM from 'react-dom';
|
import { cx, css } from 'emotion';
|
||||||
import classNames from 'classnames';
|
|
||||||
import { ZBX_ACK_ACTION_ADD_MESSAGE, ZBX_ACK_ACTION_ACK, ZBX_ACK_ACTION_CHANGE_SEVERITY, ZBX_ACK_ACTION_CLOSE } from '../../datasource-zabbix/constants';
|
import { ZBX_ACK_ACTION_ADD_MESSAGE, ZBX_ACK_ACTION_ACK, ZBX_ACK_ACTION_CHANGE_SEVERITY, ZBX_ACK_ACTION_CLOSE } from '../../datasource-zabbix/constants';
|
||||||
import { Button, Input, VerticalGroup, Spinner } from '@grafana/ui';
|
import { Button, VerticalGroup, Spinner, Modal, Input, Forms, stylesFactory, withTheme, Themeable } from '@grafana/ui';
|
||||||
import { FAIcon } from '../../components';
|
import { FAIcon } from '../../components';
|
||||||
|
|
||||||
import * as grafanaUi from '@grafana/ui';
|
import * as grafanaUi from '@grafana/ui';
|
||||||
const Checkbox: any = grafanaUi.Forms?.Checkbox || (grafanaUi as any).Checkbox;
|
import { GrafanaTheme } from '@grafana/data';
|
||||||
const RadioButtonGroup: any = grafanaUi.Forms?.RadioButtonGroup || (grafanaUi as any).RadioButtonGroup;
|
const Checkbox: any = Forms?.Checkbox || (grafanaUi as any).Checkbox;
|
||||||
|
const RadioButtonGroup: any = Forms?.RadioButtonGroup || (grafanaUi as any).RadioButtonGroup;
|
||||||
|
|
||||||
const KEYBOARD_ENTER_KEY = 13;
|
const KEYBOARD_ENTER_KEY = 13;
|
||||||
const KEYBOARD_ESCAPE_KEY = 27;
|
const KEYBOARD_ESCAPE_KEY = 27;
|
||||||
|
|
||||||
interface Props {
|
interface Props extends Themeable {
|
||||||
canAck?: boolean;
|
canAck?: boolean;
|
||||||
canClose?: boolean;
|
canClose?: boolean;
|
||||||
isOpen?: boolean;
|
|
||||||
severity?: number;
|
severity?: number;
|
||||||
withBackdrop?: boolean;
|
withBackdrop?: boolean;
|
||||||
onSubmit: (data?: AckProblemData) => Promise<any> | any;
|
onSubmit: (data?: AckProblemData) => Promise<any> | any;
|
||||||
onClose?: () => void;
|
onDismiss?: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface State {
|
interface State {
|
||||||
@@ -50,9 +49,7 @@ const severityOptions = [
|
|||||||
{value: 5, label: 'Disaster'}
|
{value: 5, label: 'Disaster'}
|
||||||
];
|
];
|
||||||
|
|
||||||
export class AckModal extends PureComponent<Props, State> {
|
export class AckModalUnthemed extends PureComponent<Props, State> {
|
||||||
modalContainer: HTMLElement;
|
|
||||||
|
|
||||||
static defaultProps: Partial<Props> = {
|
static defaultProps: Partial<Props> = {
|
||||||
withBackdrop: true,
|
withBackdrop: true,
|
||||||
};
|
};
|
||||||
@@ -70,8 +67,6 @@ export class AckModal extends PureComponent<Props, State> {
|
|||||||
selectedSeverity: props.severity || 0,
|
selectedSeverity: props.severity || 0,
|
||||||
loading: false,
|
loading: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
this.modalContainer = document.body;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
|
handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
@@ -108,7 +103,7 @@ export class AckModal extends PureComponent<Props, State> {
|
|||||||
|
|
||||||
dismiss = () => {
|
dismiss = () => {
|
||||||
this.setState({ value: '', error: false, errorMessage: '', ackError: '', loading: false });
|
this.setState({ value: '', error: false, errorMessage: '', ackError: '', loading: false });
|
||||||
this.props.onClose();
|
this.props.onDismiss();
|
||||||
}
|
}
|
||||||
|
|
||||||
submit = () => {
|
submit = () => {
|
||||||
@@ -141,7 +136,7 @@ export class AckModal extends PureComponent<Props, State> {
|
|||||||
}
|
}
|
||||||
ackData.action = action;
|
ackData.action = action;
|
||||||
|
|
||||||
this.props.onSubmit(ackData).then((response) => {
|
this.props.onSubmit(ackData).then(() => {
|
||||||
this.dismiss();
|
this.dismiss();
|
||||||
}).catch(err => {
|
}).catch(err => {
|
||||||
this.setState({
|
this.setState({
|
||||||
@@ -151,87 +146,131 @@ export class AckModal extends PureComponent<Props, State> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
renderActions() {
|
||||||
const { canClose, canAck } = this.props;
|
const { canClose } = this.props;
|
||||||
if (!this.props.isOpen || !this.modalContainer) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
const inputClass = classNames({ 'zbx-ack-error': this.state.error });
|
const actions = [
|
||||||
|
<Checkbox key="ack" label="Acknowledge" value={this.state.acknowledge} onChange={this.onAcknowledgeToggle} />,
|
||||||
const modalNode = (
|
<Checkbox
|
||||||
<div className="modal modal--narrow zbx-ack-modal" key="modal">
|
key="change-severity"
|
||||||
<div className="modal-body">
|
label="Change severity"
|
||||||
<div className="modal-header">
|
description=""
|
||||||
<h2 className="modal-header-title" style={{ display: 'flex' }}>
|
value={this.state.changeSeverity}
|
||||||
{this.state.loading ? <Spinner size={18} /> : <FAIcon icon="reply-all" />}
|
onChange={this.onChangeSeverityToggle}
|
||||||
<span className="p-l-1">Acknowledge Problem</span>
|
/>,
|
||||||
</h2>
|
this.state.changeSeverity &&
|
||||||
|
<RadioButtonGroup
|
||||||
<a className="modal-header-close" onClick={this.dismiss}>
|
key="severity"
|
||||||
<FAIcon icon="remove" />
|
size="sm"
|
||||||
</a>
|
options={severityOptions}
|
||||||
</div>
|
value={this.state.selectedSeverity}
|
||||||
<div className="modal-content">
|
onChange={this.onChangeSelectedSeverity}
|
||||||
<div className="gf-form">
|
/>,
|
||||||
<label className="gf-form-hint">
|
canClose &&
|
||||||
<Input className={inputClass}
|
<Checkbox key="close" label="Close problem" disabled={!canClose} value={this.state.closeProblem} onChange={this.onCloseProblemToggle} />,
|
||||||
type="text"
|
|
||||||
name="message"
|
|
||||||
placeholder="Message"
|
|
||||||
maxLength={64}
|
|
||||||
autoComplete="off"
|
|
||||||
autoFocus={true}
|
|
||||||
value={this.state.value}
|
|
||||||
onChange={this.handleChange}
|
|
||||||
onKeyUp={this.handleKeyUp}>
|
|
||||||
</Input>
|
|
||||||
<small className="gf-form-hint-text muted">Press Enter to submit</small>
|
|
||||||
{this.state.error &&
|
|
||||||
<small className="gf-form-hint-text muted ack-error-message">{this.state.errorMessage}</small>
|
|
||||||
}
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="gf-form">
|
|
||||||
<VerticalGroup>
|
|
||||||
<Checkbox label="Acknowledge" value={this.state.acknowledge} onChange={this.onAcknowledgeToggle} />
|
|
||||||
<Checkbox label="Change severity" description="" value={this.state.changeSeverity} onChange={this.onChangeSeverityToggle} />
|
|
||||||
{this.state.changeSeverity &&
|
|
||||||
<RadioButtonGroup
|
|
||||||
size="sm"
|
|
||||||
options={severityOptions}
|
|
||||||
value={this.state.selectedSeverity}
|
|
||||||
onChange={this.onChangeSelectedSeverity}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
{canClose &&
|
|
||||||
<Checkbox label="Close problem" disabled={!canClose} value={this.state.closeProblem} onChange={this.onCloseProblemToggle} />
|
|
||||||
}
|
|
||||||
</VerticalGroup>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{this.state.ackError &&
|
|
||||||
<div className="gf-form ack-request-error">
|
|
||||||
<span className="ack-error-message">{this.state.ackError}</span>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
|
|
||||||
<div className="gf-form-button-row text-center">
|
|
||||||
<Button variant="primary" onClick={this.submit}>Update</Button>
|
|
||||||
<Button variant="secondary" onClick={this.dismiss}>Cancel</Button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
|
|
||||||
const modalNodeWithBackdrop = [
|
|
||||||
modalNode,
|
|
||||||
<div className="modal-backdrop in" key="modal-backdrop" onClick={this.handleBackdropClick}></div>
|
|
||||||
];
|
];
|
||||||
|
|
||||||
const modal = this.props.withBackdrop ? modalNodeWithBackdrop : modalNode;
|
// <VerticalGroup /> doesn't handle empty elements properly, so don't return it
|
||||||
return ReactDOM.createPortal(modal, this.modalContainer);
|
return actions.filter(e => e);
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { theme } = this.props;
|
||||||
|
|
||||||
|
const styles = getStyles(theme);
|
||||||
|
const modalClass = cx(styles.modal);
|
||||||
|
const modalTitleClass = cx(styles.modalHeaderTitle);
|
||||||
|
const inputGroupClass = cx('gf-form', styles.inputGroup);
|
||||||
|
const inputClass = cx(this.state.error && styles.input);
|
||||||
|
const inputHintClass = cx('gf-form-hint-text', styles.inputHint);
|
||||||
|
const inputErrorClass = cx('gf-form-hint-text', styles.inputError);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Modal
|
||||||
|
isOpen={true}
|
||||||
|
onDismiss={this.dismiss}
|
||||||
|
className={modalClass}
|
||||||
|
title={
|
||||||
|
<div className={modalTitleClass}>
|
||||||
|
{this.state.loading ? <Spinner size={18} /> : <FAIcon icon="reply-all" />}
|
||||||
|
<span className="p-l-1">Acknowledge Problem</span>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<div className={inputGroupClass}>
|
||||||
|
<label className="gf-form-hint">
|
||||||
|
<Input className={inputClass}
|
||||||
|
type="text"
|
||||||
|
name="message"
|
||||||
|
placeholder="Message"
|
||||||
|
maxLength={64}
|
||||||
|
autoComplete="off"
|
||||||
|
autoFocus={true}
|
||||||
|
value={this.state.value}
|
||||||
|
onChange={this.handleChange}
|
||||||
|
onKeyUp={this.handleKeyUp}>
|
||||||
|
</Input>
|
||||||
|
<small className={inputHintClass}>Press Enter to submit</small>
|
||||||
|
{this.state.error &&
|
||||||
|
<small className={inputErrorClass}>{this.state.errorMessage}</small>
|
||||||
|
}
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="gf-form">
|
||||||
|
<VerticalGroup>
|
||||||
|
{this.renderActions()}
|
||||||
|
</VerticalGroup>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{this.state.ackError &&
|
||||||
|
<div className="gf-form ack-request-error">
|
||||||
|
<span className={styles.ackError}>{this.state.ackError}</span>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
<div className="gf-form-button-row text-center">
|
||||||
|
<Button variant="primary" onClick={this.submit}>Update</Button>
|
||||||
|
<Button variant="secondary" onClick={this.dismiss}>Cancel</Button>
|
||||||
|
</div>
|
||||||
|
</Modal>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const getStyles = stylesFactory((theme: GrafanaTheme) => {
|
||||||
|
return {
|
||||||
|
modal: css`
|
||||||
|
width: 500px;
|
||||||
|
`,
|
||||||
|
modalHeaderTitle: css`
|
||||||
|
font-size: ${theme.typography.heading.h3};
|
||||||
|
padding-top: ${theme.spacing.sm};
|
||||||
|
margin: 0 ${theme.spacing.md};
|
||||||
|
display: flex;
|
||||||
|
`,
|
||||||
|
inputGroup: css`
|
||||||
|
margin-bottom: 16px;
|
||||||
|
`,
|
||||||
|
input: css`
|
||||||
|
border-color: ${theme.colors.red};
|
||||||
|
border-radius: 2px;
|
||||||
|
outline-offset: 2px;
|
||||||
|
box-shadow: 0 0 0 2px ${theme.colors.pageBg}, 0 0 0px 4px ${theme.colors.red};
|
||||||
|
`,
|
||||||
|
inputHint: css`
|
||||||
|
display: inherit;
|
||||||
|
float: right;
|
||||||
|
color: ${theme.colors.textWeak};
|
||||||
|
`,
|
||||||
|
inputError: css`
|
||||||
|
float: left;
|
||||||
|
color: ${theme.colors.red};
|
||||||
|
`,
|
||||||
|
ackError: css`
|
||||||
|
color: ${theme.colors.red};
|
||||||
|
`,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
export const AckModal = withTheme(AckModalUnthemed);
|
||||||
|
|||||||
@@ -6,10 +6,10 @@ import { isNewProblem, formatLastChange } from '../../utils';
|
|||||||
import { ProblemsPanelOptions, TriggerSeverity } from '../../types';
|
import { ProblemsPanelOptions, TriggerSeverity } from '../../types';
|
||||||
import { AckProblemData, AckModal } from '../AckModal';
|
import { AckProblemData, AckModal } from '../AckModal';
|
||||||
import EventTag from '../EventTag';
|
import EventTag from '../EventTag';
|
||||||
import Tooltip from '../../../components/Tooltip/Tooltip';
|
|
||||||
import AlertAcknowledges from './AlertAcknowledges';
|
import AlertAcknowledges from './AlertAcknowledges';
|
||||||
import AlertIcon from './AlertIcon';
|
import AlertIcon from './AlertIcon';
|
||||||
import { ProblemDTO, ZBXTag } from '../../../datasource-zabbix/types';
|
import { ProblemDTO, ZBXTag } from '../../../datasource-zabbix/types';
|
||||||
|
import { ModalController, Tooltip } from '../../../components';
|
||||||
|
|
||||||
interface AlertCardProps {
|
interface AlertCardProps {
|
||||||
problem: ProblemDTO;
|
problem: ProblemDTO;
|
||||||
@@ -18,15 +18,7 @@ interface AlertCardProps {
|
|||||||
onProblemAck?: (problem: ProblemDTO, data: AckProblemData) => Promise<any> | any;
|
onProblemAck?: (problem: ProblemDTO, data: AckProblemData) => Promise<any> | any;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface AlertCardState {
|
export default class AlertCard extends PureComponent<AlertCardProps> {
|
||||||
showAckDialog: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default class AlertCard extends PureComponent<AlertCardProps, AlertCardState> {
|
|
||||||
constructor(props) {
|
|
||||||
super(props);
|
|
||||||
this.state = { showAckDialog: false };
|
|
||||||
}
|
|
||||||
|
|
||||||
handleTagClick = (tag: ZBXTag, ctrlKey?: boolean, shiftKey?: boolean) => {
|
handleTagClick = (tag: ZBXTag, ctrlKey?: boolean, shiftKey?: boolean) => {
|
||||||
if (this.props.onTagClick) {
|
if (this.props.onTagClick) {
|
||||||
@@ -36,23 +28,7 @@ export default class AlertCard extends PureComponent<AlertCardProps, AlertCardSt
|
|||||||
|
|
||||||
ackProblem = (data: AckProblemData) => {
|
ackProblem = (data: AckProblemData) => {
|
||||||
const problem = this.props.problem;
|
const problem = this.props.problem;
|
||||||
return this.props.onProblemAck(problem, data).then(result => {
|
return this.props.onProblemAck(problem, data);
|
||||||
this.closeAckDialog();
|
|
||||||
}).catch(err => {
|
|
||||||
console.log(err);
|
|
||||||
this.closeAckDialog();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
showAckDialog = () => {
|
|
||||||
const problem = this.props.problem;
|
|
||||||
if (problem.showAckButton) {
|
|
||||||
this.setState({ showAckDialog: true });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
closeAckDialog = () => {
|
|
||||||
this.setState({ showAckDialog: false });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
@@ -61,10 +37,11 @@ export default class AlertCard extends PureComponent<AlertCardProps, AlertCardSt
|
|||||||
const cardClass = classNames('alert-rule-item', 'zbx-trigger-card', { 'zbx-trigger-highlighted': panelOptions.highlightBackground });
|
const cardClass = classNames('alert-rule-item', 'zbx-trigger-card', { 'zbx-trigger-highlighted': panelOptions.highlightBackground });
|
||||||
const descriptionClass = classNames('alert-rule-item__text', { 'zbx-description--newline': panelOptions.descriptionAtNewLine });
|
const descriptionClass = classNames('alert-rule-item__text', { 'zbx-description--newline': panelOptions.descriptionAtNewLine });
|
||||||
|
|
||||||
|
const problemSeverity = Number(problem.severity);
|
||||||
let severityDesc: TriggerSeverity;
|
let severityDesc: TriggerSeverity;
|
||||||
severityDesc = _.find(panelOptions.triggerSeverity, s => s.priority === Number(problem.severity));
|
severityDesc = _.find(panelOptions.triggerSeverity, s => s.priority === problemSeverity);
|
||||||
if (problem.severity) {
|
if (problem.severity) {
|
||||||
severityDesc = _.find(panelOptions.triggerSeverity, s => s.priority === Number(problem.severity));
|
severityDesc = _.find(panelOptions.triggerSeverity, s => s.priority === problemSeverity);
|
||||||
}
|
}
|
||||||
|
|
||||||
const lastchange = formatLastChange(problem.timestamp, panelOptions.customLastChangeFormat && panelOptions.lastChangeFormat);
|
const lastchange = formatLastChange(problem.timestamp, panelOptions.customLastChangeFormat && panelOptions.lastChangeFormat);
|
||||||
@@ -161,14 +138,24 @@ export default class AlertCard extends PureComponent<AlertCardProps, AlertCardSt
|
|||||||
</Tooltip>
|
</Tooltip>
|
||||||
)}
|
)}
|
||||||
{problem.eventid && (
|
{problem.eventid && (
|
||||||
<AlertAcknowledgesButton problem={problem} onClick={this.showAckDialog} />
|
<ModalController>
|
||||||
|
{({ showModal, hideModal }) => (
|
||||||
|
<AlertAcknowledgesButton
|
||||||
|
problem={problem}
|
||||||
|
onClick={() => {
|
||||||
|
showModal(AckModal, {
|
||||||
|
canClose: problem.manual_close === '1',
|
||||||
|
severity: problemSeverity,
|
||||||
|
onSubmit: this.ackProblem,
|
||||||
|
onDismiss: hideModal,
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</ModalController>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<AckModal
|
|
||||||
isOpen={this.state.showAckDialog}
|
|
||||||
onSubmit={this.ackProblem}
|
|
||||||
onClose={this.closeAckDialog} />
|
|
||||||
</li>
|
</li>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import EventTag from '../EventTag';
|
|||||||
import ProblemStatusBar from './ProblemStatusBar';
|
import ProblemStatusBar from './ProblemStatusBar';
|
||||||
import AcknowledgesList from './AcknowledgesList';
|
import AcknowledgesList from './AcknowledgesList';
|
||||||
import ProblemTimeline from './ProblemTimeline';
|
import ProblemTimeline from './ProblemTimeline';
|
||||||
import { FAIcon, Tooltip } from '../../../components';
|
import { FAIcon, Tooltip, ModalController } from '../../../components';
|
||||||
import { renderUrl } from '../../utils';
|
import { renderUrl } from '../../utils';
|
||||||
import { getLocationSrv } from '@grafana/runtime';
|
import { getLocationSrv } from '@grafana/runtime';
|
||||||
|
|
||||||
@@ -28,17 +28,15 @@ interface ProblemDetailsState {
|
|||||||
events: ZBXEvent[];
|
events: ZBXEvent[];
|
||||||
alerts: ZBXAlert[];
|
alerts: ZBXAlert[];
|
||||||
show: boolean;
|
show: boolean;
|
||||||
showAckDialog: boolean;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default class ProblemDetails extends PureComponent<ProblemDetailsProps, ProblemDetailsState> {
|
export class ProblemDetails extends PureComponent<ProblemDetailsProps, ProblemDetailsState> {
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props);
|
super(props);
|
||||||
this.state = {
|
this.state = {
|
||||||
events: [],
|
events: [],
|
||||||
alerts: [],
|
alerts: [],
|
||||||
show: false,
|
show: false,
|
||||||
showAckDialog: false,
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -76,17 +74,7 @@ export default class ProblemDetails extends PureComponent<ProblemDetailsProps, P
|
|||||||
|
|
||||||
ackProblem = (data: AckProblemData) => {
|
ackProblem = (data: AckProblemData) => {
|
||||||
const problem = this.props.original as ProblemDTO;
|
const problem = this.props.original as ProblemDTO;
|
||||||
return this.props.onProblemAck(problem, data).then(result => {
|
return this.props.onProblemAck(problem, data);
|
||||||
this.closeAckDialog();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
showAckDialog = () => {
|
|
||||||
this.setState({ showAckDialog: true });
|
|
||||||
}
|
|
||||||
|
|
||||||
closeAckDialog = () => {
|
|
||||||
this.setState({ showAckDialog: false });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
openInExplore = () => {
|
openInExplore = () => {
|
||||||
@@ -149,10 +137,23 @@ export default class ProblemDetails extends PureComponent<ProblemDetailsProps, P
|
|||||||
<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">
|
||||||
<ProblemActionButton className="navbar-button navbar-button--settings"
|
<ModalController>
|
||||||
icon="reply-all"
|
{({ showModal, hideModal }) => (
|
||||||
tooltip="Acknowledge problem"
|
<ProblemActionButton
|
||||||
onClick={this.showAckDialog} />
|
className="navbar-button navbar-button--settings"
|
||||||
|
icon="reply-all"
|
||||||
|
tooltip="Acknowledge problem"
|
||||||
|
onClick={() => {
|
||||||
|
showModal(AckModal, {
|
||||||
|
canClose: problem.manual_close === '1',
|
||||||
|
severity: problemSeverity,
|
||||||
|
onSubmit: this.ackProblem,
|
||||||
|
onDismiss: hideModal,
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</ModalController>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
@@ -208,12 +209,6 @@ export default class ProblemDetails extends PureComponent<ProblemDetailsProps, P
|
|||||||
{problem.groups && <ProblemGroups groups={problem.groups} className="problem-details-right-item" />}
|
{problem.groups && <ProblemGroups groups={problem.groups} className="problem-details-right-item" />}
|
||||||
{problem.hosts && <ProblemHosts hosts={problem.hosts} className="problem-details-right-item" />}
|
{problem.hosts && <ProblemHosts hosts={problem.hosts} className="problem-details-right-item" />}
|
||||||
</div>
|
</div>
|
||||||
<AckModal
|
|
||||||
canClose={problem.manual_close === '1'}
|
|
||||||
severity={problemSeverity}
|
|
||||||
isOpen={this.state.showAckDialog}
|
|
||||||
onSubmit={this.ackProblem}
|
|
||||||
onClose={this.closeAckDialog} />
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import _ from 'lodash';
|
|||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
import { isNewProblem } from '../../utils';
|
import { isNewProblem } from '../../utils';
|
||||||
import EventTag from '../EventTag';
|
import EventTag from '../EventTag';
|
||||||
import ProblemDetails from './ProblemDetails';
|
import { ProblemDetails } from './ProblemDetails';
|
||||||
import { AckProblemData } from '../AckModal';
|
import { AckProblemData } from '../AckModal';
|
||||||
import { GFHeartIcon, FAIcon } from '../../../components';
|
import { GFHeartIcon, FAIcon } from '../../../components';
|
||||||
import { ProblemsPanelOptions, GFTimeRange, RTCell, TriggerSeverity, RTResized } from '../../types';
|
import { ProblemsPanelOptions, GFTimeRange, RTCell, TriggerSeverity, RTResized } from '../../types';
|
||||||
|
|||||||
Reference in New Issue
Block a user