Commit d86e773c by Torkel Ödegaard

wip: minor progress

parent 60f700a1
...@@ -3,21 +3,16 @@ import React, { Component } from 'react'; ...@@ -3,21 +3,16 @@ import React, { Component } from 'react';
import { hot } from 'react-hot-loader'; import { hot } from 'react-hot-loader';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
// Utils & Services
import locationUtil from 'app/core/utils/location_util';
import { getBackendSrv } from 'app/core/services/backend_srv';
import { createErrorNotification } from 'app/core/copy/appNotification';
// Components // Components
import { LoadingPlaceholder } from '@grafana/ui'; import { LoadingPlaceholder } from '@grafana/ui';
// Redux // Redux
import { updateLocation } from 'app/core/actions'; import { initDashboard } from '../state/initDashboard';
import { notifyApp } from 'app/core/actions';
// Types // Types
import { StoreState } from 'app/types'; import { StoreState } from 'app/types';
import { DashboardModel } from 'app/features/dashboard/state'; import { DashboardModel } from 'app/features/dashboard/state';
import { DashboardLoadingState } from 'app/types/dashboard';
interface Props { interface Props {
panelId: string; panelId: string;
...@@ -26,8 +21,9 @@ interface Props { ...@@ -26,8 +21,9 @@ interface Props {
urlType?: string; urlType?: string;
$scope: any; $scope: any;
$injector: any; $injector: any;
updateLocation: typeof updateLocation; initDashboard: typeof initDashboard;
notifyApp: typeof notifyApp; loadingState: DashboardLoadingState;
dashboard: DashboardModel;
} }
interface State { interface State {
...@@ -42,81 +38,54 @@ export class DashboardPage extends Component<Props, State> { ...@@ -42,81 +38,54 @@ export class DashboardPage extends Component<Props, State> {
}; };
async componentDidMount() { async componentDidMount() {
const { $injector, urlUid, urlType, urlSlug } = this.props; this.props.initDashboard({
injector: this.props.$injector,
// handle old urls with no uid scope: this.props.$scope,
if (!urlUid && !(urlType === 'script' || urlType === 'snapshot')) { urlSlug: this.props.urlSlug,
this.redirectToNewUrl(); urlUid: this.props.urlUid,
return; urlType: this.props.urlType,
} })
const loaderSrv = $injector.get('dashboardLoaderSrv'); // const { $injector, urlUid, urlType, urlSlug } = this.props;
const dashDTO = await loaderSrv.loadDashboard(urlType, urlSlug, urlUid); //
// // handle old urls with no uid
try { // if (!urlUid && !(urlType === 'script' || urlType === 'snapshot')) {
this.initDashboard(dashDTO); // this.redirectToNewUrl();
} catch (err) { // return;
this.props.notifyApp(createErrorNotification('Failed to init dashboard', err.toString())); // }
console.log(err); //
} // const loaderSrv = $injector.get('dashboardLoaderSrv');
} // const dashDTO = await loaderSrv.loadDashboard(urlType, urlSlug, urlUid);
//
redirectToNewUrl() { // try {
getBackendSrv() // this.initDashboard(dashDTO);
.getDashboardBySlug(this.props.urlSlug) // } catch (err) {
.then(res => { // this.props.notifyApp(createErrorNotification('Failed to init dashboard', err.toString()));
if (res) { // console.log(err);
const url = locationUtil.stripBaseFromUrl(res.meta.url.replace('/d/', '/d-solo/')); // }
this.props.updateLocation(url);
}
});
} }
initDashboard(dashDTO: any) { // redirectToNewUrl() {
const dashboard = new DashboardModel(dashDTO.dashboard, dashDTO.meta); // getBackendSrv()
// .getDashboardBySlug(this.props.urlSlug)
// init services // .then(res => {
this.timeSrv.init(dashboard); // if (res) {
this.annotationsSrv.init(dashboard); // const url = locationUtil.stripBaseFromUrl(res.meta.url.replace('/d/', '/d-solo/'));
// this.props.updateLocation(url);
// template values service needs to initialize completely before // }
// the rest of the dashboard can load // });
this.variableSrv // }
.init(dashboard) //
// template values failes are non fatal // initDashboard(dashDTO: any) {
.catch(this.onInitFailed.bind(this, 'Templating init failed', false)) // const dashboard = new DashboardModel(dashDTO.dashboard, dashDTO.meta);
// continue // this.setState({ dashboard });
.finally(() => { // }
this.dashboard = dashboard;
this.dashboard.processRepeats();
this.dashboard.updateSubmenuVisibility();
this.dashboard.autoFitPanels(window.innerHeight);
this.unsavedChangesSrv.init(dashboard, this.$scope);
// TODO refactor ViewStateSrv
this.$scope.dashboard = dashboard;
this.dashboardViewState = this.dashboardViewStateSrv.create(this.$scope);
this.keybindingSrv.setupDashboardBindings(this.$scope, dashboard);
this.setWindowTitleAndTheme();
appEvents.emit('dashboard-initialized', dashboard);
})
.catch(this.onInitFailed.bind(this, 'Dashboard init failed', true));
this.setState({ dashboard });
}
render() { render() {
const { notFound, dashboard } = this.state; const { loadingState, dashboard } = this.props;
if (notFound) {
return <div className="alert alert-error">Dashboard not found</div>;
}
if (!dashboard) { if (!dashboard) {
return <LoadingPlaceholder text="Loading dashboard" />; return <LoadingPlaceholder text={loadingState.toString()} />;
} }
return <div>title: {dashboard.title}</div>; return <div>title: {dashboard.title}</div>;
...@@ -128,11 +97,12 @@ const mapStateToProps = (state: StoreState) => ({ ...@@ -128,11 +97,12 @@ const mapStateToProps = (state: StoreState) => ({
urlSlug: state.location.routeParams.slug, urlSlug: state.location.routeParams.slug,
urlType: state.location.routeParams.type, urlType: state.location.routeParams.type,
panelId: state.location.query.panelId, panelId: state.location.query.panelId,
loadingState: state.dashboard.loadingState,
dashboard: state.dashboard as DashboardModel,
}); });
const mapDispatchToProps = { const mapDispatchToProps = {
updateLocation, initDashboard
notifyApp,
}; };
export default hot(module)(connect(mapStateToProps, mapDispatchToProps)(DashboardPage)); export default hot(module)(connect(mapStateToProps, mapDispatchToProps)(DashboardPage));
// Libaries
import { StoreState } from 'app/types'; import { StoreState } from 'app/types';
import { ThunkAction } from 'redux-thunk'; import { ThunkAction } from 'redux-thunk';
// Services & Utils
import { getBackendSrv } from 'app/core/services/backend_srv'; import { getBackendSrv } from 'app/core/services/backend_srv';
import appEvents from 'app/core/app_events'; import { actionCreatorFactory } from 'app/core/redux';
import { ActionOf } from 'app/core/redux/actionCreatorFactory';
import { createSuccessNotification } from 'app/core/copy/appNotification';
// Actions
import { loadPluginDashboards } from '../../plugins/state/actions'; import { loadPluginDashboards } from '../../plugins/state/actions';
import { notifyApp } from 'app/core/actions';
// Types
import { import {
DashboardAcl, DashboardAcl,
DashboardAclDTO, DashboardAclDTO,
...@@ -10,30 +20,14 @@ import { ...@@ -10,30 +20,14 @@ import {
DashboardAclUpdateDTO, DashboardAclUpdateDTO,
NewDashboardAclItem, NewDashboardAclItem,
} from 'app/types/acl'; } from 'app/types/acl';
import { DashboardLoadingState } from 'app/types/dashboard';
export enum ActionTypes { export const loadDashboardPermissions = actionCreatorFactory<DashboardAclDTO[]>('LOAD_DASHBOARD_PERMISSIONS').create();
LoadDashboardPermissions = 'LOAD_DASHBOARD_PERMISSIONS', export const setDashboardLoadingState = actionCreatorFactory<DashboardLoadingState>('SET_DASHBOARD_LOADING_STATE').create();
LoadStarredDashboards = 'LOAD_STARRED_DASHBOARDS',
}
export interface LoadDashboardPermissionsAction {
type: ActionTypes.LoadDashboardPermissions;
payload: DashboardAcl[];
}
export interface LoadStarredDashboardsAction {
type: ActionTypes.LoadStarredDashboards;
payload: DashboardAcl[];
}
export type Action = LoadDashboardPermissionsAction | LoadStarredDashboardsAction; export type Action = ActionOf<DashboardAclDTO[]>;
type ThunkResult<R> = ThunkAction<R, StoreState, undefined, any>; export type ThunkResult<R> = ThunkAction<R, StoreState, undefined, any>;
export const loadDashboardPermissions = (items: DashboardAclDTO[]): LoadDashboardPermissionsAction => ({
type: ActionTypes.LoadDashboardPermissions,
payload: items,
});
export function getDashboardPermissions(id: number): ThunkResult<void> { export function getDashboardPermissions(id: number): ThunkResult<void> {
return async dispatch => { return async dispatch => {
...@@ -124,7 +118,7 @@ export function addDashboardPermission(dashboardId: number, newItem: NewDashboar ...@@ -124,7 +118,7 @@ export function addDashboardPermission(dashboardId: number, newItem: NewDashboar
export function importDashboard(data, dashboardTitle: string): ThunkResult<void> { export function importDashboard(data, dashboardTitle: string): ThunkResult<void> {
return async dispatch => { return async dispatch => {
await getBackendSrv().post('/api/dashboards/import', data); await getBackendSrv().post('/api/dashboards/import', data);
appEvents.emit('alert-success', ['Dashboard Imported', dashboardTitle]); dispatch(notifyApp(createSuccessNotification('Dashboard Imported', dashboardTitle)));
dispatch(loadPluginDashboards()); dispatch(loadPluginDashboards());
}; };
} }
...@@ -135,3 +129,4 @@ export function removeDashboard(uri: string): ThunkResult<void> { ...@@ -135,3 +129,4 @@ export function removeDashboard(uri: string): ThunkResult<void> {
dispatch(loadPluginDashboards()); dispatch(loadPluginDashboards());
}; };
} }
// Libaries
import { StoreState } from 'app/types';
import { ThunkAction } from 'redux-thunk';
// Services & Utils
import { getBackendSrv } from 'app/core/services/backend_srv';
import { createErrorNotification } from 'app/core/copy/appNotification';
export function initDashboard(dashboard: DashboardModel, $injector: any, $scope: any) { // Actions
import { updateLocation } from 'app/core/actions';
import { notifyApp } from 'app/core/actions';
import locationUtil from 'app/core/utils/location_util';
import { setDashboardLoadingState, ThunkResult } from './actions';
// Types
import { DashboardLoadingState } from 'app/types/dashboard';
import { DashboardModel } from './DashboardModel';
export interface InitDashboardArgs {
injector: any;
scope: any;
urlUid?: string;
urlSlug?: string;
urlType?: string;
}
export function initDashboard({ injector, scope, urlUid, urlSlug, urlType }: InitDashboardArgs): ThunkResult<void> {
return async dispatch => {
const loaderSrv = injector.get('dashboardLoaderSrv');
dispatch(setDashboardLoadingState(DashboardLoadingState.Fetching));
try {
// fetch dashboard from api
const dashDTO = await loaderSrv.loadDashboard(urlType, urlSlug, urlUid);
// set initializing state
dispatch(setDashboardLoadingState(DashboardLoadingState.Initializing));
// create model
const dashboard = new DashboardModel(dashDTO.dashboard, dashDTO.meta);
// init services
injector.get('timeSrv').init(dashboard);
injector.get('annotationsSrv').init(dashboard);
// template values service needs to initialize completely before
// the rest of the dashboard can load
injector.get('variableSrv').init(dashboard)
.catch(err => {
dispatch(notifyApp(createErrorNotification('Templating init failed')));
})
.finally(() => {
dashboard.processRepeats();
dashboard.updateSubmenuVisibility();
dashboard.autoFitPanels(window.innerHeight);
injector.get('unsavedChangesSrv').init(dashboard, scope);
scope.dashboard = dashboard;
injector.get('dashboardViewStateSrv').create(scope);
injector.get('keybindingSrv').setupDashboardBindings(scope, dashboard);
})
.catch(err => {
dispatch(setDashboardLoadingState(DashboardLoadingState.Error));
});
} catch (err) {
dispatch(setDashboardLoadingState(DashboardLoadingState.Error));
}
};
} }
import { Action, ActionTypes } from './actions'; import { Action } from './actions';
import { OrgRole, PermissionLevel, DashboardState } from 'app/types'; import { OrgRole, PermissionLevel, DashboardState } from 'app/types';
import { initialState, dashboardReducer } from './reducers'; import { initialState, dashboardReducer } from './reducers';
...@@ -8,7 +8,7 @@ describe('dashboard reducer', () => { ...@@ -8,7 +8,7 @@ describe('dashboard reducer', () => {
beforeEach(() => { beforeEach(() => {
const action: Action = { const action: Action = {
type: ActionTypes.LoadDashboardPermissions, type: 'LOAD_DASHBOARD_PERMISSIONS',
payload: [ payload: [
{ id: 2, dashboardId: 1, role: OrgRole.Viewer, permission: PermissionLevel.View }, { id: 2, dashboardId: 1, role: OrgRole.Viewer, permission: PermissionLevel.View },
{ id: 3, dashboardId: 1, role: OrgRole.Editor, permission: PermissionLevel.Edit }, { id: 3, dashboardId: 1, role: OrgRole.Editor, permission: PermissionLevel.Edit },
......
import { DashboardState } from 'app/types'; import { DashboardState, DashboardLoadingState } from 'app/types/dashboard';
import { Action, ActionTypes } from './actions'; import { loadDashboardPermissions, setDashboardLoadingState } from './actions';
import { reducerFactory } from 'app/core/redux';
import { processAclItems } from 'app/core/utils/acl'; import { processAclItems } from 'app/core/utils/acl';
export const initialState: DashboardState = { export const initialState: DashboardState = {
loadingState: DashboardLoadingState.NotStarted,
dashboard: null,
permissions: [], permissions: [],
}; };
export const dashboardReducer = (state = initialState, action: Action): DashboardState => { export const dashboardReducer = reducerFactory(initialState)
switch (action.type) { .addMapper({
case ActionTypes.LoadDashboardPermissions: filter: loadDashboardPermissions,
return { mapper: (state, action) => ({
...state, ...state,
permissions: processAclItems(action.payload), permissions: processAclItems(action.payload),
}; }),
} })
return state; .addMapper({
}; filter: setDashboardLoadingState,
mapper: (state, action) => ({
...state,
loadingState: action.payload
}),
})
.create()
export default { export default {
dashboard: dashboardReducer, dashboard: dashboardReducer,
......
import { DashboardAcl } from './acl'; import { DashboardAcl } from './acl';
export interface Dashboard {
}
export enum DashboardLoadingState {
NotStarted = 'Not started',
Fetching = 'Fetching',
Initializing = 'Initializing',
Error = 'Error',
Done = 'Done',
}
export interface DashboardState { export interface DashboardState {
permissions: DashboardAcl[]; dashboard: Dashboard | null;
loadingState: DashboardLoadingState;
permissions: DashboardAcl[] | null;
} }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment