mirror of
https://github.com/supabase/supabase.git
synced 2026-10-09 19:35:06 +03:00
## What kind of change does this PR introduce? Bug fix. Resolves DEPR-318. ## What is the current behavior? New users who confirm their email land on `/sign-in`, then `/organizations`, then get bounced to `/new` via a `useEffect`. Cancelling org creation with zero orgs sends them back to `/organizations`, which immediately redirects into `/new` again. ## What is the new behavior? - Signup email verification redirects to `/new` directly - `/organizations` with zero orgs shows the existing empty state instead of force-redirecting ## To test ### One-time setup Assuming you don’t already have a staging account with **zero** orgs: 1. On **supabase.green**, sign up with a fresh email and confirm it 2. Stop at org creation. Do **not** create an org ### On this PR Using the [studio-staging preview](https://studio-staging-git-dnywh-fixremove-org-redirect-supabase.vercel.app/) from Vercel checks: 4. Sign in on the preview with that account 5. Open `/dashboard/organizations`. Expect the **Create an organization** empty state, with no redirect to `/new` 6. Open `/dashboard/new`, click **Cancel**. Expect to land on `/organizations` and stay there ### Compare on supabase.green Optional. Just to show what happens currently on `master`: 7. Repeat steps 3–5 on **supabase.green**. `/organizations` should bounce to `/new`, and **Cancel** should send you back into org creation <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Bug Fixes** * Improved signup redirects by preserving valid destinations and relevant query parameters. * Added safer fallback behavior for missing, invalid, or unsupported destinations. * Improved handling of signup redirects provided in multiple formats. * Prevented automatic redirection from the organizations page when no organizations exist. * **Style** * Updated the organizations page title capitalization for consistency. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
111 lines
4.0 KiB
TypeScript
111 lines
4.0 KiB
TypeScript
import { Plus, Search } from 'lucide-react'
|
|
import Head from 'next/head'
|
|
import Link from 'next/link'
|
|
import { useState } from 'react'
|
|
import { Button, Skeleton } from 'ui'
|
|
import { Input } from 'ui-patterns/DataInputs/Input'
|
|
|
|
import { NoOrganizationsState } from '@/components/interfaces/Home/ProjectList/EmptyStates'
|
|
import { OrganizationCard } from '@/components/interfaces/Organization/OrganizationCard'
|
|
import { AppLayout } from '@/components/layouts/AppLayout/AppLayout'
|
|
import { DefaultLayout } from '@/components/layouts/DefaultLayout'
|
|
import { PageLayout } from '@/components/layouts/PageLayout/PageLayout'
|
|
import { ScaffoldContainer, ScaffoldSection } from '@/components/layouts/Scaffold'
|
|
import { AlertError } from '@/components/ui/AlertError'
|
|
import { NoSearchResults } from '@/components/ui/NoSearchResults'
|
|
import { useOrganizationsQuery } from '@/data/organizations/organizations-query'
|
|
import { useCustomContent } from '@/hooks/custom-content/useCustomContent'
|
|
import { useIsFeatureEnabled } from '@/hooks/misc/useIsFeatureEnabled'
|
|
import { withAuth } from '@/hooks/misc/withAuth'
|
|
import { buildStudioPageTitle } from '@/lib/page-title'
|
|
import type { NextPageWithLayout } from '@/types'
|
|
|
|
const OrganizationsPage: NextPageWithLayout = () => {
|
|
const { appTitle } = useCustomContent(['app:title'])
|
|
const [search, setSearch] = useState('')
|
|
const pageTitle = buildStudioPageTitle({
|
|
section: 'Organizations',
|
|
brand: appTitle || 'Supabase',
|
|
})
|
|
|
|
const {
|
|
data: organizations = [],
|
|
error,
|
|
isPending: isLoading,
|
|
isError,
|
|
isSuccess,
|
|
} = useOrganizationsQuery()
|
|
|
|
const organizationCreationEnabled = useIsFeatureEnabled('organizations:create')
|
|
const filteredOrganizations =
|
|
search.length === 0
|
|
? organizations
|
|
: organizations?.filter(
|
|
(x) => x.name.toLowerCase().includes(search) || x.slug.toLowerCase().includes(search)
|
|
)
|
|
|
|
return (
|
|
<>
|
|
<Head>
|
|
<title>{pageTitle}</title>
|
|
<meta name="description" content="Supabase Studio" />
|
|
</Head>
|
|
<ScaffoldContainer>
|
|
<ScaffoldSection isFullWidth className="flex flex-col gap-y-4">
|
|
{organizations.length > 0 && (
|
|
<div className="flex items-center justify-between gap-x-2 md:gap-x-3">
|
|
<Input
|
|
size="tiny"
|
|
placeholder="Search for an organization"
|
|
icon={<Search />}
|
|
className="w-full flex-1 md:w-64"
|
|
value={search}
|
|
onChange={(event) => setSearch(event.target.value)}
|
|
/>
|
|
|
|
{organizationCreationEnabled && (
|
|
<Button asChild icon={<Plus />} variant="primary" className="w-min">
|
|
<Link href={`/new`}>New organization</Link>
|
|
</Button>
|
|
)}
|
|
</div>
|
|
)}
|
|
|
|
{isSuccess && organizations.length === 0 && !isError && <NoOrganizationsState />}
|
|
|
|
{search.length > 0 && filteredOrganizations.length === 0 && (
|
|
<NoSearchResults searchString={search} />
|
|
)}
|
|
|
|
<div className="grid gap-4 md:grid-cols-2 lg:grid-cols-3">
|
|
{isLoading && (
|
|
<>
|
|
<Skeleton className="h-[70px] rounded-md" />
|
|
<Skeleton className="h-[70px] rounded-md" />
|
|
<Skeleton className="h-[70px] rounded-md" />
|
|
</>
|
|
)}
|
|
{isError && <AlertError error={error} subject="Failed to load organizations" />}
|
|
{isSuccess &&
|
|
filteredOrganizations.map((org) => (
|
|
<OrganizationCard key={org.id} organization={org} />
|
|
))}
|
|
</div>
|
|
</ScaffoldSection>
|
|
</ScaffoldContainer>
|
|
</>
|
|
)
|
|
}
|
|
|
|
OrganizationsPage.getLayout = (page) => (
|
|
<AppLayout>
|
|
<DefaultLayout hideMobileMenu headerTitle="Organizations">
|
|
<PageLayout title="Your organizations" className="max-w-[1200px] lg:px-6 mx-auto">
|
|
{page}
|
|
</PageLayout>
|
|
</DefaultLayout>
|
|
</AppLayout>
|
|
)
|
|
|
|
export default withAuth(OrganizationsPage)
|