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
dd0afd0a
Commit
dd0afd0a
authored
Feb 06, 2019
by
Torkel Ödegaard
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Big refactoring for dashboard init redux actions
parent
8574dca0
Hide whitespace changes
Inline
Side-by-side
Showing
15 changed files
with
225 additions
and
212 deletions
+225
-212
public/app/core/components/AlertBox/AlertBox.tsx
+41
-0
public/app/core/components/AppNotifications/AppNotificationItem.tsx
+8
-12
public/app/core/copy/appNotification.ts
+2
-13
public/app/core/utils/errors.ts
+15
-0
public/app/features/dashboard/components/DashNav/DashNavCtrl.ts
+0
-117
public/app/features/dashboard/components/DashNav/index.ts
+0
-1
public/app/features/dashboard/containers/DashboardPage.test.tsx
+6
-6
public/app/features/dashboard/containers/DashboardPage.tsx
+40
-14
public/app/features/dashboard/containers/SoloPanelPage.tsx
+0
-1
public/app/features/dashboard/state/actions.ts
+22
-6
public/app/features/dashboard/state/initDashboard.test.ts
+4
-6
public/app/features/dashboard/state/initDashboard.ts
+15
-18
public/app/features/dashboard/state/reducers.ts
+54
-12
public/app/types/dashboard.ts
+13
-6
public/sass/pages/_dashboard.scss
+5
-0
No files found.
public/app/core/components/AlertBox/AlertBox.tsx
0 → 100644
View file @
dd0afd0a
import
React
,
{
FunctionComponent
}
from
'react'
;
import
{
AppNotificationSeverity
}
from
'app/types'
;
interface
Props
{
title
:
string
;
icon
?:
string
;
text
?:
string
;
severity
:
AppNotificationSeverity
;
onClose
?:
()
=>
void
;
}
function
getIconFromSeverity
(
severity
:
AppNotificationSeverity
):
string
{
switch
(
severity
)
{
case
AppNotificationSeverity
.
Error
:
{
return
'fa fa-exclamation-triangle'
;
}
case
AppNotificationSeverity
.
Success
:
{
return
'fa fa-check'
;
}
default
:
return
null
;
}
}
export
const
AlertBox
:
FunctionComponent
<
Props
>
=
({
title
,
icon
,
text
,
severity
,
onClose
})
=>
{
return
(
<
div
className=
{
`alert alert-${severity}`
}
>
<
div
className=
"alert-icon"
>
<
i
className=
{
icon
||
getIconFromSeverity
(
severity
)
}
/>
</
div
>
<
div
className=
"alert-body"
>
<
div
className=
"alert-title"
>
{
title
}
</
div
>
{
text
&&
<
div
className=
"alert-text"
>
{
text
}
</
div
>
}
</
div
>
{
onClose
&&
(
<
button
type=
"button"
className=
"alert-close"
onClick=
{
onClose
}
>
<
i
className=
"fa fa fa-remove"
/>
</
button
>
)
}
</
div
>
);
};
public/app/core/components/AppNotifications/AppNotificationItem.tsx
View file @
dd0afd0a
import
React
,
{
Component
}
from
'react'
;
import
{
AppNotification
}
from
'app/types'
;
import
{
AlertBox
}
from
'../AlertBox/AlertBox'
;
interface
Props
{
appNotification
:
AppNotification
;
...
...
@@ -22,18 +23,13 @@ export default class AppNotificationItem extends Component<Props> {
const
{
appNotification
,
onClearNotification
}
=
this
.
props
;
return
(
<
div
className=
{
`alert-${appNotification.severity} alert`
}
>
<
div
className=
"alert-icon"
>
<
i
className=
{
appNotification
.
icon
}
/>
</
div
>
<
div
className=
"alert-body"
>
<
div
className=
"alert-title"
>
{
appNotification
.
title
}
</
div
>
<
div
className=
"alert-text"
>
{
appNotification
.
text
}
</
div
>
</
div
>
<
button
type=
"button"
className=
"alert-close"
onClick=
{
()
=>
onClearNotification
(
appNotification
.
id
)
}
>
<
i
className=
"fa fa fa-remove"
/>
</
button
>
</
div
>
<
AlertBox
severity=
{
appNotification
.
severity
}
title=
{
appNotification
.
title
}
text=
{
appNotification
.
text
}
icon=
{
appNotification
.
icon
}
onClose=
{
()
=>
onClearNotification
(
appNotification
.
id
)
}
/>
);
}
}
public/app/core/copy/appNotification.ts
View file @
dd0afd0a
import
_
from
'lodash'
;
import
{
AppNotification
,
AppNotificationSeverity
,
AppNotificationTimeout
}
from
'app/types'
;
import
{
getMessageFromError
}
from
'app/core/utils/errors'
;
const
defaultSuccessNotification
:
AppNotification
=
{
title
:
''
,
...
...
@@ -33,21 +33,10 @@ export const createSuccessNotification = (title: string, text?: string): AppNoti
});
export
const
createErrorNotification
=
(
title
:
string
,
text
?:
any
):
AppNotification
=>
{
// Handling if text is an error object
if
(
text
&&
!
_
.
isString
(
text
))
{
if
(
text
.
message
)
{
text
=
text
.
message
;
}
else
if
(
text
.
data
&&
text
.
data
.
message
)
{
text
=
text
.
data
.
message
;
}
else
{
text
=
text
.
toString
();
}
}
return
{
...
defaultErrorNotification
,
title
:
title
,
text
:
text
,
text
:
getMessageFromError
(
text
)
,
id
:
Date
.
now
(),
};
};
...
...
public/app/core/utils/errors.ts
0 → 100644
View file @
dd0afd0a
import
_
from
'lodash'
;
export
function
getMessageFromError
(
err
:
any
):
string
|
null
{
if
(
err
&&
!
_
.
isString
(
err
))
{
if
(
err
.
message
)
{
return
err
.
message
;
}
else
if
(
err
.
data
&&
err
.
data
.
message
)
{
return
err
.
data
.
message
;
}
else
{
return
JSON
.
stringify
(
err
);
}
}
return
null
;
}
public/app/features/dashboard/components/DashNav/DashNavCtrl.ts
deleted
100644 → 0
View file @
8574dca0
import
moment
from
'moment'
;
import
angular
from
'angular'
;
import
{
appEvents
,
NavModel
}
from
'app/core/core'
;
import
{
DashboardModel
}
from
'../../state/DashboardModel'
;
export
class
DashNavCtrl
{
dashboard
:
DashboardModel
;
navModel
:
NavModel
;
titleTooltip
:
string
;
/** @ngInject */
constructor
(
private
$scope
,
private
dashboardSrv
,
private
$location
,
public
playlistSrv
)
{
if
(
this
.
dashboard
.
meta
.
isSnapshot
)
{
const
meta
=
this
.
dashboard
.
meta
;
this
.
titleTooltip
=
'Created: '
+
moment
(
meta
.
created
).
calendar
();
if
(
meta
.
expires
)
{
this
.
titleTooltip
+=
'<br>Expires: '
+
moment
(
meta
.
expires
).
fromNow
()
+
'<br>'
;
}
}
}
toggleSettings
()
{
const
search
=
this
.
$location
.
search
();
if
(
search
.
editview
)
{
delete
search
.
editview
;
}
else
{
search
.
editview
=
'settings'
;
}
this
.
$location
.
search
(
search
);
}
toggleViewMode
()
{
appEvents
.
emit
(
'toggle-kiosk-mode'
);
}
close
()
{
const
search
=
this
.
$location
.
search
();
if
(
search
.
editview
)
{
delete
search
.
editview
;
}
else
if
(
search
.
fullscreen
)
{
delete
search
.
fullscreen
;
delete
search
.
edit
;
delete
search
.
tab
;
delete
search
.
panelId
;
}
this
.
$location
.
search
(
search
);
}
starDashboard
()
{
this
.
dashboardSrv
.
starDashboard
(
this
.
dashboard
.
id
,
this
.
dashboard
.
meta
.
isStarred
).
then
(
newState
=>
{
this
.
dashboard
.
meta
.
isStarred
=
newState
;
});
}
shareDashboard
(
tabIndex
)
{
const
modalScope
=
this
.
$scope
.
$new
();
modalScope
.
tabIndex
=
tabIndex
;
modalScope
.
dashboard
=
this
.
dashboard
;
appEvents
.
emit
(
'show-modal'
,
{
src
:
'public/app/features/dashboard/components/ShareModal/template.html'
,
scope
:
modalScope
,
});
}
hideTooltip
(
evt
)
{
angular
.
element
(
evt
.
currentTarget
).
tooltip
(
'hide'
);
}
saveDashboard
()
{
return
this
.
dashboardSrv
.
saveDashboard
();
}
showSearch
()
{
if
(
this
.
dashboard
.
meta
.
fullscreen
)
{
this
.
close
();
return
;
}
appEvents
.
emit
(
'show-dash-search'
);
}
addPanel
()
{
appEvents
.
emit
(
'dash-scroll'
,
{
animate
:
true
,
evt
:
0
});
if
(
this
.
dashboard
.
panels
.
length
>
0
&&
this
.
dashboard
.
panels
[
0
].
type
===
'add-panel'
)
{
return
;
// Return if the "Add panel" exists already
}
this
.
dashboard
.
addPanel
({
type
:
'add-panel'
,
gridPos
:
{
x
:
0
,
y
:
0
,
w
:
12
,
h
:
8
},
title
:
'Panel Title'
,
});
}
navItemClicked
(
navItem
,
evt
)
{
if
(
navItem
.
clickHandler
)
{
navItem
.
clickHandler
();
evt
.
preventDefault
();
}
}
}
export
function
dashNavDirective
()
{
return
{
restrict
:
'E'
,
templateUrl
:
'public/app/features/dashboard/components/DashNav/template.html'
,
controller
:
DashNavCtrl
,
bindToController
:
true
,
controllerAs
:
'ctrl'
,
transclude
:
true
,
scope
:
{
dashboard
:
'='
},
};
}
angular
.
module
(
'grafana.directives'
).
directive
(
'dashnav'
,
dashNavDirective
);
public/app/features/dashboard/components/DashNav/index.ts
View file @
dd0afd0a
export
{
DashNavCtrl
}
from
'./DashNavCtrl'
;
import
DashNav
from
'./DashNav'
;
export
{
DashNav
};
public/app/features/dashboard/containers/DashboardPage.test.tsx
View file @
dd0afd0a
...
...
@@ -2,8 +2,8 @@ import React from 'react';
import
{
shallow
,
ShallowWrapper
}
from
'enzyme'
;
import
{
DashboardPage
,
Props
,
State
}
from
'./DashboardPage'
;
import
{
DashboardModel
}
from
'../state'
;
import
{
setDashboardModel
}
from
'../state/actions'
;
import
{
DashboardRouteInfo
,
Dashboard
LoadingStat
e
}
from
'app/types'
;
import
{
cleanUpDashboard
}
from
'../state/actions'
;
import
{
DashboardRouteInfo
,
Dashboard
InitPhas
e
}
from
'app/types'
;
jest
.
mock
(
'sass/_variables.scss'
,
()
=>
({
panelhorizontalpadding
:
10
,
...
...
@@ -22,13 +22,13 @@ function setup(propOverrides?: Partial<Props>): ShallowWrapper<Props, State, Das
routeInfo
:
DashboardRouteInfo
.
Normal
,
urlEdit
:
false
,
urlFullscreen
:
false
,
loadingState
:
DashboardLoadingState
.
Done
,
is
Loading
Slow
:
false
,
initPhase
:
DashboardInitPhase
.
Completed
,
is
Init
Slow
:
false
,
initDashboard
:
jest
.
fn
(),
updateLocation
:
jest
.
fn
(),
notifyApp
:
jest
.
fn
(),
dashboard
:
null
,
setDashboardModel
:
setDashboardModel
,
cleanUpDashboard
:
cleanUpDashboard
,
};
Object
.
assign
(
props
,
propOverrides
);
...
...
@@ -66,7 +66,7 @@ describe('DashboardPage', () => {
canEdit
:
true
,
canSave
:
true
,
});
wrapper
.
setProps
({
dashboard
,
loadingState
:
DashboardLoadingState
.
Done
});
wrapper
.
setProps
({
dashboard
,
initPhase
:
DashboardInitPhase
.
Completed
});
});
it
(
'Should update title'
,
()
=>
{
...
...
public/app/features/dashboard/containers/DashboardPage.tsx
View file @
dd0afd0a
...
...
@@ -7,6 +7,7 @@ import classNames from 'classnames';
// Services & Utils
import
{
createErrorNotification
}
from
'app/core/copy/appNotification'
;
import
{
getMessageFromError
}
from
'app/core/utils/errors'
;
// Components
import
{
DashboardGrid
}
from
'../dashgrid/DashboardGrid'
;
...
...
@@ -14,15 +15,22 @@ import { DashNav } from '../components/DashNav';
import
{
SubMenu
}
from
'../components/SubMenu'
;
import
{
DashboardSettings
}
from
'../components/DashboardSettings'
;
import
{
CustomScrollbar
}
from
'@grafana/ui'
;
import
{
AlertBox
}
from
'app/core/components/AlertBox/AlertBox'
;
// Redux
import
{
initDashboard
}
from
'../state/initDashboard'
;
import
{
setDashboardModel
}
from
'../state/actions'
;
import
{
cleanUpDashboard
}
from
'../state/actions'
;
import
{
updateLocation
}
from
'app/core/actions'
;
import
{
notifyApp
}
from
'app/core/actions'
;
// Types
import
{
StoreState
,
DashboardLoadingState
,
DashboardRouteInfo
}
from
'app/types'
;
import
{
StoreState
,
DashboardInitPhase
,
DashboardRouteInfo
,
DashboardInitError
,
AppNotificationSeverity
,
}
from
'app/types'
;
import
{
DashboardModel
,
PanelModel
}
from
'app/features/dashboard/state'
;
export
interface
Props
{
...
...
@@ -37,11 +45,12 @@ export interface Props {
routeInfo
:
DashboardRouteInfo
;
urlEdit
:
boolean
;
urlFullscreen
:
boolean
;
loadingState
:
DashboardLoadingStat
e
;
is
Loading
Slow
:
boolean
;
initPhase
:
DashboardInitPhas
e
;
is
Init
Slow
:
boolean
;
dashboard
:
DashboardModel
|
null
;
initError
?:
DashboardInitError
;
initDashboard
:
typeof
initDashboard
;
setDashboardModel
:
typeof
setDashboardModel
;
cleanUpDashboard
:
typeof
cleanUpDashboard
;
notifyApp
:
typeof
notifyApp
;
updateLocation
:
typeof
updateLocation
;
}
...
...
@@ -83,7 +92,7 @@ export class DashboardPage extends PureComponent<Props, State> {
componentWillUnmount
()
{
if
(
this
.
props
.
dashboard
)
{
this
.
props
.
dashboard
.
destroy
();
this
.
props
.
setDashboardModel
(
null
);
this
.
props
.
cleanUpDashboard
(
);
}
}
...
...
@@ -204,23 +213,37 @@ export class DashboardPage extends PureComponent<Props, State> {
this
.
setState
({
scrollTop
:
0
});
};
render
Loading
State
()
{
render
SlowInit
State
()
{
return
(
<
div
className=
"dashboard-loading"
>
<
div
className=
"dashboard-loading__text"
>
<
i
className=
"fa fa-spinner fa-spin"
/>
Dashboard
{
this
.
props
.
loadingStat
e
}
<
i
className=
"fa fa-spinner fa-spin"
/>
{
this
.
props
.
initPhas
e
}
</
div
>
</
div
>
);
}
renderInitFailedState
()
{
const
{
initError
}
=
this
.
props
;
return
(
<
div
className=
"dashboard-loading"
>
<
AlertBox
severity=
{
AppNotificationSeverity
.
Error
}
title=
{
initError
.
message
}
text=
{
getMessageFromError
(
initError
.
error
)
}
/>
</
div
>
);
}
render
()
{
const
{
dashboard
,
editview
,
$injector
,
is
LoadingSlow
}
=
this
.
props
;
const
{
dashboard
,
editview
,
$injector
,
is
InitSlow
,
initError
}
=
this
.
props
;
const
{
isSettingsOpening
,
isEditing
,
isFullscreen
,
scrollTop
}
=
this
.
state
;
if
(
!
dashboard
)
{
if
(
is
Loading
Slow
)
{
return
this
.
render
Loading
State
();
if
(
is
Init
Slow
)
{
return
this
.
render
SlowInit
State
();
}
return
null
;
}
...
...
@@ -249,6 +272,8 @@ export class DashboardPage extends PureComponent<Props, State> {
<
CustomScrollbar
autoHeightMin=
{
'100%'
}
setScrollTop=
{
this
.
setScrollTop
}
scrollTop=
{
scrollTop
}
>
{
editview
&&
<
DashboardSettings
dashboard=
{
dashboard
}
/>
}
{
initError
&&
this
.
renderInitFailedState
()
}
<
div
className=
{
gridWrapperClasses
}
>
{
dashboard
.
meta
.
submenuEnabled
&&
<
SubMenu
dashboard=
{
dashboard
}
/>
}
<
DashboardGrid
dashboard=
{
dashboard
}
isEditing=
{
isEditing
}
isFullscreen=
{
isFullscreen
}
/>
...
...
@@ -269,14 +294,15 @@ const mapStateToProps = (state: StoreState) => ({
urlFolderId
:
state
.
location
.
query
.
folderId
,
urlFullscreen
:
state
.
location
.
query
.
fullscreen
===
true
,
urlEdit
:
state
.
location
.
query
.
edit
===
true
,
loadingState
:
state
.
dashboard
.
loadingState
,
isLoadingSlow
:
state
.
dashboard
.
isLoadingSlow
,
initPhase
:
state
.
dashboard
.
initPhase
,
isInitSlow
:
state
.
dashboard
.
isInitSlow
,
initError
:
state
.
dashboard
.
initError
,
dashboard
:
state
.
dashboard
.
model
as
DashboardModel
,
});
const
mapDispatchToProps
=
{
initDashboard
,
setDashboardModel
,
cleanUpDashboard
,
notifyApp
,
updateLocation
,
};
...
...
public/app/features/dashboard/containers/SoloPanelPage.tsx
View file @
dd0afd0a
...
...
@@ -100,7 +100,6 @@ const mapStateToProps = (state: StoreState) => ({
urlSlug
:
state
.
location
.
routeParams
.
slug
,
urlType
:
state
.
location
.
routeParams
.
type
,
urlPanelId
:
state
.
location
.
query
.
panelId
,
loadingState
:
state
.
dashboard
.
loadingState
,
dashboard
:
state
.
dashboard
.
model
as
DashboardModel
,
});
...
...
public/app/features/dashboard/state/actions.ts
View file @
dd0afd0a
...
...
@@ -8,20 +8,36 @@ import { loadPluginDashboards } from '../../plugins/state/actions';
import
{
notifyApp
}
from
'app/core/actions'
;
// Types
import
{
ThunkResult
}
from
'app/types'
;
import
{
ThunkResult
,
DashboardAcl
,
DashboardAclDTO
,
PermissionLevel
,
DashboardAclUpdateDTO
,
NewDashboardAclItem
,
}
from
'app/types/acl'
;
import
{
DashboardLoadingState
,
MutableDashboard
}
from
'app/types/dashboard'
;
MutableDashboard
,
DashboardInitError
,
}
from
'app/types'
;
export
const
loadDashboardPermissions
=
actionCreatorFactory
<
DashboardAclDTO
[]
>
(
'LOAD_DASHBOARD_PERMISSIONS'
).
create
();
export
const
setDashboardLoadingState
=
actionCreatorFactory
<
DashboardLoadingState
>
(
'SET_DASHBOARD_LOADING_STATE'
).
create
();
export
const
setDashboardModel
=
actionCreatorFactory
<
MutableDashboard
>
(
'SET_DASHBOARD_MODEL'
).
create
();
export
const
setDashboardLoadingSlow
=
noPayloadActionCreatorFactory
(
'SET_DASHBOARD_LOADING_SLOW'
).
create
();
export
const
dashboardInitFetching
=
noPayloadActionCreatorFactory
(
'DASHBOARD_INIT_FETCHING'
).
create
();
export
const
dashboardInitServices
=
noPayloadActionCreatorFactory
(
'DASHBOARD_INIT_SERVICES'
).
create
();
export
const
dashboardInitSlow
=
noPayloadActionCreatorFactory
(
'SET_DASHBOARD_INIT_SLOW'
).
create
();
export
const
dashboardInitCompleted
=
actionCreatorFactory
<
MutableDashboard
>
(
'DASHBOARD_INIT_COMLETED'
).
create
();
/*
* Unrecoverable init failure (fetch or model creation failed)
*/
export
const
dashboardInitFailed
=
actionCreatorFactory
<
DashboardInitError
>
(
'DASHBOARD_INIT_FAILED'
).
create
();
/*
* When leaving dashboard, resets state
* */
export
const
cleanUpDashboard
=
noPayloadActionCreatorFactory
(
'DASHBOARD_CLEAN_UP'
).
create
();
export
function
getDashboardPermissions
(
id
:
number
):
ThunkResult
<
void
>
{
return
async
dispatch
=>
{
...
...
public/app/features/dashboard/state/initDashboard.test.ts
View file @
dd0afd0a
import
configureMockStore
from
'redux-mock-store'
;
import
thunk
from
'redux-thunk'
;
import
{
initDashboard
,
InitDashboardArgs
}
from
'./initDashboard'
;
import
{
DashboardRouteInfo
,
DashboardLoadingState
}
from
'app/types'
;
import
{
DashboardRouteInfo
}
from
'app/types'
;
const
mockStore
=
configureMockStore
([
thunk
]);
...
...
@@ -98,13 +98,11 @@ describeInitScenario('Initializing new dashboard', ctx => {
});
it
(
'Should send action to set loading state to fetching'
,
()
=>
{
expect
(
ctx
.
actions
[
0
].
type
).
toBe
(
'SET_DASHBOARD_LOADING_STATE'
);
expect
(
ctx
.
actions
[
0
].
payload
).
toBe
(
DashboardLoadingState
.
Fetching
);
expect
(
ctx
.
actions
[
0
].
type
).
toBe
(
'DASHBOARD_INIT_FETCHING'
);
});
it
(
'Should send action to set loading state to Initializing'
,
()
=>
{
expect
(
ctx
.
actions
[
1
].
type
).
toBe
(
'SET_DASHBOARD_LOADING_STATE'
);
expect
(
ctx
.
actions
[
1
].
payload
).
toBe
(
DashboardLoadingState
.
Initializing
);
expect
(
ctx
.
actions
[
1
].
type
).
toBe
(
'DASHBOARD_INIT_SERVICES'
);
});
it
(
'Should update location with orgId query param'
,
()
=>
{
...
...
@@ -113,7 +111,7 @@ describeInitScenario('Initializing new dashboard', ctx => {
});
it
(
'Should send action to set dashboard model'
,
()
=>
{
expect
(
ctx
.
actions
[
3
].
type
).
toBe
(
'
SET_DASHBOARD_MODEL
'
);
expect
(
ctx
.
actions
[
3
].
type
).
toBe
(
'
DASHBOARD_INIT_COMLETED
'
);
expect
(
ctx
.
actions
[
3
].
payload
.
title
).
toBe
(
'New dashboard'
);
});
...
...
public/app/features/dashboard/state/initDashboard.ts
View file @
dd0afd0a
...
...
@@ -12,17 +12,16 @@ import { KeybindingSrv } from 'app/core/services/keybindingSrv';
import
{
updateLocation
}
from
'app/core/actions'
;
import
{
notifyApp
}
from
'app/core/actions'
;
import
locationUtil
from
'app/core/utils/location_util'
;
import
{
setDashboardLoadingState
,
setDashboardModel
,
setDashboardLoadingSlow
}
from
'./actions'
;
import
{
dashboardInitFetching
,
dashboardInitCompleted
,
dashboardInitFailed
,
dashboardInitSlow
,
dashboardInitServices
,
}
from
'./actions'
;
// Types
import
{
DashboardLoadingState
,
DashboardRouteInfo
,
StoreState
,
ThunkDispatch
,
ThunkResult
,
DashboardDTO
,
}
from
'app/types'
;
import
{
DashboardRouteInfo
,
StoreState
,
ThunkDispatch
,
ThunkResult
,
DashboardDTO
}
from
'app/types'
;
import
{
DashboardModel
}
from
'./DashboardModel'
;
export
interface
InitDashboardArgs
{
...
...
@@ -106,8 +105,7 @@ async function fetchDashboard(
throw
{
message
:
'Unknown route '
+
args
.
routeInfo
};
}
}
catch
(
err
)
{
dispatch
(
setDashboardLoadingState
(
DashboardLoadingState
.
Error
));
dispatch
(
notifyApp
(
createErrorNotification
(
'Dashboard fetch failed'
,
err
)));
dispatch
(
dashboardInitFailed
({
message
:
'Failed to fetch dashboard'
,
error
:
err
}));
console
.
log
(
err
);
return
null
;
}
...
...
@@ -125,13 +123,13 @@ async function fetchDashboard(
export
function
initDashboard
(
args
:
InitDashboardArgs
):
ThunkResult
<
void
>
{
return
async
(
dispatch
,
getState
)
=>
{
// set fetching state
dispatch
(
setDashboardLoadingState
(
DashboardLoadingState
.
Fetching
));
dispatch
(
dashboardInitFetching
(
));
// Detect slow loading / initializing and set state flag
// This is in order to not show loading indication for fast loading dashboards as it creates blinking/flashing
setTimeout
(()
=>
{
if
(
getState
().
dashboard
.
model
===
null
)
{
dispatch
(
setDashboardLoading
Slow
());
dispatch
(
dashboardInit
Slow
());
}
},
500
);
...
...
@@ -144,15 +142,14 @@ export function initDashboard(args: InitDashboardArgs): ThunkResult<void> {
}
// set initializing state
dispatch
(
setDashboardLoadingState
(
DashboardLoadingState
.
Initializing
));
dispatch
(
dashboardInitServices
(
));
// create model
let
dashboard
:
DashboardModel
;
try
{
dashboard
=
new
DashboardModel
(
dashDTO
.
dashboard
,
dashDTO
.
meta
);
}
catch
(
err
)
{
dispatch
(
setDashboardLoadingState
(
DashboardLoadingState
.
Error
));
dispatch
(
notifyApp
(
createErrorNotification
(
'Dashboard model initializing failure'
,
err
)));
dispatch
(
dashboardInitFailed
({
message
:
'Failed create dashboard model'
,
error
:
err
}));
console
.
log
(
err
);
return
;
}
...
...
@@ -203,8 +200,8 @@ export function initDashboard(args: InitDashboardArgs): ThunkResult<void> {
// legacy srv state
dashboardSrv
.
setCurrent
(
dashboard
);
//
set model in redux (even though it's mutable)
dispatch
(
setDashboardModel
(
dashboard
));
//
yay we are done
dispatch
(
dashboardInitCompleted
(
dashboard
));
};
}
...
...
public/app/features/dashboard/state/reducers.ts
View file @
dd0afd0a
import
{
DashboardState
,
DashboardLoadingState
}
from
'app/types/dashboard'
;
import
{
loadDashboardPermissions
,
setDashboardLoadingState
,
setDashboardModel
,
setDashboardLoadingSlow
}
from
'./actions'
;
import
{
DashboardState
,
DashboardInitPhase
}
from
'app/types'
;
import
{
loadDashboardPermissions
,
dashboardInitFetching
,
dashboardInitSlow
,
dashboardInitServices
,
dashboardInitFailed
,
dashboardInitCompleted
,
cleanUpDashboard
,
}
from
'./actions'
;
import
{
reducerFactory
}
from
'app/core/redux'
;
import
{
processAclItems
}
from
'app/core/utils/acl'
;
import
{
DashboardModel
}
from
'./DashboardModel'
;
export
const
initialState
:
DashboardState
=
{
loadingState
:
DashboardLoadingStat
e
.
NotStarted
,
is
Loading
Slow
:
false
,
initPhase
:
DashboardInitPhas
e
.
NotStarted
,
is
Init
Slow
:
false
,
model
:
null
,
permissions
:
[],
};
...
...
@@ -19,27 +28,60 @@ export const dashboardReducer = reducerFactory(initialState)
}),
})
.
addMapper
({
filter
:
setDashboardLoadingState
,
mapper
:
(
state
,
action
)
=>
({
filter
:
dashboardInitFetching
,
mapper
:
state
=>
({
...
state
,
initPhase
:
DashboardInitPhase
.
Fetching
,
}),
})
.
addMapper
({
filter
:
dashboardInitServices
,
mapper
:
state
=>
({
...
state
,
initPhase
:
DashboardInitPhase
.
Services
,
}),
})
.
addMapper
({
filter
:
dashboardInitSlow
,
mapper
:
state
=>
({
...
state
,
loadingState
:
action
.
payload
isInitSlow
:
true
,
}),
})
.
addMapper
({
filter
:
setDashboardModel
,
filter
:
dashboardInitFailed
,
mapper
:
(
state
,
action
)
=>
({
...
state
,
model
:
action
.
payload
,
isLoadingSlow
:
false
,
initPhase
:
DashboardInitPhase
.
Failed
,
isInitSlow
:
false
,
initError
:
action
.
payload
,
model
:
new
DashboardModel
({
title
:
'Dashboard init failed'
},
{
canSave
:
false
,
canEdit
:
false
}),
}),
})
.
addMapper
({
filter
:
setDashboardLoadingSlow
,
filter
:
dashboardInitCompleted
,
mapper
:
(
state
,
action
)
=>
({
...
state
,
isLoadingSlow
:
true
,
initPhase
:
DashboardInitPhase
.
Completed
,
model
:
action
.
payload
,
isInitSlow
:
false
,
}),
})
.
addMapper
({
filter
:
cleanUpDashboard
,
mapper
:
(
state
,
action
)
=>
{
// tear down current dashboard
state
.
model
.
destroy
();
return
{
...
state
,
initPhase
:
DashboardInitPhase
.
NotStarted
,
model
:
null
,
isInitSlow
:
false
,
initError
:
null
,
};
},
})
.
create
();
export
default
{
...
...
public/app/types/dashboard.ts
View file @
dd0afd0a
...
...
@@ -2,6 +2,7 @@ import { DashboardAcl } from './acl';
export
interface
MutableDashboard
{
meta
:
DashboardMeta
;
destroy
:
()
=>
void
;
}
export
interface
DashboardDTO
{
...
...
@@ -44,12 +45,17 @@ export enum DashboardRouteInfo {
Scripted
=
'scripted-dashboard'
,
}
export
enum
Dashboard
LoadingStat
e
{
export
enum
Dashboard
InitPhas
e
{
NotStarted
=
'Not started'
,
Fetching
=
'Fetching'
,
Initializing
=
'Initializing'
,
Error
=
'Error'
,
Done
=
'Done'
,
Services
=
'Services'
,
Failed
=
'Failed'
,
Completed
=
'Completed'
,
}
export
interface
DashboardInitError
{
message
:
string
;
error
:
any
;
}
export
const
KIOSK_MODE_TV
=
'tv'
;
...
...
@@ -57,7 +63,8 @@ export type KioskUrlValue = 'tv' | '1' | true;
export
interface
DashboardState
{
model
:
MutableDashboard
|
null
;
loadingState
:
DashboardLoadingState
;
isLoadingSlow
:
boolean
;
initPhase
:
DashboardInitPhase
;
isInitSlow
:
boolean
;
initError
?:
DashboardInitError
;
permissions
:
DashboardAcl
[]
|
null
;
}
public/sass/pages/_dashboard.scss
View file @
dd0afd0a
...
...
@@ -282,6 +282,11 @@ div.flot-text {
display
:
flex
;
align-items
:
center
;
justify-content
:
center
;
.alert
{
max-width
:
600px
;
min-width
:
600px
;
}
}
.dashboard-loading__text
{
...
...
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