integrations redesign

This commit is contained in:
Saxon Fletcher committed 2025-09-03 16:20:13 +10:00
1 parent 7e44382aec
commit 9e72ee2881
4 files changed
+148 -166

No files matched your search

@@ -3,10 +3,13 @@ import { useMemo } from 'react'
import { useParams } from 'common'
import {
ScaffoldContainer,
ScaffoldDescription,
ScaffoldSection,
ScaffoldSectionContent,
ScaffoldSectionDescription,
ScaffoldSectionDetail,
ScaffoldSectionTitle,
ScaffoldTitle,
} from 'components/layouts/Scaffold'
import NoPermission from 'components/ui/NoPermission'
import UpgradeToPro from 'components/ui/UpgradeToPro'
@@ -21,7 +24,7 @@ import GitHubIntegrationConnectionForm from './GitHubIntegrationConnectionForm'
const IntegrationImageHandler = ({ title }: { title: 'vercel' | 'github' }) => {
return (
<img
className="border rounded-lg shadow w-full sm:w-48 mt-6 border-body"
className="border rounded-lg shadow w-full sm:w-32 border-body"
src={`${BASE_PATH}/img/integrations/covers/${title}-cover.png`}
alt={`${title} cover`}
/>
@@ -51,44 +54,40 @@ const GitHubSection = () => {
const GitHubTitle = `GitHub Integration`
return (
<ScaffoldContainer>
<ScaffoldSection>
<ScaffoldSectionDetail title={GitHubTitle}>
<p>Connect any of your GitHub repositories to a project.</p>
<IntegrationImageHandler title="github" />
</ScaffoldSectionDetail>
<ScaffoldSectionContent>
{isLoadingPermissions ? (
<GenericSkeletonLoader />
) : !canReadGitHubConnection ? (
<NoPermission resourceText="view this organization's GitHub connections" />
) : (
<div className="space-y-6">
<div>
<h5 className="text-foreground mb-2">How does the GitHub integration work?</h5>
<p className="text-foreground-light text-sm mb-6">
Connecting to GitHub allows you to sync preview branches with a chosen GitHub
branch, keep your production branch in sync, and automatically create preview
branches for every pull request.
</p>
{promptProPlanUpgrade && (
<div className="mb-6">
<UpgradeToPro
primaryText="Upgrade to unlock GitHub integration"
secondaryText="Connect your GitHub repository to automatically sync preview branches and deploy changes."
source="github-integration"
/>
</div>
)}
<div className={cn(promptProPlanUpgrade && 'opacity-25 pointer-events-none')}>
<GitHubIntegrationConnectionForm connection={existingConnection} />
</div>
<ScaffoldSection isFullWidth>
<div className="flex items-center gap-6 mb-8">
<IntegrationImageHandler title="github" />
<div>
<ScaffoldSectionTitle>{GitHubTitle}</ScaffoldSectionTitle>
<ScaffoldSectionDescription className="mb-0 mt-1 max-w-2xl">
Connect your GitHub repositories to sync preview branches, keep production in sync, and
automatically create preview branches for pull requests.
</ScaffoldSectionDescription>
</div>
</div>
<div>
{isLoadingPermissions ? (
<GenericSkeletonLoader />
) : !canReadGitHubConnection ? (
<NoPermission resourceText="view this organization's GitHub connections" />
) : (
<div>
{promptProPlanUpgrade && (
<div className="mb-6">
<UpgradeToPro
primaryText="Upgrade to unlock GitHub integration"
secondaryText="Connect your GitHub repository to automatically sync preview branches and deploy changes."
source="github-integration"
/>
</div>
)}
<div className={cn(promptProPlanUpgrade && 'opacity-25 pointer-events-none')}>
<GitHubIntegrationConnectionForm connection={existingConnection} />
</div>
)}
</ScaffoldSectionContent>
</ScaffoldSection>
</ScaffoldContainer>
</div>
)}
</div>
</ScaffoldSection>
)
}
@@ -1,7 +1,7 @@
import Link from 'next/link'
import SidePanelVercelProjectLinker from 'components/interfaces/Organization/IntegrationSettings/SidePanelVercelProjectLinker'
import { ScaffoldContainer, ScaffoldDivider } from 'components/layouts/Scaffold'
import { ScaffoldDivider, ScaffoldSection } from 'components/layouts/Scaffold'
import { useIsFeatureEnabled } from 'hooks/misc/useIsFeatureEnabled'
import { useProjectByRefQuery, useSelectedProjectQuery } from 'hooks/misc/useSelectedProject'
import { BASE_PATH } from 'lib/constants'
@@ -12,7 +12,7 @@ import VercelSection from './VercelIntegration/VercelSection'
export const IntegrationImageHandler = ({ title }: { title: 'vercel' | 'github' }) => {
return (
<img
className="border rounded-lg shadow w-full sm:w-48 mt-6 border-body"
className="border rounded-lg shadow w-full sm:w-32 border-body"
src={`${BASE_PATH}/img/integrations/covers/${title}-cover.png`}
alt={`${title} cover`}
/>
@@ -29,7 +29,7 @@ const IntegrationSettings = () => {
return (
<>
{isBranch && (
<ScaffoldContainer>
<ScaffoldSection isFullWidth>
<Alert_Shadcn_ variant="default" className="mt-6">
<WarningIcon />
<AlertTitle_Shadcn_>
@@ -43,12 +43,11 @@ const IntegrationSettings = () => {
.
</AlertDescription_Shadcn_>
</Alert_Shadcn_>
</ScaffoldContainer>
</ScaffoldSection>
)}
<GitHubSection />
{showVercelIntegration && (
<>
<ScaffoldDivider />
<VercelSection isProjectScoped={true} />
<SidePanelVercelProjectLinker />
</>
@@ -12,10 +12,9 @@ import {
} from 'components/interfaces/Integrations/VercelGithub/IntegrationPanels'
import { Markdown } from 'components/interfaces/Markdown'
import {
ScaffoldContainer,
ScaffoldSection,
ScaffoldSectionContent,
ScaffoldSectionDetail,
ScaffoldSectionTitle,
ScaffoldSectionDescription,
} from 'components/layouts/Scaffold'
import NoPermission from 'components/ui/NoPermission'
import { GenericSkeletonLoader } from 'components/ui/ShimmeringLoader'
@@ -32,7 +31,7 @@ import { useSelectedProjectQuery } from 'hooks/misc/useSelectedProject'
import { pluralize } from 'lib/helpers'
import { getIntegrationConfigurationUrl } from 'lib/integration-utils'
import { useSidePanelsStateSnapshot } from 'state/side-panels'
import { Button, cn } from 'ui'
import { Button, Card, CardContent, cn } from 'ui'
import { IntegrationImageHandler } from '../IntegrationsSettings'
import VercelIntegrationConnectionForm from './VercelIntegrationConnectionForm'
@@ -114,22 +113,9 @@ const VercelSection = ({ isProjectScoped }: { isProjectScoped: boolean }) => {
[deleteVercelConnection, org?.slug]
)
// Markdown Content
// Section title
const VercelTitle = `Vercel Integration`
const VercelDetailsSection = `
Connect your Vercel teams to your Supabase organization.
`
const VercelContentSectionTop = `
### How does the Vercel integration work?
Supabase will keep your environment variables up to date in each of the projects you assign to a Supabase project.
You can also link multiple Vercel Projects to the same Supabase project.
`
const VercelContentSectionBottom =
vercelProjectCount > 0 && vercelIntegration !== undefined
? `
@@ -159,103 +145,109 @@ You can change the scope of the access for Supabase by configuring
)} `
return (
<ScaffoldContainer>
<ScaffoldSection>
<ScaffoldSectionDetail title={VercelTitle}>
<Markdown content={VercelDetailsSection} />
<IntegrationImageHandler title="vercel" />
</ScaffoldSectionDetail>
<ScaffoldSectionContent>
{isLoadingPermissions ? (
<GenericSkeletonLoader />
) : !canReadVercelConnection ? (
<NoPermission resourceText="view this organization's Vercel connections" />
) : (
<>
<Markdown content={VercelContentSectionTop} />
{vercelIntegration ? (
<div key={vercelIntegration.id}>
<IntegrationInstallation title={'Vercel'} integration={vercelIntegration} />
{connections.length > 0 ? (
<>
<IntegrationConnectionHeader
title={ConnectionHeaderTitle}
markdown={`Repository connections for Vercel`}
/>
<ul className="flex flex-col">
{connections.map((connection) => (
<div
key={connection.id}
className={cn(
isProjectScoped && 'relative flex flex-col -gap-[1px] [&>li]:pb-0'
)}
>
<IntegrationConnectionItem
connection={connection}
disabled={isBranch || !canUpdateVercelConnection}
type={'Vercel' as IntegrationName}
onDeleteConnection={onDeleteVercelConnection}
className={cn(isProjectScoped && '!rounded-b-none !mb-0')}
/>
{isProjectScoped ? (
<div className="relative pl-8 ml-6 border-l border-muted pb-6">
<div className="border-b border-l border-r rounded-b-lg">
<VercelIntegrationConnectionForm
connection={connection}
integration={vercelIntegration}
disabled={isBranch || !canUpdateVercelConnection}
/>
</div>
</div>
) : null}
</div>
))}
</ul>
</>
) : (
<ScaffoldSection isFullWidth>
<div className="flex items-center gap-6 mb-8">
<IntegrationImageHandler title="vercel" />
<div>
<ScaffoldSectionTitle>{VercelTitle}</ScaffoldSectionTitle>
<ScaffoldSectionDescription className="mb-0 mt-1 max-w-2xl">
Connect your Vercel team to keep environment variables in sync across linked projects,
and link multiple Vercel projects to the same Supabase project.
</ScaffoldSectionDescription>
</div>
</div>
<div>
{isLoadingPermissions ? (
<GenericSkeletonLoader />
) : !canReadVercelConnection ? (
<NoPermission resourceText="view this organization's Vercel connections" />
) : (
<>
{vercelIntegration ? (
<div key={vercelIntegration.id}>
<IntegrationInstallation title={'Vercel'} integration={vercelIntegration} />
{connections.length > 0 ? (
<>
<IntegrationConnectionHeader
title={ConnectionHeaderTitle}
className="pb-0"
markdown={`Repository connections for Vercel`}
/>
)}
<EmptyIntegrationConnection
disabled={isBranch || !canCreateVercelConnection}
onClick={() => onAddVercelConnection(vercelIntegration.id)}
>
Add new project connection
</EmptyIntegrationConnection>
</div>
) : (
<div>
<Button
icon={<ExternalLink />}
asChild={!isBranch}
type="default"
disabled={isBranch}
>
{isBranch ? (
<p>Install Vercel Integration</p>
) : (
<ul className="flex flex-col">
{connections.map((connection) => (
<div
key={connection.id}
className={cn(
isProjectScoped && 'relative flex flex-col -gap-[1px] [&>li]:pb-0'
)}
>
<IntegrationConnectionItem
connection={connection}
disabled={isBranch || !canUpdateVercelConnection}
type={'Vercel' as IntegrationName}
onDeleteConnection={onDeleteVercelConnection}
className={cn(isProjectScoped && '!rounded-b-none !mb-0')}
/>
{isProjectScoped ? (
<div className="relative pl-8 ml-6 border-l border-muted pb-6">
<div className="border-b border-l border-r rounded-b-lg">
<VercelIntegrationConnectionForm
connection={connection}
integration={vercelIntegration}
disabled={isBranch || !canUpdateVercelConnection}
/>
</div>
</div>
) : null}
</div>
))}
</ul>
</>
) : (
<IntegrationConnectionHeader
title={ConnectionHeaderTitle}
className="pb-0"
markdown={`Repository connections for Vercel`}
/>
)}
<EmptyIntegrationConnection
disabled={isBranch || !canCreateVercelConnection}
onClick={() => onAddVercelConnection(vercelIntegration.id)}
>
Add new project connection
</EmptyIntegrationConnection>
</div>
) : (
<Card>
<CardContent className="flex flex-col items-center justify-center py-12">
<p className="text-sm text-center">Install Vercel Integration</p>
<p className="text-sm text-center text-foreground-light mb-4">
Connect your Vercel account to link projects and sync environment variables.
</p>
{isBranch ? (
<Button type="default" disabled>
Install Vercel Integration
</Button>
) : (
<Button icon={<ExternalLink />} asChild type="default">
<Link href={integrationUrl} target="_blank" rel="noreferrer">
Install Vercel Integration
</Link>
)}
</Button>
</div>
)}
{VercelContentSectionBottom && (
<Markdown
extLinks
content={VercelContentSectionBottom}
className="text-foreground-lighter"
/>
)}
</>
)}
</ScaffoldSectionContent>
</ScaffoldSection>
</ScaffoldContainer>
</Button>
)}
</CardContent>
</Card>
)}
{VercelContentSectionBottom && (
<Markdown
extLinks
content={VercelContentSectionBottom}
className="text-foreground-lighter"
/>
)}
</>
)}
</div>
</ScaffoldSection>
)
}
@@ -1,31 +1,23 @@
import IntegrationSettings from 'components/interfaces/Settings/Integrations/IntegrationsSettings'
import DefaultLayout from 'components/layouts/DefaultLayout'
import SettingsLayout from 'components/layouts/ProjectSettingsLayout/SettingsLayout'
import {
ScaffoldContainer,
ScaffoldDivider,
ScaffoldHeader,
ScaffoldTitle,
} from 'components/layouts/Scaffold'
import { PageLayout } from 'components/layouts/PageLayout/PageLayout'
import { ScaffoldContainer } from 'components/layouts/Scaffold'
import type { NextPageWithLayout } from 'types'
const OrgIntegrationSettings: NextPageWithLayout = () => {
return (
<>
<ScaffoldContainer>
<ScaffoldHeader>
<ScaffoldTitle>Integrations</ScaffoldTitle>
</ScaffoldHeader>
</ScaffoldContainer>
<ScaffoldDivider />
<ScaffoldContainer bottomPadding>
<IntegrationSettings />
</>
</ScaffoldContainer>
)
}
OrgIntegrationSettings.getLayout = (page) => (
<DefaultLayout>
<SettingsLayout>{page}</SettingsLayout>
<SettingsLayout title="Integrations">
<PageLayout title="Integrations">{page}</PageLayout>
</SettingsLayout>
</DefaultLayout>
)
export default OrgIntegrationSettings