From ff80220a34081bd05b313ab94d733bf429eecd61 Mon Sep 17 00:00:00 2001 From: Hector Date: Fri, 19 Dec 2025 14:57:04 +0000 Subject: [PATCH 1/3] feat(preprod): Add treemap diff UI --- .../app/components/events/eventXrayDiff.tsx | 96 +++++++++++++++++++ .../groupEventDetailsContent.tsx | 2 + .../views/issueDetails/streamline/context.tsx | 2 + 3 files changed, 100 insertions(+) create mode 100644 static/app/components/events/eventXrayDiff.tsx diff --git a/static/app/components/events/eventXrayDiff.tsx b/static/app/components/events/eventXrayDiff.tsx new file mode 100644 index 000000000000..aef6eb1fe3d6 --- /dev/null +++ b/static/app/components/events/eventXrayDiff.tsx @@ -0,0 +1,96 @@ +import EmptyStateWarning from 'sentry/components/emptyStateWarning'; +import ErrorBoundary from 'sentry/components/errorBoundary'; +import LoadingIndicator from 'sentry/components/loadingIndicator'; +import {t} from 'sentry/locale'; +import type {Event} from 'sentry/types/event'; +import type {Project} from 'sentry/types/project'; +import {useApiQuery} from 'sentry/utils/queryClient'; +import useOrganization from 'sentry/utils/useOrganization'; +import {SectionKey} from 'sentry/views/issueDetails/streamline/context'; +import {InterimSection} from 'sentry/views/issueDetails/streamline/interimSection'; +import {TreemapDiffSection} from 'sentry/views/preprod/buildComparison/main/treemapDiffSection'; +import type {SizeAnalysisComparisonResults} from 'sentry/views/preprod/types/appSizeTypes'; + +type ContentProps = { + baseMetricId: string; + headMetricId: string; + project: Project; +}; + +function EventXrayDiffContent({baseMetricId, headMetricId, project}: ContentProps) { + const organization = useOrganization(); + + const query = useApiQuery( + [ + `/projects/${organization.slug}/${project.id}/preprodartifacts/size-analysis/compare/${headMetricId}/${baseMetricId}/download/`, + ], + { + staleTime: 0, + } + ); + + if (query.isLoading) { + return ; + } + + const diffItems = query.data?.diff_items; + + if (!diffItems || diffItems.length === 0) { + return No diff found.; + } + + return ; +} + +type SectionProps = { + baseMetricId: string; + headMetricId: string; + project: Project; +}; + +function EventXrayDiffSection({baseMetricId, headMetricId, project}: SectionProps) { + return ( + + + + + + ); +} + +interface MetricIds { + baseMetricId: string; + headMetricId: string; +} + +function getMetricIds(event: Event): MetricIds | undefined { + const headMetricId = event.occurrence?.evidenceData?.headSizeMetricId; + const baseMetricId = event.occurrence?.evidenceData?.baseSizeMetricId; + + if (baseMetricId && headMetricId) { + return { + baseMetricId, + headMetricId, + }; + } + return undefined; +} + +type Props = { + event: Event; + project: Project; +}; + +function EventXrayDiff(props: Props) { + const ids = getMetricIds(props.event); + if (ids) { + return ; + } + return null; +} + +export {EventXrayDiff}; diff --git a/static/app/views/issueDetails/groupEventDetails/groupEventDetailsContent.tsx b/static/app/views/issueDetails/groupEventDetails/groupEventDetailsContent.tsx index 538fbc440f1a..527da01c08db 100644 --- a/static/app/views/issueDetails/groupEventDetails/groupEventDetailsContent.tsx +++ b/static/app/views/issueDetails/groupEventDetails/groupEventDetailsContent.tsx @@ -29,6 +29,7 @@ import {EventTagsAndScreenshot} from 'sentry/components/events/eventTagsAndScree import {ScreenshotDataSection} from 'sentry/components/events/eventTagsAndScreenshot/screenshot/screenshotDataSection'; import {EventTagsDataSection} from 'sentry/components/events/eventTagsAndScreenshot/tags'; import {EventViewHierarchy} from 'sentry/components/events/eventViewHierarchy'; +import {EventXrayDiff} from 'sentry/components/events/eventXrayDiff'; import {EventFeatureFlagSection} from 'sentry/components/events/featureFlags/eventFeatureFlagSection'; import {EventGroupingInfoSection} from 'sentry/components/events/groupingInfo/groupingInfoSection'; import HighlightsDataSection from 'sentry/components/events/highlights/highlightsDataSection'; @@ -414,6 +415,7 @@ export function EventDetailsContent({ + diff --git a/static/app/views/issueDetails/streamline/context.tsx b/static/app/views/issueDetails/streamline/context.tsx index be5b7d7bee36..51a4e12ae359 100644 --- a/static/app/views/issueDetails/streamline/context.tsx +++ b/static/app/views/issueDetails/streamline/context.tsx @@ -94,6 +94,8 @@ export const enum SectionKey { MCP_OUTPUT = 'mcp-output', SPAN_LINKS = 'span-links', + + XRAY_DIFF = 'xray-diff', } /** From 0a3405e6f7864b8bb4e1238f6dcd54a8e8504dee Mon Sep 17 00:00:00 2001 From: Hector Date: Fri, 19 Dec 2025 19:39:59 +0000 Subject: [PATCH 2/3] Add error state --- static/app/components/events/eventXrayDiff.tsx | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/static/app/components/events/eventXrayDiff.tsx b/static/app/components/events/eventXrayDiff.tsx index aef6eb1fe3d6..5f36b6f875c6 100644 --- a/static/app/components/events/eventXrayDiff.tsx +++ b/static/app/components/events/eventXrayDiff.tsx @@ -1,5 +1,6 @@ import EmptyStateWarning from 'sentry/components/emptyStateWarning'; import ErrorBoundary from 'sentry/components/errorBoundary'; +import LoadingError from 'sentry/components/loadingError'; import LoadingIndicator from 'sentry/components/loadingIndicator'; import {t} from 'sentry/locale'; import type {Event} from 'sentry/types/event'; @@ -33,10 +34,19 @@ function EventXrayDiffContent({baseMetricId, headMetricId, project}: ContentProp return ; } + if (query.isError) { + return ( + + ); + } + const diffItems = query.data?.diff_items; if (!diffItems || diffItems.length === 0) { - return No diff found.; + return {t('No diff found.')}; } return ; From 117f483b9c1c7de4eae1cd56cb73212cdfc816cb Mon Sep 17 00:00:00 2001 From: Hector Date: Tue, 6 Jan 2026 12:59:44 +0000 Subject: [PATCH 3/3] ref ContentProps to SectionProps --- static/app/components/events/eventXrayDiff.tsx | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/static/app/components/events/eventXrayDiff.tsx b/static/app/components/events/eventXrayDiff.tsx index 5f36b6f875c6..faf8f4ab944b 100644 --- a/static/app/components/events/eventXrayDiff.tsx +++ b/static/app/components/events/eventXrayDiff.tsx @@ -12,11 +12,8 @@ import {InterimSection} from 'sentry/views/issueDetails/streamline/interimSectio import {TreemapDiffSection} from 'sentry/views/preprod/buildComparison/main/treemapDiffSection'; import type {SizeAnalysisComparisonResults} from 'sentry/views/preprod/types/appSizeTypes'; -type ContentProps = { - baseMetricId: string; - headMetricId: string; - project: Project; -}; +// Currently Content and Section props are identical. +type ContentProps = SectionProps; function EventXrayDiffContent({baseMetricId, headMetricId, project}: ContentProps) { const organization = useOrganization();