From 4d69b06d4d15dd89b8da3dc0f4af28b6b6b9c752 Mon Sep 17 00:00:00 2001 From: Quinn Slack Date: Mon, 24 Jun 2024 13:37:39 -0700 Subject: [PATCH] remove extraneous Cody onboarding steps (#63373) - Remove the "personal or work" step - Remove the "do you want us to call you?" step - Cleaner design of the Cody dashboard - Improve some wording - Remove CodySurveyToast (unused) and user.CompletedPostSignup. These have not been used in many months. On Amplitude, the relevant events have no data. Fix https://linear.app/sourcegraph/issue/PRIME-375/remove-personal-or-work-survey-signup-step image ## Test plan n/a --- client/shared/src/auth.ts | 1 - .../settings/temporary/TemporarySettings.ts | 2 - client/shared/src/telemetry/event-names.ts | 1 - .../src/testing/integration/graphQlResults.ts | 1 - client/web-sveltekit/src/params/reporev.ts | 3 - client/web/BUILD.bazel | 12 - client/web/src/LegacyLayout.tsx | 14 +- client/web/src/auth/PostSignUpPage.tsx | 20 - client/web/src/auth/SignUpPage.tsx | 6 +- .../PostSignUpPage.test.tsx.snap | 41 -- .../web/src/auth/components/ExternalsAuth.tsx | 2 - client/web/src/cody/chat/CodyChatPage.tsx | 20 +- .../CodyMarketingPage/CodyMarketingPage.tsx | 4 +- .../src/cody/dashboard/CodyDashboardPage.tsx | 8 +- client/web/src/cody/editorGroups.ts | 94 ----- .../management/CodyManagementPage.module.scss | 42 +- .../cody/management/CodyManagementPage.tsx | 185 ++++----- .../src/cody/management/SubscriptionStats.tsx | 52 ++- .../management/UseCodyInEditorSection.tsx | 337 +++++++++------ .../onboarding/CodyOnboarding.module.scss | 195 --------- .../src/cody/onboarding/CodyOnboarding.tsx | 129 ------ client/web/src/cody/onboarding/EditorStep.tsx | 118 ------ .../web/src/cody/onboarding/PurposeStep.tsx | 61 --- .../web/src/cody/onboarding/WelcomeStep.tsx | 75 ---- .../onboarding/instructions/CodyFeatures.tsx | 105 ----- .../onboarding/instructions/JetBrains.tsx | 167 -------- .../cody/onboarding/instructions/NeoVim.tsx | 114 ----- .../cody/onboarding/instructions/VsCode.tsx | 162 -------- .../CodySubscriptionPage.module.scss | 4 - .../CommunitySearchContextPage.story.tsx | 1 - .../code-monitoring/testing/util.ts | 1 - .../SearchContextForm.story.tsx | 1 - client/web/src/get-cody/GetCodyPage.tsx | 25 -- .../utils/override-insights-graphql-api.ts | 1 - client/web/src/jscontext.ts | 1 - .../toast/CodySurveyToast.module.scss | 101 ----- .../src/marketing/toast/CodySurveyToast.tsx | 390 ------------------ client/web/src/marketing/toast/index.ts | 1 - client/web/src/routes.constants.ts | 3 - client/web/src/routes.tsx | 15 - .../src/storm/pages/LayoutPage/LayoutPage.tsx | 19 +- client/web/src/util/constants.ts | 1 - .../src/global-styles/utilities/flex.scss | 12 + cmd/frontend/graphqlbackend/schema.graphql | 16 - cmd/frontend/graphqlbackend/user.go | 28 -- cmd/frontend/graphqlbackend/user_test.go | 108 ----- .../graphqlbackend/user_usage_stats.go | 40 -- .../internal/app/jscontext/jscontext.go | 7 - cmd/frontend/internal/app/router/router.go | 2 - cmd/frontend/internal/app/ui/router.go | 3 - internal/database/users.go | 7 +- internal/database/users_test.go | 17 - internal/types/types.go | 1 - 53 files changed, 347 insertions(+), 2429 deletions(-) delete mode 100644 client/web/src/auth/PostSignUpPage.tsx delete mode 100644 client/web/src/auth/__snapshots__/PostSignUpPage.test.tsx.snap delete mode 100644 client/web/src/cody/editorGroups.ts delete mode 100644 client/web/src/cody/onboarding/CodyOnboarding.module.scss delete mode 100644 client/web/src/cody/onboarding/CodyOnboarding.tsx delete mode 100644 client/web/src/cody/onboarding/EditorStep.tsx delete mode 100644 client/web/src/cody/onboarding/PurposeStep.tsx delete mode 100644 client/web/src/cody/onboarding/WelcomeStep.tsx delete mode 100644 client/web/src/cody/onboarding/instructions/CodyFeatures.tsx delete mode 100644 client/web/src/cody/onboarding/instructions/JetBrains.tsx delete mode 100644 client/web/src/cody/onboarding/instructions/NeoVim.tsx delete mode 100644 client/web/src/cody/onboarding/instructions/VsCode.tsx delete mode 100644 client/web/src/get-cody/GetCodyPage.tsx delete mode 100644 client/web/src/marketing/toast/CodySurveyToast.module.scss delete mode 100644 client/web/src/marketing/toast/CodySurveyToast.tsx diff --git a/client/shared/src/auth.ts b/client/shared/src/auth.ts index c19c7b7423e..5a47be71071 100644 --- a/client/shared/src/auth.ts +++ b/client/shared/src/auth.ts @@ -42,7 +42,6 @@ export const currentAuthStateQuery = gql` viewerCanAdminister tosAccepted hasVerifiedEmail - completedPostSignup emails { email verified diff --git a/client/shared/src/settings/temporary/TemporarySettings.ts b/client/shared/src/settings/temporary/TemporarySettings.ts index ebcf4d54856..fed061a2866 100644 --- a/client/shared/src/settings/temporary/TemporarySettings.ts +++ b/client/shared/src/settings/temporary/TemporarySettings.ts @@ -93,7 +93,6 @@ export interface TemporarySettingsSchema { 'admin.hasCompletedLicenseCheck': boolean 'simple.search.toggle': boolean 'cody.onboarding.completed': boolean - 'cody.onboarding.step': number /** OpenCodeGraph */ 'openCodeGraph.annotations.visible': boolean @@ -161,7 +160,6 @@ const TEMPORARY_SETTINGS: Record = { 'admin.hasCompletedLicenseCheck': null, 'simple.search.toggle': null, 'cody.onboarding.completed': null, - 'cody.onboarding.step': null, 'openCodeGraph.annotations.visible': null, } diff --git a/client/shared/src/telemetry/event-names.ts b/client/shared/src/telemetry/event-names.ts index 14ba76ff154..02a667aa4b3 100644 --- a/client/shared/src/telemetry/event-names.ts +++ b/client/shared/src/telemetry/event-names.ts @@ -15,7 +15,6 @@ export const enum EventName { CODY_CHAT_SCOPE_INFERRED_REPO_DISABLED = 'web:codyChat:inferredRepoDisabled', CODY_CHAT_SCOPE_INFERRED_FILE_ENABLED = 'web:codyChat:inferredFileEnabled', CODY_CHAT_SCOPE_INFERRED_FILE_DISABLED = 'web:codyChat:inferredFileDisabled', - VIEW_GET_CODY = 'GetCody', CODY_EDITOR_WIDGET_VIEWED = 'web:codyEditorWidget:viewed', CODY_SIDEBAR_CHAT_OPENED = 'web:codySidebar:chatOpened', diff --git a/client/shared/src/testing/integration/graphQlResults.ts b/client/shared/src/testing/integration/graphQlResults.ts index 6af205c3b0d..2f798ba25cd 100644 --- a/client/shared/src/testing/integration/graphQlResults.ts +++ b/client/shared/src/testing/integration/graphQlResults.ts @@ -21,7 +21,6 @@ export const currentUserMock = { emails: [{ email: 'felix@sourcegraph.com', isPrimary: true, verified: true }], latestSettings: null, hasVerifiedEmail: true, - completedPostSignup: true, permissions: { __typename: 'PermissionConnection', nodes: [ diff --git a/client/web-sveltekit/src/params/reporev.ts b/client/web-sveltekit/src/params/reporev.ts index 8bebfeec4d7..7cb8cc4fdfb 100644 --- a/client/web-sveltekit/src/params/reporev.ts +++ b/client/web-sveltekit/src/params/reporev.ts @@ -33,12 +33,9 @@ const topLevelPaths = [ 'search/cody', 'app', 'cody', - 'get-cody', - 'post-sign-up', 'unlock-account', 'password-reset', 'survey', - 'welcome', 'embed', 'users', 'user', diff --git a/client/web/BUILD.bazel b/client/web/BUILD.bazel index f3d2657a8b4..fd9c9c3e836 100644 --- a/client/web/BUILD.bazel +++ b/client/web/BUILD.bazel @@ -160,7 +160,6 @@ ts_project( "src/auth/AuthPageWrapper.tsx", "src/auth/CloudSignUpPage.tsx", "src/auth/OrDivider.tsx", - "src/auth/PostSignUpPage.tsx", "src/auth/RequestAccessPage.tsx", "src/auth/ResetPasswordPage.tsx", "src/auth/SignInPage.tsx", @@ -228,7 +227,6 @@ ts_project( "src/cody/components/ScopeSelector/index.tsx", "src/cody/components/ScopeSelector/useRepoSuggestions.ts", "src/cody/dashboard/CodyDashboardPage.tsx", - "src/cody/editorGroups.ts", "src/cody/invites/AcceptInviteBanner.tsx", "src/cody/invites/InviteUsers.tsx", "src/cody/invites/useInviteParams.ts", @@ -263,14 +261,6 @@ ts_project( "src/cody/management/subscription/manage/utils.ts", "src/cody/management/subscription/new/CodyProCheckoutForm.tsx", "src/cody/management/subscription/new/NewCodyProSubscriptionPage.tsx", - "src/cody/onboarding/CodyOnboarding.tsx", - "src/cody/onboarding/EditorStep.tsx", - "src/cody/onboarding/PurposeStep.tsx", - "src/cody/onboarding/WelcomeStep.tsx", - "src/cody/onboarding/instructions/CodyFeatures.tsx", - "src/cody/onboarding/instructions/JetBrains.tsx", - "src/cody/onboarding/instructions/NeoVim.tsx", - "src/cody/onboarding/instructions/VsCode.tsx", "src/cody/sidebar/CodySidebar.tsx", "src/cody/sidebar/Provider.tsx", "src/cody/sidebar/index.tsx", @@ -1103,7 +1093,6 @@ ts_project( "src/fuzzyFinder/SearchValue.ts", "src/fuzzyFinder/SearchValueRankingCache.ts", "src/fuzzyFinder/WordSensitiveFuzzySearch.ts", - "src/get-cody/GetCodyPage.tsx", "src/global/GlobalAlert.tsx", "src/global/GlobalAlerts.tsx", "src/global/Notices.tsx", @@ -1126,7 +1115,6 @@ ts_project( "src/marketing/components/TweetFeedback.tsx", "src/marketing/page/SurveyForm.tsx", "src/marketing/page/SurveyPage.tsx", - "src/marketing/toast/CodySurveyToast.tsx", "src/marketing/toast/SurveySuccessToast.tsx", "src/marketing/toast/SurveyToastContent.tsx", "src/marketing/toast/SurveyToastTrigger.tsx", diff --git a/client/web/src/LegacyLayout.tsx b/client/web/src/LegacyLayout.tsx index 5fce92638d0..78fb4cb23c7 100644 --- a/client/web/src/LegacyLayout.tsx +++ b/client/web/src/LegacyLayout.tsx @@ -98,15 +98,9 @@ export const LegacyLayout: FC = props => { const isSiteInit = location.pathname === PageRoutes.SiteAdminInit.toString() const isSignInOrUp = routeMatch && - [ - PageRoutes.SignIn, - PageRoutes.SignUp, - PageRoutes.PasswordReset, - PageRoutes.Welcome, - PageRoutes.RequestAccess, - ].includes(routeMatch as PageRoutes) - const isGetCodyPage = location.pathname === PageRoutes.GetCody.toString() - const isPostSignUpPage = location.pathname === PageRoutes.PostSignUp.toString() + [PageRoutes.SignIn, PageRoutes.SignUp, PageRoutes.PasswordReset, PageRoutes.RequestAccess].includes( + routeMatch as PageRoutes + ) const [newSearchNavigation] = useNewSearchNavigation() const [enableContrastCompliantSyntaxHighlighting] = useFeatureFlag('contrast-compliant-syntax-highlighting') @@ -228,7 +222,7 @@ export const LegacyLayout: FC = props => { telemetryRecorder={props.platformContext.telemetryRecorder} /> )} - {!isSiteInit && !isSignInOrUp && !isGetCodyPage && !isPostSignUpPage && ( + {!isSiteInit && !isSignInOrUp && ( <> {newSearchNavigation ? ( { - const location = useLocation() - const returnTo = getReturnTo(location) - - // Redirects Cody PLG users without asking - const params = new URLSearchParams() - params.set('returnTo', returnTo) - - const navigateTo = CodyProRoutes.Manage + '?' + params.toString() - - return -} diff --git a/client/web/src/auth/SignUpPage.tsx b/client/web/src/auth/SignUpPage.tsx index f0ac059171e..b0773c1a372 100644 --- a/client/web/src/auth/SignUpPage.tsx +++ b/client/web/src/auth/SignUpPage.tsx @@ -11,13 +11,12 @@ import { Container, Link, Text } from '@sourcegraph/wildcard' import type { AuthenticatedUser } from '../auth' import { PageTitle } from '../components/PageTitle' import type { SourcegraphContext } from '../jscontext' -import { PageRoutes } from '../routes.constants' import { EventName } from '../util/constants' import { AuthPageWrapper } from './AuthPageWrapper' import { CloudSignUpPage, ShowEmailFormQueryParameter } from './CloudSignUpPage' import { getReturnTo } from './SignInSignUpCommon' -import { type SignUpArguments, SignUpForm } from './SignUpForm' +import { SignUpForm, type SignUpArguments } from './SignUpForm' import { VsCodeSignUpPage } from './VsCodeSignUpPage' import styles from './SignUpPage.module.scss' @@ -92,8 +91,7 @@ export const SignUpPage: React.FunctionComponent renders customized redirect when user has completed post signup flow 1`] = ``; - -exports[`PostSignUpPage > renders post signup page - with cody survey 1`] = ` - -
-
- -
-
-
-`; - -exports[`PostSignUpPage > renders post signup page - with email verification 1`] = ` - -
-
- -
-
-
-`; - -exports[`PostSignUpPage > renders redirect when user has completed post signup flow 1`] = ``; diff --git a/client/web/src/auth/components/ExternalsAuth.tsx b/client/web/src/auth/components/ExternalsAuth.tsx index 451471675fe..72877dab7bc 100644 --- a/client/web/src/auth/components/ExternalsAuth.tsx +++ b/client/web/src/auth/components/ExternalsAuth.tsx @@ -15,14 +15,12 @@ export type AuthPages = | 'vscode-signup-page' | 'cloud-signup-page' | 'cody-marketing-page' - | 'get-cody-page' | 'try-cody-widget-blob' | 'try-cody-widget-repo' const v2Pages: { [p in AuthPages]: number } = { 'vscode-signup-page': 0, 'cloud-signup-page': 1, 'cody-marketing-page': 2, - 'get-cody-page': 3, 'try-cody-widget-blob': 4, 'try-cody-widget-repo': 5, } diff --git a/client/web/src/cody/chat/CodyChatPage.tsx b/client/web/src/cody/chat/CodyChatPage.tsx index 68797f3a683..e3188727b96 100644 --- a/client/web/src/cody/chat/CodyChatPage.tsx +++ b/client/web/src/cody/chat/CodyChatPage.tsx @@ -1,14 +1,14 @@ import React, { useEffect, useState } from 'react' import { + mdiChevronRight, mdiClose, mdiCogOutline, mdiDelete, mdiDotsVertical, + mdiFormatListBulleted, mdiOpenInNew, mdiPlus, - mdiChevronRight, - mdiFormatListBulleted, } from '@mdi/js' import classNames from 'classnames' import { useLocation, useNavigate } from 'react-router-dom' @@ -20,19 +20,19 @@ import type { TelemetryV2Props } from '@sourcegraph/shared/src/telemetry' import { Badge, Button, + ButtonLink, + H3, + H4, Icon, + Link, Menu, MenuButton, - MenuList, MenuDivider, MenuItem, MenuLink, + MenuList, PageHeader, - Link, - H4, - H3, Text, - ButtonLink, Tooltip, } from '@sourcegraph/wildcard' @@ -47,7 +47,7 @@ import { ChatUI } from '../components/ChatUI' import { CodyMarketingPage } from '../components/CodyMarketingPage' import { HistoryList } from '../components/HistoryList' import { isCodyEnabled } from '../isCodyEnabled' -import { type CodyChatStore, useCodyChat } from '../useCodyChat' +import { useCodyChat, type CodyChatStore } from '../useCodyChat' import { CodyColorIcon } from './CodyPageIcon' @@ -180,7 +180,7 @@ export const CodyChatPage: React.FunctionComponent = ({ powerful recipes to help you understand codebases and generate and fix code more accurately. - + View editor extensions → @@ -209,7 +209,7 @@ export const CodyChatPage: React.FunctionComponent = ({ {!isSourcegraphDotCom && isCTADismissed && ( <> {' '} - Get Cody in your editor. + Get Cody in your editor. )} diff --git a/client/web/src/cody/components/CodyMarketingPage/CodyMarketingPage.tsx b/client/web/src/cody/components/CodyMarketingPage/CodyMarketingPage.tsx index 10bae1f87c9..dea03055fdc 100644 --- a/client/web/src/cody/components/CodyMarketingPage/CodyMarketingPage.tsx +++ b/client/web/src/cody/components/CodyMarketingPage/CodyMarketingPage.tsx @@ -27,8 +27,6 @@ interface CodyPlatformCardProps { illustration: string } -/* eslint-disable @sourcegraph/sourcegraph/check-help-links */ - const onSpeakToAnEngineer = (): void => EVENT_LOGGER.log(EventName.SPEAK_TO_AN_ENGINEER_CTA) const IDEIcon: React.FunctionComponent<{}> = () => ( @@ -64,7 +62,7 @@ const codyPlatformCardItems = ( description: ( <> The extensions combine an LLM with the context of your code to help you generate and fix code more - accurately. View supported editors. + accurately. View supported editors. ), icon: , diff --git a/client/web/src/cody/dashboard/CodyDashboardPage.tsx b/client/web/src/cody/dashboard/CodyDashboardPage.tsx index 5995c765dfc..6f7b0c11217 100644 --- a/client/web/src/cody/dashboard/CodyDashboardPage.tsx +++ b/client/web/src/cody/dashboard/CodyDashboardPage.tsx @@ -39,7 +39,7 @@ const setupOptions: SetupOption[] = [ }, { icon: , - maker: 'Jetbrains', + maker: 'JetBrains', name: 'IntelliJ', setupLink: 'https://sourcegraph.com/docs/cody/clients/install-jetbrains', }, @@ -61,20 +61,18 @@ export const CodyDashboardPage: FC = ({ telemetryRecorde Get started with Cody - Hey! πŸ‘‹ Let’s get started with Cody β€” your new AI coding assistant. + Hey! πŸ‘‹ Let’s get started with Cody, your AI coding assistant.
- Download Cody for your favorite IDE + Get Cody in your editor - Struggling with setup?{' '} Explore installation docs - .
diff --git a/client/web/src/cody/editorGroups.ts b/client/web/src/cody/editorGroups.ts deleted file mode 100644 index 2827a0cc4a6..00000000000 --- a/client/web/src/cody/editorGroups.ts +++ /dev/null @@ -1,94 +0,0 @@ -import type { IEditor } from './onboarding/CodyOnboarding' -import { JetBrainsInstructions } from './onboarding/instructions/JetBrains' -import { NeoVimInstructions } from './onboarding/instructions/NeoVim' -import { VSCodeInstructions } from './onboarding/instructions/VsCode' - -export const editorGroups: IEditor[][] = [ - [ - { - id: 1, - icon: 'VsCode', - name: 'VS Code', - publisher: 'Microsoft', - releaseStage: 'Stable', - docs: 'https://sourcegraph.com/docs/cody/clients/install-vscode', - instructions: VSCodeInstructions, - }, - { - id: 2, - icon: 'IntelliJ', - name: 'IntelliJ IDEA', - publisher: 'JetBrains', - releaseStage: 'Stable', - docs: 'https://sourcegraph.com/docs/cody/clients/install-jetbrains', - instructions: JetBrainsInstructions, - }, - { - id: 3, - icon: 'PhpStorm', - name: 'PhpStorm ', - publisher: 'JetBrains', - releaseStage: 'Stable', - docs: 'https://sourcegraph.com/docs/cody/clients/install-jetbrains', - instructions: JetBrainsInstructions, - }, - { - id: 4, - icon: 'PyCharm', - name: 'PyCharm', - publisher: 'JetBrains', - releaseStage: 'Stable', - docs: 'https://sourcegraph.com/docs/cody/clients/install-jetbrains', - instructions: JetBrainsInstructions, - }, - ], - [ - { - id: 5, - icon: 'WebStorm', - name: 'WebStorm', - publisher: 'JetBrains', - releaseStage: 'Stable', - docs: 'https://sourcegraph.com/docs/cody/clients/install-jetbrains', - instructions: JetBrainsInstructions, - }, - { - id: 6, - icon: 'RubyMine', - name: 'RubyMine', - publisher: 'JetBrains', - releaseStage: 'Stable', - docs: 'https://sourcegraph.com/docs/cody/clients/install-jetbrains', - instructions: JetBrainsInstructions, - }, - { - id: 7, - icon: 'GoLand', - name: 'GoLand', - publisher: 'JetBrains', - releaseStage: 'Stable', - docs: 'https://sourcegraph.com/docs/cody/clients/install-jetbrains', - instructions: JetBrainsInstructions, - }, - { - id: 8, - icon: 'AndroidStudio', - name: 'Android Studio', - publisher: 'Google', - releaseStage: 'Stable', - docs: 'https://sourcegraph.com/docs/cody/clients/install-jetbrains', - instructions: JetBrainsInstructions, - }, - ], - [ - { - id: 9, - icon: 'NeoVim', - name: 'Neovim', - publisher: 'Neovim Team', - releaseStage: 'Experimental', - docs: 'https://sourcegraph.com/docs/cody/clients/install-neovim', - instructions: NeoVimInstructions, - }, - ], -] diff --git a/client/web/src/cody/management/CodyManagementPage.module.scss b/client/web/src/cody/management/CodyManagementPage.module.scss index 8b1a6aba8d4..a69a4b46c3b 100644 --- a/client/web/src/cody/management/CodyManagementPage.module.scss +++ b/client/web/src/cody/management/CodyManagementPage.module.scss @@ -1,14 +1,15 @@ @import 'wildcard/src/global-styles/breakpoints'; .responsive-container { - @media (--sm-breakpoint-down) { - flex-direction: column; - align-items: center; + display: flex; + flex-wrap: wrap; + gap: 1px; - > div { - border: none !important; - align-items: center; - } + background-color: var(--border-color); + > * { + flex: 1; + min-width: 300px; + background-color: var(--color-bg-1); } } @@ -23,33 +24,6 @@ font-weight: 600; } -.counter { - font-size: 1.25rem; -} - -.ide-name { - font-size: 1rem; -} - -.modal { - width: 50rem; -} - -.release-stage { - color: var(--gray-07); -} - -.ide-header { - padding: calc(var(--spacer) * 0.5); - margin: calc(var(--spacer) * -0.5); - border-radius: var(--border-radius); -} - -.ide-header:hover { - cursor: pointer; - background: var(--subtle-bg); -} - .credit-card-emoji { font-size: 2rem; } diff --git a/client/web/src/cody/management/CodyManagementPage.tsx b/client/web/src/cody/management/CodyManagementPage.tsx index 6db526e69b2..8172217e117 100644 --- a/client/web/src/cody/management/CodyManagementPage.tsx +++ b/client/web/src/cody/management/CodyManagementPage.tsx @@ -1,37 +1,35 @@ import React, { useCallback, useEffect } from 'react' -import { mdiCreditCardOutline, mdiPlusThick } from '@mdi/js' +import { mdiCreditCardOutline, mdiHelpCircleOutline, mdiPlusThick } from '@mdi/js' import classNames from 'classnames' import { useNavigate } from 'react-router-dom' import { useQuery } from '@sourcegraph/http-client' import type { TelemetryV2Props } from '@sourcegraph/shared/src/telemetry' -import { ButtonLink, H1, H2, Icon, Link, PageHeader, Text, useSearchParameters, Button } from '@sourcegraph/wildcard' +import { Button, ButtonLink, H2, H3, Icon, Link, PageHeader, Text, useSearchParameters } from '@sourcegraph/wildcard' import type { AuthenticatedUser } from '../../auth' import { Page } from '../../components/Page' import { PageTitle } from '../../components/PageTitle' import { + CodySubscriptionPlan, type UserCodyPlanResult, type UserCodyPlanVariables, type UserCodyUsageResult, type UserCodyUsageVariables, - CodySubscriptionPlan, } from '../../graphql-operations' import { CodyProRoutes } from '../codyProRoutes' import { CodyAlert } from '../components/CodyAlert' -import { ProIcon } from '../components/CodyIcon' import { PageHeaderIcon } from '../components/PageHeaderIcon' import { AcceptInviteBanner } from '../invites/AcceptInviteBanner' import { InviteUsers } from '../invites/InviteUsers' import { isCodyEnabled } from '../isCodyEnabled' -import { CodyOnboarding, type IEditor } from '../onboarding/CodyOnboarding' import { USER_CODY_PLAN, USER_CODY_USAGE } from '../subscription/queries' import { getManageSubscriptionPageURL } from '../util' import { useSubscriptionSummary } from './api/react-query/subscriptions' import { SubscriptionStats } from './SubscriptionStats' -import { UseCodyInEditorSection } from './UseCodyInEditorSection' +import { CodyEditorsAndClients } from './UseCodyInEditorSection' import styles from './CodyManagementPage.module.scss' @@ -39,11 +37,6 @@ interface CodyManagementPageProps extends TelemetryV2Props { authenticatedUser: AuthenticatedUser | null } -export enum EditorStep { - SetupInstructions = 0, - CodyFeatures = 1, -} - export const CodyManagementPage: React.FunctionComponent = ({ authenticatedUser, telemetryRecorder, @@ -79,9 +72,6 @@ export const CodyManagementPage: React.FunctionComponent(null) - const [selectedEditorStep, setSelectedEditorStep] = React.useState(null) - const subscription = data?.currentUser?.codySubscription useEffect(() => { @@ -129,107 +119,78 @@ export const CodyManagementPage: React.FunctionComponent - - - - {welcomeToPro && ( - -

Welcome to Cody Pro

- - You now have Cody Pro with access to unlimited autocomplete, chats, and commands. - -
- )} - {getTeamInviteButton()}} - > - - - Dashboard - - - - {isAdmin && !!subscriptionSummaryQueryResult.data && ( - - )} - - {!isUserOnProTier && } - -
-
-
-

My subscription

- - {isUserOnProTier ? ( - 'You are on the Pro tier.' - ) : ( - - You are on the Free tier.{' '} - Upgrade to the Pro tier. - - )} - -
- {isUserOnProTier && ( -
- { - telemetryRecorder.recordEvent('cody.manageSubscription', 'click') - }} - > - - Manage subscription - -
+ + + + {welcomeToPro && ( + +

Welcome to Cody Pro

+ + You now have Cody Pro with access to unlimited autocomplete, chats, and commands. + +
+ )} + + {isAdmin ? ( + getTeamInviteButton() + ) : isUserOnProTier ? ( + { + telemetryRecorder.recordEvent('cody.manageSubscription', 'click') + }} + > + + Manage subscription + + ) : ( + + Upgrade plan + )} + + + Help & community +
- -
+ } + > + + + Cody dashboard + + - -
- - + )} + +
+ +
+ +

Use Cody in...

+
+ +
+ +
+ ) } - -const UpgradeToProBanner: React.FunctionComponent<{ - onClick: () => void -}> = ({ onClick }) => ( - -
-
-

- Get unlimited help with Cody Pro -

-
    -
  • Unlimited autocompletions
  • -
  • Unlimited chat messages
  • -
-
-
- - - Upgrade now - -
-
-
-) diff --git a/client/web/src/cody/management/SubscriptionStats.tsx b/client/web/src/cody/management/SubscriptionStats.tsx index 0770edc9c93..1e8cb5c7c41 100644 --- a/client/web/src/cody/management/SubscriptionStats.tsx +++ b/client/web/src/cody/management/SubscriptionStats.tsx @@ -3,8 +3,8 @@ import React from 'react' import classNames from 'classnames' import { Timestamp } from '@sourcegraph/branded/src/components/Timestamp' -import { type CodySubscriptionStatus, CodySubscriptionPlan } from '@sourcegraph/shared/src/graphql-operations' -import { Text, LoadingSpinner, H4 } from '@sourcegraph/wildcard' +import { CodySubscriptionPlan, type CodySubscriptionStatus } from '@sourcegraph/shared/src/graphql-operations' +import { ButtonLink, H4, LoadingSpinner, Text } from '@sourcegraph/wildcard' import type { UserCodyUsageResult } from '../../graphql-operations' import { AutocompletesIcon, ChatMessagesIcon } from '../components/CodyIcon' @@ -22,11 +22,13 @@ interface SubscriptionStatsProps { cancelAtPeriodEnd: boolean } usageData: UserCodyUsageResult | undefined + onClickUpgradeToProCTA: () => void } export const SubscriptionStats: React.FunctionComponent = ({ subscription, usageData, + onClickUpgradeToProCTA, }: SubscriptionStatsProps) => { const stats = usageData?.currentUser const codyCurrentPeriodChatLimit = stats?.codyCurrentPeriodChatLimit || 0 @@ -59,40 +61,42 @@ export const SubscriptionStats: React.FunctionComponent const codyProSubscriptionEndTime = subscription.currentPeriodEndAt return ( -
-
+
+
{isUserOnProTier ? : Free} - - tier - {isUserOnProTier && subscription.cancelAtPeriodEnd && ( Subscription ends )} + {!isUserOnProTier && ( + + Upgrade plan + + )}
-
+
-
+
{subscription.applyProRateLimits ? ( - + Unlimited ) : usageData?.currentUser ? ( <> {Math.min(codyCurrentPeriodCodeUsage, codyCurrentPeriodCodeLimit)} / {' '} {codyCurrentPeriodCodeLimit} @@ -115,28 +119,22 @@ export const SubscriptionStats: React.FunctionComponent ))}
-
+
-
+
{subscription.applyProRateLimits ? ( - + Unlimited ) : usageData?.currentUser ? ( <> {Math.min(codyCurrentPeriodChatUsage, codyCurrentPeriodChatLimit)} / {' '} {codyCurrentPeriodChatLimit} diff --git a/client/web/src/cody/management/UseCodyInEditorSection.tsx b/client/web/src/cody/management/UseCodyInEditorSection.tsx index cc00903d2bf..170ac2252d8 100644 --- a/client/web/src/cody/management/UseCodyInEditorSection.tsx +++ b/client/web/src/cody/management/UseCodyInEditorSection.tsx @@ -1,145 +1,214 @@ import React from 'react' -import { mdiHelpCircleOutline, mdiInformationOutline, mdiOpenInNew } from '@mdi/js' +import { mdiOpenInNew } from '@mdi/js' import classNames from 'classnames' -import type { TelemetryV2Props } from '@sourcegraph/shared/src/telemetry' -import { H2, Text, Link, Icon, H5, Modal } from '@sourcegraph/wildcard' - -import { editorGroups } from '../editorGroups' -import type { IEditor } from '../onboarding/CodyOnboarding' - -import { EditorStep } from './CodyManagementPage' +import type { TelemetryRecorder, TelemetryV2Props } from '@sourcegraph/shared/src/telemetry' +import { Badge, ButtonLink, H3, Icon, Link, LinkOrSpan, Text } from '@sourcegraph/wildcard' import styles from './CodyManagementPage.module.scss' -interface UseCodyInEditorSectionProps extends TelemetryV2Props { - selectedEditor: IEditor | null - setSelectedEditor: (editor: IEditor | null) => void - selectedEditorStep: EditorStep | null - setSelectedEditorStep: (step: EditorStep | null) => void - isUserOnProTier: boolean -} +interface UseCodyInEditorSectionProps extends TelemetryV2Props {} -export const UseCodyInEditorSection: React.FunctionComponent = props => ( -
-
-
-

Use Cody directly in your editor

- Download the Cody extension in your editor to start using Cody. -
- {props.isUserOnProTier ? ( -
- - - Join our community, read our docs, or get product/billing support - -
- ) : null} -
- {editorGroups.map((group, index) => ( -
editor.name).join('-')} - className={classNames('d-flex mt-3', styles.responsiveContainer, { - 'border-bottom pb-3': index < group.length - 1, - })} - > - {group.map((editor, index) => ( -
-
{ - props.setSelectedEditor(editor) - props.setSelectedEditorStep(EditorStep.SetupInstructions) - }} - role="button" - tabIndex={0} - onKeyDown={e => { - if (e.key === 'Enter') { - props.setSelectedEditor(editor) - props.setSelectedEditorStep(EditorStep.SetupInstructions) - } - }} - > -
- {editor.name} -
-
- - {editor.publisher} - - {editor.name} -
{editor.releaseStage}
-
-
- - {editor.instructions && ( - { - props.setSelectedEditor(editor) - props.setSelectedEditorStep(EditorStep.SetupInstructions) - }} - > - Quickstart - guide - - )} - {editor.docs && ( - - Documentation - - )} - {props.selectedEditor?.name === editor.name && - props.selectedEditorStep !== null && - editor.instructions && ( - - { - props.setSelectedEditor(null) - props.setSelectedEditorStep(null) - }} - telemetryRecorder={props.telemetryRecorder} - /> - - )} -
- ))} - {group.length < 4 - ? [...new Array(4 - group.length)].map((_, index) => ( - // eslint-disable-next-line react/no-array-index-key -
- )) - : null} -
+export const CodyEditorsAndClients: React.FunctionComponent = ({ telemetryRecorder }) => ( +
+ {EDITOR_INSTRUCTIONS.map(editor => ( + ))}
) + +const EDITOR_ICON_HEIGHT = 34 + +const EditorInstructions: React.FunctionComponent< + { editor: EditorInstructionsTile; className?: string } & TelemetryV2Props +> = ({ editor, telemetryRecorder, className }) => ( +
+ {/* eslint-disable-next-line react/forbid-dom-props */} +
+ {editor.icon && ( + {editor.name} + )} +

{editor.name}

+
+ {editor.instructions && } +
+) + +interface EditorInstructionsTile { + /** Refers to gs://sourcegraph-assets/ideIcons/ideIcon${icon}.svg. */ + icon?: string + + name: string + instructions?: React.FunctionComponent<{ + telemetryRecorder: TelemetryRecorder + }> +} + +const EDITOR_INSTRUCTIONS: EditorInstructionsTile[] = [ + { + icon: 'VsCode', + name: 'VS Code', + instructions: ({ telemetryRecorder }) => ( +
+ { + telemetryRecorder.recordEvent('cody.editorExtensionsInstructions', 'clickInstall', { + metadata: { vscode: 1 }, + }) + }} + > + Install Cody in VS Code + + { + telemetryRecorder.recordEvent('cody.editorExtensionsInstructions', 'clickMarketplace', { + metadata: { vscode: 1 }, + }) + }} + > + View in VS Code Marketplace{' '} + + + { + telemetryRecorder.recordEvent('cody.editorExtensionsInstructions', 'clickSource', { + metadata: { vscode: 1 }, + }) + }} + > + Install from source + +
+ ), + }, + { + icon: 'JetBrains', + name: 'All JetBrains IDEs', + instructions: ({ telemetryRecorder }) => ( +
+ { + telemetryRecorder.recordEvent('cody.editorExtensionsInstructions', 'clickMarketplace', { + metadata: { jetbrains: 1 }, + }) + }} + > + Install Cody from JetBrains Marketplace + + { + telemetryRecorder.recordEvent('cody.editorExtensionsInstructions', 'clickSource', { + metadata: { jetbrains: 1 }, + }) + }} + > + Install from source + + + Works in IntelliJ, PyCharm, GoLand, Android Studio, WebStorm, Rider, RubyMine, and all other + JetBrains IDEs. + +
+ ), + }, + { + name: 'Other editors & clients', + instructions: ({ telemetryRecorder }) => ( +
    + {OTHER_CLIENTS.map(client => ( +
  • + { + telemetryRecorder.recordEvent('cody.editorExtensionsInstructions', 'clickOther', { + metadata: { [client.telemetryMetadataKey]: 1 }, + }) + }} + > + {client.name} + + {client.releaseStage && ( + + {client.releaseStage} + + )} +
  • + ))} +
+ ), + }, +] + +const OTHER_CLIENTS: { + name: string + url?: string + telemetryMetadataKey: string + releaseStage?: 'Experimental' | 'Coming soon' +}[] = [ + { + name: 'Cody Web', + url: 'https://sourcegraph.com/docs/cody/clients/cody-with-sourcegraph', + telemetryMetadataKey: 'web', + releaseStage: 'Experimental', + }, + { + name: 'Neovim', + url: 'https://github.com/sourcegraph/sg.nvim#setup', + telemetryMetadataKey: 'neovim', + releaseStage: 'Experimental', + }, + { + name: 'Cody CLI', + url: 'https://sourcegraph.com/github.com/sourcegraph/cody@main/-/blob/cli/README.md', + telemetryMetadataKey: 'cli', + releaseStage: 'Experimental', + }, + { + name: 'Visual Studio', + telemetryMetadataKey: 'visualstudio', + releaseStage: 'Coming soon', + }, + { + name: 'Eclipse', + telemetryMetadataKey: 'eclipse', + releaseStage: 'Coming soon', + }, + { + name: 'Emacs', + url: 'https://github.com/sourcegraph/cody-emacs', + telemetryMetadataKey: 'emacs', + releaseStage: 'Coming soon', + }, +] diff --git a/client/web/src/cody/onboarding/CodyOnboarding.module.scss b/client/web/src/cody/onboarding/CodyOnboarding.module.scss deleted file mode 100644 index 434b7a9dee5..00000000000 --- a/client/web/src/cody/onboarding/CodyOnboarding.module.scss +++ /dev/null @@ -1,195 +0,0 @@ -.root { - :global(.theme-dark) & { - background: linear-gradient(180deg, rgba(17, 20, 27, 0.92) 0%, rgba(13, 16, 25, 0.92) 100%); - backdrop-filter: blur(7px); - } - - :global(.theme-light) & { - background: linear-gradient(180deg, rgba(255, 255, 255, 0.94) 0%, rgba(244, 244, 244, 0.9) 100%); - backdrop-filter: blur(7px); - } -} - -.modal { - width: 54rem; - border-radius: 6px; - background-image: repeating-conic-gradient( - rgba(112, 72, 232, 0.5) 50%, - rgba(0, 203, 236, 0.5) 75%, - rgba(161, 18, 255, 0.5) 100%, - rgba(255, 85, 67, 0.5) 125%, - rgba(112, 72, 232, 0.5) 150% - ); - background-origin: border-box; - - /* to allow a gradient stroke, this is actually where the color of the background is set */ - box-shadow: inset 0 100vw var(--theme-bg-plain); - - /* to let the gradient on the background act as a stroke */ - border: 1px solid transparent; - - /* to let gradient shadow be behind the modal */ - transform-style: preserve-3d; - - /* Colorful shadow on the modal */ - &::after { - content: ''; - position: absolute; - width: 90%; - height: 2.8125rem; - background: linear-gradient(90deg, #7048e8 0%, #4ac1e8 32.21%, #4d0b79 65.39%, #ff5543 104.43%), #eff2f5; - transform: translateZ(-1px); - filter: blur(30px); - opacity: 0.9; - animation: modal-shadow 6s ease-in-out infinite; - } - - :global(.theme-dark) & { - background-image: repeating-conic-gradient( - rgba(112, 72, 232, 0.3) 50%, - rgba(0, 203, 236, 0.3) 75%, - rgba(161, 18, 255, 0.5) 100%, - rgba(255, 85, 67, 0.3) 125%, - rgba(112, 72, 232, 0.3) 150% - ); - } -} - -@keyframes modal-shadow { - 0% { - transform: translateY(0) translateZ(-1px); - opacity: 0.4; - } - 50% { - transform: translateY(-25px) translateZ(-1px); - opacity: 0.9; - } - 100% { - transform: translateY(0) translateZ(-1px); - opacity: 0.4; - } -} - -.highlight-step { - :global(.theme-light) & { - background: radial-gradient(50% 100% at 50% 100%, var(--gray-04) 0%, transparent 100%); - } - - :global(.theme-dark) & { - background: radial-gradient(50% 100% at 50% 100%, var(--gray-09) 0%, transparent 100%); - } -} - -.release-stage { - color: var(--gray-07); -} - -/* Step section on instructions */ -.step { - background: #343a4d; - color: #ffffff; - padding: 0.25rem 0.675rem; - border-radius: 50%; -} - -.ide-grid:hover { - background: var(--subtle-bg); - transition: all 0.25s ease; -} - -.instructions-container { - /* adds vertical scroll to long instructions and prevents "next" and "back buttons" to be under the fold */ - max-height: 80vh; - overflow-y: auto; -} - -.responsive-container { - @media (--sm-breakpoint-down) { - flex-direction: column; - align-items: center; - - > div { - border: none !important; - align-items: center; - } - } -} - -.ide-name { - font-size: 1rem; -} - -/* Initial welcome */ -.welcome-title { - font-size: 2.5rem; - font-weight: 600; - letter-spacing: -0.2px; -} - -.welcome-subtitle { - font-size: 1.1rem; - font-weight: 300; -} - -.fade-in { - animation: 0.8s fadeInUp ease-in-out; -} - -.fade-first { - animation-delay: 3.3s; - opacity: 0; - - /* to keep opacity */ - animation-fill-mode: forwards; -} - -.fade-second { - animation-delay: 3.5s; - opacity: 0; - - /* to keep opacity */ - animation-fill-mode: forwards; -} - -.fade-third { - animation-delay: 3.8s; - opacity: 0; - - /* to keep opacity */ - animation-fill-mode: forwards; -} - -.welcome-video { - animation: 0.8s moveUp 3.5s ease-in-out; - transform: translateY(70%); - animation-fill-mode: forwards; -} - -@keyframes moveUp { - 0% { - transform: translateY(70%); - } - 100% { - transform: translateY(0%); - } -} - -@keyframes fadeInUp { - 0% { - transform: translateY(100%); - opacity: 0; - } - 100% { - transform: translateY(0%); - opacity: 1; - } -} - -.fade-in-up-animation { - animation: 1.5s fadeInUp; -} - -.blank-placeholder { - min-width: 11.25rem; - min-height: 17rem; -} diff --git a/client/web/src/cody/onboarding/CodyOnboarding.tsx b/client/web/src/cody/onboarding/CodyOnboarding.tsx deleted file mode 100644 index d3637253845..00000000000 --- a/client/web/src/cody/onboarding/CodyOnboarding.tsx +++ /dev/null @@ -1,129 +0,0 @@ -import React, { useEffect, useState } from 'react' - -import { useNavigate } from 'react-router-dom' - -import { useTemporarySetting } from '@sourcegraph/shared/src/settings/temporary' -import type { TelemetryRecorder, TelemetryV2Props } from '@sourcegraph/shared/src/telemetry' -import { Modal, useSearchParameters } from '@sourcegraph/wildcard' - -import type { AuthenticatedUser } from '../../auth' -import { useFeatureFlag } from '../../featureFlags/useFeatureFlag' - -import { EditorStep } from './EditorStep' -import { PurposeStep } from './PurposeStep' -import { WelcomeStep } from './WelcomeStep' - -import styles from './CodyOnboarding.module.scss' - -export interface IEditor { - id: number // a unique number identifier for telemetry - icon: string - name: string - publisher: string - releaseStage: string - docs?: string - instructions?: React.FC<{ - onBack?: () => void - onClose: () => void - showStep?: number - telemetryRecorder: TelemetryRecorder - }> -} - -interface CodyOnboardingProps extends TelemetryV2Props { - authenticatedUser: AuthenticatedUser | null -} - -export function CodyOnboarding({ authenticatedUser, telemetryRecorder }: CodyOnboardingProps): JSX.Element | null { - const [showEditorStep, setShowEditorStep] = useState(false) - const [completed = false, setOnboardingCompleted] = useTemporarySetting('cody.onboarding.completed', false) - const [signUpFlowEnabled, signUpFlowStatus] = useFeatureFlag('ab-shortened-install-first-signup-flow-cody-2024-04') - // steps start from 0 - const [step = -1, setOnboardingStep] = useTemporarySetting('cody.onboarding.step', 0) - - const onNext = (): void => setOnboardingStep(currentsStep => (currentsStep || 0) + 1) - - const parameters = useSearchParameters() - const enrollPro = parameters.get('pro') === 'true' - const returnToURL = parameters.get('returnTo') - // All calls with a `requestFrom` query param to this call or in the returnTo URL come from Cody clients. - const isCody = !!parameters.get('requestFrom') || !!returnToURL?.includes('requestFrom') - - const navigate = useNavigate() - - useEffect(() => { - if (completed && returnToURL) { - navigate(returnToURL) - } - }, [completed, returnToURL, navigate]) - - useEffect(() => { - if (signUpFlowStatus === 'loaded' && signUpFlowEnabled && isCody) { - setOnboardingStep(currentsStep => (currentsStep || 0) + 2) - setOnboardingCompleted(true) - setShowEditorStep(true) - } - if (signUpFlowStatus === 'loaded' && isCody) { - const metadataKey = signUpFlowEnabled ? 'treatmentVariant' : 'controlVariant' - telemetryRecorder.recordEvent('cody.onboarding.ABShortenedSignupFlowForInstalls202404', 'enroll', { - metadata: { [metadataKey]: 1 }, - }) - } - }, [signUpFlowEnabled, signUpFlowStatus, isCody, setOnboardingStep, setOnboardingCompleted, telemetryRecorder]) - - if (completed && returnToURL) { - return null - } - - if (!showEditorStep && (completed || step === -1 || step > 1)) { - return null - } - - if (!authenticatedUser) { - return null - } - - if (signUpFlowStatus !== 'loaded') { - return null - } - - const handleShowLastStep = (): void => { - setOnboardingCompleted(true) - setShowEditorStep(true) - telemetryRecorder.recordEvent('cody.onboarding.hubspotForm.fromWorkPersonalToHandRaiserTest', 'enroll', { - metadata: { controlVariant: 1 }, - }) - } - - return ( - - {step === 0 && } - {step === 1 && ( - { - onNext() - handleShowLastStep() - }} - pro={enrollPro} - telemetryRecorder={telemetryRecorder} - /> - )} - {showEditorStep && ( - { - setShowEditorStep(false) - }} - pro={enrollPro} - telemetryRecorder={telemetryRecorder} - /> - )} - - ) -} diff --git a/client/web/src/cody/onboarding/EditorStep.tsx b/client/web/src/cody/onboarding/EditorStep.tsx deleted file mode 100644 index a5c467293e3..00000000000 --- a/client/web/src/cody/onboarding/EditorStep.tsx +++ /dev/null @@ -1,118 +0,0 @@ -import { useEffect, useState } from 'react' - -import classNames from 'classnames' - -import type { TelemetryRecorder } from '@sourcegraph/shared/src/telemetry' -import { H2, Text, H5 } from '@sourcegraph/wildcard' - -import { editorGroups } from '../editorGroups' - -import type { IEditor } from './CodyOnboarding' - -import styles from './CodyOnboarding.module.scss' - -export function EditorStep({ - onCompleted, - pro, - telemetryRecorder, -}: { - onCompleted: () => void - pro: boolean - telemetryRecorder: TelemetryRecorder -}): JSX.Element { - useEffect(() => { - telemetryRecorder.recordEvent('cody.onboarding.chooseEditor', 'view', { metadata: { tier: pro ? 1 : 0 } }) - }, [pro, telemetryRecorder]) - - const [editor, setEditor] = useState(null) - - const onBack = (): void => setEditor(null) - - if (editor?.instructions) { - const Instructions = editor.instructions - - return - } - - return ( - <> -
-

Choose your editor

- - Most of Cody experience happens in the IDE. Let's get that set up. - -
-
- {editorGroups.map((group, groupIndex) => ( -
- {group.map((editor, editorIndex) => ( -
{ - setEditor(editor) - - telemetryRecorder.recordEvent('cody.onboarding.chooseEditor', 'select', { - metadata: { tier: pro ? 1 : 0, editor: editor.id }, - }) - }} - onClick={() => { - telemetryRecorder.recordEvent('cody.onboarding.chooseEditor', 'select', { - metadata: { tier: pro ? 1 : 0, editor: editor.id }, - }) - setEditor(editor) - }} - > -
-
- {editor.name} -
-
- - {editor.publisher} - - {editor.name} -
{editor.releaseStage}
-
-
-
- ))} - {group.length < 4 - ? Array.from(new Array(4 - group.length).keys()).map(item => ( -
- )) - : null} -
- ))} -
-
- { - onCompleted() - telemetryRecorder.recordEvent('cody.onboarding.chooseEditor', 'skip', { - metadata: { tier: pro ? 1 : 0 }, - }) - }} - > - Skip for now - -
- - ) -} diff --git a/client/web/src/cody/onboarding/PurposeStep.tsx b/client/web/src/cody/onboarding/PurposeStep.tsx deleted file mode 100644 index 5aa1a8134ef..00000000000 --- a/client/web/src/cody/onboarding/PurposeStep.tsx +++ /dev/null @@ -1,61 +0,0 @@ -import { useEffect } from 'react' - -import type { TelemetryRecorder } from '@sourcegraph/shared/src/telemetry' -import { H2, Text } from '@sourcegraph/wildcard' - -import type { AuthenticatedUser } from '../../auth' -import { HubSpotForm } from '../../marketing/components/HubSpotForm' - -export function PurposeStep({ - onNext, - pro, - authenticatedUser, - telemetryRecorder, -}: { - onNext: () => void - pro: boolean - authenticatedUser: AuthenticatedUser - telemetryRecorder: TelemetryRecorder -}): JSX.Element { - useEffect(() => { - telemetryRecorder.recordEvent('cody.onboarding.purpose', 'view', { metadata: { tier: pro ? 1 : 0 } }) - }, [pro, telemetryRecorder]) - - const primaryEmail = authenticatedUser.emails.find(email => email.isPrimary)?.email - - const handleFormSubmit = (form: HTMLFormElement): void => { - const choice = form[0].querySelector('input[name="cody_form_hand_raiser"]') - - if (choice) { - telemetryRecorder.recordEvent('cody.onboarding.purpose', 'select', { - metadata: { onboardingCall: choice.checked ? 1 : 0 }, - }) - } - } - - return ( - <> -
-

Would you like to learn more about Cody for enterprise?

- - If you're not ready for a conversation, we'll stick to sharing Cody onboarding resources. - -
-
- { - onNext() - }} - onFormLoadError={() => { - onNext() - }} - userId={authenticatedUser.id} - userEmail={primaryEmail} - masterFormName="qualificationSurvey" - onFormSubmit={handleFormSubmit} - /> -
- - ) -} diff --git a/client/web/src/cody/onboarding/WelcomeStep.tsx b/client/web/src/cody/onboarding/WelcomeStep.tsx deleted file mode 100644 index c2f1c3038e0..00000000000 --- a/client/web/src/cody/onboarding/WelcomeStep.tsx +++ /dev/null @@ -1,75 +0,0 @@ -import { useState, useEffect } from 'react' - -import classNames from 'classnames' - -import type { TelemetryRecorder } from '@sourcegraph/shared/src/telemetry' -import { EVENT_LOGGER } from '@sourcegraph/shared/src/telemetry/web/eventLogger' -import { useIsLightTheme } from '@sourcegraph/shared/src/theme' -import { Text, Button } from '@sourcegraph/wildcard' - -import { EventName } from '../../util/constants' - -import styles from './CodyOnboarding.module.scss' - -export function WelcomeStep({ - onNext, - pro, - telemetryRecorder, -}: { - onNext: () => void - pro: boolean - telemetryRecorder: TelemetryRecorder -}): JSX.Element { - const [show, setShow] = useState(false) - const isLightTheme = useIsLightTheme() - useEffect(() => { - EVENT_LOGGER.log( - EventName.CODY_ONBOARDING_WELCOME_VIEWED, - { tier: pro ? 'pro' : 'free' }, - { tier: pro ? 'pro' : 'free' } - ) - telemetryRecorder.recordEvent('cody.onboarding.welcome', 'view', { metadata: { tier: pro ? 1 : 0 } }) - }, [pro, telemetryRecorder]) - - useEffect(() => { - // theme is not ready on first render, it defaults to system theme. - // so we need to wait a bit before showing the welcome video. - setTimeout(() => { - setShow(true) - }, 500) - }, []) - - return ( -
- {show ? ( - <> - - - Ready to breeze through the basics and get comfortable with Cody - {pro ? ' Pro' : ''}? - - - - ) : ( -
- )} -
- ) -} diff --git a/client/web/src/cody/onboarding/instructions/CodyFeatures.tsx b/client/web/src/cody/onboarding/instructions/CodyFeatures.tsx deleted file mode 100644 index 634d227831d..00000000000 --- a/client/web/src/cody/onboarding/instructions/CodyFeatures.tsx +++ /dev/null @@ -1,105 +0,0 @@ -import { H2, Text, Link, Button } from '@sourcegraph/wildcard' - -import { EditorStep } from '../../management/CodyManagementPage' - -export function CodyFeatures({ - onClose, - showStep, - setStep, -}: { - onClose: () => void - showStep?: EditorStep - setStep: (step: EditorStep) => void -}): JSX.Element { - return ( - <> -
-

Cody features

-
-
-
- - Autocomplete - - - Let Cody automatically write code for you. Start writing a comment or a line of code and Cody - will suggest the next few lines. - - Cody Autocomplete -
-
- - Chat - - - Answer questions about programming topics generally or your codebase specifically with Cody - chat. - - Cody Chat -
-
-
-
- - Commands - - - Streamline your development process by using Cody commands to understand, improve, fix, - document, and generate unit tests for your code. - - Cody Commands -
-
- - Feedback - - - Feel free to join our Discord to leave feedback or ask questions about Cody. - -
- - Discord chat - - - GitHub Discussions - -
-
-
- {showStep === undefined ? ( -
- - -
- ) : ( -
- -
- )} - - ) -} diff --git a/client/web/src/cody/onboarding/instructions/JetBrains.tsx b/client/web/src/cody/onboarding/instructions/JetBrains.tsx deleted file mode 100644 index 651bb9ac6fd..00000000000 --- a/client/web/src/cody/onboarding/instructions/JetBrains.tsx +++ /dev/null @@ -1,167 +0,0 @@ -import { useState, useEffect } from 'react' - -import classNames from 'classnames' - -import type { TelemetryRecorder } from '@sourcegraph/shared/src/telemetry' -import { EVENT_LOGGER } from '@sourcegraph/shared/src/telemetry/web/eventLogger' -import { Button, H2, Link, Text } from '@sourcegraph/wildcard' - -import { EventName } from '../../../util/constants' -import { EditorStep } from '../../management/CodyManagementPage' - -import { CodyFeatures } from './CodyFeatures' - -import styles from '../CodyOnboarding.module.scss' - -export function JetBrainsInstructions({ - onBack, - onClose, - showStep, - telemetryRecorder, -}: { - onBack?: () => void - onClose: () => void - showStep?: EditorStep - telemetryRecorder: TelemetryRecorder -}): JSX.Element { - const [step, setStep] = useState(showStep || 0) - const marketplaceUrl = 'https://plugins.jetbrains.com/plugin/9682-sourcegraph-cody--code-search' - - useEffect(() => { - if (step === EditorStep.SetupInstructions) { - EVENT_LOGGER.log(EventName.CODY_EDITOR_SETUP_VIEWED, { editor: 'JetBrains' }) - telemetryRecorder.recordEvent('cody.editorSetupPage', 'view', { metadata: { jetBrains: 1 } }) - } else if (step === EditorStep.CodyFeatures) { - EVENT_LOGGER.log(EventName.CODY_EDITOR_FEATURES_VIEWED, { editor: 'JetBrains' }) - telemetryRecorder.recordEvent('cody.editorFeaturesPage', 'view', { metadata: { jetBrains: 1 } }) - } - }, [step, telemetryRecorder]) - - return ( - <> - {step === EditorStep.SetupInstructions && ( - <> -
-

Setup instructions for JetBrains

-
- -
-
-
-
-
1
-
-
- - Open the Plugins Page (or via the{' '} - { - event.preventDefault() - EVENT_LOGGER.log(EventName.CODY_EDITOR_SETUP_OPEN_MARKETPLACE, { - editor: 'JetBrains', - }) - telemetryRecorder.recordEvent( - 'cody.onboarding.openMarketplace', - 'click', - { metadata: { jetBrains: 1 } } - ) - window.location.href = marketplaceUrl - }} - > - JetBrains Marketplace - - ) - - - Click the cog [βš™οΈ] icon in the top right corner of your IDE and select{' '} - Plugins -
- Alternatively, go to the settings option ( - [⌘] + [,] on macOS, or File β†’ Settings on Windows ), then - select "Plugins" from the menu on the left. -
-
-
- JetBrains Menu -
- -
-
-
-
2
-
-
- - Install the Cody plugin - - - Type "Cody" in the search bar and install the plugin. - -
-
- jetBrains Menu -
- -
-
-
-
3
-
-
- - Open the plugin and log in - - - Cody will be available on the right side of your IDE. Click the Cody icon to - open the sidebar and login. -
- Log in with the same method you use to create this account. -
-
-
- jetBrains Menu -
-
- - {showStep === undefined ? ( -
- - -
- ) : ( -
- -
- )} - - )} - {step === EditorStep.CodyFeatures && ( - - )} - - ) -} diff --git a/client/web/src/cody/onboarding/instructions/NeoVim.tsx b/client/web/src/cody/onboarding/instructions/NeoVim.tsx deleted file mode 100644 index 004e7676e3b..00000000000 --- a/client/web/src/cody/onboarding/instructions/NeoVim.tsx +++ /dev/null @@ -1,114 +0,0 @@ -import { useState, useEffect } from 'react' - -import classNames from 'classnames' - -import type { TelemetryRecorder } from '@sourcegraph/shared/src/telemetry' -import { EVENT_LOGGER } from '@sourcegraph/shared/src/telemetry/web/eventLogger' -import { Button, ButtonLink, H2, Text } from '@sourcegraph/wildcard' - -import { EventName } from '../../../util/constants' -import { EditorStep } from '../../management/CodyManagementPage' - -import { CodyFeatures } from './CodyFeatures' - -import styles from '../CodyOnboarding.module.scss' - -export function NeoVimInstructions({ - onBack, - onClose, - showStep, - telemetryRecorder, -}: { - onBack?: () => void - onClose: () => void - showStep?: EditorStep - telemetryRecorder: TelemetryRecorder -}): JSX.Element { - const [step, setStep] = useState(showStep || 0) - const marketplaceUrl = 'https://github.com/sourcegraph/sg.nvim#setup' - - useEffect(() => { - if (step === EditorStep.SetupInstructions) { - EVENT_LOGGER.log(EventName.CODY_EDITOR_SETUP_VIEWED, { editor: 'NeoVim' }) - telemetryRecorder.recordEvent('cody.editorSetupPage', 'view', { metadata: { neoVim: 1 } }) - } else if (step === EditorStep.CodyFeatures) { - EVENT_LOGGER.log(EventName.CODY_EDITOR_FEATURES_VIEWED, { editor: 'NeoVim' }) - telemetryRecorder.recordEvent('cody.editorFeaturesPage', 'view', { metadata: { neoVim: 1 } }) - } - }, [step, telemetryRecorder]) - - return ( - <> - {step === EditorStep.SetupInstructions && ( - <> -
-

Setup instructions for Neovim

-
- -
-
-
-
-
1
-
-
- - Open the plugin repo on GitHub - - - Follow the instructions detailed in the readme.md file to - install the plugin. - -
-
-
- { - event.preventDefault() - EVENT_LOGGER.log(EventName.CODY_EDITOR_SETUP_OPEN_MARKETPLACE, { - editor: 'NeoVim', - }) - telemetryRecorder.recordEvent('cody.onboarding.openMarketplace', 'click', { - metadata: { neoVim: 1 }, - }) - window.location.href = marketplaceUrl - }} - > - Navigate to GitHub repo - - Neovim Repo -
-
-
- {showStep === undefined ? ( -
- - -
- ) : ( -
- -
- )} - - )} - {step === EditorStep.CodyFeatures && ( - - )} - - ) -} diff --git a/client/web/src/cody/onboarding/instructions/VsCode.tsx b/client/web/src/cody/onboarding/instructions/VsCode.tsx deleted file mode 100644 index 602b8ddd539..00000000000 --- a/client/web/src/cody/onboarding/instructions/VsCode.tsx +++ /dev/null @@ -1,162 +0,0 @@ -import { useState, useEffect } from 'react' - -import classNames from 'classnames' - -import type { TelemetryRecorder } from '@sourcegraph/shared/src/telemetry' -import { EVENT_LOGGER } from '@sourcegraph/shared/src/telemetry/web/eventLogger' -import { Button, ButtonLink, H2, Text } from '@sourcegraph/wildcard' - -import { EventName } from '../../../util/constants' -import { EditorStep } from '../../management/CodyManagementPage' - -import { CodyFeatures } from './CodyFeatures' - -import styles from '../CodyOnboarding.module.scss' - -export function VSCodeInstructions({ - onBack, - onClose, - showStep, - telemetryRecorder, -}: { - onBack?: () => void - onClose: () => void - showStep?: EditorStep - telemetryRecorder: TelemetryRecorder -}): JSX.Element { - const [step, setStep] = useState(showStep || 0) - const marketplaceUrl = 'https://marketplace.visualstudio.com/items?itemName=sourcegraph.cody-ai' - - useEffect(() => { - if (step === EditorStep.SetupInstructions) { - EVENT_LOGGER.log(EventName.CODY_EDITOR_SETUP_VIEWED, { editor: 'VS Code' }) - telemetryRecorder.recordEvent('cody.editorSetupPage', 'view', { metadata: { vsCode: 1 } }) - } else if (step === EditorStep.CodyFeatures) { - EVENT_LOGGER.log(EventName.CODY_EDITOR_FEATURES_VIEWED, { editor: 'VS Code' }) - telemetryRecorder.recordEvent('cody.editorFeaturesPage', 'view', { metadata: { vsCode: 1 } }) - } - }, [step, telemetryRecorder]) - - return ( - <> - {step === EditorStep.SetupInstructions && ( - <> -
-

Setup instructions for VS Code

-
- -
-
-
-
-
1
-
-
- - Install Cody - - - Alternatively, you can reach this page by clicking{' '} - View {'>'} Extensions and searching for{' '} - Cody AI - -
-
-
- { - event.preventDefault() - EVENT_LOGGER.log(EventName.CODY_EDITOR_SETUP_OPEN_MARKETPLACE, { - editor: 'VS Code', - }) - telemetryRecorder.recordEvent('cody.onboarding.openMarketplace', 'click', { - metadata: { vsCode: 1 }, - }) - window.location.href = marketplaceUrl - }} - > - Open Marketplace - - VS Code Marketplace -
-
-
-
-
-
2
-
-
- - Open Cody from the sidebar on the left - - - Typically Cody will be the last item in the sidebar - -
-
-
- VS Code Marketplace -
-
-
-
-
-
3
-
-
- - Log in - - - Choose the same login method you used when you created your account - -
-
-
- VS Code Marketplace -
-
-
- - {showStep === undefined ? ( -
- - -
- ) : ( -
- -
- )} - - )} - {step === EditorStep.CodyFeatures && ( - - )} - - ) -} diff --git a/client/web/src/cody/subscription/CodySubscriptionPage.module.scss b/client/web/src/cody/subscription/CodySubscriptionPage.module.scss index e15cd0c4b50..7cfae2d861d 100644 --- a/client/web/src/cody/subscription/CodySubscriptionPage.module.scss +++ b/client/web/src/cody/subscription/CodySubscriptionPage.module.scss @@ -17,10 +17,6 @@ font-size: 1.25rem; } -.ide-name { - font-size: 1rem; -} - .pro-title { font-size: 2.25rem; color: #820dde; diff --git a/client/web/src/communitySearchContexts/CommunitySearchContextPage.story.tsx b/client/web/src/communitySearchContexts/CommunitySearchContextPage.story.tsx index 3ab0eb35a47..49f7fd96d31 100644 --- a/client/web/src/communitySearchContexts/CommunitySearchContextPage.story.tsx +++ b/client/web/src/communitySearchContexts/CommunitySearchContextPage.story.tsx @@ -58,7 +58,6 @@ const authUser: AuthenticatedUser = { }, viewerCanAdminister: true, hasVerifiedEmail: true, - completedPostSignup: true, databaseID: 0, tosAccepted: true, emails: [{ email: 'alice@sourcegraph.com', isPrimary: true, verified: true }], diff --git a/client/web/src/enterprise/code-monitoring/testing/util.ts b/client/web/src/enterprise/code-monitoring/testing/util.ts index afd01b98021..9bb525f6d76 100644 --- a/client/web/src/enterprise/code-monitoring/testing/util.ts +++ b/client/web/src/enterprise/code-monitoring/testing/util.ts @@ -22,7 +22,6 @@ export const mockUser: AuthenticatedUser = { nodes: [], }, hasVerifiedEmail: true, - completedPostSignup: true, session: { __typename: 'Session', canSignOut: true }, tosAccepted: true, emails: [{ email: 'user@me.com', isPrimary: true, verified: true }], diff --git a/client/web/src/enterprise/searchContexts/SearchContextForm.story.tsx b/client/web/src/enterprise/searchContexts/SearchContextForm.story.tsx index 86bbd4ed19c..7f0bd13a81c 100644 --- a/client/web/src/enterprise/searchContexts/SearchContextForm.story.tsx +++ b/client/web/src/enterprise/searchContexts/SearchContextForm.story.tsx @@ -83,7 +83,6 @@ const authUser: AuthenticatedUser = { }, viewerCanAdminister: true, hasVerifiedEmail: true, - completedPostSignup: true, databaseID: 0, tosAccepted: true, emails: [{ email: 'alice@sourcegraph.com', isPrimary: true, verified: true }], diff --git a/client/web/src/get-cody/GetCodyPage.tsx b/client/web/src/get-cody/GetCodyPage.tsx deleted file mode 100644 index 1a324058d03..00000000000 --- a/client/web/src/get-cody/GetCodyPage.tsx +++ /dev/null @@ -1,25 +0,0 @@ -import { useState } from 'react' - -import { useNavigate, useLocation } from 'react-router-dom' - -import type { AuthenticatedUser } from '@sourcegraph/shared/src/auth' - -import { CodyProRoutes } from '../cody/codyProRoutes' - -interface GetCodyPageProps { - authenticatedUser: AuthenticatedUser | null -} - -export const GetCodyPage: React.FunctionComponent = ({ authenticatedUser }) => { - const navigate = useNavigate() - const location = useLocation() - const [search] = useState(location.search) - - if (authenticatedUser) { - navigate(`${CodyProRoutes.Manage}${search || ''}`) - } else { - window.location.href = '/cody' - } - - return <> -} diff --git a/client/web/src/integration/insights/utils/override-insights-graphql-api.ts b/client/web/src/integration/insights/utils/override-insights-graphql-api.ts index c693cc1458d..bf14007ceac 100644 --- a/client/web/src/integration/insights/utils/override-insights-graphql-api.ts +++ b/client/web/src/integration/insights/utils/override-insights-graphql-api.ts @@ -81,7 +81,6 @@ export function overrideInsightsGraphQLApi(props: OverrideGraphQLExtensionsProps url: '/users/test', settingsURL: '/users/test/settings', hasVerifiedEmail: true, - completedPostSignup: true, organizations: { nodes: [ { diff --git a/client/web/src/jscontext.ts b/client/web/src/jscontext.ts index bf300bca760..3a817d5a635 100644 --- a/client/web/src/jscontext.ts +++ b/client/web/src/jscontext.ts @@ -67,7 +67,6 @@ export type SourcegraphContextCurrentUser = Pick< | 'latestSettings' | 'permissions' | 'hasVerifiedEmail' - | 'completedPostSignup' > /** diff --git a/client/web/src/marketing/toast/CodySurveyToast.module.scss b/client/web/src/marketing/toast/CodySurveyToast.module.scss deleted file mode 100644 index 36e81ced928..00000000000 --- a/client/web/src/marketing/toast/CodySurveyToast.module.scss +++ /dev/null @@ -1,101 +0,0 @@ -.cody-survey-toast-modal { - background: linear-gradient(0deg, rgba(255, 255, 255, 0.05), rgba(255, 255, 255, 0.05)), - radial-gradient(63.3% 72.11% at 95.66% -10.78%, rgba(255, 255, 255, 0.13) 0%, rgba(255, 255, 255, 0) 100%), - var(--gray-01); - box-shadow: 0 0 10px rgba(96, 120, 169, 0.1); - border-radius: 0.5rem; - color: var(--black); - - .cody-survey-toast-modal-button { - background: var(--violet-05); - border-radius: 0.3125rem; - color: var(--white); - border: none; - - &:not(:disabled):not([aria-disabled='true']) { - &:hover, - &:focus { - background: var(--violet-05) !important; - } - } - - &:disabled, - &[aria-disabled='true'] { - background: var(--violet-03); - } - } - - label { - &:hover { - color: var(--black); - } - } -} - -.email-icon { - background: var(--violet-01); - border-radius: 0.3125rem; - color: var(--violet-05); - height: 2rem; - width: 2rem; -} - -.cody-icon { - height: 1.6875rem; - width: 1.875rem; - margin-right: 0.5rem; -} - -.resend-button { - color: var(--violet-05); -} - -.modal-overlay { - background-color: unset; -} - -.modal-checkbox[type='checkbox'] { - display: none; -} - -.modal-checkbox + label { - display: inline-block; - position: relative; - padding-left: 1.5625rem; - cursor: pointer; -} - -.modal-checkbox + label::before { - content: ''; - display: inline-block; - width: 1rem; - height: 1rem; - background-color: var(--white); /* Initial background color */ - border: 1px solid var(--gray-07); - border-radius: 0.25rem; - position: absolute; - left: 0; - top: 2px; -} - -.modal-checkbox:checked + label::before { - background-color: var(--violet-05); /* Background color when checked */ - border-color: var(--violet-05); /* Border color when checked */ -} - -.modal-checkbox + label::after { - content: ''; - display: none; - width: 0.375rem; - height: 0.625rem; - border: solid var(--white); /* Tick color */ - border-width: 0 2px 2px 0; - transform: rotate(45deg); - position: absolute; - left: 5px; - top: 4px; -} - -.modal-checkbox:checked + label::after { - display: block; -} diff --git a/client/web/src/marketing/toast/CodySurveyToast.tsx b/client/web/src/marketing/toast/CodySurveyToast.tsx deleted file mode 100644 index 76231c7c2c6..00000000000 --- a/client/web/src/marketing/toast/CodySurveyToast.tsx +++ /dev/null @@ -1,390 +0,0 @@ -import { useState, useCallback, useEffect } from 'react' - -import { mdiEmail } from '@mdi/js' -import classNames from 'classnames' -import { useLocation } from 'react-router-dom' - -import { asError, type ErrorLike } from '@sourcegraph/common' -import { gql, useMutation } from '@sourcegraph/http-client' -import { TelemetryV2Props } from '@sourcegraph/shared/src/telemetry' -import type { TelemetryProps } from '@sourcegraph/shared/src/telemetry/telemetryService' -import { Checkbox, Form, H3, Modal, Text, Button, Icon, AnchorLink } from '@sourcegraph/wildcard' - -import type { AuthenticatedUser } from '../../auth' -import { getReturnTo } from '../../auth/SignInSignUpCommon' -import { CodyColorIcon } from '../../cody/chat/CodyPageIcon' -import { CodyProRoutes } from '../../cody/codyProRoutes' -import { LoaderButton } from '../../components/LoaderButton' -import type { - SubmitCodySurveyResult, - SubmitCodySurveyVariables, - SetCompletedPostSignupVariables, - SetCompletedPostSignupResult, -} from '../../graphql-operations' -import { resendVerificationEmail } from '../../user/settings/emails/UserEmail' -import { HubSpotForm } from '../components/HubSpotForm' - -import styles from './CodySurveyToast.module.scss' - -export const SUBMIT_CODY_SURVEY = gql` - mutation SubmitCodySurvey($isForWork: Boolean!, $isForPersonal: Boolean!) { - submitCodySurvey(isForWork: $isForWork, isForPersonal: $isForPersonal) { - alwaysNil - } - } -` - -const SET_COMPLETED_POST_SIGNUP = gql` - mutation SetCompletedPostSignup($userID: ID!) { - setCompletedPostSignup(userID: $userID) { - alwaysNil - } - } -` - -const CodySurveyToastInner: React.FC< - { onSubmitEnd: () => void; userId: string; hasVerifiedEmail: boolean } & TelemetryProps & TelemetryV2Props -> = ({ userId, onSubmitEnd, telemetryService, telemetryRecorder, hasVerifiedEmail }) => { - const [isCodyForWork, setIsCodyForWork] = useState(false) - const [isCodyForPersonalStuff, setIsCodyForPersonalStuff] = useState(false) - - const handleCodyForWorkChange = useCallback>(event => { - setIsCodyForWork(event.target.checked) - }, []) - const handleCodyForPersonalStuffChange = useCallback>(event => { - setIsCodyForPersonalStuff(event.target.checked) - }, []) - - const [submitCodySurvey, { loading: loadingCodySurvey, error: submitSurveyError }] = useMutation< - SubmitCodySurveyResult, - SubmitCodySurveyVariables - >(SUBMIT_CODY_SURVEY, { - variables: { - isForWork: isCodyForWork, - isForPersonal: isCodyForPersonalStuff, - }, - }) - - const [updatePostSignupCompletion, { loading: loadingPostSignup, error: setPostSignupError }] = useMutation< - SetCompletedPostSignupResult, - SetCompletedPostSignupVariables - >(SET_COMPLETED_POST_SIGNUP, { - variables: { - userID: userId, - }, - }) - - const loading = loadingCodySurvey || loadingPostSignup - const error = !!submitSurveyError || !!setPostSignupError - - const handleSubmit = useCallback( - async (event: React.FormEvent) => { - const eventParams = { isCodyForPersonalStuff, isCodyForWork } - telemetryService.log('CodyUsageToastSubmitted', eventParams, eventParams) - telemetryRecorder.recordEvent('codySurvey', 'submit', { - metadata: { forWork: isCodyForWork ? 1 : 0, forPersonal: isCodyForPersonalStuff ? 1 : 0 }, - }) - event.preventDefault() - - try { - await submitCodySurvey() - - if (hasVerifiedEmail) { - await updatePostSignupCompletion() - } - - onSubmitEnd() - } catch (error) { - /* eslint-disable no-console */ - console.error(error) - } - }, - [ - hasVerifiedEmail, - isCodyForPersonalStuff, - isCodyForWork, - onSubmitEnd, - submitCodySurvey, - updatePostSignupCompletion, - telemetryService, - telemetryRecorder, - ] - ) - - useEffect(() => { - telemetryService.log('CodySurveyToastViewed') - telemetryRecorder.recordEvent('codySurvey', 'view') - }, [telemetryService, telemetryRecorder]) - - return ( - -

- - Just one more thing... -

- How will you be using Cody, our AI assistant? -
- - - {error && ( - - An error occurred. Please reload the page and try again. If this persists, contact support at - support@sourcegraph.com - - )} -
- -
- -
- ) -} - -const CodyQualificationSurveryToastInner: React.FC< - { onSubmitEnd: () => void; authenticatedUser: AuthenticatedUser } & TelemetryProps & TelemetryV2Props -> = ({ onSubmitEnd, telemetryService, telemetryRecorder, authenticatedUser }) => { - const [updatePostSignupCompletion, { error: setPostSignupError }] = useMutation< - SetCompletedPostSignupResult, - SetCompletedPostSignupVariables - >(SET_COMPLETED_POST_SIGNUP, { - variables: { - userID: authenticatedUser.id, - }, - }) - - const handleFormReady = useCallback( - (form: HTMLFormElement) => { - const input = form.querySelector('input[name="using_cody_for_work"]') - - // Trigger telemetry event whenever the cody for work is selected. - const handleChange = (e: Event): void => { - const target = e.target as HTMLInputElement - const isChecked = target.checked - - if (isChecked) { - telemetryService.log('ViewCodyWorkQuestionnarie') - telemetryRecorder.recordEvent('codySurvey.forWorkQuestionnaire', 'view') - } - } - - input?.addEventListener('change', handleChange) - - return () => { - input?.removeEventListener('change', handleChange) - } - }, - [telemetryService, telemetryRecorder] - ) - - const primaryEmail = authenticatedUser.emails.find(email => email.isPrimary)?.email - - const handleSubmit = useCallback(async () => { - try { - if (authenticatedUser.hasVerifiedEmail) { - await updatePostSignupCompletion() - } - - onSubmitEnd() - } catch (error) { - /* eslint-disable no-console */ - console.error(error) - } - }, [authenticatedUser.hasVerifiedEmail, onSubmitEnd, updatePostSignupCompletion]) - - useEffect(() => { - telemetryService.log('ViewCodyforWorkorPersonalForm') - telemetryRecorder.recordEvent('codySurvey.forWorkOrPersonal', 'view') - }, [telemetryService, telemetryRecorder]) - - return ( - -

- - Quick question... -

- How will you be using Cody, our AI assistant? - - - {!!setPostSignupError && ( - - An error occurred. Please reload the page and try again. If this persists, contact support at - support@sourcegraph.com - - )} -
- ) -} - -const CodyVerifyEmailToast: React.FC< - { onNext: () => void; authenticatedUser: AuthenticatedUser } & TelemetryProps & TelemetryV2Props -> = ({ onNext, authenticatedUser, telemetryService, telemetryRecorder }) => { - const [sending, setSending] = useState(false) - const [resentEmailTo, setResentEmailTo] = useState(null) - const [resendEmailError, setResendEmailError] = useState(null) - const resend = useCallback(async () => { - const email = (authenticatedUser.emails || []).find(({ verified }) => !verified)?.email - if (email) { - setSending(true) - await resendVerificationEmail(authenticatedUser.id, email, telemetryRecorder, { - onSuccess: () => { - setResentEmailTo(email) - setResendEmailError(null) - setSending(false) - }, - onError: (errors: ErrorLike) => { - setResendEmailError(asError(errors)) - setResentEmailTo(null) - setSending(false) - }, - }) - } - }, [authenticatedUser, telemetryRecorder]) - - useEffect(() => { - telemetryService.log('VerifyEmailToastViewed') - telemetryRecorder.recordEvent('codySurvey.veryEmailToast', 'view') - }, [telemetryService, telemetryRecorder]) - - return ( - -

- - Verify your email address -

- To use Cody, our AI Assistant, you'll need to verify your email address. - - Didn't get an email? - {sending ? ( - Sending... - ) : ( - <> - Click to - - . - - )} - - {resentEmailTo && ( - - Sent verification email to {resentEmailTo}. - - )} - {resendEmailError && {resendEmailError.message}.} -
- - Sign out - - -
-
- ) -} - -export const CodySurveyToast: React.FC< - { - authenticatedUser: AuthenticatedUser - showQualificationSurvey?: boolean - } & TelemetryProps & - TelemetryV2Props -> = ({ authenticatedUser, telemetryService, telemetryRecorder, showQualificationSurvey }) => { - const [showVerifyEmail, setShowVerifyEmail] = useState(!authenticatedUser.hasVerifiedEmail) - - const location = useLocation() - - const handleSubmitEnd = (): void => { - // Redirects once user submits the post-sign-up form - const returnTo = getReturnTo(location, CodyProRoutes.Manage) - window.location.replace(returnTo) - } - - const dismissVerifyEmail = useCallback(() => { - telemetryService.log('VerifyEmailToastDismissed') - telemetryRecorder.recordEvent('codySurvey.verifyEmailToast', 'dismissed') - setShowVerifyEmail(false) - }, [telemetryService, telemetryRecorder]) - - useEffect(() => { - telemetryService.log('CustomerQualificationSurveyExperiment001Enrolled') - telemetryRecorder.recordEvent('experiment', 'enroll', { metadata: { experimentId: 1 } }) - }, [telemetryService, telemetryRecorder]) - - if (showVerifyEmail) { - return ( - - ) - } - - if (showQualificationSurvey) { - return ( - - ) - } - - return ( - - ) -} diff --git a/client/web/src/marketing/toast/index.ts b/client/web/src/marketing/toast/index.ts index e14066d5f65..9677e3cbf99 100644 --- a/client/web/src/marketing/toast/index.ts +++ b/client/web/src/marketing/toast/index.ts @@ -1,2 +1 @@ export { SurveyToastTrigger as SurveyToast } from './SurveyToastTrigger' -export { CodySurveyToast } from './CodySurveyToast' diff --git a/client/web/src/routes.constants.ts b/client/web/src/routes.constants.ts index e6787798fff..51850849ffc 100644 --- a/client/web/src/routes.constants.ts +++ b/client/web/src/routes.constants.ts @@ -4,9 +4,7 @@ export enum PageRoutes { SearchConsole = '/search/console', SignIn = '/sign-in', SignUp = '/sign-up', - PostSignUp = '/post-sign-up', UnlockAccount = '/unlock-account/:token', - Welcome = '/welcome', Settings = '/settings', User = '/user/*', Organizations = '/organizations/*', @@ -23,7 +21,6 @@ export enum PageRoutes { SetupWizard = '/setup', Teams = '/teams/*', RequestAccess = '/request-access/*', - GetCody = '/get-cody', BatchChanges = '/batch-changes/*', CodeMonitoring = '/code-monitoring/*', Insights = '/insights/*', diff --git a/client/web/src/routes.tsx b/client/web/src/routes.tsx index 53e3a290606..d29f95b5d99 100644 --- a/client/web/src/routes.tsx +++ b/client/web/src/routes.tsx @@ -30,8 +30,6 @@ const RepoContainer = lazyComponent(() => import('./repo/RepoContainer'), 'RepoC const TeamsArea = lazyComponent(() => import('./team/TeamsArea'), 'TeamsArea') const CodySidebarStoreProvider = lazyComponent(() => import('./cody/sidebar/Provider'), 'CodySidebarStoreProvider') const CodyIgnoreProvider = lazyComponent(() => import('./cody/useCodyIgnore'), 'CodyIgnoreProvider') -const GetCodyPage = lazyComponent(() => import('./get-cody/GetCodyPage'), 'GetCodyPage') -const PostSignUpPage = lazyComponent(() => import('./auth/PostSignUpPage'), 'PostSignUpPage') const GlobalNotebooksArea = lazyComponent(() => import('./notebooks/GlobalNotebooksArea'), 'GlobalNotebooksArea') const GlobalBatchChangesArea = lazyComponent( @@ -87,14 +85,6 @@ const PassThroughToServer: React.FC = () => { * See https://reacttraining.com/react-router/web/example/sidebar */ export const routes: RouteObject[] = [ - { - path: PageRoutes.GetCody, - element: } />, - }, - { - path: PageRoutes.PostSignUp, - element: } />, - }, { path: PageRoutes.Index, element: , @@ -241,11 +231,6 @@ export const routes: RouteObject[] = [ /> ), }, - { - path: PageRoutes.Welcome, - // This route is deprecated after we removed the post-sign-up page experimental feature, but we keep it for now to not break links. - element: , - }, { path: PageRoutes.Settings, element: } />, diff --git a/client/web/src/storm/pages/LayoutPage/LayoutPage.tsx b/client/web/src/storm/pages/LayoutPage/LayoutPage.tsx index 188f70a09ed..0712500ad06 100644 --- a/client/web/src/storm/pages/LayoutPage/LayoutPage.tsx +++ b/client/web/src/storm/pages/LayoutPage/LayoutPage.tsx @@ -1,12 +1,12 @@ import React, { Suspense, useCallback, useLayoutEffect, useState } from 'react' import classNames from 'classnames' -import { Outlet, useLocation, Navigate, useMatches, useMatch } from 'react-router-dom' +import { Navigate, Outlet, useLocation, useMatch, useMatches } from 'react-router-dom' import { useKeyboardShortcut } from '@sourcegraph/shared/src/keyboardShortcuts/useKeyboardShortcut' import { Shortcut } from '@sourcegraph/shared/src/react-shortcuts' import { useExperimentalFeatures } from '@sourcegraph/shared/src/settings/settings' -import { useTheme, Theme } from '@sourcegraph/shared/src/theme' +import { Theme, useTheme } from '@sourcegraph/shared/src/theme' import { lazyComponent } from '@sourcegraph/shared/src/util/lazyComponent' import { FeedbackPrompt, LoadingSpinner, useLocalStorage } from '@sourcegraph/wildcard' @@ -21,7 +21,7 @@ import { useFeatureFlag } from '../../../featureFlags/useFeatureFlag' import { GlobalAlerts } from '../../../global/GlobalAlerts' import { useHandleSubmitFeedback } from '../../../hooks' import type { LegacyLayoutRouteContext } from '../../../LegacyRouteContext' -import { CodySurveyToast, SurveyToast } from '../../../marketing/toast' +import { SurveyToast } from '../../../marketing/toast' import { GlobalNavbar } from '../../../nav/GlobalNavbar' import { PageRoutes } from '../../../routes.constants' import { parseSearchURLQuery } from '../../../search' @@ -47,9 +47,8 @@ function useIsSignInOrSignUpPage(): boolean { const isSignInPage = useMatch(PageRoutes.SignIn) const isSignUpPage = useMatch(PageRoutes.SignUp) const isPasswordResetPage = useMatch(PageRoutes.PasswordReset) - const isWelcomePage = useMatch(PageRoutes.Welcome) const isRequestAccessPage = useMatch(PageRoutes.RequestAccess) - return !!(isSignInPage || isSignUpPage || isPasswordResetPage || isWelcomePage || isRequestAccessPage) + return !!(isSignInPage || isSignUpPage || isPasswordResetPage || isRequestAccessPage) } export const Layout: React.FC = props => { const location = useLocation() @@ -94,7 +93,6 @@ export const Layout: React.FC = props => { const needsRepositoryConfiguration = window.context?.needsRepositoryConfiguration const isSiteInit = location.pathname === PageRoutes.SiteAdminInit const isSignInOrUp = useIsSignInOrSignUpPage() - const isGetCodyPage = location.pathname === PageRoutes.GetCody const [enableContrastCompliantSyntaxHighlighting] = useFeatureFlag('contrast-compliant-syntax-highlighting') @@ -200,14 +198,7 @@ export const Layout: React.FC = props => { telemetryRecorder={props.platformContext.telemetryRecorder} /> )} - {!isSiteInit && props.isSourcegraphDotCom && props.authenticatedUser && ( - - )} - {!isSiteInit && !isSignInOrUp && !isGetCodyPage && ( + {!isSiteInit && !isSignInOrUp && (