|
@@ -18,7 +18,7 @@ import {Tooltip} from 'sentry/components/tooltip';
|
|
|
import {IconChevron} from 'sentry/icons/iconChevron';
|
|
|
import {t} from 'sentry/locale';
|
|
|
import {space} from 'sentry/styles/space';
|
|
|
-import {Sort} from 'sentry/utils/discover/fields';
|
|
|
+import {parseFunction, Sort} from 'sentry/utils/discover/fields';
|
|
|
import {formatAbbreviatedNumber, getDuration} from 'sentry/utils/formatters';
|
|
|
import {decodeScalar} from 'sentry/utils/queryString';
|
|
|
import {useLocation} from 'sentry/utils/useLocation';
|
|
@@ -32,15 +32,13 @@ import {calculatePerformanceScoreFromStoredTableDataRow} from 'sentry/views/perf
|
|
|
import {useProjectWebVitalsScoresQuery} from 'sentry/views/performance/browser/webVitals/utils/queries/storedScoreQueries/useProjectWebVitalsScoresQuery';
|
|
|
import {useTransactionWebVitalsQuery} from 'sentry/views/performance/browser/webVitals/utils/queries/useTransactionWebVitalsQuery';
|
|
|
import {
|
|
|
- Row,
|
|
|
+ RowWithScoreAndOpportunity,
|
|
|
SORTABLE_FIELDS,
|
|
|
SORTABLE_SCORE_FIELDS,
|
|
|
} from 'sentry/views/performance/browser/webVitals/utils/types';
|
|
|
import {useStoredScoresSetting} from 'sentry/views/performance/browser/webVitals/utils/useStoredScoresSetting';
|
|
|
import {useWebVitalsSort} from 'sentry/views/performance/browser/webVitals/utils/useWebVitalsSort';
|
|
|
|
|
|
-type RowWithScoreAndOpportunity = Row & {score: number; opportunity?: number};
|
|
|
-
|
|
|
type Column = GridColumnHeader<keyof RowWithScoreAndOpportunity>;
|
|
|
|
|
|
const columnOrder: GridColumnOrder<keyof RowWithScoreAndOpportunity>[] = [
|
|
@@ -51,7 +49,7 @@ const columnOrder: GridColumnOrder<keyof RowWithScoreAndOpportunity>[] = [
|
|
|
{key: 'p75(measurements.fid)', width: COL_WIDTH_UNDEFINED, name: 'FID'},
|
|
|
{key: 'p75(measurements.cls)', width: COL_WIDTH_UNDEFINED, name: 'CLS'},
|
|
|
{key: 'p75(measurements.ttfb)', width: COL_WIDTH_UNDEFINED, name: 'TTFB'},
|
|
|
- {key: 'score', width: COL_WIDTH_UNDEFINED, name: 'Score'},
|
|
|
+ {key: 'totalScore', width: COL_WIDTH_UNDEFINED, name: 'Score'},
|
|
|
{key: 'opportunity', width: COL_WIDTH_UNDEFINED, name: 'Opportunity'},
|
|
|
];
|
|
|
|
|
@@ -98,15 +96,13 @@ export function PagePerformanceTable() {
|
|
|
const tableData: RowWithScoreAndOpportunity[] = data.map(row => ({
|
|
|
...row,
|
|
|
opportunity: shouldUseStoredScores
|
|
|
- ? ((row.opportunity ?? 0) * 100) / scoreCount
|
|
|
- : count !== undefined
|
|
|
- ? calculateOpportunity(
|
|
|
+ ? (((row as RowWithScoreAndOpportunity).opportunity ?? 0) * 100) / scoreCount
|
|
|
+ : calculateOpportunity(
|
|
|
projectScore.totalScore ?? 0,
|
|
|
count,
|
|
|
- row.score,
|
|
|
+ row.totalScore,
|
|
|
row['count()']
|
|
|
- )
|
|
|
- : undefined,
|
|
|
+ ),
|
|
|
}));
|
|
|
const getFormattedDuration = (value: number) => {
|
|
|
return getDuration(value, value < 1 ? 0 : 2, true);
|
|
@@ -115,7 +111,7 @@ export function PagePerformanceTable() {
|
|
|
function renderHeadCell(col: Column) {
|
|
|
function generateSortLink() {
|
|
|
const key =
|
|
|
- col.key === 'score'
|
|
|
+ col.key === 'totalScore'
|
|
|
? 'avg(measurements.score.total)'
|
|
|
: col.key === 'opportunity'
|
|
|
? 'opportunity_score(measurements.score.total)'
|
|
@@ -139,7 +135,7 @@ export function PagePerformanceTable() {
|
|
|
: SORTABLE_FIELDS.filter(field => !SORTABLE_SCORE_FIELDS.includes(field));
|
|
|
const canSort = (sortableFields as unknown as string[]).includes(col.key);
|
|
|
|
|
|
- if (canSort && !['score', 'opportunity'].includes(col.key)) {
|
|
|
+ if (canSort && !['totalScore', 'opportunity'].includes(col.key)) {
|
|
|
return (
|
|
|
<SortLink
|
|
|
align="right"
|
|
@@ -150,7 +146,7 @@ export function PagePerformanceTable() {
|
|
|
/>
|
|
|
);
|
|
|
}
|
|
|
- if (col.key === 'score') {
|
|
|
+ if (col.key === 'totalScore') {
|
|
|
return (
|
|
|
<SortLink
|
|
|
title={
|
|
@@ -213,10 +209,10 @@ export function PagePerformanceTable() {
|
|
|
|
|
|
function renderBodyCell(col: Column, row: RowWithScoreAndOpportunity) {
|
|
|
const {key} = col;
|
|
|
- if (key === 'score') {
|
|
|
+ if (key === 'totalScore') {
|
|
|
return (
|
|
|
<AlignCenter>
|
|
|
- <PerformanceBadge score={row.score} />
|
|
|
+ <PerformanceBadge score={row.totalScore} />
|
|
|
</AlignCenter>
|
|
|
);
|
|
|
}
|
|
@@ -264,9 +260,32 @@ export function PagePerformanceTable() {
|
|
|
'p75(measurements.fid)',
|
|
|
].includes(key)
|
|
|
) {
|
|
|
+ const measurement = parseFunction(key)?.arguments?.[0];
|
|
|
+ const func = shouldUseStoredScores ? 'count_scores' : 'count_web_vitals';
|
|
|
+ const args = [measurement, ...(shouldUseStoredScores ? [] : ['any'])];
|
|
|
+ const countWebVitalKey = `${func}(${args.join(',')})`;
|
|
|
+ const countWebVital = row[countWebVitalKey];
|
|
|
+ if (measurement === undefined || countWebVital === 0) {
|
|
|
+ return (
|
|
|
+ <AlignRight>
|
|
|
+ <NoValue>{' \u2014 '}</NoValue>
|
|
|
+ </AlignRight>
|
|
|
+ );
|
|
|
+ }
|
|
|
return <AlignRight>{getFormattedDuration((row[key] as number) / 1000)}</AlignRight>;
|
|
|
}
|
|
|
if (key === 'p75(measurements.cls)') {
|
|
|
+ const countWebVitalKey = shouldUseStoredScores
|
|
|
+ ? 'count_scores(measurements.score.cls)'
|
|
|
+ : 'count_web_vitals(measurements.cls, any)';
|
|
|
+ const countWebVital = row[countWebVitalKey];
|
|
|
+ if (countWebVital === 0) {
|
|
|
+ return (
|
|
|
+ <AlignRight>
|
|
|
+ <NoValue>{' \u2014 '}</NoValue>
|
|
|
+ </AlignRight>
|
|
|
+ );
|
|
|
+ }
|
|
|
return <AlignRight>{Math.round((row[key] as number) * 100) / 100}</AlignRight>;
|
|
|
}
|
|
|
if (key === 'opportunity') {
|
|
@@ -406,3 +425,7 @@ const StyledTooltip = styled(Tooltip)`
|
|
|
top: 1px;
|
|
|
position: relative;
|
|
|
`;
|
|
|
+
|
|
|
+const NoValue = styled('span')`
|
|
|
+ color: ${p => p.theme.gray300};
|
|
|
+`;
|