2021-07-08 17:50:42 +02:00
|
|
|
import { css } from '@emotion/css';
|
2024-08-27 13:38:39 +02:00
|
|
|
import { ReactElement, useState } from 'react';
|
2024-08-23 09:54:13 +03:00
|
|
|
import { useLocation } from 'react-router-dom-v5-compat';
|
2022-04-22 14:33:13 +01:00
|
|
|
|
2023-05-05 09:38:18 +02:00
|
|
|
import { SelectableValue, GrafanaTheme2, PluginType } from '@grafana/data';
|
2024-09-05 15:04:37 +02:00
|
|
|
import { locationSearchToObject } from '@grafana/runtime';
|
2024-08-27 13:38:39 +02:00
|
|
|
import { Select, RadioButtonGroup, useStyles2, Tooltip, Field, Button } from '@grafana/ui';
|
2022-04-22 14:33:13 +01:00
|
|
|
import { Page } from 'app/core/components/Page/Page';
|
2024-08-27 13:38:39 +02:00
|
|
|
import { Trans } from 'app/core/internationalization';
|
2021-08-04 11:49:05 +02:00
|
|
|
import { GrafanaRouteComponentProps } from 'app/core/navigation/types';
|
2022-04-22 14:33:13 +01:00
|
|
|
import { getNavModel } from 'app/core/selectors/navModel';
|
2023-04-05 10:23:25 +02:00
|
|
|
import { ROUTES as CONNECTIONS_ROUTES } from 'app/features/connections/constants';
|
2022-09-19 10:49:35 +01:00
|
|
|
import { useSelector } from 'app/types';
|
2022-04-22 14:33:13 +01:00
|
|
|
|
|
|
|
|
import { HorizontalGroup } from '../components/HorizontalGroup';
|
2021-07-08 17:50:42 +02:00
|
|
|
import { PluginList } from '../components/PluginList';
|
2024-07-31 13:12:16 +02:00
|
|
|
import { RoadmapLinks } from '../components/RoadmapLinks';
|
2021-07-08 17:50:42 +02:00
|
|
|
import { SearchField } from '../components/SearchField';
|
2024-08-27 13:38:39 +02:00
|
|
|
import { UpdateAllModal } from '../components/UpdateAllModal';
|
2022-04-22 14:33:13 +01:00
|
|
|
import { Sorters } from '../helpers';
|
2021-07-08 17:50:42 +02:00
|
|
|
import { useHistory } from '../hooks/useHistory';
|
2024-08-27 13:38:39 +02:00
|
|
|
import { useGetAll, useGetUpdatable, useIsRemotePluginsAvailable } from '../state/hooks';
|
2021-07-08 17:50:42 +02:00
|
|
|
|
2021-08-04 11:49:05 +02:00
|
|
|
export default function Browse({ route }: GrafanaRouteComponentProps): ReactElement | null {
|
2021-07-08 17:50:42 +02:00
|
|
|
const location = useLocation();
|
2021-09-09 12:20:35 +02:00
|
|
|
const locationSearch = locationSearchToObject(location.search);
|
2022-09-19 10:49:35 +01:00
|
|
|
const navModel = useSelector((state) => getNavModel(state.navIndex, 'plugins'));
|
2021-07-30 13:23:33 +02:00
|
|
|
const styles = useStyles2(getStyles);
|
2021-07-08 17:50:42 +02:00
|
|
|
const history = useHistory();
|
2021-09-28 16:46:29 +02:00
|
|
|
const remotePluginsAvailable = useIsRemotePluginsAvailable();
|
2023-08-07 09:32:13 +01:00
|
|
|
const keyword = locationSearch.q?.toString() || '';
|
2024-05-07 14:48:09 +01:00
|
|
|
const filterBy = locationSearch.filterBy?.toString() || 'all';
|
2023-05-05 09:38:18 +02:00
|
|
|
const filterByType = (locationSearch.filterByType as PluginType | 'all') || 'all';
|
2021-09-09 12:20:35 +02:00
|
|
|
const sortBy = (locationSearch.sortBy as Sorters) || Sorters.nameAsc;
|
2023-05-05 09:38:18 +02:00
|
|
|
const { isLoading, error, plugins } = useGetAll(
|
|
|
|
|
{
|
|
|
|
|
keyword,
|
|
|
|
|
type: filterByType !== 'all' ? filterByType : undefined,
|
|
|
|
|
isInstalled: filterBy === 'installed' ? true : undefined,
|
2024-08-06 12:16:49 +02:00
|
|
|
hasUpdate: filterBy === 'has-update' ? true : undefined,
|
2023-05-05 09:38:18 +02:00
|
|
|
},
|
|
|
|
|
sortBy
|
|
|
|
|
);
|
2024-02-14 14:30:24 +01:00
|
|
|
|
2021-09-28 16:46:29 +02:00
|
|
|
const filterByOptions = [
|
|
|
|
|
{ value: 'all', label: 'All' },
|
|
|
|
|
{ value: 'installed', label: 'Installed' },
|
2024-08-06 12:16:49 +02:00
|
|
|
{ value: 'has-update', label: 'New Updates' },
|
2021-09-28 16:46:29 +02:00
|
|
|
];
|
2021-07-08 17:50:42 +02:00
|
|
|
|
2024-08-27 13:38:39 +02:00
|
|
|
const updatablePlugins = useGetUpdatable();
|
|
|
|
|
const [showUpdateModal, setShowUpdateModal] = useState(false);
|
|
|
|
|
const disableUpdateAllButton = updatablePlugins.length <= 0;
|
|
|
|
|
|
2021-07-08 17:50:42 +02:00
|
|
|
const onSortByChange = (value: SelectableValue<string>) => {
|
|
|
|
|
history.push({ query: { sortBy: value.value } });
|
|
|
|
|
};
|
|
|
|
|
|
2021-07-30 13:23:33 +02:00
|
|
|
const onFilterByChange = (value: string) => {
|
|
|
|
|
history.push({ query: { filterBy: value } });
|
|
|
|
|
};
|
|
|
|
|
|
2023-01-11 23:14:57 +01:00
|
|
|
const onFilterByTypeChange = (value: SelectableValue<string>) => {
|
|
|
|
|
history.push({ query: { filterByType: value.value } });
|
2021-07-08 17:50:42 +02:00
|
|
|
};
|
|
|
|
|
|
2022-08-22 16:51:33 +01:00
|
|
|
const onSearch = (q: string) => {
|
2023-04-18 08:15:35 +02:00
|
|
|
history.push({ query: { filterBy, filterByType, q } });
|
2021-07-08 17:50:42 +02:00
|
|
|
};
|
|
|
|
|
|
2024-08-27 13:38:39 +02:00
|
|
|
const onUpdateAll = () => {
|
|
|
|
|
setShowUpdateModal(true);
|
|
|
|
|
};
|
|
|
|
|
|
2021-07-20 15:20:24 +02:00
|
|
|
// How should we handle errors?
|
|
|
|
|
if (error) {
|
|
|
|
|
console.error(error.message);
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
2023-07-24 10:54:52 +02:00
|
|
|
const subTitle = (
|
2023-04-19 11:10:03 +02:00
|
|
|
<div>
|
2023-04-05 10:23:25 +02:00
|
|
|
Extend the Grafana experience with panel plugins and apps. To find more data sources go to{' '}
|
2023-05-02 10:51:59 +02:00
|
|
|
<a className="external-link" href={`${CONNECTIONS_ROUTES.AddNewConnection}?cat=data-source`}>
|
2023-04-19 11:10:03 +02:00
|
|
|
Connections
|
|
|
|
|
</a>
|
|
|
|
|
.
|
|
|
|
|
</div>
|
2023-04-05 10:23:25 +02:00
|
|
|
);
|
2024-08-27 13:38:39 +02:00
|
|
|
const updateAll = (
|
|
|
|
|
<Button disabled={disableUpdateAllButton} onClick={onUpdateAll}>
|
|
|
|
|
<Trans i18nKey="plugins.catalog.update-all.button">Update all</Trans>
|
|
|
|
|
{!disableUpdateAllButton ? ` (${updatablePlugins.length})` : ''}
|
|
|
|
|
</Button>
|
|
|
|
|
);
|
2023-04-05 10:23:25 +02:00
|
|
|
|
2021-07-08 17:50:42 +02:00
|
|
|
return (
|
2024-09-05 15:04:37 +02:00
|
|
|
<Page navModel={navModel} actions={updateAll} subTitle={subTitle}>
|
2021-07-08 17:50:42 +02:00
|
|
|
<Page.Contents>
|
2022-04-27 09:56:44 +02:00
|
|
|
<HorizontalGroup wrap>
|
2023-01-11 23:14:57 +01:00
|
|
|
<Field label="Search">
|
2023-05-05 09:38:18 +02:00
|
|
|
<SearchField value={keyword} onSearch={onSearch} />
|
2023-01-11 23:14:57 +01:00
|
|
|
</Field>
|
2022-04-27 09:56:44 +02:00
|
|
|
<HorizontalGroup wrap className={styles.actionBar}>
|
|
|
|
|
{/* Filter by type */}
|
2023-01-11 23:14:57 +01:00
|
|
|
<Field label="Type">
|
|
|
|
|
<Select
|
|
|
|
|
aria-label="Plugin type filter"
|
2022-04-27 09:56:44 +02:00
|
|
|
value={filterByType}
|
|
|
|
|
onChange={onFilterByTypeChange}
|
2023-01-11 23:14:57 +01:00
|
|
|
width={18}
|
2022-04-27 09:56:44 +02:00
|
|
|
options={[
|
|
|
|
|
{ value: 'all', label: 'All' },
|
|
|
|
|
{ value: 'datasource', label: 'Data sources' },
|
|
|
|
|
{ value: 'panel', label: 'Panels' },
|
|
|
|
|
{ value: 'app', label: 'Applications' },
|
|
|
|
|
]}
|
|
|
|
|
/>
|
2023-01-11 23:14:57 +01:00
|
|
|
</Field>
|
2022-04-27 09:56:44 +02:00
|
|
|
|
|
|
|
|
{/* Filter by installed / all */}
|
|
|
|
|
{remotePluginsAvailable ? (
|
2023-01-11 23:14:57 +01:00
|
|
|
<Field label="State">
|
2022-04-27 09:56:44 +02:00
|
|
|
<RadioButtonGroup value={filterBy} onChange={onFilterByChange} options={filterByOptions} />
|
2023-01-11 23:14:57 +01:00
|
|
|
</Field>
|
2022-04-27 09:56:44 +02:00
|
|
|
) : (
|
|
|
|
|
<Tooltip
|
|
|
|
|
content="This filter has been disabled because the Grafana server cannot access grafana.com"
|
|
|
|
|
placement="top"
|
|
|
|
|
>
|
2021-09-28 16:46:29 +02:00
|
|
|
<div>
|
2023-01-11 23:14:57 +01:00
|
|
|
<Field label="State">
|
|
|
|
|
<RadioButtonGroup
|
|
|
|
|
disabled={true}
|
|
|
|
|
value={filterBy}
|
|
|
|
|
onChange={onFilterByChange}
|
|
|
|
|
options={filterByOptions}
|
|
|
|
|
/>
|
|
|
|
|
</Field>
|
2021-09-28 16:46:29 +02:00
|
|
|
</div>
|
2022-04-27 09:56:44 +02:00
|
|
|
</Tooltip>
|
|
|
|
|
)}
|
2021-09-29 15:44:57 +02:00
|
|
|
|
2022-04-27 09:56:44 +02:00
|
|
|
{/* Sorting */}
|
2023-01-11 23:14:57 +01:00
|
|
|
<Field label="Sort">
|
2022-04-27 09:56:44 +02:00
|
|
|
<Select
|
|
|
|
|
aria-label="Sort Plugins List"
|
|
|
|
|
width={24}
|
|
|
|
|
value={sortBy}
|
|
|
|
|
onChange={onSortByChange}
|
|
|
|
|
options={[
|
2023-01-11 23:14:57 +01:00
|
|
|
{ value: 'nameAsc', label: 'By name (A-Z)' },
|
|
|
|
|
{ value: 'nameDesc', label: 'By name (Z-A)' },
|
|
|
|
|
{ value: 'updated', label: 'By updated date' },
|
|
|
|
|
{ value: 'published', label: 'By published date' },
|
|
|
|
|
{ value: 'downloads', label: 'By downloads' },
|
2022-04-27 09:56:44 +02:00
|
|
|
]}
|
|
|
|
|
/>
|
2023-01-11 23:14:57 +01:00
|
|
|
</Field>
|
2022-04-27 09:56:44 +02:00
|
|
|
</HorizontalGroup>
|
|
|
|
|
</HorizontalGroup>
|
|
|
|
|
<div className={styles.listWrap}>
|
2024-08-14 10:24:10 +02:00
|
|
|
<PluginList plugins={plugins} isLoading={isLoading} />
|
2022-04-27 09:56:44 +02:00
|
|
|
</div>
|
2024-07-31 13:12:16 +02:00
|
|
|
<RoadmapLinks />
|
2024-08-27 13:38:39 +02:00
|
|
|
<UpdateAllModal
|
|
|
|
|
isOpen={showUpdateModal}
|
|
|
|
|
onDismiss={() => setShowUpdateModal(false)}
|
|
|
|
|
plugins={updatablePlugins}
|
|
|
|
|
/>
|
2021-07-08 17:50:42 +02:00
|
|
|
</Page.Contents>
|
|
|
|
|
</Page>
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2021-07-30 13:23:33 +02:00
|
|
|
const getStyles = (theme: GrafanaTheme2) => ({
|
2023-12-05 16:39:23 +00:00
|
|
|
actionBar: css({
|
|
|
|
|
[theme.breakpoints.up('xl')]: {
|
|
|
|
|
marginLeft: 'auto',
|
|
|
|
|
},
|
|
|
|
|
}),
|
|
|
|
|
listWrap: css({
|
|
|
|
|
marginTop: theme.spacing(2),
|
|
|
|
|
}),
|
|
|
|
|
displayAs: css({
|
|
|
|
|
svg: {
|
|
|
|
|
marginRight: 0,
|
|
|
|
|
},
|
|
|
|
|
}),
|
2021-07-30 13:23:33 +02:00
|
|
|
});
|