mirror of
https://github.com/grafana/grafana.git
synced 2025-02-13 00:55:47 -06:00
* navtree.go: update Data sources title and subtitle * DataSourceList: move add button to header * DataSourcesList: add buttons to items The action buttons are added inside `<Card.Tags>` so that they end up at the right end of the card, as it was designed. The "Build a Dashboard" button's functionality is not defined yet. * DataSourcesListHeader: add sort picker * fix css * tests: look for the updated "Add new data source" text * tests: use an async test method to verify component updates are wrapped in an act() * update e2e selector for add data source button * fix DataSourceList{,Page} tests * add comment for en dash character * simplify sorting * add link to Build a Dashboard button * fix test * test build a dashboard and explore buttons * test sorting data source elements * DataSourceAddButton: hide button when user has no permission * PageActionBar: remove unneeded '?' * DataSourcesList: hide explore button if user has no permission * DataSourcesListPage.test: make setup prop explicit * DataSourcesList: use theme.spacing * datasources: assure explore url includes appSubUrl * fix tests and add test case for missing permissions Co-authored-by: Levente Balogh <balogh.levente.hu@gmail.com>
165 lines
4.7 KiB
TypeScript
165 lines
4.7 KiB
TypeScript
import { useContext, useEffect } from 'react';
|
|
|
|
import { DataSourcePluginMeta, DataSourceSettings, NavModelItem } from '@grafana/data';
|
|
import { cleanUpAction } from 'app/core/actions/cleanUp';
|
|
import appEvents from 'app/core/app_events';
|
|
import { contextSrv } from 'app/core/core';
|
|
import { getNavModel } from 'app/core/selectors/navModel';
|
|
import { AccessControlAction, useDispatch, useSelector } from 'app/types';
|
|
import { ShowConfirmModalEvent } from 'app/types/events';
|
|
|
|
import { DataSourceRights } from '../types';
|
|
import { constructDataSourceExploreUrl } from '../utils';
|
|
|
|
import {
|
|
initDataSourceSettings,
|
|
testDataSource,
|
|
loadDataSource,
|
|
loadDataSources,
|
|
loadDataSourcePlugins,
|
|
addDataSource,
|
|
updateDataSource,
|
|
deleteLoadedDataSource,
|
|
} from './actions';
|
|
import { DataSourcesRoutesContext } from './contexts';
|
|
import { getDataSourceLoadingNav, buildNavModel, getDataSourceNav } from './navModel';
|
|
import { getDataSource, getDataSourceMeta } from './selectors';
|
|
|
|
export const useInitDataSourceSettings = (uid: string) => {
|
|
const dispatch = useDispatch();
|
|
|
|
useEffect(() => {
|
|
dispatch(initDataSourceSettings(uid));
|
|
|
|
return function cleanUp() {
|
|
dispatch(
|
|
cleanUpAction({
|
|
cleanupAction: (state) => state.dataSourceSettings,
|
|
})
|
|
);
|
|
};
|
|
}, [uid, dispatch]);
|
|
};
|
|
|
|
export const useTestDataSource = (uid: string) => {
|
|
const dispatch = useDispatch();
|
|
|
|
return () => dispatch(testDataSource(uid));
|
|
};
|
|
|
|
export const useLoadDataSources = () => {
|
|
const dispatch = useDispatch();
|
|
|
|
useEffect(() => {
|
|
dispatch(loadDataSources());
|
|
}, [dispatch]);
|
|
};
|
|
|
|
export const useLoadDataSource = (uid: string) => {
|
|
const dispatch = useDispatch();
|
|
|
|
useEffect(() => {
|
|
dispatch(loadDataSource(uid));
|
|
}, [dispatch, uid]);
|
|
};
|
|
|
|
export const useLoadDataSourcePlugins = () => {
|
|
const dispatch = useDispatch();
|
|
|
|
useEffect(() => {
|
|
dispatch(loadDataSourcePlugins());
|
|
}, [dispatch]);
|
|
};
|
|
|
|
export const useAddDatasource = () => {
|
|
const dispatch = useDispatch();
|
|
const dataSourcesRoutes = useDataSourcesRoutes();
|
|
|
|
return (plugin: DataSourcePluginMeta) => {
|
|
dispatch(addDataSource(plugin, dataSourcesRoutes.Edit));
|
|
};
|
|
};
|
|
|
|
export const useUpdateDatasource = () => {
|
|
const dispatch = useDispatch();
|
|
|
|
return async (dataSource: DataSourceSettings) => dispatch(updateDataSource(dataSource));
|
|
};
|
|
|
|
export const useDeleteLoadedDataSource = () => {
|
|
const dispatch = useDispatch();
|
|
const { name } = useSelector((state) => state.dataSources.dataSource);
|
|
|
|
return () => {
|
|
appEvents.publish(
|
|
new ShowConfirmModalEvent({
|
|
title: 'Delete',
|
|
text: `Are you sure you want to delete the "${name}" data source?`,
|
|
yesText: 'Delete',
|
|
icon: 'trash-alt',
|
|
onConfirm: () => dispatch(deleteLoadedDataSource()),
|
|
})
|
|
);
|
|
};
|
|
};
|
|
|
|
export const useDataSource = (uid: string) => {
|
|
return useSelector((state) => getDataSource(state.dataSources, uid));
|
|
};
|
|
|
|
export const useDataSourceExploreUrl = (uid: string) => {
|
|
const dataSource = useDataSource(uid);
|
|
return constructDataSourceExploreUrl(dataSource);
|
|
};
|
|
|
|
export const useDataSourceMeta = (pluginType: string): DataSourcePluginMeta => {
|
|
return useSelector((state) => getDataSourceMeta(state.dataSources, pluginType));
|
|
};
|
|
|
|
export const useDataSourceSettings = () => {
|
|
return useSelector((state) => state.dataSourceSettings);
|
|
};
|
|
|
|
export const useDataSourceSettingsNav = (dataSourceId: string, pageId: string | null) => {
|
|
const dataSource = useDataSource(dataSourceId);
|
|
const { plugin, loadError, loading } = useDataSourceSettings();
|
|
const navIndex = useSelector((state) => state.navIndex);
|
|
const navIndexId = pageId ? `datasource-${pageId}-${dataSourceId}` : `datasource-settings-${dataSourceId}`;
|
|
|
|
if (loadError) {
|
|
const node: NavModelItem = {
|
|
text: loadError,
|
|
subTitle: 'Data Source Error',
|
|
icon: 'exclamation-triangle',
|
|
};
|
|
|
|
return {
|
|
node: node,
|
|
main: node,
|
|
};
|
|
}
|
|
|
|
if (loading || !plugin) {
|
|
return getNavModel(navIndex, navIndexId, getDataSourceLoadingNav('settings'));
|
|
}
|
|
|
|
return getNavModel(navIndex, navIndexId, getDataSourceNav(buildNavModel(dataSource, plugin), pageId || 'settings'));
|
|
};
|
|
|
|
export const useDataSourceRights = (uid: string): DataSourceRights => {
|
|
const dataSource = useDataSource(uid);
|
|
const readOnly = dataSource.readOnly === true;
|
|
const hasWriteRights = contextSrv.hasPermissionInMetadata(AccessControlAction.DataSourcesWrite, dataSource);
|
|
const hasDeleteRights = contextSrv.hasPermissionInMetadata(AccessControlAction.DataSourcesDelete, dataSource);
|
|
|
|
return {
|
|
readOnly,
|
|
hasWriteRights,
|
|
hasDeleteRights,
|
|
};
|
|
};
|
|
|
|
export const useDataSourcesRoutes = () => {
|
|
return useContext(DataSourcesRoutesContext);
|
|
};
|