|
@@ -4,15 +4,22 @@ import styled from '@emotion/styled';
|
|
import emptyStateImg from 'sentry-images/spot/replays-empty-state.svg';
|
|
import emptyStateImg from 'sentry-images/spot/replays-empty-state.svg';
|
|
|
|
|
|
import Feature from 'sentry/components/acl/feature';
|
|
import Feature from 'sentry/components/acl/feature';
|
|
|
|
+import Alert from 'sentry/components/alert';
|
|
import {Button} from 'sentry/components/button';
|
|
import {Button} from 'sentry/components/button';
|
|
import ButtonBar from 'sentry/components/buttonBar';
|
|
import ButtonBar from 'sentry/components/buttonBar';
|
|
import HookOrDefault from 'sentry/components/hookOrDefault';
|
|
import HookOrDefault from 'sentry/components/hookOrDefault';
|
|
|
|
+import ExternalLink from 'sentry/components/links/externalLink';
|
|
import OnboardingPanel from 'sentry/components/onboardingPanel';
|
|
import OnboardingPanel from 'sentry/components/onboardingPanel';
|
|
-import {t} from 'sentry/locale';
|
|
|
|
|
|
+import {Tooltip} from 'sentry/components/tooltip';
|
|
|
|
+import {replayPlatforms} from 'sentry/data/platformCategories';
|
|
|
|
+import {IconInfo} from 'sentry/icons';
|
|
|
|
+import {t, tct} from 'sentry/locale';
|
|
import PreferencesStore from 'sentry/stores/preferencesStore';
|
|
import PreferencesStore from 'sentry/stores/preferencesStore';
|
|
import {useLegacyStore} from 'sentry/stores/useLegacyStore';
|
|
import {useLegacyStore} from 'sentry/stores/useLegacyStore';
|
|
import {useReplayOnboardingSidebarPanel} from 'sentry/utils/replays/hooks/useReplayOnboarding';
|
|
import {useReplayOnboardingSidebarPanel} from 'sentry/utils/replays/hooks/useReplayOnboarding';
|
|
import useOrganization from 'sentry/utils/useOrganization';
|
|
import useOrganization from 'sentry/utils/useOrganization';
|
|
|
|
+import usePageFilters from 'sentry/utils/usePageFilters';
|
|
|
|
+import useProjects from 'sentry/utils/useProjects';
|
|
|
|
|
|
type Breakpoints = {
|
|
type Breakpoints = {
|
|
large: string;
|
|
large: string;
|
|
@@ -28,6 +35,34 @@ const OnboardingCTAHook = HookOrDefault({
|
|
|
|
|
|
export default function ReplayOnboardingPanel() {
|
|
export default function ReplayOnboardingPanel() {
|
|
const preferences = useLegacyStore(PreferencesStore);
|
|
const preferences = useLegacyStore(PreferencesStore);
|
|
|
|
+ const pageFilters = usePageFilters();
|
|
|
|
+ const projects = useProjects();
|
|
|
|
+ const organization = useOrganization();
|
|
|
|
+ const canCreateProjects = organization.access.includes('project:admin');
|
|
|
|
+
|
|
|
|
+ const selectedProjects = projects.projects.filter(p =>
|
|
|
|
+ pageFilters.selection.projects.includes(Number(p.id))
|
|
|
|
+ );
|
|
|
|
+
|
|
|
|
+ const hasSelectedProjects = selectedProjects.length > 0;
|
|
|
|
+
|
|
|
|
+ const allProjectsUnsupported = projects.projects.every(
|
|
|
|
+ p => !replayPlatforms.includes(p.platform!)
|
|
|
|
+ );
|
|
|
|
+
|
|
|
|
+ const allSelectedProjectsUnsupported = selectedProjects.every(
|
|
|
|
+ p => !replayPlatforms.includes(p.platform!)
|
|
|
|
+ );
|
|
|
|
+
|
|
|
|
+ // if all projects are unsupported we should prompt the user to create a project
|
|
|
|
+ // else we prompt to setup
|
|
|
|
+ const primaryAction = allProjectsUnsupported ? 'create' : 'setup';
|
|
|
|
+ // disable "create" if the user has insufficient permissions
|
|
|
|
+ // disable "setup" if the current selected pageFilters are not supported
|
|
|
|
+ const primaryActionDisabled =
|
|
|
|
+ primaryAction === 'create'
|
|
|
|
+ ? !canCreateProjects
|
|
|
|
+ : allSelectedProjectsUnsupported && hasSelectedProjects;
|
|
|
|
|
|
const breakpoints = preferences.collapsed
|
|
const breakpoints = preferences.collapsed
|
|
? {
|
|
? {
|
|
@@ -43,26 +78,115 @@ export default function ReplayOnboardingPanel() {
|
|
xlarge: '1450px',
|
|
xlarge: '1450px',
|
|
};
|
|
};
|
|
|
|
|
|
- const organization = useOrganization();
|
|
|
|
-
|
|
|
|
return (
|
|
return (
|
|
- <OnboardingPanel image={<HeroImage src={emptyStateImg} breakpoints={breakpoints} />}>
|
|
|
|
- <Feature
|
|
|
|
- features={['session-replay-ga']}
|
|
|
|
- organization={organization}
|
|
|
|
- renderDisabled={() => <SetupReplaysCTA />}
|
|
|
|
|
|
+ <Fragment>
|
|
|
|
+ {hasSelectedProjects && allSelectedProjectsUnsupported && (
|
|
|
|
+ <Alert icon={<IconInfo />}>
|
|
|
|
+ {tct(
|
|
|
|
+ `[projectMsg] [action] a project using our [link], or equivalent framework SDK.`,
|
|
|
|
+ {
|
|
|
|
+ action: primaryAction === 'create' ? t('Create') : t('Select'),
|
|
|
|
+ projectMsg: (
|
|
|
|
+ <strong>
|
|
|
|
+ {t(
|
|
|
|
+ `Session Replay isn't available for project %s.`,
|
|
|
|
+ selectedProjects[0].slug
|
|
|
|
+ )}
|
|
|
|
+ </strong>
|
|
|
|
+ ),
|
|
|
|
+ link: (
|
|
|
|
+ <ExternalLink href="https://docs.sentry.io/platforms/javascript/session-replay/">
|
|
|
|
+ {t('Sentry browser SDK package')}
|
|
|
|
+ </ExternalLink>
|
|
|
|
+ ),
|
|
|
|
+ }
|
|
|
|
+ )}
|
|
|
|
+ </Alert>
|
|
|
|
+ )}
|
|
|
|
+ <OnboardingPanel
|
|
|
|
+ image={<HeroImage src={emptyStateImg} breakpoints={breakpoints} />}
|
|
>
|
|
>
|
|
- <OnboardingCTAHook organization={organization}>
|
|
|
|
- <SetupReplaysCTA />
|
|
|
|
- </OnboardingCTAHook>
|
|
|
|
- </Feature>
|
|
|
|
- </OnboardingPanel>
|
|
|
|
|
|
+ <Feature
|
|
|
|
+ features={['session-replay-ga']}
|
|
|
|
+ organization={organization}
|
|
|
|
+ renderDisabled={() => (
|
|
|
|
+ <SetupReplaysCTA
|
|
|
|
+ orgSlug={organization.slug}
|
|
|
|
+ primaryAction={primaryAction}
|
|
|
|
+ disabled={primaryActionDisabled}
|
|
|
|
+ />
|
|
|
|
+ )}
|
|
|
|
+ >
|
|
|
|
+ <OnboardingCTAHook organization={organization}>
|
|
|
|
+ <SetupReplaysCTA
|
|
|
|
+ orgSlug={organization.slug}
|
|
|
|
+ primaryAction={primaryAction}
|
|
|
|
+ disabled={primaryActionDisabled}
|
|
|
|
+ />
|
|
|
|
+ </OnboardingCTAHook>
|
|
|
|
+ </Feature>
|
|
|
|
+ </OnboardingPanel>
|
|
|
|
+ </Fragment>
|
|
);
|
|
);
|
|
}
|
|
}
|
|
|
|
|
|
-function SetupReplaysCTA() {
|
|
|
|
|
|
+interface SetupReplaysCTAProps {
|
|
|
|
+ orgSlug: string;
|
|
|
|
+ primaryAction: 'setup' | 'create';
|
|
|
|
+ disabled?: boolean;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+export function SetupReplaysCTA({
|
|
|
|
+ disabled,
|
|
|
|
+ primaryAction = 'setup',
|
|
|
|
+ orgSlug,
|
|
|
|
+}: SetupReplaysCTAProps) {
|
|
const {activateSidebar} = useReplayOnboardingSidebarPanel();
|
|
const {activateSidebar} = useReplayOnboardingSidebarPanel();
|
|
|
|
|
|
|
|
+ function renderCTA() {
|
|
|
|
+ if (primaryAction === 'setup') {
|
|
|
|
+ return (
|
|
|
|
+ <Tooltip
|
|
|
|
+ title={
|
|
|
|
+ <span data-test-id="setup-replays-tooltip">
|
|
|
|
+ {t('Select a supported project from the projects dropdown.')}
|
|
|
|
+ </span>
|
|
|
|
+ }
|
|
|
|
+ disabled={!disabled} // we only want to show the tooltip when the button is disabled
|
|
|
|
+ >
|
|
|
|
+ <Button
|
|
|
|
+ data-test-id="setup-replays-btn"
|
|
|
|
+ onClick={activateSidebar}
|
|
|
|
+ priority="primary"
|
|
|
|
+ disabled={disabled}
|
|
|
|
+ >
|
|
|
|
+ {t('Set Up Replays')}
|
|
|
|
+ </Button>
|
|
|
|
+ </Tooltip>
|
|
|
|
+ );
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return (
|
|
|
|
+ <Tooltip
|
|
|
|
+ title={
|
|
|
|
+ <span data-test-id="create-project-tooltip">
|
|
|
|
+ {t('Only admins, managers, and owners, can create projects.')}
|
|
|
|
+ </span>
|
|
|
|
+ }
|
|
|
|
+ disabled={!disabled}
|
|
|
|
+ >
|
|
|
|
+ <Button
|
|
|
|
+ data-test-id="create-project-btn"
|
|
|
|
+ to={`/organizations/${orgSlug}/projects/new/`}
|
|
|
|
+ priority="primary"
|
|
|
|
+ disabled={disabled}
|
|
|
|
+ >
|
|
|
|
+ {t('Create Project')}
|
|
|
|
+ </Button>
|
|
|
|
+ </Tooltip>
|
|
|
|
+ );
|
|
|
|
+ }
|
|
|
|
+
|
|
return (
|
|
return (
|
|
<Fragment>
|
|
<Fragment>
|
|
<h3>{t('Get to the root cause faster')}</h3>
|
|
<h3>{t('Get to the root cause faster')}</h3>
|
|
@@ -72,9 +196,7 @@ function SetupReplaysCTA() {
|
|
)}
|
|
)}
|
|
</p>
|
|
</p>
|
|
<ButtonList gap={1}>
|
|
<ButtonList gap={1}>
|
|
- <Button onClick={activateSidebar} priority="primary">
|
|
|
|
- {t('Set Up Replays')}
|
|
|
|
- </Button>
|
|
|
|
|
|
+ {renderCTA()}
|
|
<Button
|
|
<Button
|
|
href="https://docs.sentry.io/platforms/javascript/session-replay/"
|
|
href="https://docs.sentry.io/platforms/javascript/session-replay/"
|
|
external
|
|
external
|