Skip to content

Commit 8505e05

Browse files
feat(insights): add geo region selector in web vitals landing (#76062)
Work towards #75230 Adds geo region selector, for now marked as "experimental" and internal only because we need to allow data to come in so its not always working. <img width="938" alt="image" src="https://github.com/user-attachments/assets/e60c71fc-b005-435c-a605-465f5acda2b1"> This will go in many other modules and areas of our app, but for now just web vitals landing for testing purposes. --------- Co-authored-by: Ash <[email protected]>
1 parent 33e02db commit 8505e05

File tree

3 files changed

+128
-3
lines changed

3 files changed

+128
-3
lines changed

static/app/views/insights/browser/webVitals/views/webVitalsLandingPage.tsx

+8-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import React, {useState} from 'react';
1+
import React, {Fragment, useState} from 'react';
22
import styled from '@emotion/styled';
33
import omit from 'lodash/omit';
44

@@ -34,6 +34,7 @@ import {ModulePageFilterBar} from 'sentry/views/insights/common/components/modul
3434
import {ModulePageProviders} from 'sentry/views/insights/common/components/modulePageProviders';
3535
import {ModulesOnboarding} from 'sentry/views/insights/common/components/modulesOnboarding';
3636
import {useModuleBreadcrumbs} from 'sentry/views/insights/common/utils/useModuleBreadcrumbs';
37+
import SubregionSelector from 'sentry/views/insights/common/views/spans/selectors/subregionSelector';
3738
import {ModuleName, SpanIndexedField} from 'sentry/views/insights/types';
3839

3940
export function WebVitalsLandingPage() {
@@ -84,7 +85,12 @@ export function WebVitalsLandingPage() {
8485
<TopMenuContainer>
8586
<ModulePageFilterBar
8687
moduleName={ModuleName.VITAL}
87-
extraFilters={<BrowserTypeSelector />}
88+
extraFilters={
89+
<Fragment>
90+
<BrowserTypeSelector />
91+
<SubregionSelector />
92+
</Fragment>
93+
}
8894
/>
8995
</TopMenuContainer>
9096
<MainContentContainer>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
import {Fragment} from 'react';
2+
import styled from '@emotion/styled';
3+
4+
import FeatureBadge from 'sentry/components/badge/featureBadge';
5+
import {
6+
CompactSelect,
7+
type SelectOption,
8+
type SelectProps,
9+
} from 'sentry/components/compactSelect';
10+
import {t} from 'sentry/locale';
11+
import {space} from 'sentry/styles/space';
12+
import {trackAnalytics} from 'sentry/utils/analytics';
13+
import {decodeList} from 'sentry/utils/queryString';
14+
import {useLocation} from 'sentry/utils/useLocation';
15+
import {useNavigate} from 'sentry/utils/useNavigate';
16+
import useOrganization from 'sentry/utils/useOrganization';
17+
import {useSpanMetrics} from 'sentry/views/insights/common/queries/useDiscover';
18+
import {SpanMetricsField, subregionCodeToName} from 'sentry/views/insights/types';
19+
20+
export default function SubregionSelector() {
21+
const organization = useOrganization();
22+
const location = useLocation();
23+
const navigate = useNavigate();
24+
const hasGeoSelectorFeature = organization.features.includes('insights-region-filter');
25+
26+
const value = decodeList(location.query[SpanMetricsField.USER_GEO_SUBREGION]);
27+
const {data, isLoading} = useSpanMetrics(
28+
{fields: [SpanMetricsField.USER_GEO_SUBREGION], enabled: hasGeoSelectorFeature},
29+
'api.insights.user-geo-subregion-selector'
30+
);
31+
32+
type Options = SelectProps<string>['options'];
33+
34+
const options: Options =
35+
data?.map(row => {
36+
const subregionCode = row[SpanMetricsField.USER_GEO_SUBREGION];
37+
const text = subregionCodeToName[subregionCode] || '';
38+
return {
39+
value: subregionCode,
40+
label: text,
41+
textValue: text,
42+
};
43+
}) ?? [];
44+
45+
if (!hasGeoSelectorFeature) {
46+
return <Fragment />;
47+
}
48+
49+
return (
50+
<CompactSelect
51+
triggerProps={{
52+
prefix: (
53+
<Fragment>
54+
<StyledFeatureBadge type="experimental" />
55+
{t('Geo region')}
56+
</Fragment>
57+
),
58+
}}
59+
multiple
60+
loading={isLoading}
61+
clearable
62+
value={value}
63+
triggerLabel={value.length === 0 ? t('All') : undefined}
64+
menuTitle={t('Filter region')}
65+
options={options}
66+
onChange={(selectedOptions: SelectOption<string>[]) => {
67+
trackAnalytics('insight.vital.select_browser_value', {
68+
organization,
69+
browsers: selectedOptions.map(v => v.value),
70+
});
71+
72+
navigate({
73+
...location,
74+
query: {
75+
...location.query,
76+
[SpanMetricsField.USER_GEO_SUBREGION]: selectedOptions.map(
77+
option => option.value
78+
),
79+
},
80+
});
81+
}}
82+
/>
83+
);
84+
}
85+
86+
const StyledFeatureBadge = styled(FeatureBadge)`
87+
margin-right: ${space(1)};
88+
`;

static/app/views/insights/types.tsx

+32-1
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ export enum SpanMetricsField {
5656
URL_FULL = 'url.full',
5757
USER_AGENT_ORIGINAL = 'user_agent.original',
5858
CLIENT_ADDRESS = 'client.address',
59+
USER_GEO_SUBREGION = 'user.geo.subregion',
5960
}
6061

6162
export type SpanNumberFields =
@@ -83,7 +84,8 @@ export type SpanStringFields =
8384
| 'span.status_code'
8485
| 'span.ai.pipeline.group'
8586
| 'project'
86-
| 'messaging.destination.name';
87+
| 'messaging.destination.name'
88+
| SpanMetricsField.USER_GEO_SUBREGION;
8789

8890
export type SpanMetricsQueryFilters = {
8991
[Field in SpanStringFields]?: string;
@@ -220,6 +222,7 @@ export enum SpanIndexedField {
220222
MESSAGING_MESSAGE_RECEIVE_LATENCY = 'measurements.messaging.message.receive.latency',
221223
MESSAGING_MESSAGE_RETRY_COUNT = 'measurements.messaging.message.retry.count',
222224
MESSAGING_MESSAGE_DESTINATION_NAME = 'messaging.destination.name',
225+
USER_GEO_SUBREGION = 'user.geo.subregion',
223226
}
224227

225228
export type SpanIndexedResponse = {
@@ -287,6 +290,7 @@ export type SpanIndexedResponse = {
287290
[SpanIndexedField.MESSAGING_MESSAGE_RECEIVE_LATENCY]: number;
288291
[SpanIndexedField.MESSAGING_MESSAGE_RETRY_COUNT]: number;
289292
[SpanIndexedField.MESSAGING_MESSAGE_DESTINATION_NAME]: string;
293+
[SpanIndexedField.USER_GEO_SUBREGION]: string;
290294
};
291295

292296
export type SpanIndexedPropery = keyof SpanIndexedResponse;
@@ -336,3 +340,30 @@ export type MetricsQueryFilters = {
336340
} & {
337341
[SpanIndexedField.PROJECT_ID]?: string;
338342
};
343+
344+
// Maps the subregion code to the subregion name according to UN m49 standard
345+
// We also define this in relay in `country_subregion.rs`
346+
export const subregionCodeToName = {
347+
'21': 'North America',
348+
'13': 'Central America',
349+
'29': 'Caribbean',
350+
'5': 'South America',
351+
'154': 'Northern Europe',
352+
'155': 'Western Europe',
353+
'39': 'Southern Europe',
354+
'151': 'Eastern Europe',
355+
'30': 'Eastern Asia',
356+
'34': 'Southern Asia',
357+
'35': 'South Eastern Asia',
358+
'145': 'Western Asia',
359+
'143': 'Central Asia',
360+
'15': 'Northern Africa',
361+
'11': 'Western Africa',
362+
'17': 'Middle Africa',
363+
'14': 'Eastern Africa',
364+
'18': 'Southern Africa',
365+
'54': 'Melanesia',
366+
'57': 'Micronesia',
367+
'61': 'Polynesia',
368+
'53': 'Australia and New Zealand',
369+
};

0 commit comments

Comments
 (0)