grafana_bak/public/app/features/trails/DataTrailsApp.tsx
2025-04-01 10:38:02 +09:00

164 lines
4.5 KiB
TypeScript

import { css } from '@emotion/css';
import { useEffect, useState } from 'react';
import { Routes, Route } from 'react-router-dom-v5-compat';
import {
DataQueryRequest,
DataSourceGetTagKeysOptions,
DataSourceGetTagValuesOptions,
PageLayoutType,
} from '@grafana/data';
import { config, locationService } from '@grafana/runtime';
import { SceneComponentProps, SceneObjectBase, SceneObjectState, UrlSyncContextProvider } from '@grafana/scenes';
import { useStyles2 } from '@grafana/ui/';
import { Page } from 'app/core/components/Page/Page';
import { getClosestScopesFacade, ScopesFacade, ScopesSelector } from 'app/features/scopes';
import { AppChromeUpdate } from '../../core/components/AppChrome/AppChromeUpdate';
import { DataTrail } from './DataTrail';
import { DataTrailsHome } from './DataTrailsHome';
import { getTrailStore } from './TrailStore/TrailStore';
import { HOME_ROUTE, RefreshMetricsEvent, TRAILS_ROUTE } from './shared';
import { getMetricName, getUrlForTrail, newMetricsTrail } from './utils';
export interface DataTrailsAppState extends SceneObjectState {
trail: DataTrail;
home: DataTrailsHome;
}
export class DataTrailsApp extends SceneObjectBase<DataTrailsAppState> {
private _scopesFacade: ScopesFacade | null;
public constructor(state: DataTrailsAppState) {
super(state);
this._scopesFacade = getClosestScopesFacade(this);
}
public enrichDataRequest(): Partial<DataQueryRequest> {
if (!config.featureToggles.promQLScope) {
return {};
}
return {
scopes: this._scopesFacade?.value,
};
}
public enrichFiltersRequest(): Partial<DataSourceGetTagKeysOptions | DataSourceGetTagValuesOptions> {
if (!config.featureToggles.promQLScope) {
return {};
}
return {
scopes: this._scopesFacade?.value,
};
}
goToUrlForTrail(trail: DataTrail) {
locationService.push(getUrlForTrail(trail));
this.setState({ trail });
}
static Component = ({ model }: SceneComponentProps<DataTrailsApp>) => {
const { trail, home } = model.useState();
return (
<Routes>
{/* The routes are relative to the HOME_ROUTE */}
<Route
path={'/'}
element={
<Page
navId="explore/metrics"
layout={PageLayoutType.Standard}
// Returning null to prevent default behavior which renders a header
renderTitle={() => null}
subTitle=""
>
<home.Component model={home} />
</Page>
}
/>
<Route path={TRAILS_ROUTE.replace(HOME_ROUTE, '')} element={<DataTrailView trail={trail} />} />
</Routes>
);
};
}
function DataTrailView({ trail }: { trail: DataTrail }) {
const styles = useStyles2(getStyles);
const [isInitialized, setIsInitialized] = useState(false);
const { metric } = trail.useState();
useEffect(() => {
if (!isInitialized) {
if (trail.state.metric !== undefined) {
getTrailStore().setRecentTrail(trail);
}
setIsInitialized(true);
}
}, [trail, isInitialized]);
if (!isInitialized) {
return null;
}
return (
<UrlSyncContextProvider scene={trail}>
<Page navId="explore/metrics" pageNav={{ text: getMetricName(metric) }} layout={PageLayoutType.Custom}>
{config.featureToggles.enableScopesInMetricsExplore && (
<AppChromeUpdate
actions={
<div className={styles.topNavContainer}>
<ScopesSelector />
</div>
}
/>
)}
<trail.Component model={trail} />
</Page>
</UrlSyncContextProvider>
);
}
let dataTrailsApp: DataTrailsApp;
export function getDataTrailsApp() {
if (!dataTrailsApp) {
const $behaviors = config.featureToggles.enableScopesInMetricsExplore
? [
new ScopesFacade({
handler: (facade) => {
const trail = facade.parent && 'trail' in facade.parent.state ? facade.parent.state.trail : undefined;
if (trail instanceof DataTrail) {
trail.publishEvent(new RefreshMetricsEvent());
trail.checkDataSourceForOTelResources();
}
},
}),
]
: undefined;
dataTrailsApp = new DataTrailsApp({
trail: newMetricsTrail(),
home: new DataTrailsHome({}),
$behaviors,
});
}
return dataTrailsApp;
}
const getStyles = () => ({
topNavContainer: css({
width: '100%',
height: '100%',
display: 'flex',
flexDirection: 'row',
justifyItems: 'flex-start',
}),
});