Browse Source

ref(feature): Remove heartbeat feature (#53509)

Priscila Oliveira 1 year ago
parent
commit
f873c4f96d

+ 0 - 166
static/app/components/onboarding/footer.spec.tsx

@@ -1,166 +0,0 @@
-import {initializeOrg} from 'sentry-test/initializeOrg';
-import {
-  render,
-  renderGlobalModal,
-  screen,
-  userEvent,
-  waitFor,
-} from 'sentry-test/reactTestingLibrary';
-
-import {Footer} from 'sentry/components/onboarding/footer';
-import {OnboardingContextProvider} from 'sentry/components/onboarding/onboardingContext';
-import {OnboardingProjectStatus} from 'sentry/types';
-import * as useSessionStorage from 'sentry/utils/useSessionStorage';
-
-describe('Onboarding Footer', function () {
-  it('waiting for error ui', async function () {
-    const {project, organization, router, route} = initializeOrg();
-
-    MockApiClient.addMockResponse({
-      url: `/projects/${organization.slug}/${project.slug}/`,
-      body: project,
-    });
-
-    render(
-      <OnboardingContextProvider>
-        <Footer
-          projectId={project.id}
-          projectSlug={project.slug}
-          location={router.location}
-          route={route}
-          router={router}
-          newOrg
-        />
-      </OnboardingContextProvider>,
-      {
-        organization,
-      }
-    );
-
-    // Error status
-    expect(screen.getByText('Waiting for error')).toBeInTheDocument();
-
-    // Explore Sentry button disabled
-    expect(screen.getByRole('button', {name: 'Explore Sentry'})).toBeDisabled();
-
-    await userEvent.hover(screen.getByRole('button', {name: 'Explore Sentry'}));
-
-    // Explore Sentry button tooltip
-    await waitFor(() => {
-      expect(screen.getAllByText('Waiting for error')).toHaveLength(2);
-    });
-
-    renderGlobalModal();
-
-    // Skip onboarding link
-    await userEvent.click(screen.getByRole('button', {name: 'Skip Onboarding'}));
-
-    // Display are you sure modal
-    expect(await screen.findByText('Are you sure?')).toBeInTheDocument();
-  });
-
-  it('processing error ui', async function () {
-    const {project, organization, router, route} = initializeOrg();
-
-    // Mock useSessionStorage hook to return the mocked session data
-    jest.spyOn(useSessionStorage, 'useSessionStorage').mockImplementation(() => [
-      {
-        projects: {
-          [project.id]: {
-            status: OnboardingProjectStatus.PROCESSING,
-            firstIssueId: '1',
-            slug: project.slug,
-          },
-        },
-      },
-      jest.fn(),
-      jest.fn(),
-    ]);
-
-    render(
-      <OnboardingContextProvider>
-        <Footer
-          projectId={project.id}
-          projectSlug={project.slug}
-          location={router.location}
-          route={route}
-          router={router}
-          newOrg
-        />
-      </OnboardingContextProvider>,
-      {
-        organization,
-      }
-    );
-
-    // Skip onboarding link is gone
-    expect(
-      screen.queryByRole('button', {name: 'Skip Onboarding'})
-    ).not.toBeInTheDocument();
-
-    // Error status
-    expect(screen.getByText('Processing error')).toBeInTheDocument();
-
-    renderGlobalModal();
-
-    await userEvent.click(screen.getByRole('button', {name: 'Explore Sentry'}));
-
-    // Display are you sure modal
-    expect(await screen.findByText('Are you sure?')).toBeInTheDocument();
-  });
-
-  it('error processed ui', async function () {
-    const {project, organization, router, route} = initializeOrg();
-
-    // Mock useSessionStorage hook to return the mocked session data
-    jest.spyOn(useSessionStorage, 'useSessionStorage').mockImplementation(() => [
-      {
-        projects: {
-          [project.id]: {
-            status: OnboardingProjectStatus.PROCESSED,
-            firstIssueId: '1',
-            slug: project.slug,
-          },
-        },
-      },
-      jest.fn(),
-      jest.fn(),
-    ]);
-
-    render(
-      <OnboardingContextProvider>
-        <Footer
-          projectId={project.id}
-          projectSlug={project.slug}
-          location={router.location}
-          route={route}
-          router={router}
-          newOrg
-        />
-      </OnboardingContextProvider>,
-      {
-        organization,
-      }
-    );
-
-    // Skip onboarding link is gone
-    expect(
-      screen.queryByRole('button', {name: 'Skip Onboarding'})
-    ).not.toBeInTheDocument();
-
-    // Error status
-    expect(screen.getByText('Error Processed!')).toBeInTheDocument();
-
-    // View error button is rendered
-    expect(screen.getByRole('button', {name: 'View Error'})).toBeInTheDocument();
-
-    await userEvent.click(screen.getByRole('button', {name: 'View Error'}));
-
-    expect(router.push).toHaveBeenCalledWith(
-      expect.objectContaining({
-        pathname:
-          '/organizations/org-slug/issues/1/?referrer=onboarding-first-event-footer',
-      })
-    );
-  });
-});

+ 0 - 403
static/app/components/onboarding/footer.tsx

@@ -1,403 +0,0 @@
-import {useCallback, useContext, useEffect, useState} from 'react';
-import {RouteComponentProps} from 'react-router';
-import isPropValid from '@emotion/is-prop-valid';
-import {css} from '@emotion/react';
-import styled from '@emotion/styled';
-import {Location} from 'history';
-
-import {addSuccessMessage} from 'sentry/actionCreators/indicator';
-import {openModal} from 'sentry/actionCreators/modal';
-import {Button} from 'sentry/components/button';
-import {OnboardingContext} from 'sentry/components/onboarding/onboardingContext';
-import {IconCheckmark, IconCircle, IconRefresh} from 'sentry/icons';
-import {t} from 'sentry/locale';
-import PreferencesStore from 'sentry/stores/preferencesStore';
-import {useLegacyStore} from 'sentry/stores/useLegacyStore';
-import {space} from 'sentry/styles/space';
-import {Group, OnboardingProjectStatus, Project} from 'sentry/types';
-import {trackAnalytics} from 'sentry/utils/analytics';
-import {useApiQuery} from 'sentry/utils/queryClient';
-import useOrganization from 'sentry/utils/useOrganization';
-import useProjects from 'sentry/utils/useProjects';
-import GenericFooter from 'sentry/views/onboarding/components/genericFooter';
-
-export type OnboardingState = {
-  status: OnboardingProjectStatus;
-  firstIssueId?: string;
-};
-
-const DEFAULT_POLL_INTERVAL = 5000;
-
-type Props = Pick<RouteComponentProps<{}, {}>, 'router' | 'route' | 'location'> & {
-  projectSlug: Project['slug'];
-  newOrg?: boolean;
-  projectId?: Project['id'];
-};
-
-async function openChangeRouteModal({
-  nextLocation,
-  router,
-}: {
-  nextLocation: Location;
-  router: RouteComponentProps<{}, {}>['router'];
-}) {
-  const mod = await import('sentry/components/onboarding/changeRouteModal');
-
-  const {ChangeRouteModal} = mod;
-
-  openModal(deps => (
-    <ChangeRouteModal {...deps} router={router} nextLocation={nextLocation} />
-  ));
-}
-
-export function Footer({projectSlug, projectId, router, newOrg}: Props) {
-  const organization = useOrganization();
-  const preferences = useLegacyStore(PreferencesStore);
-  const [firstError, setFirstError] = useState<string | null>(null);
-  const [firstIssue, setFirstIssue] = useState<Group | undefined>(undefined);
-  const {projects} = useProjects();
-  const onboardingContext = useContext(OnboardingContext);
-  const projectData = projectId ? onboardingContext.data.projects[projectId] : undefined;
-  const selectedProject = projects.find(project => project.slug === projectSlug);
-
-  useApiQuery<Project>([`/projects/${organization.slug}/${projectSlug}/`], {
-    staleTime: 0,
-    refetchInterval: DEFAULT_POLL_INTERVAL,
-    enabled:
-      !!projectSlug &&
-      !firstError &&
-      projectData?.status === OnboardingProjectStatus.WAITING, // Fetch only if the project is available and we have not yet received an error,
-    onSuccess: ([data]) => {
-      setFirstError(data.firstEvent);
-    },
-  });
-
-  // Locate the projects first issue group. The project.firstEvent field will
-  // *not* include sample events, while just looking at the issues list will.
-  // We will wait until the project.firstEvent is set and then locate the
-  // event given that event datetime
-  useApiQuery<Group[]>([`/projects/${organization.slug}/${projectSlug}/issues/`], {
-    staleTime: 0,
-    enabled:
-      !!firstError &&
-      !firstIssue &&
-      projectData?.status === OnboardingProjectStatus.PROCESSING, // Only fetch if an error event is received and we have not yet located the first issue,
-    onSuccess: ([data]) => {
-      setFirstIssue(data.find((issue: Group) => issue.firstSeen === firstError));
-    },
-  });
-
-  useEffect(() => {
-    if (!projectId || !!projectData) {
-      return;
-    }
-
-    onboardingContext.setData({
-      ...onboardingContext.data,
-      projects: {
-        ...onboardingContext.data.projects,
-        [projectId]: {
-          slug: projectSlug,
-          status: OnboardingProjectStatus.WAITING,
-        },
-      },
-    });
-  }, [projectData, onboardingContext, projectSlug, projectId]);
-
-  useEffect(() => {
-    if (!projectId) {
-      return;
-    }
-
-    if (!firstError) {
-      return;
-    }
-
-    if (projectData?.status !== OnboardingProjectStatus.WAITING) {
-      return;
-    }
-
-    trackAnalytics('onboarding.first_error_received', {
-      organization,
-      new_organization: !!newOrg,
-      project_id: projectId,
-      platform: selectedProject?.platform ?? 'other',
-    });
-
-    onboardingContext.setData({
-      ...onboardingContext.data,
-      projects: {
-        ...onboardingContext.data.projects,
-        [projectId]: {
-          slug: projectSlug,
-          status: OnboardingProjectStatus.PROCESSING,
-        },
-      },
-    });
-
-    addSuccessMessage(t('First error received'));
-  }, [
-    firstError,
-    newOrg,
-    organization,
-    projectId,
-    projectData,
-    onboardingContext,
-    projectSlug,
-    selectedProject,
-  ]);
-
-  useEffect(() => {
-    if (!projectId) {
-      return;
-    }
-
-    if (!firstIssue) {
-      return;
-    }
-
-    if (projectData?.status !== OnboardingProjectStatus.PROCESSING) {
-      return;
-    }
-
-    trackAnalytics('onboarding.first_error_processed', {
-      organization,
-      new_organization: !!newOrg,
-      project_id: projectId,
-      platform: selectedProject?.platform ?? 'other',
-    });
-
-    onboardingContext.setData({
-      ...onboardingContext.data,
-      projects: {
-        ...onboardingContext.data.projects,
-        [projectId]: {
-          slug: projectSlug,
-          status: OnboardingProjectStatus.PROCESSED,
-          firstIssueId: firstIssue.id,
-        },
-      },
-    });
-
-    addSuccessMessage(t('First error processed'));
-  }, [
-    firstIssue,
-    newOrg,
-    organization,
-    projectData,
-    projectId,
-    onboardingContext,
-    projectSlug,
-    selectedProject,
-  ]);
-
-  // The explore button is only showed if Sentry has not yet received any errors OR the issue is still being processed
-  const handleExploreSentry = useCallback(() => {
-    if (!projectId) {
-      return;
-    }
-
-    if (
-      onboardingContext.data.projects[projectId].status ===
-      OnboardingProjectStatus.WAITING
-    ) {
-      return;
-    }
-
-    trackAnalytics('onboarding.explore_sentry_button_clicked', {
-      organization,
-      project_id: projectId,
-      platform: selectedProject?.platform ?? 'other',
-    });
-
-    router.push({
-      ...router.location,
-      pathname: `/organizations/${organization.slug}/issues/?referrer=onboarding-first-event-footer`,
-    });
-  }, [organization, projectId, router, onboardingContext, selectedProject]);
-
-  const handleSkipOnboarding = useCallback(() => {
-    if (!projectId) {
-      return;
-    }
-
-    if (
-      onboardingContext.data.projects[projectId].status !==
-      OnboardingProjectStatus.WAITING
-    ) {
-      return;
-    }
-
-    trackAnalytics('growth.onboarding_clicked_skip', {
-      organization,
-      source: 'targeted_onboarding_first_event_footer',
-    });
-
-    const selectedProjectId = selectedProject?.id;
-
-    let pathname = `/organizations/${organization.slug}/issues/?`;
-    if (selectedProjectId) {
-      pathname += `project=${selectedProjectId}&`;
-    }
-
-    openChangeRouteModal({
-      router,
-      nextLocation: {
-        ...router.location,
-        pathname: (pathname += `referrer=onboarding-first-event-footer-skip`),
-      },
-    });
-  }, [router, organization, selectedProject, onboardingContext, projectId]);
-
-  const handleViewError = useCallback(() => {
-    if (!projectId) {
-      return;
-    }
-
-    trackAnalytics('onboarding.view_error_button_clicked', {
-      organization,
-      new_organization: !!newOrg,
-      project_id: projectId,
-      platform: selectedProject?.platform ?? 'other',
-    });
-
-    router.push({
-      ...router.location,
-      pathname: `/organizations/${organization.slug}/issues/${onboardingContext.data.projects[projectId].firstIssueId}/?referrer=onboarding-first-event-footer`,
-    });
-  }, [organization, newOrg, router, onboardingContext, projectId, selectedProject]);
-
-  return (
-    <Wrapper newOrg={!!newOrg} sidebarCollapsed={!!preferences.collapsed}>
-      <Column>
-        {projectData?.status === OnboardingProjectStatus.WAITING && newOrg && (
-          <Button onClick={handleSkipOnboarding} priority="link">
-            {t('Skip Onboarding')}
-          </Button>
-        )}
-      </Column>
-      <StatusesColumn>
-        {projectData?.status === OnboardingProjectStatus.WAITING ? (
-          <WaitingForErrorStatus>
-            <IconCircle size="sm" />
-            {t('Waiting for error')}
-          </WaitingForErrorStatus>
-        ) : projectData?.status === OnboardingProjectStatus.PROCESSED ? (
-          <ErrorProcessedStatus>
-            <IconCheckmark isCircled size="sm" color="green300" />
-            {t('Error Processed!')}
-          </ErrorProcessedStatus>
-        ) : (
-          <ErrorProcessingStatus>
-            <RefreshIcon size="sm" />
-            {t('Processing error')}
-          </ErrorProcessingStatus>
-        )}
-      </StatusesColumn>
-      <ActionsColumn>
-        {projectData?.status === OnboardingProjectStatus.PROCESSED ? (
-          <Button priority="primary" onClick={handleViewError}>
-            {t('View Error')}
-          </Button>
-        ) : (
-          <Button
-            priority="primary"
-            disabled={projectData?.status === OnboardingProjectStatus.WAITING}
-            onClick={handleExploreSentry}
-            title={
-              projectData?.status === OnboardingProjectStatus.WAITING
-                ? t('Waiting for error')
-                : undefined
-            }
-          >
-            {t('Explore Sentry')}
-          </Button>
-        )}
-      </ActionsColumn>
-    </Wrapper>
-  );
-}
-
-const Wrapper = styled(GenericFooter, {
-  shouldForwardProp: prop => isPropValid(prop),
-})<{
-  newOrg: boolean;
-  sidebarCollapsed: boolean;
-}>`
-  display: none;
-  display: flex;
-  flex-direction: row;
-  padding: ${space(2)} ${space(4)};
-  justify-content: space-between;
-  align-items: center;
-
-  @media (min-width: ${p => p.theme.breakpoints.small}) {
-    display: grid;
-    grid-template-columns: repeat(3, 1fr);
-    align-items: center;
-    gap: ${space(3)};
-  }
-  ${p =>
-    !p.newOrg &&
-    css`
-      @media (min-width: ${p.theme.breakpoints.medium}) {
-        width: calc(
-          100% -
-            ${p.theme.sidebar[p.sidebarCollapsed ? 'collapsedWidth' : 'expandedWidth']}
-        );
-        right: 0;
-        left: auto;
-      }
-    `}
-`;
-
-const Column = styled('div')`
-  display: flex;
-`;
-
-const StatusesColumn = styled('div')`
-  display: flex;
-  justify-content: center;
-`;
-
-const ActionsColumn = styled('div')`
-  display: none;
-  @media (min-width: ${p => p.theme.breakpoints.small}) {
-    display: flex;
-    justify-content: flex-end;
-  }
-`;
-
-const WaitingForErrorStatus = styled('div')`
-  display: grid;
-  grid-template-columns: max-content max-content;
-  gap: ${space(0.75)};
-  align-items: center;
-  padding: ${space(1)} ${space(1.5)};
-  border: 1.5px solid ${p => p.theme.gray500};
-  border-radius: 76px;
-  color: ${p => p.theme.gray500};
-  line-height: ${p => p.theme.fontSizeLarge};
-`;
-
-const ErrorProcessingStatus = styled(WaitingForErrorStatus)`
-  border-color: ${p => p.theme.gray200};
-  color: ${p => p.theme.gray300};
-  position: relative;
-
-  @keyframes rotate {
-    100% {
-      transform: rotate(360deg);
-    }
-  }
-`;
-
-const ErrorProcessedStatus = styled(WaitingForErrorStatus)`
-  border-radius: 44px;
-  background: ${p => p.theme.inverted.background};
-  color: ${p => p.theme.inverted.textColor};
-`;
-
-const RefreshIcon = styled(IconRefresh)`
-  animation: rotate 1s linear infinite;
-`;

+ 0 - 391
static/app/components/onboarding/footerWithViewSampleErrorButton.tsx

@@ -1,391 +0,0 @@
-import {useCallback, useContext, useEffect, useState} from 'react';
-import {RouteComponentProps} from 'react-router';
-import isPropValid from '@emotion/is-prop-valid';
-import {css} from '@emotion/react';
-import styled from '@emotion/styled';
-import {Location} from 'history';
-
-import {addSuccessMessage} from 'sentry/actionCreators/indicator';
-import {openModal} from 'sentry/actionCreators/modal';
-import {Button} from 'sentry/components/button';
-import {OnboardingContext} from 'sentry/components/onboarding/onboardingContext';
-import {IconCheckmark, IconCircle, IconRefresh} from 'sentry/icons';
-import {t} from 'sentry/locale';
-import PreferencesStore from 'sentry/stores/preferencesStore';
-import {useLegacyStore} from 'sentry/stores/useLegacyStore';
-import {space} from 'sentry/styles/space';
-import {Group, OnboardingProjectStatus, Project} from 'sentry/types';
-import {trackAnalytics} from 'sentry/utils/analytics';
-import {useApiQuery} from 'sentry/utils/queryClient';
-import useOrganization from 'sentry/utils/useOrganization';
-import useProjects from 'sentry/utils/useProjects';
-import GenericFooter from 'sentry/views/onboarding/components/genericFooter';
-import CreateSampleEventButton from 'sentry/views/onboarding/createSampleEventButton';
-
-export type OnboardingState = {
-  status: OnboardingProjectStatus;
-  firstIssueId?: string;
-};
-
-const DEFAULT_POLL_INTERVAL = 5000;
-
-type Props = Pick<RouteComponentProps<{}, {}>, 'router' | 'route' | 'location'> & {
-  projectSlug: Project['slug'];
-  newOrg?: boolean;
-  projectId?: Project['id'];
-};
-
-async function openChangeRouteModal({
-  nextLocation,
-  router,
-}: {
-  nextLocation: Location;
-  router: RouteComponentProps<{}, {}>['router'];
-}) {
-  const mod = await import('sentry/components/onboarding/changeRouteModal');
-
-  const {ChangeRouteModal} = mod;
-
-  openModal(deps => (
-    <ChangeRouteModal {...deps} router={router} nextLocation={nextLocation} />
-  ));
-}
-
-export function FooterWithViewSampleErrorButton({
-  projectSlug,
-  projectId,
-  router,
-  newOrg,
-}: Props) {
-  const organization = useOrganization();
-  const preferences = useLegacyStore(PreferencesStore);
-  const [firstError, setFirstError] = useState<string | null>(null);
-  const [firstIssue, setFirstIssue] = useState<Group | undefined>(undefined);
-  const {projects} = useProjects();
-  const onboardingContext = useContext(OnboardingContext);
-  const projectData = projectId ? onboardingContext.data.projects[projectId] : undefined;
-  const selectedProject = projects.find(project => project.slug === projectSlug);
-
-  useApiQuery<Project>([`/projects/${organization.slug}/${projectSlug}/`], {
-    staleTime: 0,
-    refetchInterval: DEFAULT_POLL_INTERVAL,
-    enabled:
-      !!projectSlug &&
-      !firstError &&
-      projectData?.status === OnboardingProjectStatus.WAITING, // Fetch only if the project is available and we have not yet received an error,
-    onSuccess: ([data]) => {
-      setFirstError(data.firstEvent);
-    },
-  });
-
-  // Locate the projects first issue group. The project.firstEvent field will
-  // *not* include sample events, while just looking at the issues list will.
-  // We will wait until the project.firstEvent is set and then locate the
-  // event given that event datetime
-  useApiQuery<Group[]>([`/projects/${organization.slug}/${projectSlug}/issues/`], {
-    staleTime: 0,
-    enabled:
-      !!firstError &&
-      !firstIssue &&
-      projectData?.status === OnboardingProjectStatus.PROCESSING, // Only fetch if an error event is received and we have not yet located the first issue,
-    onSuccess: ([data]) => {
-      setFirstIssue(data.find((issue: Group) => issue.firstSeen === firstError));
-    },
-  });
-
-  useEffect(() => {
-    if (!projectId || !!projectData) {
-      return;
-    }
-
-    onboardingContext.setData({
-      ...onboardingContext.data,
-      projects: {
-        ...onboardingContext.data.projects,
-        [projectId]: {
-          slug: projectSlug,
-          status: OnboardingProjectStatus.WAITING,
-        },
-      },
-    });
-  }, [projectData, onboardingContext, projectSlug, projectId]);
-
-  useEffect(() => {
-    if (!projectId) {
-      return;
-    }
-
-    if (!firstError) {
-      return;
-    }
-
-    if (projectData?.status !== OnboardingProjectStatus.WAITING) {
-      return;
-    }
-
-    trackAnalytics('onboarding.first_error_received', {
-      organization,
-      new_organization: !!newOrg,
-      project_id: projectId,
-      platform: selectedProject?.platform ?? 'other',
-    });
-
-    onboardingContext.setData({
-      ...onboardingContext.data,
-      projects: {
-        ...onboardingContext.data.projects,
-        [projectId]: {
-          slug: projectSlug,
-          status: OnboardingProjectStatus.PROCESSING,
-        },
-      },
-    });
-
-    addSuccessMessage(t('First error received'));
-  }, [
-    firstError,
-    newOrg,
-    organization,
-    projectId,
-    projectData,
-    onboardingContext,
-    projectSlug,
-    selectedProject,
-  ]);
-
-  useEffect(() => {
-    if (!projectId) {
-      return;
-    }
-
-    if (!firstIssue) {
-      return;
-    }
-
-    if (projectData?.status !== OnboardingProjectStatus.PROCESSING) {
-      return;
-    }
-
-    trackAnalytics('onboarding.first_error_processed', {
-      organization,
-      new_organization: !!newOrg,
-      project_id: projectId,
-      platform: selectedProject?.platform ?? 'other',
-    });
-
-    onboardingContext.setData({
-      ...onboardingContext.data,
-      projects: {
-        ...onboardingContext.data.projects,
-        [projectId]: {
-          slug: projectSlug,
-          status: OnboardingProjectStatus.PROCESSED,
-          firstIssueId: firstIssue.id,
-        },
-      },
-    });
-
-    addSuccessMessage(t('First error processed'));
-  }, [
-    firstIssue,
-    newOrg,
-    organization,
-    projectData,
-    projectId,
-    onboardingContext,
-    projectSlug,
-    selectedProject,
-  ]);
-
-  const handleSkipOnboarding = useCallback(() => {
-    if (!projectId) {
-      return;
-    }
-
-    if (
-      onboardingContext.data.projects[projectId].status !==
-      OnboardingProjectStatus.WAITING
-    ) {
-      return;
-    }
-
-    trackAnalytics('growth.onboarding_clicked_skip', {
-      organization,
-      source: 'targeted_onboarding_first_event_footer',
-    });
-
-    const selectedProjectId = selectedProject?.id;
-
-    let pathname = `/organizations/${organization.slug}/issues/?`;
-    if (selectedProjectId) {
-      pathname += `project=${selectedProjectId}&`;
-    }
-
-    openChangeRouteModal({
-      router,
-      nextLocation: {
-        ...router.location,
-        pathname: (pathname += `referrer=onboarding-first-event-footer-skip`),
-      },
-    });
-  }, [router, organization, onboardingContext, selectedProject, projectId]);
-
-  const handleViewError = useCallback(() => {
-    if (!projectId) {
-      return;
-    }
-
-    trackAnalytics('onboarding.view_error_button_clicked', {
-      organization,
-      new_organization: !!newOrg,
-      project_id: projectId,
-      platform: selectedProject?.platform ?? 'other',
-    });
-
-    router.push({
-      ...router.location,
-      pathname: `/organizations/${organization.slug}/issues/${onboardingContext.data.projects[projectId].firstIssueId}/?referrer=onboarding-first-event-footer`,
-    });
-  }, [organization, newOrg, router, onboardingContext, projectId, selectedProject]);
-
-  return (
-    <Wrapper newOrg={!!newOrg} sidebarCollapsed={!!preferences.collapsed}>
-      <Column>
-        {projectData?.status === OnboardingProjectStatus.WAITING && newOrg && (
-          <Button onClick={handleSkipOnboarding} priority="link">
-            {t('Skip Onboarding')}
-          </Button>
-        )}
-      </Column>
-      <StatusesColumn>
-        {projectData?.status === OnboardingProjectStatus.WAITING ? (
-          <WaitingForErrorStatus>
-            <IconCircle size="sm" />
-            {t('Waiting for error')}
-          </WaitingForErrorStatus>
-        ) : projectData?.status === OnboardingProjectStatus.PROCESSED ? (
-          <ErrorProcessedStatus>
-            <IconCheckmark isCircled size="sm" color="green300" />
-            {t('Error Processed!')}
-          </ErrorProcessedStatus>
-        ) : (
-          <ErrorProcessingStatus>
-            <RefreshIcon size="sm" />
-            {t('Processing error')}
-          </ErrorProcessingStatus>
-        )}
-      </StatusesColumn>
-      <ActionsColumn>
-        {projectData?.status === OnboardingProjectStatus.PROCESSED ? (
-          <Button priority="primary" onClick={handleViewError}>
-            {t('View Error')}
-          </Button>
-        ) : (
-          <CreateSampleEventButton
-            project={selectedProject}
-            source="targted-onboarding-heartbeat-footer"
-            priority="primary"
-            onCreateSampleGroup={() => {
-              if (!projectId) {
-                return;
-              }
-
-              trackAnalytics('onboarding.view_sample_error_button_clicked', {
-                new_organization: !!newOrg,
-                project_id: projectId,
-                platform: selectedProject?.platform ?? 'other',
-                organization,
-              });
-            }}
-          >
-            {t('View Sample Error')}
-          </CreateSampleEventButton>
-        )}
-      </ActionsColumn>
-    </Wrapper>
-  );
-}
-
-const Wrapper = styled(GenericFooter, {
-  shouldForwardProp: prop => isPropValid(prop),
-})<{
-  newOrg: boolean;
-  sidebarCollapsed: boolean;
-}>`
-  display: none;
-  display: flex;
-  flex-direction: row;
-  padding: ${space(2)} ${space(4)};
-  justify-content: space-between;
-  align-items: center;
-
-  @media (min-width: ${p => p.theme.breakpoints.small}) {
-    display: grid;
-    grid-template-columns: repeat(3, 1fr);
-    align-items: center;
-    gap: ${space(3)};
-  }
-  ${p =>
-    !p.newOrg &&
-    css`
-      @media (min-width: ${p.theme.breakpoints.medium}) {
-        width: calc(
-          100% -
-            ${p.theme.sidebar[p.sidebarCollapsed ? 'collapsedWidth' : 'expandedWidth']}
-        );
-        right: 0;
-        left: auto;
-      }
-    `}
-`;
-
-const Column = styled('div')`
-  display: flex;
-`;
-
-const StatusesColumn = styled('div')`
-  display: flex;
-  justify-content: center;
-`;
-
-const ActionsColumn = styled('div')`
-  display: none;
-  @media (min-width: ${p => p.theme.breakpoints.small}) {
-    display: flex;
-    justify-content: flex-end;
-  }
-`;
-
-const WaitingForErrorStatus = styled('div')`
-  display: grid;
-  grid-template-columns: max-content max-content;
-  gap: ${space(0.75)};
-  align-items: center;
-  padding: ${space(1)} ${space(1.5)};
-  border: 1.5px solid ${p => p.theme.gray500};
-  border-radius: 76px;
-  color: ${p => p.theme.gray500};
-  line-height: ${p => p.theme.fontSizeLarge};
-`;
-
-const ErrorProcessingStatus = styled(WaitingForErrorStatus)`
-  border-color: ${p => p.theme.gray200};
-  color: ${p => p.theme.gray300};
-  position: relative;
-
-  @keyframes rotate {
-    100% {
-      transform: rotate(360deg);
-    }
-  }
-`;
-
-const ErrorProcessedStatus = styled(WaitingForErrorStatus)`
-  border-radius: 44px;
-  background: ${p => p.theme.inverted.background};
-  color: ${p => p.theme.inverted.textColor};
-`;
-
-const RefreshIcon = styled(IconRefresh)`
-  animation: rotate 1s linear infinite;
-`;

+ 0 - 6
static/app/data/experimentConfig.tsx

@@ -12,12 +12,6 @@ export const unassignedValue = -1;
  * Frontend experiment configuration object
  */
 export const experimentList = [
-  {
-    key: 'OnboardingNewFooterExperiment',
-    type: ExperimentType.ORGANIZATION,
-    parameter: 'scenario',
-    assignments: ['baseline', 'variant1', 'variant2'],
-  },
   {
     key: 'ExtendTrialByInvitingMemberExperiment',
     type: ExperimentType.ORGANIZATION,

+ 3 - 8
static/app/views/onboarding/onboarding.tsx

@@ -138,10 +138,6 @@ function Onboarding(props: Props) {
     props.location.pathname,
   ]);
 
-  const heartbeatFooter = !!organization?.features.includes(
-    'onboarding-heartbeat-footer'
-  );
-
   const projectDeletionOnBackClick = !!organization?.features.includes(
     'onboarding-project-deletion-on-back-click'
   );
@@ -404,7 +400,7 @@ function Onboarding(props: Props) {
           />
         </UpsellWrapper>
       </Header>
-      <Container hasFooter={containerHasFooter} heartbeatFooter={heartbeatFooter}>
+      <Container hasFooter={containerHasFooter}>
         <Confirm bypass={!shallProjectBeDeleted} {...goBackDeletionAlertModalProps}>
           <Back animate={stepIndex > 0 ? 'visible' : 'hidden'} />
         </Confirm>
@@ -439,14 +435,13 @@ function Onboarding(props: Props) {
   );
 }
 
-const Container = styled('div')<{hasFooter: boolean; heartbeatFooter: boolean}>`
+const Container = styled('div')<{hasFooter: boolean}>`
   flex-grow: 1;
   display: flex;
   flex-direction: column;
   position: relative;
   background: ${p => p.theme.background};
-  padding: ${p =>
-    p.heartbeatFooter ? `120px ${space(3)} 0 ${space(3)}` : `120px ${space(3)}`};
+  padding: 120px ${space(3)};
   width: 100%;
   margin: 0 auto;
   padding-bottom: ${p => p.hasFooter && '72px'};

+ 15 - 73
static/app/views/onboarding/setupDocs.tsx

@@ -7,8 +7,6 @@ import {loadDocs} from 'sentry/actionCreators/projects';
 import {Alert} from 'sentry/components/alert';
 import LoadingError from 'sentry/components/loadingError';
 import {DocumentationWrapper} from 'sentry/components/onboarding/documentationWrapper';
-import {Footer} from 'sentry/components/onboarding/footer';
-import {FooterWithViewSampleErrorButton} from 'sentry/components/onboarding/footerWithViewSampleErrorButton';
 import {
   migratedDocs,
   SdkDocumentation,
@@ -25,7 +23,6 @@ import {trackAnalytics} from 'sentry/utils/analytics';
 import getDynamicText from 'sentry/utils/getDynamicText';
 import {platformToIntegrationMap} from 'sentry/utils/integrationUtil';
 import useApi from 'sentry/utils/useApi';
-import {useExperiment} from 'sentry/utils/useExperiment';
 import useOrganization from 'sentry/utils/useOrganization';
 import SetupIntroduction from 'sentry/views/onboarding/components/setupIntroduction';
 import {SetupDocsLoader} from 'sentry/views/onboarding/setupDocsLoader';
@@ -85,21 +82,10 @@ function ProjectDocs(props: {
   );
 }
 
-function SetupDocs({route, router, location, recentCreatedProject: project}: StepProps) {
+function SetupDocs({location, recentCreatedProject: project}: StepProps) {
   const api = useApi();
   const organization = useOrganization();
 
-  const {
-    logExperiment: newFooterLogExperiment,
-    experimentAssignment: newFooterAssignment,
-  } = useExperiment('OnboardingNewFooterExperiment', {
-    logExperimentOnMount: false,
-  });
-
-  const heartbeatFooter = !!organization?.features.includes(
-    'onboarding-heartbeat-footer'
-  );
-
   const products = useMemo<ProductSolution[]>(
     () => (location.query.product ?? []) as ProductSolution[],
     [location.query.product]
@@ -197,13 +183,6 @@ function SetupDocs({route, router, location, recentCreatedProject: project}: Ste
     fetchData();
   }, [fetchData, location.query.product, project?.platform]);
 
-  // log experiment on mount if feature enabled
-  useEffect(() => {
-    if (heartbeatFooter) {
-      newFooterLogExperiment();
-    }
-  }, [newFooterLogExperiment, heartbeatFooter]);
-
   if (!project) {
     return null;
   }
@@ -270,57 +249,20 @@ function SetupDocs({route, router, location, recentCreatedProject: project}: Ste
         </MainContent>
       </Wrapper>
 
-      {heartbeatFooter ? (
-        newFooterAssignment === 'variant2' ? (
-          <FooterWithViewSampleErrorButton
-            projectSlug={project.slug}
-            projectId={project.id}
-            route={route}
-            router={router}
-            location={location}
-            newOrg
-          />
-        ) : newFooterAssignment === 'variant1' ? (
-          <Footer
-            projectSlug={project.slug}
-            projectId={project.id}
-            route={route}
-            router={router}
-            location={location}
-            newOrg
-          />
-        ) : (
-          <FirstEventFooter
-            project={project}
-            organization={organization}
-            isLast
-            onClickSetupLater={() => {
-              const orgIssuesURL = `/organizations/${organization.slug}/issues/?project=${project.id}&referrer=onboarding-setup-docs`;
-              trackAnalytics('growth.onboarding_clicked_setup_platform_later', {
-                organization,
-                platform: currentPlatformKey,
-                project_id: project.id,
-              });
-              browserHistory.push(orgIssuesURL);
-            }}
-          />
-        )
-      ) : (
-        <FirstEventFooter
-          project={project}
-          organization={organization}
-          isLast
-          onClickSetupLater={() => {
-            const orgIssuesURL = `/organizations/${organization.slug}/issues/?project=${project.id}&referrer=onboarding-setup-docs`;
-            trackAnalytics('growth.onboarding_clicked_setup_platform_later', {
-              organization,
-              platform: currentPlatformKey,
-              project_id: project.id,
-            });
-            browserHistory.push(orgIssuesURL);
-          }}
-        />
-      )}
+      <FirstEventFooter
+        project={project}
+        organization={organization}
+        isLast
+        onClickSetupLater={() => {
+          const orgIssuesURL = `/organizations/${organization.slug}/issues/?project=${project.id}&referrer=onboarding-setup-docs`;
+          trackAnalytics('growth.onboarding_clicked_setup_platform_later', {
+            organization,
+            platform: currentPlatformKey,
+            project_id: project.id,
+          });
+          browserHistory.push(orgIssuesURL);
+        }}
+      />
     </Fragment>
   );
 }

+ 1 - 27
static/app/views/organizationDetails/body.tsx

@@ -9,7 +9,6 @@ import {t, tct} from 'sentry/locale';
 import AlertStore from 'sentry/stores/alertStore';
 import {Organization} from 'sentry/types';
 import useApi from 'sentry/utils/useApi';
-import {useRouteContext} from 'sentry/utils/useRouteContext';
 import withOrganization from 'sentry/utils/withOrganization';
 
 type OrganizationProps = {
@@ -106,7 +105,6 @@ function DeletionPending({organization}: OrganizationProps) {
 
 function OrganizationDetailsBody({children, organization}: BodyProps) {
   const status = organization?.status?.id;
-  const routeContext = useRouteContext();
 
   if (organization && status === 'pending_deletion') {
     return <DeletionPending organization={organization} />;
@@ -116,34 +114,10 @@ function OrganizationDetailsBody({children, organization}: BodyProps) {
     return <DeletionInProgress organization={organization} />;
   }
 
-  const heartbeatFooter = !!organization?.features.includes(
-    'onboarding-heartbeat-footer'
-  );
-  const slug = organization?.slug;
-
-  const gettingStartedRoutes = [
-    `/getting-started/${routeContext.params.projectId}/${routeContext.params.platform}/`,
-    `/${slug}/${routeContext.params.projectId}/getting-started/${routeContext.params.platform}/`,
-  ];
-
-  const onboardingRoutes = [
-    `/onboarding/welcome/`,
-    `/onboarding/setup-docs/`,
-    `/onboarding/select-platform/`,
-    `/onboarding/${slug}/welcome/`,
-    `/onboarding/${slug}/setup-docs/`,
-    `/onboarding/${slug}/select-platform/`,
-  ];
-
-  const showFooter = !heartbeatFooter
-    ? true
-    : !gettingStartedRoutes.includes(routeContext.location.pathname) &&
-      !onboardingRoutes.includes(routeContext.location.pathname);
-
   return (
     <Fragment>
       <ErrorBoundary>{children}</ErrorBoundary>
-      {showFooter && <Footer />}
+      <Footer />
     </Fragment>
   );
 }

+ 23 - 38
static/app/views/projectInstall/platform.tsx

@@ -15,7 +15,6 @@ import ExternalLink from 'sentry/components/links/externalLink';
 import LoadingError from 'sentry/components/loadingError';
 import LoadingIndicator from 'sentry/components/loadingIndicator';
 import {DocumentationWrapper} from 'sentry/components/onboarding/documentationWrapper';
-import {Footer} from 'sentry/components/onboarding/footer';
 import {
   migratedDocs,
   SdkDocumentation,
@@ -121,7 +120,7 @@ export function SetUpGeneralSdkDoc({
   );
 }
 
-export function ProjectInstallPlatform({location, params, route, router}: Props) {
+export function ProjectInstallPlatform({location, params, router}: Props) {
   const organization = useOrganization();
   const api = useApi();
   const gettingStartedWithProjectContext = useContext(GettingStartedWithProjectContext);
@@ -202,10 +201,6 @@ export function ProjectInstallPlatform({location, params, route, router}: Props)
     projectAlertRulesIsError,
   ]);
 
-  const heartbeatFooter = !!organization?.features.includes(
-    'onboarding-heartbeat-footer'
-  );
-
   const projectDeletionOnBackClick = !!organization?.features.includes(
     'onboarding-project-deletion-on-back-click'
   );
@@ -420,38 +415,28 @@ export function ProjectInstallPlatform({location, params, route, router}: Props)
           </Feature>
         )}
 
-        {isGettingStarted && heartbeatFooter ? (
-          <Footer
-            projectSlug={params.projectId}
-            projectId={project?.id}
-            route={route}
-            router={router}
-            location={location}
-          />
-        ) : (
-          <StyledButtonBar gap={1}>
-            <Button
-              priority="primary"
-              busy={loadingProjects}
-              to={{
-                pathname: issueStreamLink,
-                query: project?.id,
-                hash: '#welcome',
-              }}
-            >
-              {t('Take me to Issues')}
-            </Button>
-            <Button
-              busy={loadingProjects}
-              to={{
-                pathname: performanceOverviewLink,
-                query: project?.id,
-              }}
-            >
-              {t('Take me to Performance')}
-            </Button>
-          </StyledButtonBar>
-        )}
+        <StyledButtonBar gap={1}>
+          <Button
+            priority="primary"
+            busy={loadingProjects}
+            to={{
+              pathname: issueStreamLink,
+              query: project?.id,
+              hash: '#welcome',
+            }}
+          >
+            {t('Take me to Issues')}
+          </Button>
+          <Button
+            busy={loadingProjects}
+            to={{
+              pathname: performanceOverviewLink,
+              query: project?.id,
+            }}
+          >
+            {t('Take me to Performance')}
+          </Button>
+        </StyledButtonBar>
       </div>
     </Fragment>
   );