diff --git a/docs/pr/pending-invite-choice.png b/docs/pr/pending-invite-choice.png new file mode 100644 index 0000000000..ddf1963b10 Binary files /dev/null and b/docs/pr/pending-invite-choice.png differ diff --git a/messages/en.json b/messages/en.json index a978cb2da9..61e7f9c375 100644 --- a/messages/en.json +++ b/messages/en.json @@ -1504,6 +1504,25 @@ "personal-information": "Personal Information", "picture-delete-fail": "Cannot delete app picture, please check console log", "picture-uploaded": "Picture uploaded successfully", + "pending-invite-badge": "Organization invitation", + "pending-invite-card-copy": "This invitation matches the email on your Capgo account.", + "pending-invite-create-org": "Decline all and continue", + "pending-invite-decline": "Decline", + "pending-invite-decline-failed": "Could not decline the invitation", + "pending-invite-declined": "Invitation declined", + "pending-invite-join": "Join organization", + "pending-invite-join-failed": "Could not join the organization", + "pending-invite-joined": "Organization joined", + "pending-invite-load-failed": "Could not load your organization invitation", + "pending-invite-logo-alt": "{name} logo", + "pending-invite-subtitle": "Review this invitation before continuing. You can join the organization or decline it.", + "pending-invite-subtitle-multiple": "Review these invitations before continuing. You can join or decline each organization.", + "pending-invite-summary": "Next step", + "pending-invite-summary-create": "Decline invitations that are not for you, then continue.", + "pending-invite-summary-join": "Accept an invite to add that organization to your account.", + "pending-invite-summary-title": "Choose where this account belongs", + "pending-invite-title": "Join your organization", + "pending-invite-title-multiple": "Choose an organization", "plan": "Plan", "plan-bandwidth": "GB Bandwidth", "plan-desc": "Start building for free, then add a plan to go live.", diff --git a/src/modules/auth.ts b/src/modules/auth.ts index 070de284e6..024c277178 100644 --- a/src/modules/auth.ts +++ b/src/modules/auth.ts @@ -204,11 +204,21 @@ async function guard( function shouldRedirectToOrgOnboarding() { if (to.path.startsWith('/onboarding/organization')) return false + if (to.path.startsWith('/onboarding/invitation')) + return false if (!inviteOrgId) return true return !organizationStore.organizations.some(org => org.gid === inviteOrgId && org.role.startsWith('invite')) } + function shouldRedirectToPendingInviteOnboarding(organizationsLoaded: boolean) { + if (!organizationsLoaded) + return false + if (to.path.startsWith('/onboarding/invitation')) + return false + return organizationStore.organizations.some(org => org.role.startsWith('invite')) + } + if (hasAuth && sessionUser) { const authConfirmedAt = main.auth?.email_confirmed_at if (!main.auth || main.auth.id !== sessionUser.id || authConfirmedAt !== sessionUser.email_confirmed_at) { @@ -266,6 +276,15 @@ async function guard( } const organizationsLoaded = await tryLoadOrganizations(() => organizationStore.fetchOrganizations()) + if (shouldRedirectToPendingInviteOnboarding(organizationsLoaded)) { + return next({ + path: '/onboarding/invitation', + query: { + to: to.path.startsWith('/onboarding/') ? '/dashboard' : to.fullPath, + }, + }) + } + if (organizationsLoaded && isAdminRoute) { try { main.isAdmin = await isPlatformAdmin() @@ -332,6 +351,15 @@ async function guard( } let organizationsLoaded = await tryLoadOrganizations(() => organizationStore.dedupFetchOrganizations()) + if (shouldRedirectToPendingInviteOnboarding(organizationsLoaded)) { + return next({ + path: '/onboarding/invitation', + query: { + to: to.path.startsWith('/onboarding/') ? '/dashboard' : to.fullPath, + }, + }) + } + if (organizationsLoaded && !organizationStore.hasOrganizations && isSsoUser(sessionUser)) { const didProvisionSsoMembership = await maybeProvisionSsoMembership(supabase, sessionData?.session ?? null) if (didProvisionSsoMembership === 'redirect_login') { diff --git a/src/pages/onboarding/invitation.vue b/src/pages/onboarding/invitation.vue new file mode 100644 index 0000000000..02b64973ce --- /dev/null +++ b/src/pages/onboarding/invitation.vue @@ -0,0 +1,322 @@ + + + + + +meta: + middleware: auth + diff --git a/src/pages/register.vue b/src/pages/register.vue index 3b24485841..7a4dfd0662 100644 --- a/src/pages/register.vue +++ b/src/pages/register.vue @@ -73,7 +73,7 @@ async function submit(form: { first_name: string, last_name: string, password: s console.error('Failed to seed user profile after signup', profileError) } - router.push('/onboarding/organization') + router.push('/dashboard') } diff --git a/src/route-map.d.ts b/src/route-map.d.ts index f2e42a3b3c..e1aca953d9 100644 --- a/src/route-map.d.ts +++ b/src/route-map.d.ts @@ -352,6 +352,13 @@ declare module 'vue-router/auto-routes' { Record, | never >, + '/onboarding/invitation': RouteRecordInfo< + '/onboarding/invitation', + '/onboarding/invitation', + Record, + Record, + | never + >, '/onboarding/organization': RouteRecordInfo< '/onboarding/organization', '/onboarding/organization', @@ -816,6 +823,12 @@ declare module 'vue-router/auto-routes' { views: | never } + 'src/pages/onboarding/invitation.vue': { + routes: + | '/onboarding/invitation' + views: + | never + } 'src/pages/onboarding/organization.vue': { routes: | '/onboarding/organization'