Skip to content

Commit 268aa81

Browse files
authored
fix: deprecate old landing page and create a new wrapper around it (#75147)
Deprecate the old so we can start picking and replacing components on the landing page
1 parent 8cbfd1b commit 268aa81

File tree

1 file changed

+235
-11
lines changed

1 file changed

+235
-11
lines changed

static/app/views/profiling/content.tsx

Lines changed: 235 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ interface ProfilingContentProps {
5252
location: Location;
5353
}
5454

55-
function ProfilingContent({location}: ProfilingContentProps) {
55+
function ProfilingContentLegacy({location}: ProfilingContentProps) {
5656
const organization = useOrganization();
5757
const {selection} = usePageFilters();
5858
const cursor = decodeScalar(location.query.cursor);
@@ -213,16 +213,230 @@ function ProfilingContent({location}: ProfilingContentProps) {
213213
'profiling-global-suspect-functions'
214214
) ? (
215215
<Fragment>
216-
{organization.features.includes(
217-
'continuous-profiling-ui'
218-
) ? null : (
219-
<ProfilesChartWidget
220-
chartHeight={150}
221-
referrer="api.profiling.landing-chart"
222-
userQuery={query}
223-
selection={selection}
216+
<ProfilesChartWidget
217+
chartHeight={150}
218+
referrer="api.profiling.landing-chart"
219+
userQuery={query}
220+
selection={selection}
221+
/>
222+
<WidgetsContainer>
223+
<LandingWidgetSelector
224+
cursorName={LEFT_WIDGET_CURSOR}
225+
widgetHeight="340px"
226+
defaultWidget="slowest functions"
227+
query={query}
228+
storageKey="profiling-landing-widget-0"
224229
/>
225-
)}
230+
<LandingWidgetSelector
231+
cursorName={RIGHT_WIDGET_CURSOR}
232+
widgetHeight="340px"
233+
defaultWidget="regressed functions"
234+
query={query}
235+
storageKey="profiling-landing-widget-1"
236+
/>
237+
</WidgetsContainer>
238+
</Fragment>
239+
) : (
240+
<PanelsGrid>
241+
<ProfilingSlowestTransactionsPanel />
242+
<ProfilesChart
243+
referrer="api.profiling.landing-chart"
244+
query={query}
245+
selection={selection}
246+
hideCount
247+
/>
248+
</PanelsGrid>
249+
)}
250+
<ProfileEventsTable
251+
columns={fields.slice()}
252+
data={transactions.status === 'success' ? transactions.data : null}
253+
error={
254+
transactions.status === 'error'
255+
? t('Unable to load profiles')
256+
: null
257+
}
258+
isLoading={transactions.status === 'loading'}
259+
sort={sort}
260+
sortableColumns={new Set(fields)}
261+
/>
262+
<Pagination
263+
pageLinks={
264+
transactions.status === 'success'
265+
? transactions.getResponseHeader?.('Link') ?? null
266+
: null
267+
}
268+
/>
269+
</Fragment>
270+
)}
271+
</Layout.Main>
272+
</Layout.Body>
273+
</Layout.Page>
274+
</PageFiltersContainer>
275+
</SentryDocumentTitle>
276+
);
277+
}
278+
279+
function ProfilingContent({location}: ProfilingContentProps) {
280+
const organization = useOrganization();
281+
const {selection} = usePageFilters();
282+
const cursor = decodeScalar(location.query.cursor);
283+
const query = decodeScalar(location.query.query, '');
284+
285+
const fields = ALL_FIELDS;
286+
287+
const sort = formatSort<FieldType>(decodeScalar(location.query.sort), fields, {
288+
key: 'count()',
289+
order: 'desc',
290+
});
291+
292+
const {projects} = useProjects();
293+
294+
const transactions = useProfileEvents<FieldType>({
295+
cursor,
296+
fields,
297+
query,
298+
sort,
299+
referrer: 'api.profiling.landing-table',
300+
});
301+
302+
const transactionsError =
303+
transactions.status === 'error' ? formatError(transactions.error) : null;
304+
305+
useEffect(() => {
306+
trackAnalytics('profiling_views.landing', {
307+
organization,
308+
});
309+
}, [organization]);
310+
311+
const handleSearch: SmartSearchBarProps['onSearch'] = useCallback(
312+
(searchQuery: string) => {
313+
browserHistory.push({
314+
...location,
315+
query: {
316+
...location.query,
317+
cursor: undefined,
318+
query: searchQuery || undefined,
319+
},
320+
});
321+
},
322+
[location]
323+
);
324+
325+
// Open the modal on demand
326+
const onSetupProfilingClick = useCallback(() => {
327+
trackAnalytics('profiling_views.onboarding', {
328+
organization,
329+
});
330+
SidebarPanelStore.activatePanel(SidebarPanelKey.PROFILING_ONBOARDING);
331+
}, [organization]);
332+
333+
const shouldShowProfilingOnboardingPanel = useMemo((): boolean => {
334+
// if it's My Projects or All projects, only show onboarding if we can't
335+
// find any projects with profiles
336+
if (
337+
selection.projects.length === 0 ||
338+
selection.projects[0] === ALL_ACCESS_PROJECTS
339+
) {
340+
return projects.every(project => !project.hasProfiles);
341+
}
342+
343+
// otherwise, only show onboarding if we can't find any projects with profiles
344+
// from those that were selected
345+
const projectsWithProfiles = new Set(
346+
projects.filter(project => project.hasProfiles).map(project => project.id)
347+
);
348+
return selection.projects.every(
349+
project => !projectsWithProfiles.has(String(project))
350+
);
351+
}, [selection.projects, projects]);
352+
353+
return (
354+
<SentryDocumentTitle title={t('Profiling')} orgSlug={organization.slug}>
355+
<PageFiltersContainer
356+
defaultSelection={{datetime: DEFAULT_PROFILING_DATETIME_SELECTION}}
357+
>
358+
<Layout.Page>
359+
<ProfilingBetaAlertBanner organization={organization} />
360+
<Layout.Header>
361+
<StyledHeaderContent>
362+
<Layout.Title>
363+
{t('Profiling')}
364+
<PageHeadingQuestionTooltip
365+
docsUrl="https://docs.sentry.io/product/profiling/"
366+
title={t(
367+
'Profiling collects detailed information in production about the functions executing in your application and how long they take to run, giving you code-level visibility into your hot paths.'
368+
)}
369+
/>
370+
</Layout.Title>
371+
<FeedbackWidgetButton />
372+
</StyledHeaderContent>
373+
</Layout.Header>
374+
<Layout.Body>
375+
<Layout.Main fullWidth>
376+
{transactionsError && (
377+
<Alert type="error" showIcon>
378+
{transactionsError}
379+
</Alert>
380+
)}
381+
<ActionBar>
382+
<PageFilterBar condensed>
383+
<ProjectPageFilter resetParamsOnChange={CURSOR_PARAMS} />
384+
<EnvironmentPageFilter resetParamsOnChange={CURSOR_PARAMS} />
385+
<DatePageFilter resetParamsOnChange={CURSOR_PARAMS} />
386+
</PageFilterBar>
387+
<SearchBar
388+
searchSource="profile_landing"
389+
organization={organization}
390+
projectIds={selection.projects}
391+
query={query}
392+
onSearch={handleSearch}
393+
maxQueryLength={MAX_QUERY_LENGTH}
394+
/>
395+
</ActionBar>
396+
{shouldShowProfilingOnboardingPanel ? (
397+
<Fragment>
398+
<ProfilingOnboardingPanel
399+
content={
400+
// If user is on m2, show default
401+
<ProfilingAM1OrMMXUpgrade
402+
organization={organization}
403+
fallback={
404+
<Fragment>
405+
<h3>{t('Function level insights')}</h3>
406+
<p>
407+
{t(
408+
'Discover slow-to-execute or resource intensive functions within your application'
409+
)}
410+
</p>
411+
</Fragment>
412+
}
413+
/>
414+
}
415+
>
416+
<ProfilingUpgradeButton
417+
data-test-id="profiling-upgrade"
418+
organization={organization}
419+
priority="primary"
420+
onClick={onSetupProfilingClick}
421+
fallback={
422+
<Button onClick={onSetupProfilingClick} priority="primary">
423+
{t('Set Up Profiling')}
424+
</Button>
425+
}
426+
>
427+
{t('Set Up Profiling')}
428+
</ProfilingUpgradeButton>
429+
<Button href="https://docs.sentry.io/product/profiling/" external>
430+
{t('Read Docs')}
431+
</Button>
432+
</ProfilingOnboardingPanel>
433+
</Fragment>
434+
) : (
435+
<Fragment>
436+
{organization.features.includes(
437+
'profiling-global-suspect-functions'
438+
) ? (
439+
<Fragment>
226440
<WidgetsContainer>
227441
<LandingWidgetSelector
228442
cursorName={LEFT_WIDGET_CURSOR}
@@ -326,4 +540,14 @@ const WidgetsContainer = styled('div')`
326540
}
327541
`;
328542

329-
export default ProfilingContent;
543+
function ProfilingContentWrapper(props: ProfilingContentProps) {
544+
const organization = useOrganization();
545+
546+
if (organization.features.includes('continuous-profiling-compat')) {
547+
return <ProfilingContent {...props} />;
548+
}
549+
550+
return <ProfilingContentLegacy {...props} />;
551+
}
552+
553+
export default ProfilingContentWrapper;

0 commit comments

Comments
 (0)