Skip to content
This repository was archived by the owner on Sep 30, 2024. It is now read-only.

Commit 50471a6

Browse files
authored
PLG: Allow users who are already on a team accept invites (#63231)
Closes https://github.com/sourcegraph/sourcegraph/issues/63078 Part of sourcegraph/self-serve-cody#804 (see also its backend counterpart sourcegraph/self-serve-cody#858) [Figma](https://www.figma.com/design/FMSdn1oKccJRHQPgf7053o/Cody-PLG-GA?node-id=4042-4927&t=Ob2UbemEkft4ofJZ-0) ### Summary 1. Moved the accept invite UI to the "/cody/manage" page. 2. Handled cases where the invited user is already a Cody Pro user. 3. Fixed styles in the CodyAlert component to ensure images are visible. ### Implementation Details 1. Added the `useInviteState` hook, which returns `initialInviteStatus` and `initialUserStatus`. We track the initial statuses to determine the appropriate UI variant to display. For example, if the initial invite status is "sent" (indicating the invite can be accepted) and the initial user status is `UserInviteStatus.NoCurrentTeam` (indicating the user is not a member of any team), a confirmation banner is shown for the user to accept or decline the invite. After the user responds, the invite query is invalidated, updating the status to "accepted", "canceled", or "errored" based on their action. Depending on the result, a success or error banner is displayed, or the banner is hidden if the invite is canceled. More configuration examples can be found in the Screenshots section. All possible states are detailed in the `AcceptInviteBannerContent`. 2. For users who are the sole admin of their current team, the banner is shown on the "/cody/manage" page. The design requires showing a banner on the "/cody/team/manage" page to suggest transferring the admin role to another team member. However, this page is not yet ready. To sync the banner state with user role changes or deletion actions, the members list query must be invalidated after each action. The current implementation of the "cody/manage/team" page does not support refetching with the `useSSCQuery` hook. To resolve this, we need to migrate the "cody/manage/team" page to use React Query to allow query invalidation after each action. For now, users who are sole admins see a banner on the "cody/manage" page suggesting transferring the admin role, with a link to the "/cody/manage/team" page. <!-- 💡 To write a useful PR description, make sure that your description covers: - WHAT this PR is changing: - How was it PREVIOUSLY. - How it will be from NOW on. - WHY this PR is needed. - CONTEXT, i.e. to which initiative, project or RFC it belongs. The structure of the description doesn't matter as much as covering these points, so use your best judgement based on your context. Learn how to write good pull request description: https://www.notion.so/sourcegraph/Write-a-good-pull-request-description-610a7fd3e613496eb76f450db5a49b6e?pvs=4 --> ### Screenshots | Description | Screenshot | |--|--| | Failed to define user state OR invite status is not "sent" (thus can't be accepted) | <img width="1516" alt="Screenshot 2024-06-14 at 14 39 41" src="https://github.com/sourcegraph/sourcegraph/assets/25318659/cf712239-6a8e-4a66-a4a2-c1932ba70ffd"> | | User is not on a Cody Pro team | <img width="1516" alt="Screenshot 2024-06-14 at 14 34 30" src="https://github.com/sourcegraph/sourcegraph/assets/25318659/d6b53dc4-c7df-45eb-a743-9baddfdd8aa3"><img width="1516" alt="Screenshot 2024-06-14 at 14 34 40" src="https://github.com/sourcegraph/sourcegraph/assets/25318659/617957cf-8259-4056-a117-8b806ece6efe">| |on the team they've been invited to|<img width="1516" alt="Screenshot 2024-06-14 at 14 35 41" src="https://github.com/sourcegraph/sourcegraph/assets/25318659/eaa871ce-acd3-4a7e-a25c-74011a42af58">| | User is the sole admin of another team |<img width="1516" alt="Screenshot 2024-06-14 at 14 38 42" src="https://github.com/sourcegraph/sourcegraph/assets/25318659/1382e5a0-4375-4002-93a4-ec25d354317f">| | User is on another team |<img width="1516" alt="Screenshot 2024-06-14 at 14 36 38" src="https://github.com/sourcegraph/sourcegraph/assets/25318659/2bf20073-f49b-4fb1-9996-6143671c1727"><img width="1516" alt="Screenshot 2024-06-14 at 14 36 43" src="https://github.com/sourcegraph/sourcegraph/assets/25318659/349e9445-b2ec-402d-ac0a-6b2517abde9c">| ## Test plan - Checkout the branch from sourcegraph/self-serve-cody#858 and run SSC locally - Run Sourcegraph in dotcom mode - As a Cody Pro team admin send invites to users that have different statuses (are not on a team, are members of the team they were invited to, are members of another team, are sole admins of their teams) - As the invited user: - click the invite link from the email - modify the hostname in the URL so that it points to the local Sourcegraph instance - ensure the correct banner is displayed - ensure user can accept/decline the invite (if applicable for the banner type) <!-- All pull requests REQUIRE a test plan: https://docs-legacy.sourcegraph.com/dev/background-information/testing_principles --> ## Changelog <!-- 1. Ensure your pull request title is formatted as: $type($domain): $what 5. Add bullet list items for each additional detail you want to cover (see example below) 6. You can edit this after the pull request was merged, as long as release shipping it hasn't been promoted to the public. 7. For more information, please see this how-to https://www.notion.so/sourcegraph/Writing-a-changelog-entry-dd997f411d524caabf0d8d38a24a878c? Audience: TS/CSE > Customers > Teammates (in that order). Cheat sheet: $type = chore|fix|feat $domain: source|search|ci|release|plg|cody|local|... --> <!-- Example: Title: fix(search): parse quotes with the appropriate context Changelog section: ## Changelog - When a quote is used with regexp pattern type, then ... - Refactored underlying code. -->
1 parent 919f64b commit 50471a6

18 files changed

+506
-99
lines changed

client/web/BUILD.bazel

+6
Original file line numberDiff line numberDiff line change
@@ -229,7 +229,10 @@ ts_project(
229229
"src/cody/dashboard/CodyDashboardPage.tsx",
230230
"src/cody/dashboard/UpsellImage.tsx",
231231
"src/cody/editorGroups.ts",
232+
"src/cody/invites/AcceptInviteBanner.tsx",
232233
"src/cody/invites/AcceptInvitePage.tsx",
234+
"src/cody/invites/useInviteParams.ts",
235+
"src/cody/invites/useInviteState.ts",
233236
"src/cody/isCodyEnabled.tsx",
234237
"src/cody/management/CodyManagementPage.tsx",
235238
"src/cody/management/SubscriptionStats.tsx",
@@ -239,7 +242,10 @@ ts_project(
239242
"src/cody/management/api/hooks/useApiClient.tsx",
240243
"src/cody/management/api/react-query/QueryClientProvider.tsx",
241244
"src/cody/management/api/react-query/callCodyProApi.ts",
245+
"src/cody/management/api/react-query/invites.ts",
246+
"src/cody/management/api/react-query/queryKeys.ts",
242247
"src/cody/management/api/react-query/subscriptions.ts",
248+
"src/cody/management/api/react-query/teams.ts",
243249
"src/cody/management/api/stripeCheckout.ts",
244250
"src/cody/management/api/teamInvites.ts",
245251
"src/cody/management/api/teamMembers.ts",

client/web/src/cody/components/CodyAlert.module.scss

+29-5
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111

1212
.purple-no-icon {
1313
padding-left: 1.5rem;
14-
background-color: var(--violet-02);
14+
background-color: var(--violet-01);
1515
color: var(--gray-08);
1616
:global(.theme-dark) & {
1717
background-color: var(--pink);
@@ -20,7 +20,7 @@
2020
}
2121

2222
.purple-success {
23-
background-color: var(--violet-02);
23+
background-color: var(--violet-01);
2424
color: var(--violet-06);
2525
:global(.theme-dark) & {
2626
background-color: var(--pink);
@@ -47,7 +47,7 @@
4747
.purple-cody-pro {
4848
/* stylelint-disable-next-line declaration-property-unit-allowed-list */
4949
padding-left: calc(1.5rem + 135px + 1.5rem);
50-
background-color: var(--violet-02);
50+
background-color: var(--violet-01);
5151
color: var(--gray-11);
5252
:global(.theme-dark) & {
5353
background-color: var(--pink);
@@ -65,19 +65,43 @@
6565
}
6666
}
6767

68+
.green-cody-pro {
69+
/* stylelint-disable-next-line declaration-property-unit-allowed-list */
70+
padding-left: calc(1.5rem + 135px + 1.5rem);
71+
background-color: #e0f9d5;
72+
color: #054410;
73+
:global(.theme-dark) & {
74+
background-color: #51cf66;
75+
}
76+
&::after {
77+
position: absolute;
78+
bottom: 0;
79+
left: 1.5rem;
80+
/* stylelint-disable-next-line declaration-property-unit-allowed-list */
81+
width: 135px;
82+
/* stylelint-disable-next-line declaration-property-unit-allowed-list */
83+
height: 95px;
84+
background-image: url('https://storage.googleapis.com/sourcegraph-assets/cody-pro-card.png');
85+
background-size: cover;
86+
}
87+
}
88+
6889
.error {
6990
/* stylelint-disable-next-line declaration-property-unit-allowed-list */
7091
padding-left: calc(1.5rem + 124px + 1.5rem);
71-
overflow: hidden;
7292
background-color: var(--oc-orange-1);
7393
color: var(--gray-08);
7494
:global(.theme-dark) & {
7595
background-color: var(--oc-orange-4);
7696
}
7797
&::after {
78-
top: 1.5rem;
98+
left: 2rem;
99+
bottom: 0;
79100
/* stylelint-disable-next-line declaration-property-unit-allowed-list */
80101
width: 124px;
102+
/* stylelint-disable-next-line declaration-property-unit-allowed-list */
103+
height: 95px;
81104
background-image: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 124 99"><g filter="url(%23a)"><path fill="%23D9D9D9" fill-rule="evenodd" d="M10.86 5c-3.32 0-6 3.22-6 7.19v124.62c0 3.97 2.68 7.19 6 7.19h102c3.31 0 6-3.22 6-7.19V12.19c0-3.97-2.69-7.19-6-7.19h-102Zm44.82 11.17c-1.65 0-3 1.6-3 3.59 0 1.98 1.35 3.58 3 3.58h12.36c1.65 0 2.99-1.6 2.99-3.58s-1.34-3.59-3-3.59H55.69Z" clip-rule="evenodd"/><path fill="%23EFF2F5" fill-rule="evenodd" d="M10.86 5c-3.32 0-6 3.22-6 7.19v124.62c0 3.97 2.68 7.19 6 7.19h102c3.31 0 6-3.22 6-7.19V12.19c0-3.97-2.69-7.19-6-7.19h-102Zm44.82 11.17c-1.65 0-3 1.6-3 3.59 0 1.98 1.35 3.58 3 3.58h12.36c1.65 0 2.99-1.6 2.99-3.58s-1.34-3.59-3-3.59H55.69Z" clip-rule="evenodd"/><path fill="%23fff" fill-opacity=".2" fill-rule="evenodd" d="M10.86 5c-3.32 0-6 3.22-6 7.19v124.62c0 3.97 2.68 7.19 6 7.19h102c3.31 0 6-3.22 6-7.19V12.19c0-3.97-2.69-7.19-6-7.19h-102Zm44.82 11.17c-1.65 0-3 1.6-3 3.59 0 1.98 1.35 3.58 3 3.58h12.36c1.65 0 2.99-1.6 2.99-3.58s-1.34-3.59-3-3.59H55.69Z" clip-rule="evenodd"/><path fill="url(%23b)" fill-rule="evenodd" d="M10.86 5c-3.32 0-6 3.22-6 7.19v124.62c0 3.97 2.68 7.19 6 7.19h102c3.31 0 6-3.22 6-7.19V12.19c0-3.97-2.69-7.19-6-7.19h-102Zm44.82 11.17c-1.65 0-3 1.6-3 3.59 0 1.98 1.35 3.58 3 3.58h12.36c1.65 0 2.99-1.6 2.99-3.58s-1.34-3.59-3-3.59H55.69Z" clip-rule="evenodd"/><path stroke="%23000" stroke-opacity=".16" stroke-width=".4" d="M5.06 12.19c0-3.9 2.63-6.99 5.8-6.99h102c3.17 0 5.8 3.1 5.8 6.99v124.62c0 3.9-2.63 6.99-5.8 6.99h-102c-3.17 0-5.8-3.1-5.8-6.99V12.19Zm50.62 3.78c-1.8 0-3.2 1.73-3.2 3.79 0 2.05 1.4 3.78 3.2 3.78h12.36c1.8 0 3.19-1.73 3.19-3.78 0-2.06-1.4-3.79-3.2-3.79H55.69Z"/><path fill="%23DBE2F0" d="M63.67 69.33h-3.34v-6.66h3.34v6.66Zm0 6.67h-3.34v-3.33h3.34V76Zm-20 5h36.66L62 49.33 43.67 81Z"/></g><defs><linearGradient id="b" x1="62.09" x2="62.09" y1="137.07" y2="5" gradientUnits="userSpaceOnUse"><stop offset=".43" stop-color="%23fff"/><stop offset="1" stop-color="%23fff" stop-opacity="0"/></linearGradient><filter id="a" width="123" height="148" x=".36" y=".5" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feColorMatrix in="SourceAlpha" result="hardAlpha" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0"/><feMorphology in="SourceAlpha" operator="dilate" radius="1" result="effect1_dropShadow_5081_15909"/><feOffset/><feGaussianBlur stdDeviation="1.75"/><feColorMatrix values="0 0 0 0 0.141522 0 0 0 0 0.159783 0 0 0 0 0.21 0 0 0 0.31 0"/><feBlend in2="BackgroundImageFix" result="effect1_dropShadow_5081_15909"/><feBlend in="SourceGraphic" in2="effect1_dropShadow_5081_15909" result="shape"/></filter></defs></svg>');
105+
background-repeat: no-repeat;
82106
}
83107
}

client/web/src/cody/components/CodyAlert.tsx

+2-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import classNames from 'classnames'
55
import styles from './CodyAlert.module.scss'
66

77
interface CodyAlertProps extends React.HTMLAttributes<HTMLDivElement> {
8-
variant: 'purple' | 'greenSuccess' | 'purpleSuccess' | 'purpleCodyPro' | 'error'
8+
variant: 'purple' | 'greenSuccess' | 'purpleSuccess' | 'purpleCodyPro' | 'greenCodyPro' | 'error'
99
className?: string
1010
children: React.ReactNode
1111
}
@@ -19,6 +19,7 @@ export const CodyAlert: React.FunctionComponent<CodyAlertProps> = ({ variant, cl
1919
[styles.greenSuccess]: variant === 'greenSuccess',
2020
[styles.purpleSuccess]: variant === 'purpleSuccess',
2121
[styles.purpleCodyPro]: variant === 'purpleCodyPro',
22+
[styles.greenCodyPro]: variant === 'greenCodyPro',
2223
[styles.error]: variant === 'error',
2324
},
2425
className
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,172 @@
1+
import { Button, ButtonLink, H1, Text } from '@sourcegraph/wildcard'
2+
3+
import { CodyProRoutes } from '../codyProRoutes'
4+
import { CodyAlert } from '../components/CodyAlert'
5+
import { useAcceptInvite, useCancelInvite } from '../management/api/react-query/invites'
6+
7+
import { useInviteParams } from './useInviteParams'
8+
import { UserInviteStatus, useInviteState } from './useInviteState'
9+
10+
export const AcceptInviteBanner: React.FC<{ onSuccess: () => unknown }> = ({ onSuccess }) => {
11+
const { inviteParams, clearInviteParams } = useInviteParams()
12+
if (!inviteParams) {
13+
return null
14+
}
15+
return (
16+
<AcceptInviteBannerContent
17+
teamId={inviteParams.teamId}
18+
inviteId={inviteParams.inviteId}
19+
onSuccess={onSuccess}
20+
clearInviteParams={clearInviteParams}
21+
/>
22+
)
23+
}
24+
25+
const AcceptInviteBannerContent: React.FC<{
26+
teamId: string
27+
inviteId: string
28+
onSuccess: () => unknown
29+
clearInviteParams: () => void
30+
}> = ({ teamId, inviteId, onSuccess, clearInviteParams }) => {
31+
const inviteState = useInviteState(teamId, inviteId)
32+
const acceptInviteMutation = useAcceptInvite()
33+
const cancelInviteMutation = useCancelInvite()
34+
35+
if (inviteState.status === 'loading') {
36+
return null
37+
}
38+
39+
if (
40+
inviteState.status === 'error' ||
41+
inviteState.initialInviteStatus !== 'sent' ||
42+
inviteState.initialUserStatus === UserInviteStatus.Error
43+
) {
44+
return (
45+
<CodyAlert variant="error">
46+
<H1 as="p" className="mb-2">
47+
Issue with invite
48+
</H1>
49+
<Text className="mb-0">The invitation is no longer valid. Contact your team admin.</Text>
50+
</CodyAlert>
51+
)
52+
}
53+
54+
switch (inviteState.initialUserStatus) {
55+
case UserInviteStatus.NoCurrentTeam:
56+
case UserInviteStatus.AnotherTeamMember: {
57+
// Invite has been canceled. Remove the banner.
58+
if (cancelInviteMutation.isSuccess || cancelInviteMutation.isError) {
59+
return null
60+
}
61+
62+
switch (acceptInviteMutation.status) {
63+
case 'error': {
64+
return (
65+
<CodyAlert variant="error">
66+
<H1 as="p" className="mb-2">
67+
Issue with invite
68+
</H1>
69+
<Text className="mb-0">
70+
Accepting invite failed with error: {acceptInviteMutation.error.message}.
71+
</Text>
72+
</CodyAlert>
73+
)
74+
}
75+
case 'success': {
76+
return (
77+
<CodyAlert variant="greenCodyPro">
78+
<H1 as="p" className="mb-2">
79+
Pro team change complete!
80+
</H1>
81+
<Text>
82+
{inviteState.initialUserStatus === UserInviteStatus.NoCurrentTeam
83+
? 'You successfully joined the new Cody Pro team.'
84+
: 'Your pro team has been successfully changed.'}
85+
</Text>
86+
</CodyAlert>
87+
)
88+
}
89+
case 'idle':
90+
case 'pending':
91+
default: {
92+
return (
93+
<CodyAlert variant="purple">
94+
<H1 as="p" className="mb-2">
95+
Join new Cody Pro team?
96+
</H1>
97+
<Text>You've been invited to a new Cody Pro team by {inviteState.sentBy}.</Text>
98+
<Text>
99+
{inviteState.initialUserStatus === UserInviteStatus.NoCurrentTeam
100+
? 'You will get unlimited autocompletions, chat messages and commands.'
101+
: 'This will terminate your current Cody Pro plan, and place you on the new Cody Pro team. You will not lose access to your Cody Pro benefits.'}
102+
</Text>
103+
<div>
104+
<Button
105+
variant="primary"
106+
disabled={acceptInviteMutation.isPending || cancelInviteMutation.isPending}
107+
className="mr-3"
108+
onClick={() =>
109+
acceptInviteMutation.mutate(
110+
{ teamId, inviteId },
111+
{ onSuccess, onSettled: clearInviteParams }
112+
)
113+
}
114+
>
115+
Accept
116+
</Button>
117+
<Button
118+
variant="secondary"
119+
disabled={acceptInviteMutation.isPending || cancelInviteMutation.isPending}
120+
onClick={() =>
121+
cancelInviteMutation.mutate(
122+
{ teamId, inviteId },
123+
{ onSettled: clearInviteParams }
124+
)
125+
}
126+
>
127+
Decline
128+
</Button>
129+
</div>
130+
</CodyAlert>
131+
)
132+
}
133+
}
134+
}
135+
case UserInviteStatus.InvitedTeamMember: {
136+
if (cancelInviteMutation.isIdle) {
137+
void cancelInviteMutation.mutate({ teamId, inviteId }, { onSettled: clearInviteParams })
138+
}
139+
return (
140+
<CodyAlert variant="error">
141+
<H1 as="p" className="mb-2">
142+
Issue with invite
143+
</H1>
144+
<Text>You've been invited to a Cody Pro team by {inviteState.sentBy}.</Text>
145+
<Text className="mb-0">You cannot accept this invite as as you are already on this team.</Text>
146+
</CodyAlert>
147+
)
148+
}
149+
case UserInviteStatus.AnotherTeamSoleAdmin: {
150+
return (
151+
<CodyAlert variant="error">
152+
<H1 as="p" className="mb-2">
153+
Issue with invite
154+
</H1>
155+
<Text className="mb-0">You've been invited to a new Cody Pro team by {inviteState.sentBy}.</Text>
156+
<Text>
157+
To accept this invite you need to transfer your administrative role to another member of your
158+
team and click the invite link again.
159+
</Text>
160+
<div>
161+
<ButtonLink variant="primary" to={CodyProRoutes.ManageTeam}>
162+
Manage team
163+
</ButtonLink>
164+
</div>
165+
</CodyAlert>
166+
)
167+
}
168+
default: {
169+
return null
170+
}
171+
}
172+
}
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,12 @@
11
import React, { useEffect } from 'react'
22

3-
import classNames from 'classnames'
4-
import { useNavigate } from 'react-router-dom'
3+
import { Navigate, useLocation } from 'react-router-dom'
54

65
import type { TelemetryV2Props } from '@sourcegraph/shared/src/telemetry'
7-
import { PageHeader, Text, H3, useSearchParameters } from '@sourcegraph/wildcard'
86

97
import type { AuthenticatedUser } from '../../auth'
108
import { withAuthenticatedUser } from '../../auth/withAuthenticatedUser'
11-
import { Page } from '../../components/Page'
12-
import { PageTitle } from '../../components/PageTitle'
13-
import { CodyAlert } from '../components/CodyAlert'
14-
import { WhiteIcon } from '../components/WhiteIcon'
15-
import { requestSSC } from '../util'
9+
import { CodyProRoutes } from '../codyProRoutes'
1610

1711
interface CodyAcceptInvitePageProps extends TelemetryV2Props {
1812
authenticatedUser: AuthenticatedUser
@@ -21,71 +15,14 @@ interface CodyAcceptInvitePageProps extends TelemetryV2Props {
2115
const AuthenticatedCodyAcceptInvitePage: React.FunctionComponent<CodyAcceptInvitePageProps> = ({
2216
telemetryRecorder,
2317
}) => {
18+
const location = useLocation()
19+
2420
useEffect(() => {
2521
telemetryRecorder.recordEvent('cody.invites.accept', 'view')
2622
}, [telemetryRecorder])
2723

28-
const navigate = useNavigate()
29-
30-
// Process query params
31-
const parameters = useSearchParameters()
32-
const teamId = parameters.get('teamID')
33-
const inviteId = parameters.get('inviteID')
34-
const [loading, setLoading] = React.useState(true)
35-
const [errorMessage, setErrorMessage] = React.useState<string | null>(null)
36-
37-
useEffect(() => {
38-
async function postAcceptInvite(): Promise<void> {
39-
if (!teamId || !inviteId) {
40-
setErrorMessage('Invalid invite ID or team ID')
41-
setLoading(false)
42-
return
43-
}
44-
const response = await requestSSC(`/team/${teamId}/invites/${inviteId}/accept`, 'POST')
45-
setLoading(false)
46-
if (response.ok) {
47-
// Wait a second before navigating to the manage team page so that the user sees the success alert.
48-
await new Promise(resolve => setTimeout(resolve, 1000))
49-
navigate('/cody/manage?welcome=1')
50-
} else {
51-
setErrorMessage(await response.text())
52-
}
53-
}
54-
55-
void postAcceptInvite()
56-
}, [inviteId, navigate, teamId])
57-
58-
return (
59-
<Page className={classNames('d-flex flex-column')}>
60-
<PageTitle title="Manage Cody team" />
61-
<PageHeader className="mb-4 mt-4">
62-
<PageHeader.Heading as="h2" styleAs="h1">
63-
<div className="d-inline-flex align-items-center">
64-
<WhiteIcon name="mdi-account-multiple-plus-gradient" className="mr-3" />
65-
Join Cody Pro team
66-
</div>
67-
</PageHeader.Heading>
68-
</PageHeader>
69-
70-
{errorMessage ? (
71-
<CodyAlert variant="error">
72-
<H3>We couldn't accept the invite.</H3>
73-
<Text size="small" className="text-muted mb-0">
74-
{errorMessage}
75-
</Text>
76-
</CodyAlert>
77-
) : null}
78-
79-
{!loading && !errorMessage ? (
80-
<CodyAlert variant="greenSuccess">
81-
<H3>Invite accepted!</H3>
82-
<Text size="small" className="mb-0">
83-
You are now a member of the team. We'll now redirect you to the team page.
84-
</Text>
85-
</CodyAlert>
86-
) : null}
87-
</Page>
88-
)
24+
// navigate to the manage page and passthrough the search params
25+
return <Navigate to={CodyProRoutes.Manage + location.search} replace={true} />
8926
}
9027

9128
export const CodyAcceptInvitePage = withAuthenticatedUser(AuthenticatedCodyAcceptInvitePage)

0 commit comments

Comments
 (0)