diff --git a/apps/studio/.github/eslint-rule-baselines.json b/apps/studio/.github/eslint-rule-baselines.json index 97ba9ff3652..47a1447a26b 100644 --- a/apps/studio/.github/eslint-rule-baselines.json +++ b/apps/studio/.github/eslint-rule-baselines.json @@ -742,8 +742,6 @@ "components/layouts/DatabaseLayout/DatabaseLayout.tsx": 1, "components/layouts/EdgeFunctionsLayout/EdgeFunctionDetailsLayout.tsx": 1, "components/layouts/EdgeFunctionsLayout/EdgeFunctionsLayout.tsx": 1, - "components/layouts/IntegrationsLayout/IntegrationWindowLayout.tsx": 1, - "components/layouts/IntegrationsLayout/VercelIntegrationWindowLayout.tsx": 1, "components/layouts/JWTKeys/JWTKeysLayout.tsx": 1, "components/layouts/LogsLayout/LogsLayout.tsx": 1, "components/layouts/Navigation/NavigationBar/MobileNavigationBar.tsx": 1, diff --git a/apps/studio/TANSTACK_MIGRATION.md b/apps/studio/TANSTACK_MIGRATION.md index 728290838b2..5afb7202e9e 100644 --- a/apps/studio/TANSTACK_MIGRATION.md +++ b/apps/studio/TANSTACK_MIGRATION.md @@ -78,7 +78,7 @@ These are the layout-only TanStack files. Most hold a single product layout comp - [x] `routes/_app/account.tsx` — AccountLayout (reads `accountLayoutTitle` from leaf `staticData`) - [x] `routes/_app/org.tsx` — OrganizationLayout (reads `orgLayoutTitle` from leaf `staticData`). **Delta vs plan:** placed at `_app/org.tsx` (wraps both `/org/` index and `/org/$slug/*`) instead of `_app/org/$slug.tsx`. PageLayout stays inline on `/org/$slug/index.tsx` since only that one route uses it. - [x] `routes/_app/new.tsx` — skipped; only `_app/new/index.tsx` lives under \_app (inlines WizardLayout). `new/$slug` is top-level (no AppLayout) so a sub-shell would not actually share state. -- [x] `routes/integrations/vercel.tsx` — VercelIntegrationWindowLayout. **Delta vs plan:** placed at top-level rather than under `_app/` — Next getLayout for all three leaves wraps only in VercelIntegrationWindowLayout, no AppLayout/DefaultLayout. +- [x] `routes/integrations/vercel.tsx` — passthrough `Outlet` only (no shared window layout). **Delta vs plan:** placed at top-level rather than under `_app/`. All three Vercel leaves (install, marketplace choose-project, deploy-button new-project) render their own `InterstitialLayout` inline; the old `VercelIntegrationWindowLayout` was removed. ### Project shell diff --git a/apps/studio/components/interfaces/ProjectCreation/AdvancedConfiguration.tsx b/apps/studio/components/interfaces/ProjectCreation/AdvancedConfiguration.tsx index 9651283d523..c9a621a4d72 100644 --- a/apps/studio/components/interfaces/ProjectCreation/AdvancedConfiguration.tsx +++ b/apps/studio/components/interfaces/ProjectCreation/AdvancedConfiguration.tsx @@ -2,8 +2,6 @@ import { useFlag } from 'common' import { UseFormReturn } from 'react-hook-form' import { Badge, - Card, - CardContent, cn, FormControl, FormField, @@ -20,6 +18,7 @@ import { FormItemLayout } from 'ui-patterns/form/FormItemLayout/FormItemLayout' import { CreateProjectForm } from './ProjectCreation.schema' import { DocsButton } from '@/components/ui/DocsButton' +import Panel from '@/components/ui/Panel' import { DOCS_URL } from '@/lib/constants' interface AdvancedConfigurationProps { @@ -30,93 +29,91 @@ export const AdvancedConfiguration = ({ form }: AdvancedConfigurationProps) => { const disableOrioleProjectCreation = useFlag('disableOrioleProjectCreation') return ( - - - - ( - <> - - - field.onChange(value === 'true')} - defaultValue={field.value.toString()} - > - - - - Postgres - Default - - } - description="Recommended for production workloads" - className="[&>div>div>p]:text-left [&>div>div>p]:text-xs [&>div>div>label]:flex [&>div>div>label]:items-center [&>div>div>label]:gap-x-2" - /> - - - - - - - - Postgres with OrioleDB - Alpha - - } - description="Not recommended for production workloads" - className={cn( - '[&>div>div>p]:text-left [&>div>div>p]:text-xs [&>div>div>label]:flex [&>div>div>label]:items-center [&>div>div>label]:gap-x-2', - form.getValues('useOrioleDb') ? 'rounded-b-none!' : '' - )} - disabled={disableOrioleProjectCreation} - /> - - {disableOrioleProjectCreation && ( - - OrioleDB is temporarily disabled for new projects. Please try again - later. - - )} - - - - - - {form.getValues('useOrioleDb') && ( - - - - )} - - - )} - /> - - - + + + ( + <> + + + field.onChange(value === 'true')} + defaultValue={field.value.toString()} + > + + + + Postgres + Default + + } + description="Recommended for production workloads" + className="[&>div>div>p]:text-left [&>div>div>p]:text-xs [&>div>div>label]:flex [&>div>div>label]:items-center [&>div>div>label]:gap-x-2" + /> + + + + + + + + Postgres with OrioleDB + Alpha + + } + description="Not recommended for production workloads" + className={cn( + '[&>div>div>p]:text-left [&>div>div>p]:text-xs [&>div>div>label]:flex [&>div>div>label]:items-center [&>div>div>label]:gap-x-2', + form.getValues('useOrioleDb') ? 'rounded-b-none!' : '' + )} + disabled={disableOrioleProjectCreation} + /> + + {disableOrioleProjectCreation && ( + + OrioleDB is temporarily disabled for new projects. Please try again + later. + + )} + + + + + + {form.getValues('useOrioleDb') && ( + + + + )} + + + )} + /> + + ) } diff --git a/apps/studio/components/interfaces/ProjectCreation/InternalOnlyConfiguration.tsx b/apps/studio/components/interfaces/ProjectCreation/InternalOnlyConfiguration.tsx index 3737650ec10..12858f3df5f 100644 --- a/apps/studio/components/interfaces/ProjectCreation/InternalOnlyConfiguration.tsx +++ b/apps/studio/components/interfaces/ProjectCreation/InternalOnlyConfiguration.tsx @@ -1,7 +1,7 @@ import { useParams } from 'common' import { UseFormReturn } from 'react-hook-form' import { type CloudProvider } from 'shared-data' -import { Card, CardContent, FormControl, FormField, Input } from 'ui' +import { FormControl, FormField, Input } from 'ui' import { CollapsibleCardSection } from 'ui-patterns/CollapsibleCardSection' import { FormItemLayout } from 'ui-patterns/form/FormItemLayout/FormItemLayout' @@ -9,6 +9,7 @@ import { CloudProviderSelector } from './CloudProviderSelector' import { HighAvailabilityInput } from './HighAvailabilityInput' import { PostgresVersionSelector } from './PostgresVersionSelector' import { CreateProjectForm } from './ProjectCreation.schema' +import Panel from '@/components/ui/Panel' interface InternalOnlyConfigurationProps { form: UseFormReturn @@ -19,76 +20,74 @@ export const InternalOnlyConfiguration = ({ form }: InternalOnlyConfigurationPro const showNonProdFields = process.env.NEXT_PUBLIC_ENVIRONMENT !== 'prod' return ( - - - -
-
- ( - - )} - /> + + +
+
+ ( + + )} + /> - -
- - {showNonProdFields && ( -
-

- The settings below are only applicable for local/staging projects -

-
- - - ( - - - - - - )} - /> - - ( - - - - - - )} - /> -
-
- )} +
-
- - + + {showNonProdFields && ( +
+

+ The settings below are only applicable for local/staging projects +

+
+ + + ( + + + + + + )} + /> + + ( + + + + + + )} + /> +
+
+ )} +
+ + ) } diff --git a/apps/studio/components/interfaces/ProjectCreation/ProjectCreationForm.tsx b/apps/studio/components/interfaces/ProjectCreation/ProjectCreationForm.tsx index 75516031101..cd455528204 100644 --- a/apps/studio/components/interfaces/ProjectCreation/ProjectCreationForm.tsx +++ b/apps/studio/components/interfaces/ProjectCreation/ProjectCreationForm.tsx @@ -8,7 +8,7 @@ import { useEffect, useMemo, useRef, useState } from 'react' import { useForm, useFormState } from 'react-hook-form' import { type CloudProvider } from 'shared-data' import { toast } from 'sonner' -import { Button, Form, useWatch } from 'ui' +import { Button, cn, Form, useWatch } from 'ui' import { Admonition } from 'ui-patterns/admonition' import ConfirmationModal from 'ui-patterns/Dialogs/ConfirmationModal' import { z } from 'zod' @@ -92,6 +92,8 @@ interface ProjectCreationFormProps { * - "Cancel" button * - Shows the following: * - "Data seeding" section + * - When embedded in the Vercel interstitial, flattens Panel chrome so the shared + * form fields sit inside InterstitialLayout without a nested card * Eventually we could looking into reducing the differences more, e.g having data seeding * for both ways, and showing GitHub repository field for Vercel integration */ @@ -581,14 +583,20 @@ export const ProjectCreationForm = ({ > -

Create a new project

-

- Your project will have its own dedicated instance and full Postgres database. An API - will be set up so you can easily interact with your new database. -

-
+ !isVercelIntegrationFlow && ( +
+

Create a new project

+

+ Your project will have its own dedicated instance and full Postgres database. An + API will be set up so you can easily interact with your new database. +

+
+ ) } footer={ ) => { - return ( -
-
- -
{children}
- - {docsHref && ( - - - Docs - - )} - - - Support - - -
- ) -} - -const INTEGRATION_LAYOUT_MAX_WIDTH = '' // 'max-w-[720px]' - -export default withAuth(IntegrationWindowLayout) - -export const IntegrationWindowLayoutWithoutAuth = IntegrationWindowLayout - -export type HeaderProps = { - title: string - integrationIcon: ReactNode -} - -const Header = ({ title, integrationIcon }: HeaderProps) => { - return ( -
- -
-
-
- Supabase -
- - {integrationIcon} -
- - {title} - -
-
-
- ) -} - -const maxWidthClasses = 'mx-auto w-full max-w-[1600px]' -const paddingClasses = 'px-6 lg:px-14 xl:px-28 2xl:px-32' - -export const IntegrationScaffoldContainer = forwardRef< - HTMLDivElement, - React.HTMLAttributes ->(({ className, ...props }, ref) => { - return
-}) - -IntegrationScaffoldContainer.displayName = 'IntegrationScaffoldContainer' diff --git a/apps/studio/components/layouts/IntegrationsLayout/VercelIntegrationWindowLayout.tsx b/apps/studio/components/layouts/IntegrationsLayout/VercelIntegrationWindowLayout.tsx deleted file mode 100644 index 96df39b67b1..00000000000 --- a/apps/studio/components/layouts/IntegrationsLayout/VercelIntegrationWindowLayout.tsx +++ /dev/null @@ -1,36 +0,0 @@ -import { useParams } from 'common' -import { PropsWithChildren } from 'react' -import InlineSVG from 'react-inlinesvg' - -import IntegrationWindowLayout from './IntegrationWindowLayout' -import { BASE_PATH } from '@/lib/constants' -import { useIntegrationInstallationSnapshot } from '@/state/integration-installation' - -const VERCEL_ICON = ( -
- -
-) - -const VercelIntegrationWindowLayout = ({ children }: PropsWithChildren<{}>) => { - const { externalId } = useParams() - - const snapshot = useIntegrationInstallationSnapshot() - - const title = externalId - ? 'Supabase + Vercel Deploy Button' - : 'Supabase + Vercel Integration Marketplace Connector' - - return ( - - {children} - - ) -} - -export default VercelIntegrationWindowLayout diff --git a/apps/studio/components/layouts/InterstitialLayout.tsx b/apps/studio/components/layouts/InterstitialLayout.tsx index fa61322feee..d17b469956e 100644 --- a/apps/studio/components/layouts/InterstitialLayout.tsx +++ b/apps/studio/components/layouts/InterstitialLayout.tsx @@ -16,6 +16,8 @@ interface InterstitialLayoutProps { footer?: ReactNode containerClassName?: string cardClassName?: string + /** Shared max-width for the card and footer column. Defaults to `max-w-[400px]`. */ + widthClassName?: string titleClassName?: string descriptionClassName?: string } @@ -34,6 +36,7 @@ export const InterstitialLayout = ({ footer, containerClassName, cardClassName, + widthClassName = 'max-w-[400px]', titleClassName, descriptionClassName, children, @@ -67,7 +70,7 @@ export const InterstitialLayout = ({ {(logo || title || description) && ( @@ -92,7 +95,7 @@ export const InterstitialLayout = ({ )} > {footer ? ( -
+
{card}
{footer}
diff --git a/apps/studio/pages/integrations/vercel/[slug]/deploy-button/new-project.tsx b/apps/studio/pages/integrations/vercel/[slug]/deploy-button/new-project.tsx index 2e1f3b8d277..02e1c050ab2 100644 --- a/apps/studio/pages/integrations/vercel/[slug]/deploy-button/new-project.tsx +++ b/apps/studio/pages/integrations/vercel/[slug]/deploy-button/new-project.tsx @@ -1,19 +1,29 @@ import { useParams } from 'common' +import Head from 'next/head' import { useEffect, useState } from 'react' -import { Admonition } from 'ui-patterns/admonition' import { isVercelUrl } from '@/components/interfaces/Integrations/Vercel/VercelIntegration.utils' +import { + VercelIntegrationFooter, + VercelIntegrationLogo, +} from '@/components/interfaces/Integrations/Vercel/VercelIntegrationInterstitial' import { ProjectCreationForm } from '@/components/interfaces/ProjectCreation/ProjectCreationForm' -import VercelIntegrationWindowLayout from '@/components/layouts/IntegrationsLayout/VercelIntegrationWindowLayout' -import { ScaffoldColumn, ScaffoldContainer } from '@/components/layouts/Scaffold' +import { InterstitialLayout } from '@/components/layouts/InterstitialLayout' import { useProjectSettingsV2Query } from '@/data/config/project-settings-v2-query' import { useIntegrationsQuery } from '@/data/integrations/integrations-query' import { useIntegrationVercelConnectionsCreateMutation } from '@/data/integrations/integrations-vercel-connections-create-mutation' import { useVercelProjectsQuery } from '@/data/integrations/integrations-vercel-projects-query' import { useOrganizationsQuery } from '@/data/organizations/organizations-query' +import { withAuth } from '@/hooks/misc/withAuth' +import { buildStudioPageTitle } from '@/lib/page-title' import { useIntegrationInstallationSnapshot } from '@/state/integration-installation' import type { NextPageWithLayout } from '@/types' +const PAGE_TITLE = buildStudioPageTitle({ + section: 'Create Vercel Project', + brand: 'Supabase', +}) + const VercelIntegration: NextPageWithLayout = () => { const { slug, next, currentProjectId: foreignProjectId } = useParams() const snapshot = useIntegrationInstallationSnapshot() @@ -91,23 +101,22 @@ const VercelIntegration: NextPageWithLayout = () => { }, [data, isSuccess]) return ( - - - + <> + + {PAGE_TITLE} + + } + title="Create a new project" + description="Your project will have its own dedicated instance and full Postgres database. An API will be set up so you can easily interact with your new database." + footer={} + widthClassName="max-w-2xl" + > - - + + ) } -VercelIntegration.getLayout = (page) => ( - {page} -) - -export default VercelIntegration +export default withAuth(VercelIntegration) diff --git a/apps/studio/routes/integrations/vercel.tsx b/apps/studio/routes/integrations/vercel.tsx index e31fc665ab9..8e9bcd49623 100644 --- a/apps/studio/routes/integrations/vercel.tsx +++ b/apps/studio/routes/integrations/vercel.tsx @@ -4,11 +4,9 @@ export const Route = createFileRoute('/integrations/vercel')({ component: VercelIntegrationPassthrough, }) -// No shared layout here. Since #47623, the install and -// marketplace/choose-project pages render their own InterstitialLayout and -// have no Next getLayout, so the Next runtime shows them without any window -// chrome. Only deploy-button/new-project still uses -// VercelIntegrationWindowLayout, and its route wraps it at the leaf. +// No shared layout here. Since #47623, the Vercel install, marketplace +// choose-project, and deploy-button/new-project pages each render their own +// InterstitialLayout and have no Next getLayout / window chrome. function VercelIntegrationPassthrough() { return } diff --git a/apps/studio/routes/integrations/vercel/$slug/deploy-button/new-project.tsx b/apps/studio/routes/integrations/vercel/$slug/deploy-button/new-project.tsx index 0b09a4cf7e9..11f685c8208 100644 --- a/apps/studio/routes/integrations/vercel/$slug/deploy-button/new-project.tsx +++ b/apps/studio/routes/integrations/vercel/$slug/deploy-button/new-project.tsx @@ -1,18 +1,11 @@ import { createFileRoute } from '@tanstack/react-router' -import VercelIntegrationWindowLayout from '@/components/layouts/IntegrationsLayout/VercelIntegrationWindowLayout' import VercelIntegration from '@/pages/integrations/vercel/[slug]/deploy-button/new-project' export const Route = createFileRoute('/integrations/vercel/$slug/deploy-button/new-project')({ component: VercelDeployButtonNewProjectRoute, }) -// Mirrors the page's Next getLayout, which wraps this leaf (and only this -// leaf) in VercelIntegrationWindowLayout. function VercelDeployButtonNewProjectRoute() { - return ( - - - - ) + return } diff --git a/packages/ui-patterns/src/CollapsibleCardSection.tsx b/packages/ui-patterns/src/CollapsibleCardSection.tsx index 17e3268a76a..a0d3df4ceb1 100644 --- a/packages/ui-patterns/src/CollapsibleCardSection.tsx +++ b/packages/ui-patterns/src/CollapsibleCardSection.tsx @@ -25,7 +25,8 @@ export const CollapsibleCardSection = ({ {description &&

{description}

}