import { css } from '@emotion/css'; import { useMemo, useState, MouseEvent } from 'react'; import { useLocation } from 'react-router-dom-v5-compat'; import { PluginType, GrafanaTheme2, SelectableValue } from '@grafana/data'; import { locationSearchToObject, reportInteraction } from '@grafana/runtime'; import { LoadingPlaceholder, EmptyState, Field, RadioButtonGroup, Tooltip, Combobox, useStyles2 } from '@grafana/ui'; import { contextSrv } from 'app/core/core'; import { useQueryParams } from 'app/core/hooks/useQueryParams'; import { t, Trans } from 'app/core/internationalization'; import { HorizontalGroup } from 'app/features/plugins/admin/components/HorizontalGroup'; import { RoadmapLinks } from 'app/features/plugins/admin/components/RoadmapLinks'; import { SearchField } from 'app/features/plugins/admin/components/SearchField'; import { Sorters } from 'app/features/plugins/admin/helpers'; import { useHistory } from 'app/features/plugins/admin/hooks/useHistory'; import { useGetAll, useIsRemotePluginsAvailable } from 'app/features/plugins/admin/state/hooks'; import { AccessControlAction } from 'app/types'; import { ROUTES } from '../../constants'; import { CardGrid, type CardGridItem } from './CardGrid'; import { CategoryHeader } from './CategoryHeader'; import { NoAccessModal } from './NoAccessModal'; const getStyles = (theme: GrafanaTheme2) => ({ spacer: css({ height: theme.spacing(2), }), modal: css({ width: '500px', }), modalContent: css({ overflow: 'visible', }), actionBar: css({ [theme.breakpoints.up('xl')]: { marginLeft: 'auto', }, }), }); export function AddNewConnection() { const [queryParams, setQueryParams] = useQueryParams(); const searchTerm = queryParams.search ? String(queryParams.search) : ''; const [isNoAccessModalOpen, setIsNoAccessModalOpen] = useState(false); const [focusedItem, setFocusedItem] = useState(null); const location = useLocation(); const history = useHistory(); const locationSearch = locationSearchToObject(location.search); const sortBy = (locationSearch.sortBy as Sorters) || Sorters.nameAsc; const filterBy = locationSearch.filterBy?.toString() || 'all'; const canCreateDataSources = contextSrv.hasPermission(AccessControlAction.DataSourcesCreate); const styles = useStyles2(getStyles); const handleSearchChange = (val: string) => { setQueryParams({ search: val, }); }; const remotePluginsAvailable = useIsRemotePluginsAvailable(); const { error, plugins, isLoading } = useGetAll( { keyword: searchTerm, type: PluginType.datasource, isInstalled: filterBy === 'installed' ? true : undefined, hasUpdate: filterBy === 'has-update' ? true : undefined, }, sortBy ); const filterByOptions = [ { value: 'all', label: 'All' }, { value: 'installed', label: 'Installed' }, { value: 'has-update', label: 'New Updates' }, ]; const onClickCardGridItem = (e: MouseEvent, item: CardGridItem) => { if (!canCreateDataSources) { e.preventDefault(); e.stopPropagation(); openModal(item); reportInteraction('connections_plugin_card_clicked', { plugin_id: item.id, creator_team: 'grafana_plugins_catalog', schema_version: '1.0.0', }); } }; const openModal = (item: CardGridItem) => { setIsNoAccessModalOpen(true); setFocusedItem(item); }; const closeModal = () => { setIsNoAccessModalOpen(false); setFocusedItem(null); }; const cardGridItems = useMemo( () => plugins.map((plugin) => ({ ...plugin, logo: plugin.info.logos.small, url: ROUTES.DataSourcesDetails.replace(':id', plugin.id), })), [plugins] ); const onSortByChange = (value: SelectableValue) => { history.push({ query: { sortBy: value.value } }); }; const onFilterByChange = (value: string) => { history.push({ query: { filterBy: value } }); }; const showNoResults = useMemo(() => !isLoading && !error && plugins.length < 1, [isLoading, error, plugins]); const categoryHeaderLabel = t('connections.connect-data.category-header-label', 'Data sources'); return ( <> {focusedItem && } {/* Filter by installed / all */} {remotePluginsAvailable ? ( ) : (
)} {/* Sorting */}
{isLoading ? ( ) : !!error ? ( Error message: "{{ error: error.message }}" ) : ( )} {showNoResults && ( )} ); }