Files
2024-02-15 17:01:50 +00:00

180 lines
5.2 KiB
TypeScript

import {Component, Fragment} from 'react';
import type {Location} from 'history';
import Panel from 'sentry/components/panels/panel';
import type {Organization} from 'sentry/types';
import type EventView from 'sentry/utils/discover/eventView';
import type {WebVital} from 'sentry/utils/fields';
import HistogramQuery from 'sentry/utils/performance/histogram/histogramQuery';
import type {DataFilter, HistogramData} from 'sentry/utils/performance/histogram/types';
import {WEB_VITAL_DETAILS} from 'sentry/utils/performance/vitals/constants';
import type {VitalGroup} from 'sentry/utils/performance/vitals/types';
import type {VitalData} from 'sentry/utils/performance/vitals/vitalsCardsDiscoverQuery';
import {decodeScalar} from 'sentry/utils/queryString';
import {NUM_BUCKETS, VITAL_GROUPS} from './constants';
import VitalCard from './vitalCard';
type Props = {
eventView: EventView;
location: Location;
organization: Organization;
results: object;
dataFilter?: DataFilter;
};
class VitalsPanel extends Component<Props> {
renderVitalCard(
vital: WebVital,
isLoading: boolean,
error: boolean,
data: VitalData | null,
histogram: HistogramData,
color: [string],
min?: number,
max?: number,
precision?: number
) {
const {location, organization, eventView, dataFilter} = this.props;
const vitalDetails = WEB_VITAL_DETAILS[vital];
const zoomed = min !== undefined || max !== undefined;
return (
<HistogramQuery
location={location}
orgSlug={organization.slug}
eventView={eventView}
numBuckets={NUM_BUCKETS}
fields={zoomed ? [vital] : []}
min={min}
max={max}
precision={precision}
dataFilter={dataFilter}
>
{results => {
const loading = zoomed ? results.isLoading : isLoading;
const errored = zoomed ? results.error !== null : error;
const chartData = zoomed ? results.histograms?.[vital] ?? histogram : histogram;
return (
<VitalCard
location={location}
isLoading={loading}
error={errored}
vital={vital}
vitalDetails={vitalDetails}
summaryData={data}
chartData={chartData}
colors={color}
eventView={eventView}
organization={organization}
min={min}
max={max}
precision={precision}
dataFilter={dataFilter}
/>
);
}}
</HistogramQuery>
);
}
renderVitalGroup(group: VitalGroup, summaryResults) {
const {location, organization, eventView, dataFilter} = this.props;
const {vitals, colors, min, max, precision} = group;
const bounds = vitals.reduce(
(
allBounds: Partial<
Record<WebVital, {end: string | undefined; start: string | undefined}>
>,
vital: WebVital
) => {
const slug = WEB_VITAL_DETAILS[vital].slug;
allBounds[vital] = {
start: decodeScalar(location.query[`${slug}Start`]),
end: decodeScalar(location.query[`${slug}End`]),
};
return allBounds;
},
{}
);
return (
<HistogramQuery
location={location}
orgSlug={organization.slug}
eventView={eventView}
numBuckets={NUM_BUCKETS}
fields={vitals}
min={min}
max={max}
precision={precision}
dataFilter={dataFilter}
>
{multiHistogramResults => {
const isLoading = summaryResults.isLoading || multiHistogramResults.isLoading;
const error =
summaryResults.error !== null || multiHistogramResults.error !== null;
return (
<Fragment>
{vitals.map((vital, index) => {
const data = summaryResults?.vitalsData?.[vital] ?? null;
const histogram = multiHistogramResults.histograms?.[vital] ?? [];
const {start, end} = bounds[vital] ?? {};
return (
<Fragment key={vital}>
{this.renderVitalCard(
vital,
isLoading,
error,
data,
histogram,
[colors[index]],
parseBound(start, precision),
parseBound(end, precision),
precision
)}
</Fragment>
);
})}
</Fragment>
);
}}
</HistogramQuery>
);
}
render() {
const {results} = this.props;
return (
<Panel>
<Fragment>
{VITAL_GROUPS.map(vitalGroup => (
<Fragment key={vitalGroup.vitals.join('')}>
{this.renderVitalGroup(vitalGroup, results)}
</Fragment>
))}
</Fragment>
</Panel>
);
}
}
function parseBound(
boundString: string | undefined,
precision: number | undefined
): number | undefined {
if (boundString === undefined) {
return undefined;
}
if (precision === undefined || precision === 0) {
return parseInt(boundString, 10);
}
return parseFloat(boundString);
}
export default VitalsPanel;