From 4ecd33c79ceab85ad2b91b3ab13ba6dac59bee74 Mon Sep 17 00:00:00 2001 From: Peter Holmberg Date: Mon, 8 Oct 2018 14:09:02 +0200 Subject: [PATCH] Fixed nav model --- .../datasources/EditDataSourcePage.tsx | 90 +++++++++++++++++ .../app/features/datasources/state/actions.ts | 27 +++++- .../features/datasources/state/navModel.ts | 97 +++++++++++++++++++ .../features/datasources/state/reducers.ts | 4 + .../features/datasources/state/selectors.ts | 9 ++ public/app/routes/routes.ts | 15 ++- public/app/types/datasources.ts | 1 + 7 files changed, 232 insertions(+), 11 deletions(-) create mode 100644 public/app/features/datasources/EditDataSourcePage.tsx create mode 100644 public/app/features/datasources/state/navModel.ts diff --git a/public/app/features/datasources/EditDataSourcePage.tsx b/public/app/features/datasources/EditDataSourcePage.tsx new file mode 100644 index 00000000000..7c19266d53a --- /dev/null +++ b/public/app/features/datasources/EditDataSourcePage.tsx @@ -0,0 +1,90 @@ +import React, { PureComponent } from 'react'; +import { hot } from 'react-hot-loader'; +import { connect } from 'react-redux'; +import PageHeader from '../../core/components/PageHeader/PageHeader'; +import { DataSource, NavModel } from 'app/types'; +import { loadDataSource } from './state/actions'; +import { getNavModel } from '../../core/selectors/navModel'; +import { getRouteParamsId, getRouteParamsPage } from '../../core/selectors/location'; +import { getDataSourceLoadingNav } from './state/navModel'; +import { getDataSource } from './state/selectors'; + +export interface Props { + navModel: NavModel; + dataSource: DataSource; + dataSourceId: number; + pageName: string; + loadDataSource: typeof loadDataSource; +} + +enum PageTypes { + Settings = 'settings', + Permissions = 'permissions', + Dashboards = 'dashboards', +} + +export class EditDataSourcePage extends PureComponent { + componentDidMount() { + this.fetchDataSource(); + } + + async fetchDataSource() { + await this.props.loadDataSource(this.props.dataSourceId); + } + + isValidPage(currentPage) { + return (Object as any).values(PageTypes).includes(currentPage); + } + + getCurrentPage() { + const currentPage = this.props.pageName; + + return this.isValidPage(currentPage) ? currentPage : PageTypes.Settings; + } + + renderPage() { + switch (this.getCurrentPage()) { + case PageTypes.Settings: + return
Settings
; + + case PageTypes.Permissions: + return
Permissions
; + + case PageTypes.Dashboards: + return
Dashboards
; + } + + return null; + } + + render() { + const { navModel } = this.props; + + return ( +
+ +
+ {this.renderPage()} +
+ ); + } +} + +function mapStateToProps(state) { + const pageName = getRouteParamsPage(state.location) || 'settings'; + const dataSourceId = getRouteParamsId(state.location); + const dataSourceLoadingNav = getDataSourceLoadingNav(pageName); + + return { + navModel: getNavModel(state.navIndex, `datasource-${pageName}-${dataSourceId}`, dataSourceLoadingNav), + dataSourceId: dataSourceId, + dataSource: getDataSource(state.dataSources, dataSourceId), + pageName: pageName, + }; +} + +const mapDispatchToProps = { + loadDataSource, +}; + +export default hot(module)(connect(mapStateToProps, mapDispatchToProps)(EditDataSourcePage)); diff --git a/public/app/features/datasources/state/actions.ts b/public/app/features/datasources/state/actions.ts index 33d6b79c5df..2b6b986774f 100644 --- a/public/app/features/datasources/state/actions.ts +++ b/public/app/features/datasources/state/actions.ts @@ -2,12 +2,14 @@ import { ThunkAction } from 'redux-thunk'; import { DataSource, Plugin, StoreState } from 'app/types'; import { getBackendSrv } from '../../../core/services/backend_srv'; import { LayoutMode } from '../../../core/components/LayoutSelector/LayoutSelector'; -import { updateLocation } from '../../../core/actions'; +import { updateLocation, updateNavIndex, UpdateNavIndexAction } from '../../../core/actions'; import { UpdateLocationAction } from '../../../core/actions/location'; +import { buildNavModel } from './navModel'; export enum ActionTypes { LoadDataSources = 'LOAD_DATA_SOURCES', LoadDataSourceTypes = 'LOAD_DATA_SOURCE_TYPES', + LoadDataSource = 'LOAD_DATA_SOURCE', SetDataSourcesSearchQuery = 'SET_DATA_SOURCES_SEARCH_QUERY', SetDataSourcesLayoutMode = 'SET_DATA_SOURCES_LAYOUT_MODE', SetDataSourceTypeSearchQuery = 'SET_DATA_SOURCE_TYPE_SEARCH_QUERY', @@ -38,11 +40,21 @@ export interface SetDataSourceTypeSearchQueryAction { payload: string; } +export interface LoadDataSourceAction { + type: ActionTypes.LoadDataSource; + payload: DataSource; +} + const dataSourcesLoaded = (dataSources: DataSource[]): LoadDataSourcesAction => ({ type: ActionTypes.LoadDataSources, payload: dataSources, }); +const dataSourceLoaded = (dataSource: DataSource): LoadDataSourceAction => ({ + type: ActionTypes.LoadDataSource, + payload: dataSource, +}); + const dataSourceTypesLoaded = (dataSourceTypes: Plugin[]): LoadDataSourceTypesAction => ({ type: ActionTypes.LoadDataSourceTypes, payload: dataSourceTypes, @@ -69,7 +81,9 @@ export type Action = | SetDataSourcesLayoutModeAction | UpdateLocationAction | LoadDataSourceTypesAction - | SetDataSourceTypeSearchQueryAction; + | SetDataSourceTypeSearchQueryAction + | LoadDataSourceAction + | UpdateNavIndexAction; type ThunkResult = ThunkAction; @@ -80,6 +94,15 @@ export function loadDataSources(): ThunkResult { }; } +export function loadDataSource(id: number): ThunkResult { + return async dispatch => { + const dataSource = await getBackendSrv().get(`/api/datasources/${id}`); + const pluginInfo = await getBackendSrv().get(`/api/plugins/${dataSource.type}/settings`); + dispatch(dataSourceLoaded(dataSource)); + dispatch(updateNavIndex(buildNavModel(dataSource, pluginInfo))); + }; +} + export function addDataSource(plugin: Plugin): ThunkResult { return async (dispatch, getStore) => { await dispatch(loadDataSources()); diff --git a/public/app/features/datasources/state/navModel.ts b/public/app/features/datasources/state/navModel.ts new file mode 100644 index 00000000000..d80ab5d52a2 --- /dev/null +++ b/public/app/features/datasources/state/navModel.ts @@ -0,0 +1,97 @@ +import { DataSource, NavModel, NavModelItem, PluginMeta } from 'app/types'; + +export function buildNavModel(dataSource: DataSource, pluginMeta: PluginMeta): NavModelItem { + const navModel = { + img: pluginMeta.info.logos.large, + id: 'datasource-' + dataSource.id, + subTitle: `Type: ${pluginMeta.name}`, + url: '', + text: dataSource.name, + breadcrumbs: [{ title: 'Data Sources', url: 'datasources' }], + children: [ + { + active: false, + icon: 'fa fa-fw fa-sliders', + id: `datasource-settings-${dataSource.id}`, + text: 'Settings', + url: `datasources/edit/${dataSource.id}/settings`, + }, + { + active: false, + icon: 'fa fa-fw fa-sliders', + id: `datasource-permissions-${dataSource.id}`, + text: 'Permissions', + url: `datasources/edit/${dataSource.id}/permissions`, + }, + ], + }; + + if (pluginMeta.includes && pluginMeta.includes.length > 0) { + navModel.children.push({ + active: false, + icon: 'gicon gicon-dashboard', + id: `datasource-dashboards-${dataSource.id}`, + text: 'Dashboards', + url: `datasources/edit/${dataSource.id}/dashboards`, + }); + } + + return navModel; +} + +export function getDataSourceLoadingNav(pageName: string): NavModel { + const main = buildNavModel( + { + access: '', + basicAuth: false, + database: '', + id: 1, + isDefault: false, + jsonData: { authType: 'credentials', defaultRegion: 'eu-west-2' }, + name: 'Loading', + orgId: 1, + password: '', + readOnly: false, + type: 'Loading', + typeLogoUrl: 'public/img/icn-datasource.svg', + url: '', + user: '', + }, + { + id: '1', + name: '', + info: { + author: { + name: '', + url: '', + }, + description: '', + links: [''], + logos: { + large: '', + small: '', + }, + screenshots: '', + updated: '', + version: '', + }, + includes: [{ type: '', name: '', path: '' }], + } + ); + + let node: NavModelItem; + + // find active page + for (const child of main.children) { + if (child.id.indexOf(pageName) > 0) { + child.active = true; + node = child; + break; + } + } + + return { + main: main, + node: node, + }; +} diff --git a/public/app/features/datasources/state/reducers.ts b/public/app/features/datasources/state/reducers.ts index acb228d3ed6..075483945a4 100644 --- a/public/app/features/datasources/state/reducers.ts +++ b/public/app/features/datasources/state/reducers.ts @@ -4,6 +4,7 @@ import { LayoutModes } from '../../../core/components/LayoutSelector/LayoutSelec const initialState: DataSourcesState = { dataSources: [] as DataSource[], + dataSource: {} as DataSource, layoutMode: LayoutModes.Grid, searchQuery: '', dataSourcesCount: 0, @@ -16,6 +17,9 @@ export const dataSourcesReducer = (state = initialState, action: Action): DataSo case ActionTypes.LoadDataSources: return { ...state, dataSources: action.payload, dataSourcesCount: action.payload.length }; + case ActionTypes.LoadDataSource: + return { ...state, dataSource: action.payload }; + case ActionTypes.SetDataSourcesSearchQuery: return { ...state, searchQuery: action.payload }; diff --git a/public/app/features/datasources/state/selectors.ts b/public/app/features/datasources/state/selectors.ts index 80e1400114f..eef176eb49a 100644 --- a/public/app/features/datasources/state/selectors.ts +++ b/public/app/features/datasources/state/selectors.ts @@ -1,3 +1,5 @@ +import { DataSource } from '../../../types'; + export const getDataSources = state => { const regex = new RegExp(state.searchQuery, 'i'); @@ -14,6 +16,13 @@ export const getDataSourceTypes = state => { }); }; +export const getDataSource = (state, dataSourceId): DataSource | null => { + if (state.dataSource.id === parseInt(dataSourceId, 10)) { + return state.dataSource; + } + return null; +}; + export const getDataSourcesSearchQuery = state => state.searchQuery; export const getDataSourcesLayoutMode = state => state.layoutMode; export const getDataSourcesCount = state => state.dataSourcesCount; diff --git a/public/app/routes/routes.ts b/public/app/routes/routes.ts index 9aecf53e7bb..54f4a6718e1 100644 --- a/public/app/routes/routes.ts +++ b/public/app/routes/routes.ts @@ -12,6 +12,7 @@ import FolderPermissions from 'app/features/folders/FolderPermissions'; import DataSourcesListPage from 'app/features/datasources/DataSourcesListPage'; import NewDataSourcePage from '../features/datasources/NewDataSourcePage'; import UsersListPage from 'app/features/users/UsersListPage'; +import EditDataSourcePage from 'app/features/datasources/EditDataSourcePage'; /** @ngInject */ export function setupAngularRoutes($routeProvider, $locationProvider) { @@ -71,15 +72,11 @@ export function setupAngularRoutes($routeProvider, $locationProvider) { component: () => DataSourcesListPage, }, }) - .when('/datasources/edit/:id', { - templateUrl: 'public/app/features/plugins/partials/ds_edit.html', - controller: 'DataSourceEditCtrl', - controllerAs: 'ctrl', - }) - .when('/datasources/edit/:id/dashboards', { - templateUrl: 'public/app/features/plugins/partials/ds_dashboards.html', - controller: 'DataSourceDashboardsCtrl', - controllerAs: 'ctrl', + .when('/datasources/edit/:id/:page?', { + template: '', + resolve: { + component: () => EditDataSourcePage, + }, }) .when('/datasources/new', { template: '', diff --git a/public/app/types/datasources.ts b/public/app/types/datasources.ts index 4d8d755f106..9d35794b89e 100644 --- a/public/app/types/datasources.ts +++ b/public/app/types/datasources.ts @@ -25,4 +25,5 @@ export interface DataSourcesState { layoutMode: LayoutMode; dataSourcesCount: number; dataSourceTypes: Plugin[]; + dataSource: DataSource; }