Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
N
nexpie-grafana-theme
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Registry
Registry
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Kornkitt Poolsup
nexpie-grafana-theme
Commits
2de57f09
Unverified
Commit
2de57f09
authored
Jan 04, 2019
by
Torkel Ödegaard
Committed by
GitHub
Jan 04, 2019
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #14707 from grafana/14388/alert-tab-ux-update
Alert tab ux update
parents
bb15eb30
e0c28ba7
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
10 changed files
with
229 additions
and
41 deletions
+229
-41
public/app/core/components/EmptyListCTA/EmptyListCTA.tsx
+8
-6
public/app/features/alerting/AlertTabCtrl.ts
+5
-3
public/app/features/alerting/partials/alert_tab.html
+0
-0
public/app/features/dashboard/dashgrid/AlertTab.tsx
+80
-11
public/app/features/dashboard/dashgrid/EditorTabBody.tsx
+13
-9
public/app/features/dashboard/dashgrid/PanelEditor.tsx
+1
-1
public/app/features/dashboard/dashgrid/QueriesTab.tsx
+8
-8
public/app/features/dashboard/dashgrid/StateHistory.tsx
+110
-0
public/app/features/dashboard/dashgrid/VisualizationTab.tsx
+3
-3
public/sass/pages/_alerting.scss
+1
-0
No files found.
public/app/core/components/EmptyListCTA/EmptyListCTA.tsx
View file @
2de57f09
...
...
@@ -24,12 +24,14 @@ class EmptyListCTA extends Component<Props, any> {
<
i
className=
{
buttonIcon
}
/>
{
buttonTitle
}
</
a
>
<
div
className=
"empty-list-cta__pro-tip"
>
<
i
className=
"fa fa-rocket"
/>
ProTip:
{
proTip
}
<
a
className=
"text-link empty-list-cta__pro-tip-link"
href=
{
proTipLink
}
target=
{
proTipTarget
}
>
{
proTipLinkTitle
}
</
a
>
</
div
>
{
proTip
&&
(
<
div
className=
"empty-list-cta__pro-tip"
>
<
i
className=
"fa fa-rocket"
/>
ProTip:
{
proTip
}
<
a
className=
"text-link empty-list-cta__pro-tip-link"
href=
{
proTipLink
}
target=
{
proTipTarget
}
>
{
proTipLinkTitle
}
</
a
>
</
div
>
)
}
</
div
>
);
}
...
...
public/app/features/alerting/AlertTabCtrl.ts
View file @
2de57f09
...
...
@@ -45,6 +45,7 @@ export class AlertTabCtrl {
this
.
noDataModes
=
alertDef
.
noDataModes
;
this
.
executionErrorModes
=
alertDef
.
executionErrorModes
;
this
.
appSubUrl
=
config
.
appSubUrl
;
this
.
panelCtrl
.
_enableAlert
=
this
.
enable
;
}
$onInit
()
{
...
...
@@ -114,7 +115,7 @@ export class AlertTabCtrl {
}
getNotifications
()
{
return
Promise
.
resolve
(
return
this
.
$q
.
when
(
this
.
notifications
.
map
(
item
=>
{
return
this
.
uiSegmentSrv
.
newSegment
(
item
.
name
);
})
...
...
@@ -147,6 +148,7 @@ export class AlertTabCtrl {
// reset plus button
this
.
addNotificationSegment
.
value
=
this
.
uiSegmentSrv
.
newPlusButton
().
value
;
this
.
addNotificationSegment
.
html
=
this
.
uiSegmentSrv
.
newPlusButton
().
html
;
this
.
addNotificationSegment
.
fake
=
true
;
}
removeNotification
(
index
)
{
...
...
@@ -353,11 +355,11 @@ export class AlertTabCtrl {
});
}
enable
()
{
enable
=
()
=>
{
this
.
panel
.
alert
=
{};
this
.
initModel
();
this
.
panel
.
alert
.
for
=
'5m'
;
//default value for new alerts. for existing alerts we use 0m to avoid breaking changes
}
}
;
evaluatorParamsChanged
()
{
ThresholdMapper
.
alertToGraphThresholds
(
this
.
panel
);
...
...
public/app/features/alerting/partials/alert_tab.html
View file @
2de57f09
This diff is collapsed.
Click to expand it.
public/app/features/dashboard/dashgrid/AlertTab.tsx
View file @
2de57f09
// Libraries
import
React
,
{
PureComponent
}
from
'react'
;
import
{
getAngularLoader
,
AngularComponent
}
from
'app/core/services/AngularLoader'
;
import
{
EditorTabBody
}
from
'./EditorTabBody'
;
// Services & Utils
import
{
AngularComponent
,
getAngularLoader
}
from
'app/core/services/AngularLoader'
;
import
appEvents
from
'app/core/app_events'
;
// Components
import
{
EditorTabBody
,
EditorToolbarView
}
from
'./EditorTabBody'
;
import
EmptyListCTA
from
'app/core/components/EmptyListCTA/EmptyListCTA'
;
import
StateHistory
from
'./StateHistory'
;
import
'app/features/alerting/AlertTabCtrl'
;
// Types
import
{
DashboardModel
}
from
'../dashboard_model'
;
import
{
PanelModel
}
from
'../panel_model'
;
interface
Props
{
angularPanel
?:
AngularComponent
;
dashboard
:
DashboardModel
;
panel
:
PanelModel
;
}
export
class
AlertTab
extends
PureComponent
<
Props
>
{
element
:
any
;
component
:
AngularComponent
;
constructor
(
props
)
{
super
(
props
);
}
panelCtrl
:
any
;
componentDidMount
()
{
if
(
this
.
shouldLoadAlertTab
())
{
...
...
@@ -29,7 +39,7 @@ export class AlertTab extends PureComponent<Props> {
}
shouldLoadAlertTab
()
{
return
this
.
props
.
angularPanel
&&
this
.
element
;
return
this
.
props
.
angularPanel
&&
this
.
element
&&
!
this
.
component
;
}
componentWillUnmount
()
{
...
...
@@ -51,21 +61,80 @@ export class AlertTab extends PureComponent<Props> {
return
;
}
const
panelCtrl
=
scope
.
$$childHead
.
ctrl
;
this
.
panelCtrl
=
scope
.
$$childHead
.
ctrl
;
const
loader
=
getAngularLoader
();
const
template
=
'<alert-tab />'
;
const
scopeProps
=
{
ctrl
:
panelCtrl
,
ctrl
:
this
.
panelCtrl
,
};
this
.
component
=
loader
.
load
(
this
.
element
,
scopeProps
,
template
);
}
stateHistory
=
():
EditorToolbarView
=>
{
return
{
title
:
'State history'
,
render
:
()
=>
{
return
(
<
StateHistory
dashboard=
{
this
.
props
.
dashboard
}
panelId=
{
this
.
props
.
panel
.
id
}
onRefresh=
{
this
.
panelCtrl
.
refresh
}
/>
);
},
};
};
deleteAlert
=
():
EditorToolbarView
=>
{
const
{
panel
}
=
this
.
props
;
return
{
title
:
'Delete'
,
btnType
:
'danger'
,
onClick
:
()
=>
{
appEvents
.
emit
(
'confirm-modal'
,
{
title
:
'Delete Alert'
,
text
:
'Are you sure you want to delete this alert rule?'
,
text2
:
'You need to save dashboard for the delete to take effect'
,
icon
:
'fa-trash'
,
yesText
:
'Delete'
,
onConfirm
:
()
=>
{
delete
panel
.
alert
;
panel
.
thresholds
=
[];
this
.
panelCtrl
.
alertState
=
null
;
this
.
panelCtrl
.
render
();
this
.
forceUpdate
();
},
});
},
};
};
onAddAlert
=
()
=>
{
this
.
panelCtrl
.
_enableAlert
();
this
.
component
.
digest
();
this
.
forceUpdate
();
};
render
()
{
const
{
alert
}
=
this
.
props
.
panel
;
const
toolbarItems
=
alert
?
[
this
.
stateHistory
(),
this
.
deleteAlert
()]
:
[];
const
model
=
{
title
:
'Panel has no alert rule defined'
,
icon
:
'icon-gf icon-gf-alert'
,
onClick
:
this
.
onAddAlert
,
buttonTitle
:
'Create Alert'
,
};
return
(
<
EditorTabBody
heading=
"Alert"
toolbarItems=
{
[]
}
>
<
div
ref=
{
element
=>
(
this
.
element
=
element
)
}
/>
<
EditorTabBody
heading=
"Alert"
toolbarItems=
{
toolbarItems
}
>
<>
<
div
ref=
{
element
=>
(
this
.
element
=
element
)
}
/>
{
!
alert
&&
<
EmptyListCTA
model=
{
model
}
/>
}
</>
</
EditorTabBody
>
);
}
...
...
public/app/features/dashboard/dashgrid/EditorTabBody.tsx
View file @
2de57f09
...
...
@@ -10,21 +10,22 @@ interface Props {
children
:
JSX
.
Element
;
heading
:
string
;
renderToolbar
?:
()
=>
JSX
.
Element
;
toolbarItems
?:
EditorTool
B
arView
[];
toolbarItems
?:
EditorTool
b
arView
[];
}
export
interface
EditorTool
B
arView
{
export
interface
EditorTool
b
arView
{
title
?:
string
;
heading
?:
string
;
imgSrc
?:
string
;
icon
?:
string
;
disabled
?:
boolean
;
onClick
?:
()
=>
void
;
render
:
(
closeFunction
?:
any
)
=>
JSX
.
Element
|
JSX
.
Element
[];
render
?:
()
=>
JSX
.
Element
;
action
?:
()
=>
void
;
btnType
?:
'danger'
;
}
interface
State
{
openView
?:
EditorTool
B
arView
;
openView
?:
EditorTool
b
arView
;
isOpen
:
boolean
;
fadeIn
:
boolean
;
}
...
...
@@ -48,7 +49,7 @@ export class EditorTabBody extends PureComponent<Props, State> {
this
.
setState
({
fadeIn
:
true
});
}
onToggleToolBarView
=
(
item
:
EditorTool
B
arView
)
=>
{
onToggleToolBarView
=
(
item
:
EditorTool
b
arView
)
=>
{
this
.
setState
({
openView
:
item
,
isOpen
:
!
this
.
state
.
isOpen
,
...
...
@@ -74,12 +75,15 @@ export class EditorTabBody extends PureComponent<Props, State> {
return
state
;
}
renderButton
(
view
:
EditorTool
B
arView
)
{
renderButton
(
view
:
EditorTool
b
arView
)
{
const
onClick
=
()
=>
{
if
(
view
.
onClick
)
{
view
.
onClick
();
}
this
.
onToggleToolBarView
(
view
);
if
(
view
.
render
)
{
this
.
onToggleToolBarView
(
view
);
}
};
return
(
...
...
@@ -91,7 +95,7 @@ export class EditorTabBody extends PureComponent<Props, State> {
);
}
renderOpenView
(
view
:
EditorTool
B
arView
)
{
renderOpenView
(
view
:
EditorTool
b
arView
)
{
return
(
<
PanelOptionSection
title=
{
view
.
title
||
view
.
heading
}
onClose=
{
this
.
onCloseOpenView
}
>
{
view
.
render
()
}
...
...
public/app/features/dashboard/dashgrid/PanelEditor.tsx
View file @
2de57f09
...
...
@@ -54,7 +54,7 @@ export class PanelEditor extends PureComponent<PanelEditorProps> {
case
'queries'
:
return
<
QueriesTab
panel=
{
panel
}
dashboard=
{
dashboard
}
/>;
case
'alert'
:
return
<
AlertTab
angularPanel=
{
angularPanel
}
/>;
return
<
AlertTab
angularPanel=
{
angularPanel
}
dashboard=
{
dashboard
}
panel=
{
panel
}
/>;
case
'visualization'
:
return
(
<
VisualizationTab
...
...
public/app/features/dashboard/dashgrid/QueriesTab.tsx
View file @
2de57f09
// Libraries
import
React
,
{
SFC
,
PureComponent
}
from
'react'
;
import
React
,
{
PureComponent
,
SFC
}
from
'react'
;
import
_
from
'lodash'
;
// Components
import
'
./../..
/panel/metrics_tab'
;
import
{
EditorTabBody
}
from
'./EditorTabBody'
;
import
'
app/features
/panel/metrics_tab'
;
import
{
EditorTabBody
,
EditorToolbarView
}
from
'./EditorTabBody'
;
import
{
DataSourcePicker
}
from
'app/core/components/Select/DataSourcePicker'
;
import
{
QueryInspector
}
from
'./QueryInspector'
;
import
{
QueryOptions
}
from
'./QueryOptions'
;
...
...
@@ -13,14 +13,14 @@ import { PanelOptionSection } from './PanelOptionSection';
// Services
import
{
getDatasourceSrv
}
from
'app/features/plugins/datasource_srv'
;
import
{
getBackendSrv
,
BackendSrv
}
from
'app/core/services/backend_srv'
;
import
{
getAngularLoader
,
AngularComponent
}
from
'app/core/services/AngularLoader'
;
import
{
BackendSrv
,
get
BackendSrv
}
from
'app/core/services/backend_srv'
;
import
{
AngularComponent
,
getAngularLoader
}
from
'app/core/services/AngularLoader'
;
import
config
from
'app/core/config'
;
// Types
import
{
PanelModel
}
from
'../panel_model'
;
import
{
DashboardModel
}
from
'../dashboard_model'
;
import
{
Data
SourceSelectItem
,
DataQuery
}
from
'app/types'
;
import
{
Data
Query
,
DataSourceSelectItem
}
from
'app/types'
;
import
{
PluginHelp
}
from
'app/core/components/PluginHelp/PluginHelp'
;
interface
Props
{
...
...
@@ -204,12 +204,12 @@ export class QueriesTab extends PureComponent<Props, State> {
const
{
panel
}
=
this
.
props
;
const
{
currentDS
,
isAddingMixed
}
=
this
.
state
;
const
queryInspector
=
{
const
queryInspector
:
EditorToolbarView
=
{
title
:
'Query Inspector'
,
render
:
this
.
renderQueryInspector
,
};
const
dsHelp
=
{
const
dsHelp
:
EditorToolbarView
=
{
heading
:
'Help'
,
icon
:
'fa fa-question'
,
render
:
this
.
renderHelp
,
...
...
public/app/features/dashboard/dashgrid/StateHistory.tsx
0 → 100644
View file @
2de57f09
import
React
,
{
PureComponent
}
from
'react'
;
import
alertDef
from
'../../alerting/state/alertDef'
;
import
{
getBackendSrv
}
from
'app/core/services/backend_srv'
;
import
{
DashboardModel
}
from
'../dashboard_model'
;
import
appEvents
from
'../../../core/app_events'
;
interface
Props
{
dashboard
:
DashboardModel
;
panelId
:
number
;
onRefresh
:
()
=>
void
;
}
interface
State
{
stateHistoryItems
:
any
[];
}
class
StateHistory
extends
PureComponent
<
Props
,
State
>
{
state
=
{
stateHistoryItems
:
[],
};
componentDidMount
():
void
{
const
{
dashboard
,
panelId
}
=
this
.
props
;
getBackendSrv
()
.
get
(
`/api/annotations?dashboardId=
${
dashboard
.
id
}
&panelId=
${
panelId
}
&limit=50&type=alert`
)
.
then
(
res
=>
{
const
items
=
res
.
map
(
item
=>
{
return
{
stateModel
:
alertDef
.
getStateDisplayModel
(
item
.
newState
),
time
:
dashboard
.
formatDate
(
item
.
time
,
'MMM D, YYYY HH:mm:ss'
),
info
:
alertDef
.
getAlertAnnotationInfo
(
item
),
};
});
this
.
setState
({
stateHistoryItems
:
items
,
});
});
}
clearHistory
=
()
=>
{
const
{
dashboard
,
onRefresh
,
panelId
}
=
this
.
props
;
appEvents
.
emit
(
'confirm-modal'
,
{
title
:
'Delete Alert History'
,
text
:
'Are you sure you want to remove all history & annotations for this alert?'
,
icon
:
'fa-trash'
,
yesText
:
'Yes'
,
onConfirm
:
()
=>
{
getBackendSrv
()
.
post
(
'/api/annotations/mass-delete'
,
{
dashboardId
:
dashboard
.
id
,
panelId
:
panelId
,
})
.
then
(()
=>
{
onRefresh
();
});
this
.
setState
({
stateHistoryItems
:
[],
});
},
});
};
render
()
{
const
{
stateHistoryItems
}
=
this
.
state
;
return
(
<
div
>
{
stateHistoryItems
.
length
>
0
&&
(
<
div
className=
"p-b-1"
>
<
span
className=
"muted"
>
Last 50 state changes
</
span
>
<
button
className=
"btn btn-mini btn-danger pull-right"
onClick=
{
this
.
clearHistory
}
>
<
i
className=
"fa fa-trash"
/>
{
` Clear history`
}
</
button
>
</
div
>
)
}
<
ol
className=
"alert-rule-list"
>
{
stateHistoryItems
.
length
>
0
?
(
stateHistoryItems
.
map
((
item
,
index
)
=>
{
return
(
<
li
className=
"alert-rule-item"
key=
{
`${item.time}-${index}`
}
>
<
div
className=
{
`alert-rule-item__icon ${item.stateModel.stateClass}`
}
>
<
i
className=
{
item
.
stateModel
.
iconClass
}
/>
</
div
>
<
div
className=
"alert-rule-item__body"
>
<
div
className=
"alert-rule-item__header"
>
<
p
className=
"alert-rule-item__name"
>
{
item
.
alertName
}
</
p
>
<
div
className=
"alert-rule-item__text"
>
<
span
className=
{
`${item.stateModel.stateClass}`
}
>
{
item
.
stateModel
.
text
}
</
span
>
</
div
>
</
div
>
{
item
.
info
}
</
div
>
<
div
className=
"alert-rule-item__time"
>
{
item
.
time
}
</
div
>
</
li
>
);
})
)
:
(
<
i
>
No state changes recorded
</
i
>
)
}
</
ol
>
</
div
>
);
}
}
export
default
StateHistory
;
public/app/features/dashboard/dashgrid/VisualizationTab.tsx
View file @
2de57f09
...
...
@@ -2,10 +2,10 @@
import
React
,
{
PureComponent
}
from
'react'
;
// Utils & Services
import
{
getAngularLoader
,
AngularComponent
}
from
'app/core/services/AngularLoader'
;
import
{
AngularComponent
,
getAngularLoader
}
from
'app/core/services/AngularLoader'
;
// Components
import
{
EditorTabBody
}
from
'./EditorTabBody'
;
import
{
EditorTabBody
,
EditorToolbarView
}
from
'./EditorTabBody'
;
import
{
VizTypePicker
}
from
'./VizTypePicker'
;
import
{
PluginHelp
}
from
'app/core/components/PluginHelp/PluginHelp'
;
import
{
FadeIn
}
from
'app/core/components/Animations/FadeIn'
;
...
...
@@ -206,7 +206,7 @@ export class VisualizationTab extends PureComponent<Props, State> {
const
{
plugin
}
=
this
.
props
;
const
{
isVizPickerOpen
,
searchQuery
}
=
this
.
state
;
const
pluginHelp
=
{
const
pluginHelp
:
EditorToolbarView
=
{
heading
:
'Help'
,
icon
:
'fa fa-question'
,
render
:
this
.
renderHelp
,
...
...
public/sass/pages/_alerting.scss
View file @
2de57f09
...
...
@@ -107,6 +107,7 @@
display
:
flex
;
flex-direction
:
column
;
flex-grow
:
1
;
justify-content
:
center
;
overflow
:
hidden
;
}
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment