@@ -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