import { css } from '@emotion/css';
import * as React from 'react';
import { useLocation } from 'react-router-dom-v5-compat';
import { useMedia } from 'react-use';
import { GrafanaTheme2, NavModelItem } from '@grafana/data';
import { config } from '@grafana/runtime';
import { Alert, Box, Stack, TabContent, useStyles2 } from '@grafana/ui';
import { Page } from 'app/core/components/Page/Page';
import { AppNotificationSeverity } from 'app/types';
import { AngularDeprecationPluginNotice } from '../../angularDeprecation/AngularDeprecationPluginNotice';
import { Loader } from '../components/Loader';
import { PluginDetailsBody } from '../components/PluginDetailsBody';
import { PluginDetailsDisabledError } from '../components/PluginDetailsDisabledError';
import { PluginDetailsPanel } from '../components/PluginDetailsPanel';
import { PluginDetailsSignature } from '../components/PluginDetailsSignature';
import { usePluginDetailsTabs } from '../hooks/usePluginDetailsTabs';
import { usePluginPageExtensions } from '../hooks/usePluginPageExtensions';
import { useGetSingle, useFetchStatus, useFetchDetailsStatus } from '../state/hooks';
import { PluginTabIds } from '../types';
import { PluginDetailsDeprecatedWarning } from './PluginDetailsDeprecatedWarning';
export type Props = {
// The ID of the plugin
pluginId: string;
// The navigation ID used for displaying the sidebar navigation
navId?: string;
// Can be used to customise the title & subtitle for the not found page
notFoundNavModel?: NavModelItem;
// Can be used to customise the content shown when a plugin with the given ID cannot be found
notFoundComponent?: React.ReactElement;
};
export function PluginDetailsPage({
pluginId,
navId = 'plugins',
notFoundComponent = ,
notFoundNavModel = {
text: 'Unknown plugin',
subTitle: 'The requested ID does not belong to any plugin',
active: true,
},
}: Props) {
const location = useLocation();
const queryParams = new URLSearchParams(location.search);
const plugin = useGetSingle(pluginId); // fetches the plugin settings for this Grafana instance
const isNarrowScreen = useMedia('(max-width: 600px)');
const { navModel, activePageId } = usePluginDetailsTabs(
plugin,
queryParams.get('page') as PluginTabIds,
isNarrowScreen
);
const { actions, info, subtitle } = usePluginPageExtensions(plugin);
const { isLoading: isFetchLoading } = useFetchStatus();
const { isLoading: isFetchDetailsLoading } = useFetchDetailsStatus();
const styles = useStyles2(getStyles);
if (isFetchLoading || isFetchDetailsLoading) {
return (
);
}
if (!plugin) {
return (
{notFoundComponent}
);
}
const conditionalProps = !config.featureToggles.pluginsDetailsRightPanel ? { info: info } : {};
return (
{plugin.angularDetected && (
)}
{!isNarrowScreen && config.featureToggles.pluginsDetailsRightPanel && (
)}
);
}
export const getStyles = (theme: GrafanaTheme2) => {
return {
alert: css({
marginBottom: theme.spacing(2),
}),
subtitle: css({
display: 'flex',
flexDirection: 'column',
gap: theme.spacing(1),
}),
// Needed due to block formatting context
tabContent: css({
paddingLeft: '5px',
width: '100%',
}),
};
};
function NotFoundPlugin() {
return (
That plugin cannot be found. Please check the url is correct or
go to the plugin catalog.
);
}