mirror of
https://github.com/supabase/supabase.git
synced 2026-10-06 01:45:10 +03:00
fix(studio): clean up onboarding returnTo paths (#49283)
## What kind of change does this PR introduce? Bug fix. Follow-up to #41041 and DEPR-318. ## What is the current behavior? Marketing "Start your project" links go to `/dashboard`, which redirects unauthenticated users to `/org` and sets `returnTo=/org`. That value survives when they switch from sign-in to sign-up, so email verification still lands on the org list instead of org creation. ## What is the new behavior? - Sign-in's **Sign up** link rewrites `returnTo=/org` (and `/organizations`) to `/new` - Docs mobile menu, www homepage/product CTAs, and solution page CTAs link to `/dashboard/sign-up` for guests - Signed-in visitors get the dashboard URL instead, so they never hit the sign-up form - Shared `DASHBOARD_SIGN_UP_URL` / `getDashboardCtaHref` helpers for www Stacked on #41041. ## To test Stacked on #41041. The studio preview below includes both PRs. www and docs have their own previews. ### Studio: sign-in → sign-up rewrite Using the [studio-staging preview](https://studio-staging-git-dnywh-fixonboarding-return-to-supabase.vercel.app/) from Vercel checks: 1. Open the preview while logged out. It should land on `/dashboard/sign-in?returnTo=%2Forg` 2. Click **Sign up**. Expect the URL to include `returnTo=%2Fnew` ### Optional: www CTAs Using the [www preview](https://zone-www-dot-com-git-dnywh-fixonboarding-return-to-supabase.vercel.app/): 3. Logged out: homepage, product, or solutions **Start your project** should go to `/dashboard/sign-up` 4. Logged in: the same CTAs should go to `/dashboard` (not sign-up) (thought this will be hard if not impossible to test on staging) ### Optional: docs CTAs Using the [docs preview](https://docs-git-dnywh-fixonboarding-return-to-supabase.vercel.app/): 5. On mobile nav while logged out, click **Start your project**. Expect `/dashboard/sign-up` ### Compare on supabase.green Optional. Just to show what happens currently on `master`: 6. Open **supabase.green** while logged out, then click **Sign up** from `/dashboard/sign-in?returnTo=%2Forg`. `returnTo` should stay as `/org` <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Start-project and sign-up links now direct visitors to registration while signed-in users continue to reach the dashboard. * Homepage, product, solution, and mobile navigation CTAs now provide consistent authentication-aware destinations. * Sign-up links preserve return destinations and existing navigation parameters. * **Bug Fixes** * Corrected mobile navigation and marketing CTA links that previously sent visitors to the dashboard root instead of the appropriate sign-up flow. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
1 parent
ee931e49a1
commit
fdf33e72c4
32 files changed
+215
-115
No files matched your search
@@ -168,7 +168,9 @@ const GlobalMobileMenu = ({ open, setOpen }: Props) => {
|
||||
<Link href="https://supabase.com/dashboard/sign-in">Sign in</Link>
|
||||
</Button>
|
||||
<Button block size="medium" asChild>
|
||||
<Link href="https://supabase.com/dashboard/new">Start your project</Link>
|
||||
<Link href="https://supabase.com/dashboard/sign-up">
|
||||
Start your project
|
||||
</Link>
|
||||
</Button>
|
||||
</>
|
||||
)}
|
||||
|
||||
@@ -16,6 +16,7 @@ import { useInboundBranding } from '@/hooks/misc/useInboundBranding'
|
||||
import { useIsFeatureEnabled } from '@/hooks/misc/useIsFeatureEnabled'
|
||||
import { IS_PLATFORM } from '@/lib/constants'
|
||||
import type { ExternalIdentityProviderConfig } from '@/lib/external-identity-providers'
|
||||
import { getSignUpReturnTo } from '@/lib/gotrue'
|
||||
import type { NextPageWithLayout } from '@/types'
|
||||
|
||||
const SignInPage: NextPageWithLayout = () => {
|
||||
@@ -131,7 +132,10 @@ const SignInPage: NextPageWithLayout = () => {
|
||||
<div className="self-center my-8 text-sm">
|
||||
<span className="text-foreground-light">Don’t have an account?</span>{' '}
|
||||
<Link
|
||||
href={{ pathname: '/sign-up', query: router.query }}
|
||||
href={{
|
||||
pathname: '/sign-up',
|
||||
query: { ...router.query, returnTo: getSignUpReturnTo(router.query.returnTo) },
|
||||
}}
|
||||
className="underline transition text-foreground hover:text-foreground-light"
|
||||
>
|
||||
Sign up
|
||||
|
||||
@@ -1,10 +1,14 @@
|
||||
'use client'
|
||||
|
||||
import { useSendTelemetryEvent } from '~/lib/telemetry'
|
||||
import { useIsLoggedIn } from 'common'
|
||||
import Link from 'next/link'
|
||||
import { Button } from 'ui'
|
||||
|
||||
import { getDashboardCtaHref } from '@/lib/dashboard-links'
|
||||
|
||||
export function CTASection() {
|
||||
const isLoggedIn = useIsLoggedIn()
|
||||
const sendTelemetryEvent = useSendTelemetryEvent()
|
||||
|
||||
return (
|
||||
@@ -17,7 +21,7 @@ export function CTASection() {
|
||||
<div className="flex items-center gap-2">
|
||||
<Button asChild size="medium">
|
||||
<Link
|
||||
href="https://supabase.com/dashboard"
|
||||
href={getDashboardCtaHref(isLoggedIn)}
|
||||
onClick={() =>
|
||||
sendTelemetryEvent({
|
||||
action: 'start_project_button_clicked',
|
||||
|
||||
@@ -1,12 +1,15 @@
|
||||
'use client'
|
||||
|
||||
import { useSendTelemetryEvent } from '~/lib/telemetry'
|
||||
import { useIsLoggedIn } from 'common'
|
||||
import Link from 'next/link'
|
||||
import { Button } from 'ui'
|
||||
|
||||
import SectionContainer from '@/components/Layouts/SectionContainer'
|
||||
import { getDashboardCtaHref } from '@/lib/dashboard-links'
|
||||
|
||||
export function Hero() {
|
||||
const isLoggedIn = useIsLoggedIn()
|
||||
const sendTelemetryEvent = useSendTelemetryEvent()
|
||||
|
||||
return (
|
||||
@@ -25,7 +28,7 @@ export function Hero() {
|
||||
<div className="flex items-center gap-2">
|
||||
<Button asChild size="medium">
|
||||
<Link
|
||||
href="https://supabase.com/dashboard"
|
||||
href={getDashboardCtaHref(isLoggedIn)}
|
||||
onClick={() =>
|
||||
sendTelemetryEvent({
|
||||
action: 'start_project_button_clicked',
|
||||
|
||||
@@ -2,6 +2,8 @@ import SectionContainerWithCn from '~/components/Layouts/SectionContainerWithCn'
|
||||
import Link from 'next/link'
|
||||
import { Button } from 'ui'
|
||||
|
||||
import { StartYourProjectButton } from '@/components/StartYourProjectButton'
|
||||
|
||||
export function Hero() {
|
||||
return (
|
||||
<SectionContainerWithCn>
|
||||
@@ -17,9 +19,7 @@ export function Hero() {
|
||||
</p>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<Button asChild size="medium">
|
||||
<Link href="https://supabase.com/dashboard">Start your project</Link>
|
||||
</Button>
|
||||
<StartYourProjectButton />
|
||||
<Button asChild size="medium" variant="default">
|
||||
<Link href="/docs/guides/auth">Documentation</Link>
|
||||
</Button>
|
||||
|
||||
@@ -2,6 +2,8 @@ import SectionContainerWithCn from '~/components/Layouts/SectionContainerWithCn'
|
||||
import Link from 'next/link'
|
||||
import { Button } from 'ui'
|
||||
|
||||
import { StartYourProjectButton } from '@/components/StartYourProjectButton'
|
||||
|
||||
export function Hero() {
|
||||
return (
|
||||
<SectionContainerWithCn>
|
||||
@@ -17,9 +19,7 @@ export function Hero() {
|
||||
</p>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<Button asChild size="medium">
|
||||
<Link href="https://supabase.com/dashboard">Start your project</Link>
|
||||
</Button>
|
||||
<StartYourProjectButton />
|
||||
<Button asChild size="medium" variant="default">
|
||||
<Link href="/docs/guides/database/overview">Documentation</Link>
|
||||
</Button>
|
||||
|
||||
@@ -6,6 +6,8 @@ import Link from 'next/link'
|
||||
import { useEffect, useRef, useState } from 'react'
|
||||
import { Button } from 'ui'
|
||||
|
||||
import { StartYourProjectButton } from '@/components/StartYourProjectButton'
|
||||
|
||||
const TERMINAL_LINES = [
|
||||
{ text: '$ supabase functions new hello-world', delay: 0 },
|
||||
{ text: 'Created function: supabase/functions/hello-world/index.ts', delay: 0.3, dim: true },
|
||||
@@ -80,9 +82,7 @@ export function CTASection() {
|
||||
<TerminalAnimation />
|
||||
|
||||
<div className="flex items-center gap-2">
|
||||
<Button asChild size="medium">
|
||||
<Link href="https://supabase.com/dashboard">Start your project</Link>
|
||||
</Button>
|
||||
<StartYourProjectButton />
|
||||
<Button asChild size="medium" variant="default">
|
||||
<Link href="/docs/guides/functions/quickstart">Quickstart guide</Link>
|
||||
</Button>
|
||||
|
||||
@@ -3,6 +3,7 @@ import Link from 'next/link'
|
||||
import { Button } from 'ui'
|
||||
|
||||
import { Highlights } from './Highlights'
|
||||
import { StartYourProjectButton } from '@/components/StartYourProjectButton'
|
||||
|
||||
export function Hero() {
|
||||
return (
|
||||
@@ -19,9 +20,7 @@ export function Hero() {
|
||||
</p>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<Button asChild size="medium">
|
||||
<Link href="https://supabase.com/dashboard">Start your project</Link>
|
||||
</Button>
|
||||
<StartYourProjectButton />
|
||||
<Button asChild size="medium" variant="default">
|
||||
<Link href="/docs/guides/functions">Documentation</Link>
|
||||
</Button>
|
||||
|
||||
@@ -2,6 +2,8 @@ import SectionContainerWithCn from '~/components/Layouts/SectionContainerWithCn'
|
||||
import Link from 'next/link'
|
||||
import { Button } from 'ui'
|
||||
|
||||
import { StartYourProjectButton } from '@/components/StartYourProjectButton'
|
||||
|
||||
export function Hero() {
|
||||
return (
|
||||
<SectionContainerWithCn>
|
||||
@@ -17,9 +19,7 @@ export function Hero() {
|
||||
</p>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<Button asChild size="medium">
|
||||
<Link href="https://supabase.com/dashboard">Start your project</Link>
|
||||
</Button>
|
||||
<StartYourProjectButton />
|
||||
<Button asChild size="medium" variant="default">
|
||||
<Link href="/docs/guides/realtime">Documentation</Link>
|
||||
</Button>
|
||||
|
||||
@@ -2,6 +2,8 @@ import SectionContainerWithCn from '~/components/Layouts/SectionContainerWithCn'
|
||||
import Link from 'next/link'
|
||||
import { Button } from 'ui'
|
||||
|
||||
import { StartYourProjectButton } from '@/components/StartYourProjectButton'
|
||||
|
||||
export function Hero() {
|
||||
return (
|
||||
<SectionContainerWithCn>
|
||||
@@ -17,9 +19,7 @@ export function Hero() {
|
||||
</p>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<Button asChild size="medium">
|
||||
<Link href="https://supabase.com/dashboard">Start your project</Link>
|
||||
</Button>
|
||||
<StartYourProjectButton />
|
||||
<Button asChild size="medium" variant="default">
|
||||
<Link href="/docs/guides/storage">Documentation</Link>
|
||||
</Button>
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
'use client'
|
||||
|
||||
import { useSendTelemetryEvent } from '~/lib/telemetry'
|
||||
import { useIsLoggedIn } from 'common'
|
||||
import Link from 'next/link'
|
||||
import { Button, cn } from 'ui'
|
||||
import { useSendTelemetryEvent } from '~/lib/telemetry'
|
||||
|
||||
import { getDashboardCtaHref } from '@/lib/dashboard-links'
|
||||
|
||||
interface Props {
|
||||
className?: string
|
||||
@@ -10,6 +13,7 @@ interface Props {
|
||||
}
|
||||
|
||||
const CTABanner = ({ darkerBg, className }: Props) => {
|
||||
const isLoggedIn = useIsLoggedIn()
|
||||
const sendTelemetryEvent = useSendTelemetryEvent()
|
||||
return (
|
||||
<div
|
||||
@@ -28,7 +32,7 @@ const CTABanner = ({ darkerBg, className }: Props) => {
|
||||
<div className="flex items-center justify-center gap-2 col-span-12 mt-4">
|
||||
<Button asChild size="medium">
|
||||
<Link
|
||||
href="https://supabase.com/dashboard"
|
||||
href={getDashboardCtaHref(isLoggedIn)}
|
||||
onClick={() =>
|
||||
sendTelemetryEvent({
|
||||
action: 'start_project_button_clicked',
|
||||
|
||||
@@ -1,10 +1,14 @@
|
||||
'use client'
|
||||
|
||||
import { useSendTelemetryEvent } from '~/lib/telemetry'
|
||||
import { useIsLoggedIn } from 'common'
|
||||
import Link from 'next/link'
|
||||
import { Button } from 'ui'
|
||||
|
||||
import { getDashboardCtaHref } from '@/lib/dashboard-links'
|
||||
|
||||
export function CTASection() {
|
||||
const isLoggedIn = useIsLoggedIn()
|
||||
const sendTelemetryEvent = useSendTelemetryEvent()
|
||||
|
||||
return (
|
||||
@@ -17,7 +21,7 @@ export function CTASection() {
|
||||
<div className="flex items-center gap-2">
|
||||
<Button asChild size="medium">
|
||||
<Link
|
||||
href="https://supabase.com/dashboard"
|
||||
href={getDashboardCtaHref(isLoggedIn)}
|
||||
onClick={() =>
|
||||
sendTelemetryEvent({
|
||||
action: 'start_project_button_clicked',
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
import { useSendTelemetryEvent } from '~/lib/telemetry'
|
||||
import { useIsLoggedIn } from 'common'
|
||||
import { BookOpen } from 'lucide-react'
|
||||
import Link from 'next/link'
|
||||
import { Button } from 'ui'
|
||||
|
||||
import ProductIcon from '../ProductIcon'
|
||||
import { getDashboardCtaHref } from '@/lib/dashboard-links'
|
||||
|
||||
type subheader = string
|
||||
interface Types {
|
||||
@@ -18,6 +20,7 @@ interface Types {
|
||||
}
|
||||
|
||||
const ProductHeader = (props: Types) => {
|
||||
const isLoggedIn = useIsLoggedIn()
|
||||
const sendTelemetryEvent = useSendTelemetryEvent()
|
||||
return (
|
||||
<div className="section-container relative pt-16 pb-0 lg:pt-28">
|
||||
@@ -52,8 +55,7 @@ const ProductHeader = (props: Types) => {
|
||||
<div className="flex flex-row md:flex-row md:items-center">
|
||||
<Button asChild size="medium">
|
||||
<Link
|
||||
href="https://supabase.com/dashboard"
|
||||
as="https://supabase.com/dashboard"
|
||||
href={getDashboardCtaHref(isLoggedIn)}
|
||||
onClick={() =>
|
||||
sendTelemetryEvent({
|
||||
action: 'start_project_button_clicked',
|
||||
|
||||
@@ -1,10 +1,14 @@
|
||||
import { CTA } from '~/types/common'
|
||||
'use client'
|
||||
|
||||
import type { CTA } from '~/types/common'
|
||||
import { useIsLoggedIn } from 'common'
|
||||
import Link from 'next/link'
|
||||
import React, { Children, type ReactNode } from 'react'
|
||||
import { Button, cn } from 'ui'
|
||||
|
||||
import SectionContainerWithCn from '../Layouts/SectionContainerWithCn'
|
||||
import ProductIcon from '../ProductIcon'
|
||||
import { resolveDashboardCtaHref } from '@/lib/dashboard-links'
|
||||
|
||||
// to do: move types to be global
|
||||
// then solutions.types.ts should extend this
|
||||
@@ -22,83 +26,90 @@ interface Props {
|
||||
sectionContainerClassName?: string
|
||||
}
|
||||
|
||||
const ProductHeader = ({ footerPosition = 'left', ...props }: Props) => (
|
||||
<div
|
||||
className={cn(
|
||||
'w-full max-w-full relative mx-auto py-16 lg:py-24 border-b bg-alternative overflow-hidden',
|
||||
props.className
|
||||
)}
|
||||
>
|
||||
<SectionContainerWithCn
|
||||
height="none"
|
||||
className={cn('grid grid-cols-12', props.sectionContainerClassName)}
|
||||
const ProductHeader = ({ footerPosition = 'left', ...props }: Props) => {
|
||||
const isLoggedIn = useIsLoggedIn()
|
||||
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
'w-full max-w-full relative mx-auto py-16 lg:py-24 border-b bg-alternative overflow-hidden',
|
||||
props.className
|
||||
)}
|
||||
>
|
||||
<div
|
||||
className={cn(
|
||||
'relative z-10 col-span-12 lg:col-span-5',
|
||||
// if not image is present, center the content
|
||||
!props.image &&
|
||||
'lg:col-span-12 lg:mx-auto lg:max-w-2xl text-center flex flex-col items-center'
|
||||
)}
|
||||
<SectionContainerWithCn
|
||||
height="none"
|
||||
className={cn('grid grid-cols-12', props.sectionContainerClassName)}
|
||||
>
|
||||
{(props.icon || props.title) && (
|
||||
<div className="mb-4 flex items-center gap-3">
|
||||
{props.icon && <ProductIcon icon={props.icon} />}
|
||||
{props.title && (
|
||||
<span
|
||||
className="text-brand-600 dark:text-brand font-mono uppercase"
|
||||
key={`product-name-${props.title}`}
|
||||
>
|
||||
{props.title}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
<h1 className="h1 text-3xl md:text-4xl! lg:text-4xl! 2xl:text-6xl! tracking-[-.15px] text-balance">
|
||||
{props.h1}
|
||||
</h1>
|
||||
{props.subheader && (
|
||||
<div className="">
|
||||
{Children.map(props.subheader, (subheader, i) => {
|
||||
<div
|
||||
className={cn(
|
||||
'relative z-10 col-span-12 lg:col-span-5',
|
||||
// if not image is present, center the content
|
||||
!props.image &&
|
||||
'lg:col-span-12 lg:mx-auto lg:max-w-2xl text-center flex flex-col items-center'
|
||||
)}
|
||||
>
|
||||
{(props.icon || props.title) && (
|
||||
<div className="mb-4 flex items-center gap-3">
|
||||
{props.icon && <ProductIcon icon={props.icon} />}
|
||||
{props.title && (
|
||||
<span
|
||||
className="text-brand-600 dark:text-brand font-mono uppercase"
|
||||
key={`product-name-${props.title}`}
|
||||
>
|
||||
{props.title}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
<h1 className="h1 text-3xl md:text-4xl! lg:text-4xl! 2xl:text-6xl! tracking-[-.15px] text-balance">
|
||||
{props.h1}
|
||||
</h1>
|
||||
{props.subheader && (
|
||||
<div className="">
|
||||
{Children.map(props.subheader, (subheader, i) => {
|
||||
return (
|
||||
<p className="p lg:text-lg max-w-lg lg:max-w-none text-pretty" key={i}>
|
||||
{subheader}
|
||||
</p>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
)}
|
||||
<div className="flex flex-row md:flex-row md:items-center gap-2 mt-4">
|
||||
{props.ctas?.map((cta) => {
|
||||
const href = resolveDashboardCtaHref(cta.href, isLoggedIn)
|
||||
return (
|
||||
<p className="p lg:text-lg max-w-lg lg:max-w-none text-pretty" key={i}>
|
||||
{subheader}
|
||||
</p>
|
||||
<Button
|
||||
key={cta.href}
|
||||
size="medium"
|
||||
variant={cta.type ?? 'default'}
|
||||
onClick={cta.onClick}
|
||||
asChild
|
||||
>
|
||||
<Link href={href}>{cta.label ?? 'Start for free'}</Link>
|
||||
</Button>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
)}
|
||||
<div className="flex flex-row md:flex-row md:items-center gap-2 mt-4">
|
||||
{props.ctas?.map((cta) => (
|
||||
<Button
|
||||
key={cta.href}
|
||||
size="medium"
|
||||
variant={cta.type ?? 'default'}
|
||||
onClick={cta.onClick}
|
||||
asChild
|
||||
>
|
||||
<Link href={cta.href}>{cta.label ?? 'Start for free'}</Link>
|
||||
</Button>
|
||||
))}
|
||||
{props.footer && footerPosition === 'left' && (
|
||||
<div className="ph-footer relative z-10 mt-4 md:mt-8 lg:mt-20 xl:mt-32 col-span-12">
|
||||
{props.footer}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
{props.footer && footerPosition === 'left' && (
|
||||
<div className="ph-footer relative z-10 mt-4 md:mt-8 lg:mt-20 xl:mt-32 col-span-12">
|
||||
{props.image && (
|
||||
<div className="image-container relative min-h-[300px] col-span-12 mt-8 lg:col-span-7 lg:mt-0 xl:col-span-6 xl:col-start-7">
|
||||
{props.image}
|
||||
</div>
|
||||
)}
|
||||
{props.footer && footerPosition !== 'left' && (
|
||||
<div className="relative z-10 mt-4 md:mt-8 lg:mt-20 xl:mt-32 col-span-12">
|
||||
{props.footer}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
{props.image && (
|
||||
<div className="image-container relative min-h-[300px] col-span-12 mt-8 lg:col-span-7 lg:mt-0 xl:col-span-6 xl:col-start-7">
|
||||
{props.image}
|
||||
</div>
|
||||
)}
|
||||
{props.footer && footerPosition !== 'left' && (
|
||||
<div className="relative z-10 mt-4 md:mt-8 lg:mt-20 xl:mt-32 col-span-12">
|
||||
{props.footer}
|
||||
</div>
|
||||
)}
|
||||
</SectionContainerWithCn>
|
||||
</div>
|
||||
)
|
||||
</SectionContainerWithCn>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default ProductHeader
|
||||
@@ -5,6 +5,8 @@ import Link from 'next/link'
|
||||
import React from 'react'
|
||||
import { Button, cn } from 'ui'
|
||||
|
||||
import { StartYourProjectButton } from '@/components/StartYourProjectButton'
|
||||
|
||||
export type Products = PRODUCT_SHORTNAMES
|
||||
|
||||
interface Props {
|
||||
@@ -26,9 +28,9 @@ function ProductsCta(props: Props) {
|
||||
<div className="flex flex-col col-span-1 text-center xl:text-left xl:justify-center items-center xl:items-start">
|
||||
<h2 className="h2 w-max">Ready to start building?</h2>
|
||||
<div className="flex gap-2 py-2">
|
||||
<Button asChild variant="primary" size="small" className="h-full">
|
||||
<Link href="https://supabase.com/dashboard">Start for free</Link>
|
||||
</Button>
|
||||
<StartYourProjectButton size="small" className="h-full" variant="primary">
|
||||
Start for free
|
||||
</StartYourProjectButton>
|
||||
<Button asChild variant="default" size="small">
|
||||
<Link href="https://forms.supabase.com/enterprise">Contact Enterprise</Link>
|
||||
</Button>
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
'use client'
|
||||
|
||||
import { useIsLoggedIn } from 'common'
|
||||
import Link from 'next/link'
|
||||
import type { ComponentProps } from 'react'
|
||||
import { Button } from 'ui'
|
||||
|
||||
import { getDashboardCtaHref } from '@/lib/dashboard-links'
|
||||
|
||||
type StartYourProjectButtonProps = {
|
||||
children?: React.ReactNode
|
||||
onClick?: ComponentProps<typeof Link>['onClick']
|
||||
size?: ComponentProps<typeof Button>['size']
|
||||
variant?: ComponentProps<typeof Button>['variant']
|
||||
className?: string
|
||||
}
|
||||
|
||||
export function StartYourProjectButton({
|
||||
children = 'Start your project',
|
||||
onClick,
|
||||
size = 'medium',
|
||||
variant,
|
||||
className,
|
||||
}: StartYourProjectButtonProps) {
|
||||
const isLoggedIn = useIsLoggedIn()
|
||||
|
||||
return (
|
||||
<Button asChild size={size} variant={variant} className={className}>
|
||||
<Link href={getDashboardCtaHref(isLoggedIn)} onClick={onClick}>
|
||||
{children}
|
||||
</Link>
|
||||
</Button>
|
||||
)
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
import { CubeIcon } from '@heroicons/react/outline'
|
||||
import { DASHBOARD_SIGN_UP_URL } from '@/lib/dashboard-links'
|
||||
import {
|
||||
ArrowLeftRight,
|
||||
Check,
|
||||
@@ -85,7 +86,7 @@ const data: {
|
||||
ctas: [
|
||||
{
|
||||
label: 'Start your project',
|
||||
href: 'https://supabase.com/dashboard',
|
||||
href: DASHBOARD_SIGN_UP_URL,
|
||||
type: 'primary' as const,
|
||||
},
|
||||
{
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { CubeIcon } from '@heroicons/react/outline'
|
||||
import { DASHBOARD_SIGN_UP_URL } from '@/lib/dashboard-links'
|
||||
import {
|
||||
Check,
|
||||
ClipboardCheck,
|
||||
@@ -91,7 +92,7 @@ const data: () => {
|
||||
ctas: [
|
||||
{
|
||||
label: 'Start your project',
|
||||
href: 'https://supabase.com/dashboard',
|
||||
href: DASHBOARD_SIGN_UP_URL,
|
||||
type: 'primary' as any,
|
||||
onClick: () =>
|
||||
sendTelemetryEvent({
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { CubeIcon } from '@heroicons/react/outline'
|
||||
import { DASHBOARD_SIGN_UP_URL } from '@/lib/dashboard-links'
|
||||
import { Timer } from 'lucide-react'
|
||||
|
||||
import type {
|
||||
@@ -39,7 +40,7 @@ const data: {
|
||||
ctas: [
|
||||
{
|
||||
label: 'Start your project',
|
||||
href: 'https://supabase.com/dashboard',
|
||||
href: DASHBOARD_SIGN_UP_URL,
|
||||
type: 'primary' as any,
|
||||
},
|
||||
{
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { CubeIcon } from '@heroicons/react/outline'
|
||||
import { DASHBOARD_SIGN_UP_URL } from '@/lib/dashboard-links'
|
||||
import { useBreakpoint } from 'common'
|
||||
import {
|
||||
Check,
|
||||
@@ -91,7 +92,7 @@ const data: () => {
|
||||
ctas: [
|
||||
{
|
||||
label: 'Start your project',
|
||||
href: 'https://supabase.com/dashboard',
|
||||
href: DASHBOARD_SIGN_UP_URL,
|
||||
type: 'primary' as any,
|
||||
onClick: () =>
|
||||
sendTelemetryEvent({
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { CubeIcon } from '@heroicons/react/outline'
|
||||
import { DASHBOARD_SIGN_UP_URL } from '@/lib/dashboard-links'
|
||||
import { useBreakpoint } from 'common'
|
||||
import { Check, Sparkles, Timer } from 'lucide-react'
|
||||
import dynamic from 'next/dynamic'
|
||||
@@ -69,7 +70,7 @@ const data: () => {
|
||||
ctas: [
|
||||
{
|
||||
label: 'Start your project',
|
||||
href: 'https://supabase.com/dashboard',
|
||||
href: DASHBOARD_SIGN_UP_URL,
|
||||
type: 'primary' as any,
|
||||
onClick: () =>
|
||||
sendTelemetryEvent({
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { CubeIcon } from '@heroicons/react/outline'
|
||||
import { DASHBOARD_SIGN_UP_URL } from '@/lib/dashboard-links'
|
||||
import { useBreakpoint } from 'common'
|
||||
import {
|
||||
Check,
|
||||
@@ -85,7 +86,7 @@ const data: () => {
|
||||
ctas: [
|
||||
{
|
||||
label: 'Start your project',
|
||||
href: 'https://supabase.com/dashboard',
|
||||
href: DASHBOARD_SIGN_UP_URL,
|
||||
type: 'primary' as any,
|
||||
onClick: () =>
|
||||
sendTelemetryEvent({
|
||||
|
||||
@@ -11,6 +11,7 @@ import {
|
||||
Users,
|
||||
UserX,
|
||||
} from 'lucide-react'
|
||||
import { DASHBOARD_SIGN_UP_URL } from '@/lib/dashboard-links'
|
||||
import { PRODUCT_SHORTNAMES } from 'shared-data/products'
|
||||
import { Image } from 'ui-patterns/Image'
|
||||
|
||||
@@ -90,7 +91,7 @@ const data: () => {
|
||||
ctas: [
|
||||
{
|
||||
label: 'Start your project',
|
||||
href: 'https://supabase.com/dashboard',
|
||||
href: DASHBOARD_SIGN_UP_URL,
|
||||
type: 'primary' as any,
|
||||
onClick: () =>
|
||||
sendTelemetryEvent({
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { CubeIcon } from '@heroicons/react/outline'
|
||||
import { DASHBOARD_SIGN_UP_URL } from '@/lib/dashboard-links'
|
||||
import { useBreakpoint } from 'common'
|
||||
import { Check, InfoIcon, Sparkles, Timer } from 'lucide-react'
|
||||
import dynamic from 'next/dynamic'
|
||||
@@ -65,7 +66,7 @@ const data: () => {
|
||||
ctas: [
|
||||
{
|
||||
label: 'Start your project',
|
||||
href: 'https://supabase.com/dashboard',
|
||||
href: DASHBOARD_SIGN_UP_URL,
|
||||
type: 'primary' as any,
|
||||
onClick: () =>
|
||||
sendTelemetryEvent({
|
||||
|
||||
@@ -10,6 +10,7 @@ import {
|
||||
Users,
|
||||
UserX,
|
||||
} from 'lucide-react'
|
||||
import { DASHBOARD_SIGN_UP_URL } from '@/lib/dashboard-links'
|
||||
import { Image } from 'ui-patterns/Image'
|
||||
|
||||
import { getSharedSections } from './shared-sections'
|
||||
@@ -89,7 +90,7 @@ const data: () => {
|
||||
ctas: [
|
||||
{
|
||||
label: 'Start your project',
|
||||
href: 'https://supabase.com/dashboard',
|
||||
href: DASHBOARD_SIGN_UP_URL,
|
||||
type: 'primary' as any,
|
||||
onClick: () =>
|
||||
sendTelemetryEvent({
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { useBreakpoint } from 'common'
|
||||
import { DASHBOARD_SIGN_UP_URL } from '@/lib/dashboard-links'
|
||||
import {
|
||||
Activity,
|
||||
Blocks,
|
||||
@@ -90,7 +91,7 @@ const data: () => {
|
||||
ctas: [
|
||||
{
|
||||
label: 'Start your project',
|
||||
href: 'https://supabase.com/dashboard',
|
||||
href: DASHBOARD_SIGN_UP_URL,
|
||||
type: 'primary' as any,
|
||||
onClick: () =>
|
||||
sendTelemetryEvent({
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { CubeIcon } from '@heroicons/react/outline'
|
||||
import { DASHBOARD_SIGN_UP_URL } from '@/lib/dashboard-links'
|
||||
import { useBreakpoint } from 'common'
|
||||
import {
|
||||
ArrowLeftRight,
|
||||
@@ -134,7 +135,7 @@ const data: () => {
|
||||
ctas: [
|
||||
{
|
||||
label: 'Start Building',
|
||||
href: 'https://supabase.com/dashboard',
|
||||
href: DASHBOARD_SIGN_UP_URL,
|
||||
type: 'primary' as any,
|
||||
onClick: () =>
|
||||
sendTelemetryEvent({
|
||||
@@ -751,7 +752,7 @@ const data: () => {
|
||||
description: 'Start free with any AI builder + Supabase',
|
||||
cta: {
|
||||
label: 'Create Your First App in 5 Minutes',
|
||||
href: 'https://supabase.com/dashboard',
|
||||
href: DASHBOARD_SIGN_UP_URL,
|
||||
},
|
||||
},
|
||||
{
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { Check, PointerIcon, TrendingUp, ZapIcon } from 'lucide-react'
|
||||
import { DASHBOARD_SIGN_UP_URL } from '@/lib/dashboard-links'
|
||||
import dynamic from 'next/dynamic'
|
||||
import { PRODUCT_SHORTNAMES } from 'shared-data/products'
|
||||
import { cn } from 'ui'
|
||||
@@ -54,7 +55,7 @@ const data: () => {
|
||||
ctas: [
|
||||
{
|
||||
label: 'Start your project',
|
||||
href: 'https://supabase.com/dashboard',
|
||||
href: DASHBOARD_SIGN_UP_URL,
|
||||
type: 'primary' as any,
|
||||
onClick: () =>
|
||||
sendTelemetryEvent({
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { CubeIcon } from '@heroicons/react/outline'
|
||||
import { DASHBOARD_SIGN_UP_URL } from '@/lib/dashboard-links'
|
||||
import { useBreakpoint } from 'common'
|
||||
import {
|
||||
Check,
|
||||
@@ -85,7 +86,7 @@ const data: () => {
|
||||
ctas: [
|
||||
{
|
||||
label: 'Start your project',
|
||||
href: 'https://supabase.com/dashboard',
|
||||
href: DASHBOARD_SIGN_UP_URL,
|
||||
type: 'primary' as any,
|
||||
onClick: () =>
|
||||
sendTelemetryEvent({
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { CubeIcon } from '@heroicons/react/outline'
|
||||
import { DASHBOARD_SIGN_UP_URL } from '@/lib/dashboard-links'
|
||||
import { useBreakpoint } from 'common'
|
||||
import {
|
||||
Check,
|
||||
@@ -85,7 +86,7 @@ const data: () => {
|
||||
ctas: [
|
||||
{
|
||||
label: 'Get started with Supabase',
|
||||
href: 'https://supabase.com/dashboard',
|
||||
href: DASHBOARD_SIGN_UP_URL,
|
||||
type: 'primary' as any,
|
||||
onClick: () =>
|
||||
sendTelemetryEvent({
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { useBreakpoint } from 'common'
|
||||
import { DASHBOARD_SIGN_UP_URL } from '@/lib/dashboard-links'
|
||||
import { Check, CheckCircle, Sparkles, Timer, Zap } from 'lucide-react'
|
||||
import dynamic from 'next/dynamic'
|
||||
import { PRODUCT_SHORTNAMES } from 'shared-data/products'
|
||||
@@ -62,7 +63,7 @@ const useVibeCodersContent: () => {
|
||||
ctas: [
|
||||
{
|
||||
label: 'Start Your Project',
|
||||
href: 'https://supabase.com/dashboard',
|
||||
href: DASHBOARD_SIGN_UP_URL,
|
||||
type: 'primary' as any,
|
||||
},
|
||||
],
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
export const DASHBOARD_URL = 'https://supabase.com/dashboard'
|
||||
export const DASHBOARD_SIGN_IN_URL = 'https://supabase.com/dashboard/sign-in'
|
||||
export const DASHBOARD_SIGN_UP_URL = 'https://supabase.com/dashboard/sign-up'
|
||||
|
||||
/** Homepage/product "Start your project" CTA: sign-up for guests, dashboard for signed-in users. */
|
||||
export function getDashboardCtaHref(isLoggedIn: boolean): string {
|
||||
return isLoggedIn ? DASHBOARD_URL : DASHBOARD_SIGN_UP_URL
|
||||
}
|
||||
|
||||
const DASHBOARD_CTA_HREFS = new Set([DASHBOARD_URL, DASHBOARD_SIGN_UP_URL])
|
||||
|
||||
/** Rewrite static dashboard/sign-up CTA hrefs when the visitor is already signed in. */
|
||||
export function resolveDashboardCtaHref(href: string, isLoggedIn: boolean): string {
|
||||
if (!DASHBOARD_CTA_HREFS.has(href)) return href
|
||||
return getDashboardCtaHref(isLoggedIn)
|
||||
}
|
||||
Reference in new issue
Block a user