mirror of
https://github.com/supabase/supabase.git
synced 2026-10-05 09:25:06 +03:00
feat: Enable Github on free plan under a feature flag (#44171)
Co-authored-by: Joshen Lim <joshenlimek@gmail.com>
This commit is contained in:
1 parent
b84068f173
commit
bd10880d52
4 files changed
+334
-283
No files matched your search
+251
-222
@@ -1,30 +1,11 @@
|
||||
import { zodResolver } from '@hookform/resolvers/zod'
|
||||
import { PermissionAction } from '@supabase/shared-types/out/constants'
|
||||
import { IS_PLATFORM } from 'common'
|
||||
import { useBranchCreateMutation } from 'data/branches/branch-create-mutation'
|
||||
import { useBranchUpdateMutation } from 'data/branches/branch-update-mutation'
|
||||
import { useBranchesQuery } from 'data/branches/branches-query'
|
||||
import { useGitHubAuthorizationQuery } from 'data/integrations/github-authorization-query'
|
||||
import { useCheckGithubBranchValidity } from 'data/integrations/github-branch-check-query'
|
||||
import { useGitHubConnectionCreateMutation } from 'data/integrations/github-connection-create-mutation'
|
||||
import { useGitHubConnectionDeleteMutation } from 'data/integrations/github-connection-delete-mutation'
|
||||
import { useGitHubConnectionUpdateMutation } from 'data/integrations/github-connection-update-mutation'
|
||||
import { useGitHubRepositoriesQuery } from 'data/integrations/github-repositories-query'
|
||||
import type { GitHubConnection } from 'data/integrations/integrations.types'
|
||||
import { useAsyncCheckPermissions } from 'hooks/misc/useCheckPermissions'
|
||||
import { useSelectedOrganizationQuery } from 'hooks/misc/useSelectedOrganization'
|
||||
import { useSelectedProjectQuery } from 'hooks/misc/useSelectedProject'
|
||||
import { DOCS_URL } from 'lib/constants'
|
||||
import { openInstallGitHubIntegrationWindow } from 'lib/github'
|
||||
import { EMPTY_ARR } from 'lib/void'
|
||||
import { AnimatePresence, motion } from 'framer-motion'
|
||||
import { ChevronDown, Loader2, PlusIcon, RefreshCw } from 'lucide-react'
|
||||
import { useEffect, useMemo, useState } from 'react'
|
||||
import { useForm } from 'react-hook-form'
|
||||
import { toast } from 'sonner'
|
||||
import {
|
||||
Alert_Shadcn_,
|
||||
AlertDescription_Shadcn_,
|
||||
AlertTitle_Shadcn_,
|
||||
Button,
|
||||
Card,
|
||||
CardContent,
|
||||
@@ -46,11 +27,30 @@ import {
|
||||
PopoverTrigger_Shadcn_,
|
||||
Switch,
|
||||
} from 'ui'
|
||||
import { InlineLink } from 'components/ui/InlineLink'
|
||||
import { Admonition } from 'ui-patterns/admonition'
|
||||
import ConfirmationModal from 'ui-patterns/Dialogs/ConfirmationModal'
|
||||
import { FormItemLayout } from 'ui-patterns/form/FormItemLayout/FormItemLayout'
|
||||
import * as z from 'zod'
|
||||
|
||||
import { InlineLink } from '@/components/ui/InlineLink'
|
||||
import { UpgradeToPro } from '@/components/ui/UpgradeToPro'
|
||||
import { useBranchCreateMutation } from '@/data/branches/branch-create-mutation'
|
||||
import { useBranchUpdateMutation } from '@/data/branches/branch-update-mutation'
|
||||
import { useBranchesQuery } from '@/data/branches/branches-query'
|
||||
import { useGitHubAuthorizationQuery } from '@/data/integrations/github-authorization-query'
|
||||
import { useCheckGithubBranchValidity } from '@/data/integrations/github-branch-check-query'
|
||||
import { useGitHubConnectionCreateMutation } from '@/data/integrations/github-connection-create-mutation'
|
||||
import { useGitHubConnectionDeleteMutation } from '@/data/integrations/github-connection-delete-mutation'
|
||||
import { useGitHubConnectionUpdateMutation } from '@/data/integrations/github-connection-update-mutation'
|
||||
import { useGitHubRepositoriesQuery } from '@/data/integrations/github-repositories-query'
|
||||
import type { GitHubConnection } from '@/data/integrations/integrations.types'
|
||||
import { useCheckEntitlements } from '@/hooks/misc/useCheckEntitlements'
|
||||
import { useAsyncCheckPermissions } from '@/hooks/misc/useCheckPermissions'
|
||||
import { useSelectedOrganizationQuery } from '@/hooks/misc/useSelectedOrganization'
|
||||
import { useSelectedProjectQuery } from '@/hooks/misc/useSelectedProject'
|
||||
import { DOCS_URL } from '@/lib/constants'
|
||||
import { openInstallGitHubIntegrationWindow } from '@/lib/github'
|
||||
import { EMPTY_ARR } from '@/lib/void'
|
||||
|
||||
const GITHUB_ICON = (
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 98 96" className="w-6">
|
||||
@@ -69,7 +69,7 @@ interface GitHubIntegrationConnectionFormProps {
|
||||
connection?: GitHubConnection
|
||||
}
|
||||
|
||||
const GitHubIntegrationConnectionForm = ({
|
||||
export const GitHubIntegrationConnectionForm = ({
|
||||
disabled = false,
|
||||
connection,
|
||||
}: GitHubIntegrationConnectionFormProps) => {
|
||||
@@ -82,8 +82,8 @@ const GitHubIntegrationConnectionForm = ({
|
||||
|
||||
const { hasAccess: hasAccessToGitHubIntegration, isLoading: isLoadingEntitlements } =
|
||||
useCheckEntitlements('integrations.github_connections')
|
||||
const promptProPlanUpgrade =
|
||||
IS_PLATFORM && !isLoadingEntitlements && !hasAccessToGitHubIntegration
|
||||
|
||||
const { hasAccess: hasAccessToBranching } = useCheckEntitlements('branching_limit')
|
||||
|
||||
const { can: canUpdateGitHubConnection } = useAsyncCheckPermissions(
|
||||
PermissionAction.UPDATE,
|
||||
@@ -460,7 +460,7 @@ const GitHubIntegrationConnectionForm = ({
|
||||
<Button
|
||||
type="default"
|
||||
className="justify-start h-[34px] w-full"
|
||||
disabled={disabled || isLoadingGitHubRepos}
|
||||
disabled={isLoadingGitHubRepos}
|
||||
loading={isLoadingGitHubRepos}
|
||||
icon={
|
||||
<div className="bg-black shadow rounded p-1 w-6 h-6 flex justify-center items-center">
|
||||
@@ -553,213 +553,244 @@ const GitHubIntegrationConnectionForm = ({
|
||||
)}
|
||||
/>
|
||||
</CardContent>
|
||||
<CardContent className={cn(!currentRepositoryId && 'opacity-25 pointer-events-none')}>
|
||||
<FormField_Shadcn_
|
||||
control={githubSettingsForm.control}
|
||||
name="supabaseDirectory"
|
||||
render={({ field }) => (
|
||||
<FormItemLayout
|
||||
layout="flex-row-reverse"
|
||||
label="Working directory"
|
||||
description="Path to working directory with your supabase folder"
|
||||
>
|
||||
<FormControl_Shadcn_>
|
||||
<Input_Shadcn_
|
||||
{...field}
|
||||
placeholder="supabase"
|
||||
autoComplete="off"
|
||||
disabled={disabled || !canUpdateGitHubConnection}
|
||||
/>
|
||||
</FormControl_Shadcn_>
|
||||
</FormItemLayout>
|
||||
)}
|
||||
/>
|
||||
</CardContent>
|
||||
<CardContent className={cn(!currentRepositoryId && 'opacity-25 pointer-events-none')}>
|
||||
{/* Production Branch Sync Section */}
|
||||
<div className="space-y-4">
|
||||
<FormField_Shadcn_
|
||||
control={githubSettingsForm.control}
|
||||
name="enableProductionSync"
|
||||
render={({ field }) => (
|
||||
<FormItemLayout
|
||||
layout="flex-row-reverse"
|
||||
label="Deploy to production"
|
||||
description="Deploy changes to production on push including PR merges"
|
||||
>
|
||||
<FormControl_Shadcn_>
|
||||
<Switch
|
||||
checked={field.value}
|
||||
onCheckedChange={field.onChange}
|
||||
disabled={disabled || !canUpdateGitHubConnection}
|
||||
/>
|
||||
</FormControl_Shadcn_>
|
||||
</FormItemLayout>
|
||||
)}
|
||||
/>
|
||||
|
||||
<div
|
||||
className={cn(
|
||||
'space-y-4 pl-6 border-l',
|
||||
(!enableProductionSync || disabled) && 'opacity-25 pointer-events-none'
|
||||
)}
|
||||
<AnimatePresence>
|
||||
{!!currentRepositoryId && (
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: -16 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
exit={{ opacity: 0, y: -16 }}
|
||||
>
|
||||
<FormField_Shadcn_
|
||||
control={githubSettingsForm.control}
|
||||
name="branchName"
|
||||
render={({ field }) => (
|
||||
<FormItemLayout
|
||||
layout="flex-row-reverse"
|
||||
label="Production branch name"
|
||||
description="The GitHub branch to sync with your production database (e.g., main, master)"
|
||||
>
|
||||
<div className="relative w-full">
|
||||
<CardContent>
|
||||
<FormField_Shadcn_
|
||||
control={githubSettingsForm.control}
|
||||
name="supabaseDirectory"
|
||||
render={({ field }) => (
|
||||
<FormItemLayout
|
||||
layout="flex-row-reverse"
|
||||
label="Working directory"
|
||||
description="Path to working directory with your supabase folder"
|
||||
>
|
||||
<FormControl_Shadcn_>
|
||||
<Input_Shadcn_
|
||||
{...field}
|
||||
placeholder="supabase"
|
||||
autoComplete="off"
|
||||
disabled={
|
||||
disabled || !canUpdateGitHubConnection || !enableProductionSync
|
||||
}
|
||||
disabled={!canUpdateGitHubConnection}
|
||||
/>
|
||||
</FormControl_Shadcn_>
|
||||
<div className="absolute top-2.5 right-3 flex items-center gap-2">
|
||||
{isCheckingBranch && <Loader2 size={14} className="animate-spin" />}
|
||||
</div>
|
||||
</div>
|
||||
</FormItemLayout>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
<CardContent className={cn(!currentRepositoryId && 'opacity-25 pointer-events-none')}>
|
||||
<Alert_Shadcn_ variant="warning" className="mb-4">
|
||||
<AlertTitle_Shadcn_>Branching and billing</AlertTitle_Shadcn_>
|
||||
<AlertDescription_Shadcn_>
|
||||
Branching Compute is not covered by your organization's Spend Cap. Costs
|
||||
should be closely monitored, as they may be incurred.{' '}
|
||||
<InlineLink
|
||||
href={`${DOCS_URL}/guides/platform/cost-control#usage-items-not-covered-by-the-spend-cap`}
|
||||
>
|
||||
Learn more
|
||||
</InlineLink>
|
||||
</AlertDescription_Shadcn_>
|
||||
</Alert_Shadcn_>
|
||||
{/* Automatic Branching Section */}
|
||||
<div className="space-y-4">
|
||||
<FormField_Shadcn_
|
||||
control={githubSettingsForm.control}
|
||||
name="new_branch_per_pr"
|
||||
render={({ field }) => (
|
||||
<FormItemLayout
|
||||
layout="flex-row-reverse"
|
||||
label="Automatic branching"
|
||||
description="Create preview branches for every pull request"
|
||||
>
|
||||
<FormControl_Shadcn_>
|
||||
<Switch
|
||||
checked={field.value}
|
||||
onCheckedChange={field.onChange}
|
||||
disabled={disabled || !canCreateGitHubConnection}
|
||||
</FormItemLayout>
|
||||
)}
|
||||
/>
|
||||
</CardContent>
|
||||
<CardContent>
|
||||
{/* Production Branch Sync Section */}
|
||||
<div className="space-y-4">
|
||||
<FormField_Shadcn_
|
||||
control={githubSettingsForm.control}
|
||||
name="enableProductionSync"
|
||||
render={({ field }) => (
|
||||
<FormItemLayout
|
||||
layout="flex-row-reverse"
|
||||
label="Deploy to production"
|
||||
description="Deploy changes to production on push including PR merges"
|
||||
>
|
||||
<FormControl_Shadcn_>
|
||||
<Switch
|
||||
checked={field.value}
|
||||
onCheckedChange={field.onChange}
|
||||
disabled={!canUpdateGitHubConnection}
|
||||
/>
|
||||
</FormControl_Shadcn_>
|
||||
</FormItemLayout>
|
||||
)}
|
||||
/>
|
||||
|
||||
<div
|
||||
className={cn(
|
||||
'space-y-4 pl-6 border-l',
|
||||
!enableProductionSync && 'opacity-25 pointer-events-none'
|
||||
)}
|
||||
>
|
||||
<FormField_Shadcn_
|
||||
control={githubSettingsForm.control}
|
||||
name="branchName"
|
||||
render={({ field }) => (
|
||||
<FormItemLayout
|
||||
layout="flex-row-reverse"
|
||||
label="Production branch name"
|
||||
description="The GitHub branch to sync with your production database (e.g., main, master)"
|
||||
>
|
||||
<div className="relative w-full">
|
||||
<FormControl_Shadcn_>
|
||||
<Input_Shadcn_
|
||||
{...field}
|
||||
autoComplete="off"
|
||||
disabled={!canUpdateGitHubConnection || !enableProductionSync}
|
||||
/>
|
||||
</FormControl_Shadcn_>
|
||||
<div className="absolute top-2.5 right-3 flex items-center gap-2">
|
||||
{isCheckingBranch && (
|
||||
<Loader2 size={14} className="animate-spin" />
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</FormItemLayout>
|
||||
)}
|
||||
/>
|
||||
</FormControl_Shadcn_>
|
||||
</FormItemLayout>
|
||||
)}
|
||||
/>
|
||||
|
||||
<div
|
||||
className={cn(
|
||||
'space-y-4 pl-6 border-l',
|
||||
(!newBranchPerPr || disabled) && 'opacity-25 pointer-events-none'
|
||||
)}
|
||||
>
|
||||
<FormField_Shadcn_
|
||||
control={githubSettingsForm.control}
|
||||
name="branchLimit"
|
||||
render={({ field }) => (
|
||||
<FormItemLayout
|
||||
layout="flex-row-reverse"
|
||||
label="Branch limit"
|
||||
description="Maximum number of preview branches"
|
||||
>
|
||||
<FormControl_Shadcn_>
|
||||
<Input_Shadcn_
|
||||
{...field}
|
||||
type="number"
|
||||
autoComplete="off"
|
||||
disabled={!newBranchPerPr || disabled || !canUpdateGitHubConnection}
|
||||
/>
|
||||
</FormControl_Shadcn_>
|
||||
</FormItemLayout>
|
||||
</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
<CardContent>
|
||||
{hasAccessToBranching ? (
|
||||
<Admonition type="warning" title="Branching and billing" className="mb-4">
|
||||
Branching Compute is not covered by your organization's Spend Cap.
|
||||
Costs should be closely monitored, as they may be incurred.{' '}
|
||||
<InlineLink
|
||||
href={`${DOCS_URL}/guides/platform/cost-control#usage-items-not-covered-by-the-spend-cap`}
|
||||
>
|
||||
Learn more
|
||||
</InlineLink>
|
||||
</Admonition>
|
||||
) : (
|
||||
<UpgradeToPro
|
||||
className="mb-4"
|
||||
layout="vertical"
|
||||
source="projectIntegrations"
|
||||
featureProposition="automatically create preview branches from pull requests"
|
||||
primaryText="Branching with GitHub integration"
|
||||
secondaryText="Upgrade to the Pro Plan to enable branching and automatically create preview branches for every pull request"
|
||||
docsUrl={`${DOCS_URL}/guides/deployment/branching`}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
|
||||
<FormField_Shadcn_
|
||||
control={githubSettingsForm.control}
|
||||
name="supabaseChangesOnly"
|
||||
render={({ field }) => (
|
||||
<FormItemLayout
|
||||
layout="flex-row-reverse"
|
||||
label="Supabase changes only"
|
||||
description="Only create branches when Supabase files change"
|
||||
{/* Automatic Branching Section */}
|
||||
<div className="space-y-4">
|
||||
<FormField_Shadcn_
|
||||
disabled={!hasAccessToBranching}
|
||||
control={githubSettingsForm.control}
|
||||
name="new_branch_per_pr"
|
||||
render={({ field }) => (
|
||||
<FormItemLayout
|
||||
layout="flex-row-reverse"
|
||||
label="Automatic branching"
|
||||
className={cn(!hasAccessToBranching && 'opacity-25')}
|
||||
description="Create preview branches for every pull request"
|
||||
>
|
||||
<FormControl_Shadcn_>
|
||||
<Switch
|
||||
checked={!hasAccessToBranching ? false : field.value}
|
||||
onCheckedChange={field.onChange}
|
||||
disabled={!hasAccessToBranching || !canCreateGitHubConnection}
|
||||
/>
|
||||
</FormControl_Shadcn_>
|
||||
</FormItemLayout>
|
||||
)}
|
||||
/>
|
||||
|
||||
<div
|
||||
className={cn(
|
||||
'space-y-4 pl-6 border-l',
|
||||
(!hasAccessToBranching || !newBranchPerPr) &&
|
||||
'opacity-25 pointer-events-none'
|
||||
)}
|
||||
>
|
||||
<FormControl_Shadcn_>
|
||||
<Switch
|
||||
checked={field.value}
|
||||
onCheckedChange={(val) => field.onChange(val)}
|
||||
disabled={!newBranchPerPr || disabled || !canUpdateGitHubConnection}
|
||||
/>
|
||||
</FormControl_Shadcn_>
|
||||
</FormItemLayout>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
<CardFooter className="flex justify-between items-center">
|
||||
<div>
|
||||
{connection && (
|
||||
<Button
|
||||
type="outline"
|
||||
onClick={handleRemoveIntegration}
|
||||
disabled={isDeletingConnection}
|
||||
loading={isDeletingConnection}
|
||||
>
|
||||
Disable integration
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
<div className="flex space-x-2">
|
||||
{githubSettingsForm.formState.isDirty && (
|
||||
<Button
|
||||
type="default"
|
||||
onClick={() => {
|
||||
githubSettingsForm.reset()
|
||||
}}
|
||||
disabled={disabled || !canUpdateGitHubConnection}
|
||||
>
|
||||
Cancel
|
||||
</Button>
|
||||
)}
|
||||
<Button
|
||||
type={promptProPlanUpgrade ? 'default' : 'primary'}
|
||||
htmlType="submit"
|
||||
disabled={
|
||||
disabled ||
|
||||
(!connection && !canCreateGitHubConnection) ||
|
||||
(connection && !canUpdateGitHubConnection) ||
|
||||
isCheckingBranch ||
|
||||
isLoading ||
|
||||
(!connection && !githubSettingsForm.getValues().repositoryId) ||
|
||||
(connection && !githubSettingsForm.formState.isDirty)
|
||||
}
|
||||
loading={isCheckingBranch || isLoading}
|
||||
>
|
||||
{connection ? 'Save changes' : 'Enable integration'}
|
||||
</Button>
|
||||
</div>
|
||||
</CardFooter>
|
||||
<FormField_Shadcn_
|
||||
control={githubSettingsForm.control}
|
||||
name="branchLimit"
|
||||
render={({ field }) => (
|
||||
<FormItemLayout
|
||||
layout="flex-row-reverse"
|
||||
label="Branch limit"
|
||||
description="Maximum number of preview branches"
|
||||
>
|
||||
<FormControl_Shadcn_>
|
||||
<Input_Shadcn_
|
||||
{...field}
|
||||
type="number"
|
||||
autoComplete="off"
|
||||
value={!hasAccessToBranching ? 0 : field.value}
|
||||
disabled={
|
||||
!hasAccessToBranching ||
|
||||
!newBranchPerPr ||
|
||||
!canUpdateGitHubConnection
|
||||
}
|
||||
/>
|
||||
</FormControl_Shadcn_>
|
||||
</FormItemLayout>
|
||||
)}
|
||||
/>
|
||||
|
||||
<FormField_Shadcn_
|
||||
control={githubSettingsForm.control}
|
||||
name="supabaseChangesOnly"
|
||||
render={({ field }) => (
|
||||
<FormItemLayout
|
||||
layout="flex-row-reverse"
|
||||
label="Supabase changes only"
|
||||
description="Only create branches when Supabase files change"
|
||||
>
|
||||
<FormControl_Shadcn_>
|
||||
<Switch
|
||||
checked={!hasAccessToBranching ? false : field.value}
|
||||
onCheckedChange={(val) => field.onChange(val)}
|
||||
disabled={
|
||||
!hasAccessToBranching ||
|
||||
!newBranchPerPr ||
|
||||
!canUpdateGitHubConnection
|
||||
}
|
||||
/>
|
||||
</FormControl_Shadcn_>
|
||||
</FormItemLayout>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
<CardFooter className="flex justify-between items-center">
|
||||
<div>
|
||||
{connection && (
|
||||
<Button
|
||||
type="outline"
|
||||
onClick={handleRemoveIntegration}
|
||||
disabled={isDeletingConnection}
|
||||
loading={isDeletingConnection}
|
||||
>
|
||||
Disable integration
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
<div className="flex space-x-2">
|
||||
{githubSettingsForm.formState.isDirty && (
|
||||
<Button
|
||||
type="default"
|
||||
onClick={() => githubSettingsForm.reset()}
|
||||
disabled={!canUpdateGitHubConnection}
|
||||
>
|
||||
Cancel
|
||||
</Button>
|
||||
)}
|
||||
<Button
|
||||
type="default"
|
||||
htmlType="submit"
|
||||
disabled={
|
||||
!hasAccessToGitHubIntegration ||
|
||||
(!connection && !canCreateGitHubConnection) ||
|
||||
(connection && !canUpdateGitHubConnection) ||
|
||||
isCheckingBranch ||
|
||||
isLoading ||
|
||||
(!connection && !githubSettingsForm.getValues().repositoryId) ||
|
||||
(connection && !githubSettingsForm.formState.isDirty)
|
||||
}
|
||||
loading={isCheckingBranch || isLoading}
|
||||
>
|
||||
{connection ? 'Save changes' : 'Enable integration'}
|
||||
</Button>
|
||||
</div>
|
||||
</CardFooter>
|
||||
</motion.div>
|
||||
)}
|
||||
</AnimatePresence>
|
||||
</Card>
|
||||
</form>
|
||||
</Form_Shadcn_>
|
||||
@@ -801,5 +832,3 @@ const GitHubIntegrationConnectionForm = ({
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default GitHubIntegrationConnectionForm
|
||||
+19
-15
@@ -1,22 +1,22 @@
|
||||
import { PermissionAction } from '@supabase/shared-types/out/constants'
|
||||
import { IS_PLATFORM, useFlag, useParams } from 'common'
|
||||
import { useMemo } from 'react'
|
||||
import { GenericSkeletonLoader } from 'ui-patterns/ShimmeringLoader'
|
||||
|
||||
import { useGitHubAuthorizationQuery } from '@/data/integrations/github-authorization-query'
|
||||
import { useParams } from 'common'
|
||||
import { GitHubIntegrationConnectionForm } from './GitHubIntegrationConnectionForm'
|
||||
import {
|
||||
ScaffoldContainer,
|
||||
ScaffoldSection,
|
||||
ScaffoldSectionContent,
|
||||
ScaffoldSectionDetail,
|
||||
} from 'components/layouts/Scaffold'
|
||||
import NoPermission from 'components/ui/NoPermission'
|
||||
import { UpgradeToPro } from 'components/ui/UpgradeToPro'
|
||||
import { useGitHubConnectionsQuery } from 'data/integrations/github-connections-query'
|
||||
import { useAsyncCheckPermissions } from 'hooks/misc/useCheckPermissions'
|
||||
import { useSelectedOrganizationQuery } from 'hooks/misc/useSelectedOrganization'
|
||||
import { BASE_PATH, IS_PLATFORM } from 'lib/constants'
|
||||
import { GenericSkeletonLoader } from 'ui-patterns'
|
||||
import GitHubIntegrationConnectionForm from './GitHubIntegrationConnectionForm'
|
||||
} from '@/components/layouts/Scaffold'
|
||||
import NoPermission from '@/components/ui/NoPermission'
|
||||
import { UpgradeToPro } from '@/components/ui/UpgradeToPro'
|
||||
import { useGitHubAuthorizationQuery } from '@/data/integrations/github-authorization-query'
|
||||
import { useGitHubConnectionsQuery } from '@/data/integrations/github-connections-query'
|
||||
import { useAsyncCheckPermissions } from '@/hooks/misc/useCheckPermissions'
|
||||
import { useSelectedOrganizationQuery } from '@/hooks/misc/useSelectedOrganization'
|
||||
import { BASE_PATH } from '@/lib/constants'
|
||||
|
||||
const IntegrationImageHandler = ({ title }: { title: 'vercel' | 'github' }) => {
|
||||
return (
|
||||
@@ -37,6 +37,7 @@ export const GitHubSection = () => {
|
||||
|
||||
const isProPlanAndUp = organization?.plan?.id !== 'free'
|
||||
const promptProPlanUpgrade = IS_PLATFORM && !isProPlanAndUp
|
||||
const isGithubOnFreePlan = useFlag('githubOnFreePlan')
|
||||
|
||||
const { data: gitHubAuthorization } = useGitHubAuthorizationQuery()
|
||||
|
||||
@@ -74,7 +75,7 @@ export const GitHubSection = () => {
|
||||
branches for every pull request.
|
||||
</p>
|
||||
|
||||
{promptProPlanUpgrade && (
|
||||
{!isGithubOnFreePlan && promptProPlanUpgrade && (
|
||||
<div className="mb-6">
|
||||
<UpgradeToPro
|
||||
layout="vertical"
|
||||
@@ -86,9 +87,12 @@ export const GitHubSection = () => {
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* [Joshen] Show connection form if GH has already been authorized OR no GH authorization but on a paid plan */}
|
||||
{/* So this shouldn't render if there's no GH authorization and on a free plan */}
|
||||
{(!!gitHubAuthorization || !promptProPlanUpgrade) && (
|
||||
{/* [Joshen] Show connection form only if one of the following is true:
|
||||
- GH has already been authorized OR
|
||||
- no GH authorization but on a paid plan OR
|
||||
- on a free plan but the GitHub integration flag is enabled
|
||||
*/}
|
||||
{(!!gitHubAuthorization || !promptProPlanUpgrade || isGithubOnFreePlan) && (
|
||||
<GitHubIntegrationConnectionForm
|
||||
disabled={promptProPlanUpgrade}
|
||||
connection={existingConnection}
|
||||
|
||||
+47
-35
@@ -1,4 +1,5 @@
|
||||
import { ChevronsUpDown, GitBranch } from 'lucide-react'
|
||||
import { forwardRef } from 'react'
|
||||
import { cn, SidebarMenuButton } from 'ui'
|
||||
|
||||
export interface ProjectBranchSelectorTriggerProps {
|
||||
@@ -11,41 +12,52 @@ export interface ProjectBranchSelectorTriggerProps {
|
||||
onClick?: () => void
|
||||
}
|
||||
|
||||
export function ProjectBranchSelectorTrigger({
|
||||
displayProjectName,
|
||||
selectedOrgInitial,
|
||||
isBranch,
|
||||
branchDisplayName,
|
||||
onClick,
|
||||
}: ProjectBranchSelectorTriggerProps) {
|
||||
return (
|
||||
<SidebarMenuButton
|
||||
size="lg"
|
||||
className="group py-1 gap-1.5 w-full flex h-auto text-left data-[state=open]:bg-sidebar-accent data-[state=open]:text-sidebar-accent-foreground touch-manipulation"
|
||||
onClick={onClick}
|
||||
>
|
||||
<div className="relative flex h-8 aspect-square shrink-0 items-center bg-background-muted group-hover:border-stronger justify-center rounded border border-strong text-xs">
|
||||
{selectedOrgInitial}
|
||||
</div>
|
||||
<div className="text-left flex-grow min-w-0">
|
||||
<div className="w-full truncate text-foreground leading-tight max-w-[250px]">
|
||||
{displayProjectName}
|
||||
export const ProjectBranchSelectorTrigger = forwardRef<
|
||||
HTMLButtonElement,
|
||||
ProjectBranchSelectorTriggerProps
|
||||
>(
|
||||
(
|
||||
{
|
||||
displayProjectName,
|
||||
selectedOrgInitial,
|
||||
isBranch,
|
||||
branchDisplayName,
|
||||
onClick,
|
||||
}: ProjectBranchSelectorTriggerProps,
|
||||
ref
|
||||
) => {
|
||||
return (
|
||||
<SidebarMenuButton
|
||||
size="lg"
|
||||
className="group py-1 gap-1.5 w-full flex h-auto text-left data-[state=open]:bg-sidebar-accent data-[state=open]:text-sidebar-accent-foreground touch-manipulation"
|
||||
onClick={onClick}
|
||||
ref={ref}
|
||||
>
|
||||
<div className="relative flex h-8 aspect-square shrink-0 items-center bg-background-muted group-hover:border-stronger justify-center rounded border border-strong text-xs">
|
||||
{selectedOrgInitial}
|
||||
</div>
|
||||
<div
|
||||
className={cn(
|
||||
'flex items-center gap-0.5',
|
||||
isBranch ? 'text-foreground-lighter' : 'text-warning'
|
||||
)}
|
||||
>
|
||||
<GitBranch className="shrink-0 size-3" strokeWidth={1.5} />
|
||||
<span className="truncate min-w-0 leading-tight text-xs">{branchDisplayName}</span>
|
||||
<div className="text-left flex-grow min-w-0">
|
||||
<div className="w-full truncate text-foreground leading-tight max-w-[250px]">
|
||||
{displayProjectName}
|
||||
</div>
|
||||
<div
|
||||
className={cn(
|
||||
'flex items-center gap-0.5',
|
||||
isBranch ? 'text-foreground-lighter' : 'text-warning'
|
||||
)}
|
||||
>
|
||||
<GitBranch className="shrink-0 size-3" strokeWidth={1.5} />
|
||||
<span className="truncate min-w-0 leading-tight text-xs">{branchDisplayName}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ChevronsUpDown
|
||||
strokeWidth={1.5}
|
||||
className="ml-auto text-foreground-lighter !w-4 !h-4 md:hidden md:group-hover:flex"
|
||||
/>
|
||||
</SidebarMenuButton>
|
||||
)
|
||||
}
|
||||
<ChevronsUpDown
|
||||
strokeWidth={1.5}
|
||||
className="ml-auto text-foreground-lighter !w-4 !h-4 md:hidden md:group-hover:flex"
|
||||
/>
|
||||
</SidebarMenuButton>
|
||||
)
|
||||
}
|
||||
)
|
||||
|
||||
ProjectBranchSelectorTrigger.displayName = 'ProjectBranchSelectorTrigger'
|
||||
@@ -1,7 +1,8 @@
|
||||
import { ReactNode } from 'react'
|
||||
|
||||
import { cn } from 'ui'
|
||||
import { Admonition } from 'ui-patterns'
|
||||
|
||||
import { DocsButton } from './DocsButton'
|
||||
import { UpgradePlanButton } from './UpgradePlanButton'
|
||||
|
||||
interface UpgradeToProProps {
|
||||
@@ -21,6 +22,7 @@ interface UpgradeToProProps {
|
||||
layout?: 'vertical' | 'horizontal'
|
||||
variant?: 'default' | 'primary'
|
||||
className?: string
|
||||
docsUrl?: string
|
||||
}
|
||||
|
||||
export const UpgradeToPro = ({
|
||||
@@ -37,6 +39,7 @@ export const UpgradeToPro = ({
|
||||
layout = 'horizontal',
|
||||
variant = 'primary',
|
||||
className,
|
||||
docsUrl,
|
||||
}: UpgradeToProProps) => {
|
||||
return (
|
||||
<Admonition
|
||||
@@ -47,16 +50,19 @@ export const UpgradeToPro = ({
|
||||
description={secondaryText}
|
||||
className={cn(fullWidth && 'border-0 rounded-none border-b', className)}
|
||||
actions={
|
||||
<UpgradePlanButton
|
||||
plan={planToUpgrade}
|
||||
addon={addon}
|
||||
source={source}
|
||||
featureProposition={featureProposition}
|
||||
disabled={disabled}
|
||||
variant={variant}
|
||||
>
|
||||
{buttonText}
|
||||
</UpgradePlanButton>
|
||||
<>
|
||||
<UpgradePlanButton
|
||||
plan={planToUpgrade}
|
||||
addon={addon}
|
||||
source={source}
|
||||
featureProposition={featureProposition}
|
||||
disabled={disabled}
|
||||
variant={variant}
|
||||
>
|
||||
{buttonText}
|
||||
</UpgradePlanButton>
|
||||
{!!docsUrl && <DocsButton href={docsUrl} />}
|
||||
</>
|
||||
}
|
||||
/>
|
||||
)
|
||||
|
||||
Reference in new issue
Block a user