mirror of
https://github.com/supabase/supabase.git
synced 2026-10-05 09:25:06 +03:00
fix: show run logs for gitless branches (#36949)
* fix: show run logs for gitless branches * fix: show edit branch modal for production * remove side panel github --------- Co-authored-by: Saxon Fletcher <saxonafletcher@gmail.com>
This commit is contained in:
1 parent
b05457666f
commit
83b5664e6b
10 files changed
+133
-947
No files matched your search
@@ -322,6 +322,7 @@ const BranchManagement = () => {
|
||||
) : (
|
||||
<PullRequestsEmptyState
|
||||
url={generateCreatePullRequestURL()}
|
||||
projectRef={projectRef ?? '_'}
|
||||
hasBranches={previewBranches.length > 0}
|
||||
githubConnection={githubConnection}
|
||||
gitlessBranching={gitlessBranching}
|
||||
|
||||
@@ -210,32 +210,51 @@ export const BranchRow = ({
|
||||
{isMain ? (
|
||||
<div className="flex items-center gap-x-2">
|
||||
{repo && (
|
||||
<>
|
||||
<Button asChild type="default" iconRight={<ExternalLink size={14} />}>
|
||||
<Link
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
passHref
|
||||
href={`https://github.com/${repo}`}
|
||||
>
|
||||
View Repository
|
||||
</Link>
|
||||
</Button>
|
||||
<WorkflowLogs projectRef={branch.project_ref} />
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button type="text" icon={<MoreVertical />} className="px-1" />
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent className="p-0 w-56" side="bottom" align="end">
|
||||
<Link passHref href={`/project/${projectRef}/settings/integrations`}>
|
||||
<DropdownMenuItem asChild className="gap-x-2">
|
||||
<a>Change production branch</a>
|
||||
</DropdownMenuItem>
|
||||
</Link>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
</>
|
||||
<Button asChild type="default" iconRight={<ExternalLink size={14} />}>
|
||||
<Link target="_blank" rel="noreferrer" passHref href={`https://github.com/${repo}`}>
|
||||
View Repository
|
||||
</Link>
|
||||
</Button>
|
||||
)}
|
||||
<WorkflowLogs projectRef={branch.project_ref} />
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button type="text" icon={<MoreVertical />} className="px-1" />
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent className="p-0 w-56" side="bottom" align="end">
|
||||
{repo ? (
|
||||
<Link passHref href={`/project/${projectRef}/settings/integrations`}>
|
||||
<DropdownMenuItem asChild className="gap-x-2">
|
||||
<a>Change production branch</a>
|
||||
</DropdownMenuItem>
|
||||
</Link>
|
||||
) : (
|
||||
<Tooltip>
|
||||
<TooltipTrigger
|
||||
asChild={canUpdateBranches && isBranchActiveHealthy}
|
||||
className="w-full"
|
||||
>
|
||||
<DropdownMenuItem
|
||||
className="gap-x-2"
|
||||
disabled={!canUpdateBranches || !isBranchActiveHealthy || isUpdating}
|
||||
onSelect={() => setShowEditBranchModal(true)}
|
||||
onClick={() => setShowEditBranchModal(true)}
|
||||
>
|
||||
<Pencil size={14} />
|
||||
Edit Branch
|
||||
</DropdownMenuItem>
|
||||
</TooltipTrigger>
|
||||
{(!canUpdateBranches || !isBranchActiveHealthy) && (
|
||||
<TooltipContent side="left">
|
||||
{!canUpdateBranches
|
||||
? 'You need additional permissions to edit branches'
|
||||
: 'Branch is still initializing. Please wait for the branch to become healthy before editing.'}
|
||||
</TooltipContent>
|
||||
)}
|
||||
</Tooltip>
|
||||
)}
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
</div>
|
||||
) : (
|
||||
<div className="flex items-center gap-x-2">
|
||||
|
||||
@@ -16,9 +16,11 @@ import { useCheckGithubBranchValidity } from 'data/integrations/github-branch-ch
|
||||
import { useGitHubConnectionsQuery } from 'data/integrations/github-connections-query'
|
||||
import { useSelectedOrganization } from 'hooks/misc/useSelectedOrganization'
|
||||
import { useSelectedProject } from 'hooks/misc/useSelectedProject'
|
||||
import { useFlag } from 'hooks/ui/useFlag'
|
||||
import { BASE_PATH } from 'lib/constants'
|
||||
import { sidePanelsState } from 'state/side-panels'
|
||||
import { useRouter } from 'next/router'
|
||||
import {
|
||||
Badge,
|
||||
Button,
|
||||
Dialog,
|
||||
DialogContent,
|
||||
@@ -29,7 +31,6 @@ import {
|
||||
DialogTitle,
|
||||
FormControl_Shadcn_,
|
||||
FormField_Shadcn_,
|
||||
FormItem_Shadcn_,
|
||||
FormMessage_Shadcn_,
|
||||
Form_Shadcn_,
|
||||
Input_Shadcn_,
|
||||
@@ -46,8 +47,10 @@ interface EditBranchModalProps {
|
||||
|
||||
export const EditBranchModal = ({ branch, visible, onClose }: EditBranchModalProps) => {
|
||||
const { ref } = useParams()
|
||||
const router = useRouter()
|
||||
const projectDetails = useSelectedProject()
|
||||
const selectedOrg = useSelectedOrganization()
|
||||
const gitlessBranching = useFlag('gitlessBranching')
|
||||
|
||||
const [isGitBranchValid, setIsGitBranchValid] = useState(false)
|
||||
|
||||
@@ -100,13 +103,18 @@ export const EditBranchModal = ({ branch, visible, onClose }: EditBranchModalPro
|
||||
val === branch?.name || (branches ?? []).every((b) => b.name !== val),
|
||||
'A branch with this name already exists'
|
||||
),
|
||||
gitBranchName: z.string().optional(),
|
||||
gitBranchName: z
|
||||
.string()
|
||||
.refine(
|
||||
(val) => gitlessBranching || !githubConnection || (val && val.length > 0),
|
||||
'Git branch name is required when GitHub is connected'
|
||||
),
|
||||
})
|
||||
.superRefine(async (val, ctx) => {
|
||||
if (val.gitBranchName && val.gitBranchName.length > 0 && githubConnection?.id) {
|
||||
if (val.gitBranchName && val.gitBranchName.length > 0 && githubConnection?.repository.id) {
|
||||
try {
|
||||
await checkGithubBranchValidity({
|
||||
repositoryId: githubConnection.id,
|
||||
repositoryId: githubConnection.repository.id,
|
||||
branchName: val.gitBranchName,
|
||||
})
|
||||
setIsGitBranchValid(true)
|
||||
@@ -119,8 +127,10 @@ export const EditBranchModal = ({ branch, visible, onClose }: EditBranchModalPro
|
||||
})
|
||||
}
|
||||
} else {
|
||||
// If git branch is empty or removed, it's valid
|
||||
setIsGitBranchValid(!val.gitBranchName || val.gitBranchName.length === 0)
|
||||
// If git branch is empty or removed, it's valid for gitless branching
|
||||
setIsGitBranchValid(
|
||||
!val.gitBranchName || val.gitBranchName.length === 0 || gitlessBranching
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
@@ -162,35 +172,44 @@ export const EditBranchModal = ({ branch, visible, onClose }: EditBranchModalPro
|
||||
// Pre-fill form when the modal becomes visible and branch data is available
|
||||
useEffect(() => {
|
||||
if (visible && branch) {
|
||||
setIsGitBranchValid(!!branch.git_branch) // Initial validity based on existing link
|
||||
setIsGitBranchValid(!!branch.git_branch || gitlessBranching) // Initial validity based on existing link or gitless branching
|
||||
form.reset({
|
||||
branchName: branch.name ?? '',
|
||||
gitBranchName: branch.git_branch ?? '',
|
||||
})
|
||||
}
|
||||
}, [branch, visible, form])
|
||||
}, [branch, visible, form, gitlessBranching])
|
||||
|
||||
// Handle initial state and changes for git branch validity
|
||||
useEffect(() => {
|
||||
setIsGitBranchValid(
|
||||
!form.getValues('gitBranchName') || form.getValues('gitBranchName')?.length === 0
|
||||
!form.getValues('gitBranchName') ||
|
||||
form.getValues('gitBranchName')?.length === 0 ||
|
||||
gitlessBranching
|
||||
)
|
||||
// Trigger validation if a git branch name exists initially or is entered
|
||||
if (form.getValues('gitBranchName')) {
|
||||
form.trigger('gitBranchName')
|
||||
}
|
||||
}, [githubConnection?.id, form.getValues('gitBranchName'), form.trigger, visible, branch])
|
||||
}, [
|
||||
githubConnection?.id,
|
||||
form.getValues('gitBranchName'),
|
||||
form.trigger,
|
||||
visible,
|
||||
branch,
|
||||
gitlessBranching,
|
||||
])
|
||||
|
||||
const openLinkerPanel = () => {
|
||||
onClose()
|
||||
sidePanelsState.setGithubConnectionsOpen(true)
|
||||
router.push(`/project/${projectRef}/settings/integrations`)
|
||||
}
|
||||
|
||||
return (
|
||||
<Dialog open={visible} onOpenChange={(open) => !open && onClose()}>
|
||||
<DialogContent size="large" hideClose>
|
||||
<DialogHeader padding="small">
|
||||
<DialogTitle>Edit branch "{branch?.name}"</DialogTitle> {/* Update title */}
|
||||
<DialogTitle>Edit branch "{branch?.name}"</DialogTitle>
|
||||
</DialogHeader>
|
||||
<DialogSectionSeparator />
|
||||
<Form_Shadcn_ {...form}>
|
||||
@@ -217,27 +236,37 @@ export const EditBranchModal = ({ branch, visible, onClose }: EditBranchModalPro
|
||||
control={form.control}
|
||||
name="gitBranchName"
|
||||
render={({ field }) => (
|
||||
<FormItem_Shadcn_>
|
||||
<div className="flex items-center justify-between mb-2">
|
||||
<Label>Link to Git Branch (Optional)</Label>
|
||||
<div className="flex items-center gap-2 text-sm">
|
||||
<Image
|
||||
className={cn('dark:invert')}
|
||||
src={`${BASE_PATH}/img/icons/github-icon.svg`}
|
||||
width={16}
|
||||
height={16}
|
||||
alt={`GitHub icon`}
|
||||
/>
|
||||
<Link
|
||||
href={`https://github.com/${repoOwner}/${repoName}`}
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
className="text-foreground hover:underline"
|
||||
>
|
||||
{repoOwner}/{repoName}
|
||||
</Link>
|
||||
<FormItemLayout
|
||||
label={
|
||||
<div className="flex items-center justify-between w-full gap-4">
|
||||
<span className="flex-1">
|
||||
Sync with Git branch {gitlessBranching ? '(optional)' : ''}
|
||||
</span>
|
||||
<div className="flex items-center gap-2 text-sm">
|
||||
<Image
|
||||
className={cn('dark:invert')}
|
||||
src={`${BASE_PATH}/img/icons/github-icon.svg`}
|
||||
width={16}
|
||||
height={16}
|
||||
alt={`GitHub icon`}
|
||||
/>
|
||||
<Link
|
||||
href={`https://github.com/${repoOwner}/${repoName}`}
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
className="text-foreground hover:underline"
|
||||
>
|
||||
{repoOwner}/{repoName}
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
description={
|
||||
gitlessBranching
|
||||
? 'Automatically deploy changes on every commit'
|
||||
: 'If linked, migrations from this Git branch will be automatically deployed.'
|
||||
}
|
||||
>
|
||||
<div className="relative">
|
||||
<FormControl_Shadcn_>
|
||||
<Input_Shadcn_
|
||||
@@ -253,11 +282,8 @@ export const EditBranchModal = ({ branch, visible, onClose }: EditBranchModalPro
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<p className="text-sm text-foreground-light mt-2">
|
||||
If linked, migrations from this Git branch will be automatically deployed.
|
||||
</p>
|
||||
<FormMessage_Shadcn_ />
|
||||
</FormItem_Shadcn_>
|
||||
</FormItemLayout>
|
||||
)}
|
||||
/>
|
||||
)}
|
||||
@@ -270,15 +296,23 @@ export const EditBranchModal = ({ branch, visible, onClose }: EditBranchModalPro
|
||||
)}
|
||||
{isSuccessConnections && !githubConnection && (
|
||||
<div className="flex items-center gap-2 justify-between">
|
||||
<div>
|
||||
<Label>GitHub Repository</Label>
|
||||
<div className="flex flex-col gap-1">
|
||||
<div className="flex items-center gap-2">
|
||||
<Label>Sync with a GitHub branch</Label>
|
||||
{!gitlessBranching && (
|
||||
<Badge variant="warning" size="small">
|
||||
Required
|
||||
</Badge>
|
||||
)}
|
||||
</div>
|
||||
<p className="text-sm text-foreground-light">
|
||||
Optionally connect to a GitHub repository to manage migrations automatically
|
||||
for this branch.
|
||||
{gitlessBranching
|
||||
? 'Optionally connect to a GitHub repository to manage migrations automatically for this branch.'
|
||||
: 'Keep this preview branch in sync with a chosen GitHub branch'}
|
||||
</p>
|
||||
</div>
|
||||
<Button type="default" icon={<Github />} onClick={openLinkerPanel}>
|
||||
Connect to GitHub
|
||||
{gitlessBranching ? 'Connect to GitHub' : 'Configure'}
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
@@ -290,7 +324,13 @@ export const EditBranchModal = ({ branch, visible, onClose }: EditBranchModalPro
|
||||
</Button>
|
||||
<Button
|
||||
form={formId}
|
||||
disabled={!isSuccessConnections || isUpdating || !canSubmit || isChecking}
|
||||
disabled={
|
||||
!isSuccessConnections ||
|
||||
isUpdating ||
|
||||
!canSubmit ||
|
||||
isChecking ||
|
||||
(!gitlessBranching && !githubConnection)
|
||||
}
|
||||
loading={isUpdating}
|
||||
type="primary"
|
||||
htmlType="submit"
|
||||
|
||||
@@ -55,11 +55,13 @@ export const BranchingEmptyState = () => {
|
||||
|
||||
export const PullRequestsEmptyState = ({
|
||||
url,
|
||||
projectRef,
|
||||
hasBranches,
|
||||
githubConnection,
|
||||
gitlessBranching,
|
||||
}: {
|
||||
url: string
|
||||
projectRef: string
|
||||
hasBranches: boolean
|
||||
githubConnection?: any
|
||||
gitlessBranching?: boolean
|
||||
@@ -74,12 +76,9 @@ export const PullRequestsEmptyState = ({
|
||||
Sync GitHub repos to Supabase projects for automatic branch creation and merging
|
||||
</p>
|
||||
</div>
|
||||
<Button
|
||||
type="default"
|
||||
icon={<Github />}
|
||||
onClick={() => sidePanelsState.setGithubConnectionsOpen(true)}
|
||||
>
|
||||
Connect to GitHub
|
||||
|
||||
<Button type="default" icon={<Github />} asChild>
|
||||
<Link href={`/project/${projectRef}/settings/integrations`}>Connect to GitHub</Link>
|
||||
</Button>
|
||||
</div>
|
||||
)
|
||||
|
||||
-2
@@ -28,7 +28,6 @@ import {
|
||||
} from 'lib/github'
|
||||
import { useSidePanelsStateSnapshot } from 'state/side-panels'
|
||||
import { IntegrationConnectionItem } from '../../Integrations/VercelGithub/IntegrationConnection'
|
||||
import SidePanelGitHubRepoLinker from './SidePanelGitHubRepoLinker'
|
||||
import SidePanelVercelProjectLinker from './SidePanelVercelProjectLinker'
|
||||
import { useRouter } from 'next/router'
|
||||
|
||||
@@ -183,7 +182,6 @@ The GitHub app will watch for changes in your repository such as file changes, b
|
||||
<ScaffoldDivider />
|
||||
<VercelSection isProjectScoped={false} />
|
||||
<SidePanelVercelProjectLinker />
|
||||
<SidePanelGitHubRepoLinker />
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
-742
@@ -1,742 +0,0 @@
|
||||
import { zodResolver } from '@hookform/resolvers/zod'
|
||||
import { PermissionAction } from '@supabase/shared-types/out/constants'
|
||||
import { ChevronDown, Loader2, PlusIcon } from 'lucide-react'
|
||||
import { useCallback, useEffect, useMemo, useState } from 'react'
|
||||
import { useForm } from 'react-hook-form'
|
||||
import { toast } from 'sonner'
|
||||
import * as z from 'zod'
|
||||
|
||||
import { Markdown } from 'components/interfaces/Markdown'
|
||||
import { useBranchCreateMutation } from 'data/branches/branch-create-mutation'
|
||||
import { useBranchUpdateMutation } from 'data/branches/branch-update-mutation'
|
||||
import { useBranchesQuery } from 'data/branches/branches-query'
|
||||
import { useCheckGithubBranchValidity } from 'data/integrations/github-branch-check-query'
|
||||
import { useGitHubAuthorizationQuery } from 'data/integrations/github-authorization-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 { useGitHubConnectionsQuery } from 'data/integrations/github-connections-query'
|
||||
import { useGitHubRepositoriesQuery } from 'data/integrations/github-repositories-query'
|
||||
import type { GitHubConnection } from 'data/integrations/integrations.types'
|
||||
import { useProjectsQuery } from 'data/projects/projects-query'
|
||||
import { useCheckPermissions } from 'hooks/misc/useCheckPermissions'
|
||||
import { useSelectedOrganization } from 'hooks/misc/useSelectedOrganization'
|
||||
import { useSelectedProject } from 'hooks/misc/useSelectedProject'
|
||||
import { openInstallGitHubIntegrationWindow } from 'lib/github'
|
||||
import { EMPTY_ARR } from 'lib/void'
|
||||
import { useSidePanelsStateSnapshot } from 'state/side-panels'
|
||||
import {
|
||||
Button,
|
||||
CommandEmpty_Shadcn_,
|
||||
CommandGroup_Shadcn_,
|
||||
CommandInput_Shadcn_,
|
||||
CommandItem_Shadcn_,
|
||||
CommandList_Shadcn_,
|
||||
CommandSeparator_Shadcn_,
|
||||
Command_Shadcn_,
|
||||
Form_Shadcn_,
|
||||
FormControl_Shadcn_,
|
||||
FormField_Shadcn_,
|
||||
Input_Shadcn_,
|
||||
PopoverContent_Shadcn_,
|
||||
PopoverTrigger_Shadcn_,
|
||||
Popover_Shadcn_,
|
||||
Sheet,
|
||||
SheetContent,
|
||||
SheetDescription,
|
||||
SheetFooter,
|
||||
SheetHeader,
|
||||
SheetTitle,
|
||||
Switch,
|
||||
cn,
|
||||
} from 'ui'
|
||||
import ConfirmationModal from 'ui-patterns/Dialogs/ConfirmationModal'
|
||||
import { FormItemLayout } from 'ui-patterns/form/FormItemLayout/FormItemLayout'
|
||||
|
||||
const GITHUB_ICON = (
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 98 96" className="w-6">
|
||||
<path
|
||||
fill="#ffffff"
|
||||
fillRule="evenodd"
|
||||
d="M48.854 0C21.839 0 0 22 0 49.217c0 21.756 13.993 40.172 33.405 46.69 2.427.49 3.316-1.059 3.316-2.362 0-1.141-.08-5.052-.08-9.127-13.59 2.934-16.42-5.867-16.42-5.867-2.184-5.704-5.42-7.17-5.42-7.17-4.448-3.015.324-3.015.324-3.015 4.934.326 7.523 5.052 7.523 5.052 4.367 7.496 11.404 5.378 14.235 4.074.404-3.178 1.699-5.378 3.074-6.6-10.839-1.141-22.243-5.378-22.243-24.283 0-5.378 1.94-9.778 5.014-13.2-.485-1.222-2.184-6.275.486-13.038 0 0 4.125-1.304 13.426 5.052a46.97 46.97 0 0 1 12.214-1.63c4.125 0 8.33.571 12.213 1.63 9.302-6.356 13.427-5.052 13.427-5.052 2.67 6.763.97 11.816.485 13.038 3.155 3.422 5.015 7.822 5.015 13.2 0 18.905-11.404 23.06-22.324 24.283 1.78 1.548 3.316 4.481 3.316 9.126 0 6.6-.08 11.897-.08 13.526 0 1.304.89 2.853 3.316 2.364 19.412-6.52 33.405-24.935 33.405-46.691C97.707 22 75.788 0 48.854 0z"
|
||||
clipRule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
)
|
||||
|
||||
export type SidePanelGitHubRepoLinkerProps = {
|
||||
projectRef?: string
|
||||
}
|
||||
|
||||
const SidePanelGitHubRepoLinker = ({ projectRef }: SidePanelGitHubRepoLinkerProps) => {
|
||||
const selectedProject = useSelectedProject()
|
||||
const selectedOrganization = useSelectedOrganization()
|
||||
const sidePanelStateSnapshot = useSidePanelsStateSnapshot()
|
||||
|
||||
const [isConfirmingBranchChange, setIsConfirmingBranchChange] = useState(false)
|
||||
const [isConfirmingRepoChange, setIsConfirmingRepoChange] = useState(false)
|
||||
const [selectedRepositoryId, setSelectedRepositoryId] = useState<string | undefined>()
|
||||
const [repoComboBoxOpen, setRepoComboboxOpen] = useState(false)
|
||||
|
||||
const visible = sidePanelStateSnapshot.githubConnectionsOpen
|
||||
|
||||
const canUpdateGitHubConnection = useCheckPermissions(
|
||||
PermissionAction.UPDATE,
|
||||
'integrations.github_connections'
|
||||
)
|
||||
const canCreateGitHubConnection = useCheckPermissions(
|
||||
PermissionAction.CREATE,
|
||||
'integrations.github_connections'
|
||||
)
|
||||
|
||||
const { data: gitHubAuthorization, isLoading: isLoadingGitHubAuthorization } =
|
||||
useGitHubAuthorizationQuery({ enabled: visible })
|
||||
|
||||
const { data: githubReposData, isLoading: isLoadingGitHubRepos } = useGitHubRepositoriesQuery<
|
||||
any[]
|
||||
>({
|
||||
enabled: visible && Boolean(gitHubAuthorization),
|
||||
})
|
||||
|
||||
const { data: connections } = useGitHubConnectionsQuery(
|
||||
{
|
||||
organizationId: selectedOrganization?.id,
|
||||
},
|
||||
{
|
||||
enabled: visible,
|
||||
}
|
||||
)
|
||||
|
||||
const { data: existingBranches } = useBranchesQuery(
|
||||
{ projectRef: selectedProject?.parent_project_ref || selectedProject?.ref },
|
||||
{ enabled: !!selectedProject?.ref }
|
||||
)
|
||||
|
||||
const githubRepos = useMemo(
|
||||
() =>
|
||||
githubReposData?.map((repo: any) => ({
|
||||
id: repo.id.toString(),
|
||||
name: repo.name,
|
||||
installation_id: repo.installation_id,
|
||||
})) ?? EMPTY_ARR,
|
||||
[githubReposData]
|
||||
)
|
||||
|
||||
const existingConnection = useMemo(
|
||||
() => connections?.find((c) => c.project.ref === projectRef),
|
||||
[connections, projectRef]
|
||||
)
|
||||
|
||||
const prodBranch = existingBranches?.find((branch) => branch.is_default)
|
||||
|
||||
// Mutations
|
||||
const { mutate: createBranch } = useBranchCreateMutation({
|
||||
onError: (error) => {
|
||||
console.error('Failed to enable branching:', error)
|
||||
},
|
||||
})
|
||||
|
||||
const { mutate: updateBranch } = useBranchUpdateMutation()
|
||||
|
||||
const { mutateAsync: checkGithubBranchValidity, isLoading: isCheckingBranch } =
|
||||
useCheckGithubBranchValidity({ onError: () => {} })
|
||||
|
||||
const { mutate: createConnection, isLoading: isCreatingConnection } =
|
||||
useGitHubConnectionCreateMutation({
|
||||
onSuccess: (data, variables) => {
|
||||
// Enable branching if not already enabled
|
||||
if (selectedProject && !selectedProject.is_branch_enabled) {
|
||||
createBranch({
|
||||
projectRef: variables.connection.project_ref,
|
||||
branchName: 'main',
|
||||
gitBranch: '',
|
||||
})
|
||||
}
|
||||
toast.success('Successfully linked project to repository!')
|
||||
},
|
||||
})
|
||||
|
||||
const { mutateAsync: deleteConnection } = useGitHubConnectionDeleteMutation()
|
||||
|
||||
const { mutate: updateConnectionSettings, isLoading: isUpdatingConnection } =
|
||||
useGitHubConnectionUpdateMutation()
|
||||
|
||||
// Form schema with validation
|
||||
const GitHubSettingsSchema = z
|
||||
.object({
|
||||
repositoryId: z.string().min(1, 'Please select a repository'),
|
||||
enableProductionSync: z.boolean().default(false),
|
||||
branchName: z.string(),
|
||||
new_branch_per_pr: z.boolean().default(false),
|
||||
supabaseDirectory: z.string().default('.'),
|
||||
supabaseChangesOnly: z.boolean().default(false),
|
||||
branchLimit: z.string().default('50'),
|
||||
})
|
||||
.superRefine(async (val, ctx) => {
|
||||
if (
|
||||
val.enableProductionSync &&
|
||||
val.branchName &&
|
||||
val.branchName.length > 0 &&
|
||||
val.repositoryId
|
||||
) {
|
||||
try {
|
||||
await checkGithubBranchValidity({
|
||||
repositoryId: Number(val.repositoryId),
|
||||
branchName: val.branchName,
|
||||
})
|
||||
} catch (error) {
|
||||
const selectedRepo = githubRepos.find((repo) => repo.id === val.repositoryId)
|
||||
const repoName = selectedRepo?.name || 'selected repository'
|
||||
ctx.addIssue({
|
||||
code: z.ZodIssueCode.custom,
|
||||
message: `Branch "${val.branchName}" not found in ${repoName}`,
|
||||
path: ['branchName'],
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
const githubSettingsForm = useForm<z.infer<typeof GitHubSettingsSchema>>({
|
||||
resolver: zodResolver(GitHubSettingsSchema),
|
||||
mode: 'onSubmit',
|
||||
reValidateMode: 'onBlur',
|
||||
defaultValues: {
|
||||
repositoryId: '',
|
||||
enableProductionSync: false,
|
||||
branchName: '',
|
||||
new_branch_per_pr: false,
|
||||
supabaseDirectory: '',
|
||||
supabaseChangesOnly: false,
|
||||
branchLimit: '50',
|
||||
},
|
||||
})
|
||||
|
||||
const enableProductionSync = githubSettingsForm.watch('enableProductionSync')
|
||||
const newBranchPerPr = githubSettingsForm.watch('new_branch_per_pr')
|
||||
const currentRepositoryId = githubSettingsForm.watch('repositoryId')
|
||||
|
||||
// Calculate selected repository based on current form value
|
||||
const selectedRepository = githubRepos.find(
|
||||
(repo) => repo.id === (currentRepositoryId || selectedRepositoryId)
|
||||
)
|
||||
|
||||
// Initialize form when existing connection is loaded
|
||||
useEffect(() => {
|
||||
if (existingConnection) {
|
||||
const hasGitBranch = Boolean(prodBranch?.git_branch?.trim())
|
||||
const formValues = {
|
||||
repositoryId: existingConnection.repository.id.toString(),
|
||||
enableProductionSync: hasGitBranch,
|
||||
branchName: prodBranch?.git_branch || 'main',
|
||||
new_branch_per_pr: existingConnection.new_branch_per_pr,
|
||||
supabaseDirectory: existingConnection.workdir || '',
|
||||
supabaseChangesOnly: existingConnection.supabase_changes_only,
|
||||
branchLimit: String(existingConnection.branch_limit),
|
||||
}
|
||||
|
||||
// Only reset if the form hasn't been modified or if the connection has actually changed
|
||||
if (
|
||||
!githubSettingsForm.formState.isDirty ||
|
||||
githubSettingsForm.getValues().repositoryId !== formValues.repositoryId
|
||||
) {
|
||||
githubSettingsForm.reset(formValues)
|
||||
setSelectedRepositoryId(existingConnection.repository.id.toString())
|
||||
}
|
||||
}
|
||||
}, [existingConnection?.id, prodBranch?.git_branch, prodBranch?.id])
|
||||
|
||||
// Handle clearing branch name when production sync is disabled
|
||||
useEffect(() => {
|
||||
if (!enableProductionSync) {
|
||||
githubSettingsForm.setValue('branchName', '')
|
||||
} else if (enableProductionSync && !githubSettingsForm.getValues().branchName) {
|
||||
githubSettingsForm.setValue('branchName', 'main')
|
||||
}
|
||||
}, [enableProductionSync, githubSettingsForm])
|
||||
|
||||
const handleCreateOrUpdateConnection = async (data: z.infer<typeof GitHubSettingsSchema>) => {
|
||||
if (!selectedProject?.ref || !selectedOrganization?.id) return
|
||||
|
||||
const selectedRepo = githubRepos.find((repo) => repo.id === data.repositoryId)
|
||||
if (!selectedRepo) {
|
||||
toast.error('Please select a repository')
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
if (existingConnection) {
|
||||
// Check if repository is being changed
|
||||
const isRepoChanged = existingConnection.repository.id.toString() !== data.repositoryId
|
||||
if (isRepoChanged) {
|
||||
setIsConfirmingRepoChange(true)
|
||||
return
|
||||
}
|
||||
// Update existing connection
|
||||
await handleUpdateConnection(data, existingConnection)
|
||||
} else {
|
||||
// Create new connection
|
||||
await handleCreateConnection(data, selectedRepo)
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error managing connection:', error)
|
||||
}
|
||||
}
|
||||
|
||||
const handleCreateConnection = async (
|
||||
data: z.infer<typeof GitHubSettingsSchema>,
|
||||
selectedRepo: { id: string; installation_id: number }
|
||||
) => {
|
||||
if (!selectedProject?.ref || !selectedOrganization?.id) return
|
||||
|
||||
// Create the connection
|
||||
createConnection({
|
||||
organizationId: selectedOrganization.id,
|
||||
connection: {
|
||||
installation_id: selectedRepo.installation_id,
|
||||
project_ref: selectedProject.ref,
|
||||
repository_id: Number(selectedRepo.id),
|
||||
workdir: data.supabaseDirectory,
|
||||
supabase_changes_only: data.supabaseChangesOnly,
|
||||
branch_limit: Number(data.branchLimit),
|
||||
new_branch_per_pr: data.new_branch_per_pr,
|
||||
},
|
||||
})
|
||||
|
||||
// Handle branch creation/update for production sync
|
||||
if (data.enableProductionSync && data.branchName) {
|
||||
if (prodBranch?.id) {
|
||||
updateBranch({
|
||||
id: prodBranch.id,
|
||||
projectRef: selectedProject.ref,
|
||||
gitBranch: data.branchName,
|
||||
})
|
||||
} else {
|
||||
// Create new branch if none exists
|
||||
createBranch({
|
||||
projectRef: selectedProject.ref,
|
||||
branchName: 'main',
|
||||
gitBranch: data.branchName,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
toast.success('GitHub connection created successfully')
|
||||
sidePanelStateSnapshot.setGithubConnectionsOpen(false)
|
||||
}
|
||||
|
||||
const handleUpdateConnection = async (
|
||||
data: z.infer<typeof GitHubSettingsSchema>,
|
||||
connection: GitHubConnection
|
||||
) => {
|
||||
if (!selectedProject?.ref || !selectedOrganization?.id) return
|
||||
|
||||
const originalBranchName = prodBranch?.git_branch
|
||||
|
||||
if (originalBranchName && data.branchName !== originalBranchName && data.enableProductionSync) {
|
||||
setIsConfirmingBranchChange(true)
|
||||
return
|
||||
}
|
||||
|
||||
await executeUpdate(data, connection)
|
||||
}
|
||||
|
||||
const executeUpdate = async (
|
||||
data: z.infer<typeof GitHubSettingsSchema>,
|
||||
connection: GitHubConnection
|
||||
) => {
|
||||
if (!selectedProject?.ref || !selectedOrganization?.id) return
|
||||
|
||||
// Update connection settings
|
||||
updateConnectionSettings({
|
||||
connectionId: connection.id,
|
||||
organizationId: selectedOrganization.id,
|
||||
connection: {
|
||||
workdir: data.supabaseDirectory,
|
||||
supabase_changes_only: data.supabaseChangesOnly,
|
||||
branch_limit: Number(data.branchLimit),
|
||||
new_branch_per_pr: data.new_branch_per_pr,
|
||||
},
|
||||
})
|
||||
|
||||
// Handle branch update
|
||||
if (prodBranch?.id) {
|
||||
updateBranch({
|
||||
id: prodBranch.id,
|
||||
projectRef: selectedProject.ref,
|
||||
gitBranch: data.enableProductionSync ? data.branchName : '',
|
||||
})
|
||||
}
|
||||
|
||||
toast.success('GitHub integration updated successfully')
|
||||
setIsConfirmingBranchChange(false)
|
||||
sidePanelStateSnapshot.setGithubConnectionsOpen(false)
|
||||
}
|
||||
|
||||
const onConfirmBranchChange = async () => {
|
||||
if (existingConnection) {
|
||||
await executeUpdate(githubSettingsForm.getValues(), existingConnection)
|
||||
}
|
||||
}
|
||||
|
||||
const onConfirmRepoChange = async () => {
|
||||
const data = githubSettingsForm.getValues()
|
||||
const selectedRepo = githubRepos.find((repo) => repo.id === data.repositoryId)
|
||||
|
||||
if (!selectedRepo || !existingConnection) return
|
||||
|
||||
try {
|
||||
// Delete the existing connection
|
||||
await deleteConnection({
|
||||
organizationId: selectedOrganization!.id,
|
||||
connectionId: existingConnection.id,
|
||||
})
|
||||
|
||||
// Create new connection with the new repository
|
||||
await handleCreateConnection(data, selectedRepo)
|
||||
|
||||
setIsConfirmingRepoChange(false)
|
||||
} catch (error) {
|
||||
console.error('Error changing repository:', error)
|
||||
toast.error('Failed to change repository')
|
||||
}
|
||||
}
|
||||
|
||||
const isConnected = Boolean(existingConnection)
|
||||
const isLoading = isCreatingConnection || isUpdatingConnection
|
||||
|
||||
return (
|
||||
<>
|
||||
<Sheet
|
||||
open={visible}
|
||||
onOpenChange={(open) => !open && sidePanelStateSnapshot.setGithubConnectionsOpen(false)}
|
||||
>
|
||||
<SheetContent size="lg" className="flex flex-col">
|
||||
<SheetHeader>
|
||||
<SheetTitle>GitHub Integration</SheetTitle>
|
||||
<SheetDescription>
|
||||
{isConnected
|
||||
? 'Update your GitHub integration settings below.'
|
||||
: 'Connect your Supabase project to a GitHub repository to enable automatic deployments and preview branches.'}
|
||||
</SheetDescription>
|
||||
</SheetHeader>
|
||||
|
||||
<div className="flex-1 overflow-y-auto px-5">
|
||||
{gitHubAuthorization === null ? (
|
||||
<div className="flex flex-col items-center justify-center mt-2 relative border rounded-lg p-12 bg shadow">
|
||||
<p className="text-sm text-center">Authorize with GitHub</p>
|
||||
<p className="text-sm text-center text-foreground-light">
|
||||
Connect your GitHub account to access and select repositories for integration.
|
||||
</p>
|
||||
<Button
|
||||
className="w-min mt-3"
|
||||
onClick={() => {
|
||||
openInstallGitHubIntegrationWindow('authorize')
|
||||
}}
|
||||
>
|
||||
Authorize GitHub
|
||||
</Button>
|
||||
</div>
|
||||
) : (
|
||||
<Form_Shadcn_ {...githubSettingsForm}>
|
||||
<form
|
||||
onSubmit={githubSettingsForm.handleSubmit(handleCreateOrUpdateConnection)}
|
||||
className="space-y-6"
|
||||
>
|
||||
{/* Repository Selection */}
|
||||
<FormField_Shadcn_
|
||||
control={githubSettingsForm.control}
|
||||
name="repositoryId"
|
||||
render={({ field }) => (
|
||||
<FormItemLayout
|
||||
layout="flex-row-reverse"
|
||||
label="GitHub Repository"
|
||||
description="Select the repository to connect to your project"
|
||||
>
|
||||
<Popover_Shadcn_ open={repoComboBoxOpen} onOpenChange={setRepoComboboxOpen}>
|
||||
<PopoverTrigger_Shadcn_ asChild>
|
||||
<FormControl_Shadcn_>
|
||||
<Button
|
||||
type="default"
|
||||
className="justify-start h-[34px] w-full"
|
||||
disabled={isLoadingGitHubRepos}
|
||||
loading={isLoadingGitHubRepos}
|
||||
icon={
|
||||
<div className="bg-black shadow rounded p-1 w-6 h-6 flex justify-center items-center">
|
||||
{GITHUB_ICON}
|
||||
</div>
|
||||
}
|
||||
iconRight={
|
||||
<span className="grow flex justify-end">
|
||||
<ChevronDown />
|
||||
</span>
|
||||
}
|
||||
>
|
||||
{selectedRepository
|
||||
? selectedRepository.name
|
||||
: 'Choose GitHub Repository'}
|
||||
</Button>
|
||||
</FormControl_Shadcn_>
|
||||
</PopoverTrigger_Shadcn_>
|
||||
<PopoverContent_Shadcn_ className="p-0 w-80" side="bottom" align="start">
|
||||
<Command_Shadcn_>
|
||||
<CommandInput_Shadcn_ placeholder="Search repositories..." />
|
||||
<CommandList_Shadcn_ className="!max-h-[200px]">
|
||||
<CommandEmpty_Shadcn_>No repositories found.</CommandEmpty_Shadcn_>
|
||||
<CommandGroup_Shadcn_>
|
||||
{githubRepos.map((repo, i) => (
|
||||
<CommandItem_Shadcn_
|
||||
key={repo.id}
|
||||
value={`${repo.name.replaceAll('"', '')}-${i}`}
|
||||
className="flex gap-2 items-center"
|
||||
onSelect={() => {
|
||||
setSelectedRepositoryId(repo.id)
|
||||
field.onChange(repo.id)
|
||||
setRepoComboboxOpen(false)
|
||||
}}
|
||||
>
|
||||
<div className="bg-black shadow rounded p-1 w-5 h-5 flex justify-center items-center">
|
||||
{GITHUB_ICON}
|
||||
</div>
|
||||
<span className="truncate" title={repo.name}>
|
||||
{repo.name}
|
||||
</span>
|
||||
</CommandItem_Shadcn_>
|
||||
))}
|
||||
</CommandGroup_Shadcn_>
|
||||
<CommandSeparator_Shadcn_ />
|
||||
<CommandGroup_Shadcn_>
|
||||
<CommandItem_Shadcn_
|
||||
className="flex gap-2 items-center cursor-pointer"
|
||||
onSelect={() => openInstallGitHubIntegrationWindow('install')}
|
||||
>
|
||||
<PlusIcon size={16} />
|
||||
Add GitHub Repositories
|
||||
</CommandItem_Shadcn_>
|
||||
</CommandGroup_Shadcn_>
|
||||
</CommandList_Shadcn_>
|
||||
</Command_Shadcn_>
|
||||
</PopoverContent_Shadcn_>
|
||||
</Popover_Shadcn_>
|
||||
</FormItemLayout>
|
||||
)}
|
||||
/>
|
||||
|
||||
{/* Supabase Directory */}
|
||||
<FormField_Shadcn_
|
||||
control={githubSettingsForm.control}
|
||||
name="supabaseDirectory"
|
||||
render={({ field }) => (
|
||||
<FormItemLayout
|
||||
layout="flex-row-reverse"
|
||||
label="Supabase directory"
|
||||
description="Relative path to your supabase folder"
|
||||
>
|
||||
<FormControl_Shadcn_>
|
||||
<Input_Shadcn_
|
||||
{...field}
|
||||
placeholder="supabase"
|
||||
autoComplete="off"
|
||||
disabled={!canUpdateGitHubConnection}
|
||||
/>
|
||||
</FormControl_Shadcn_>
|
||||
</FormItemLayout>
|
||||
)}
|
||||
/>
|
||||
|
||||
{/* 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>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 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={!canCreateGitHubConnection}
|
||||
/>
|
||||
</FormControl_Shadcn_>
|
||||
</FormItemLayout>
|
||||
)}
|
||||
/>
|
||||
|
||||
<div
|
||||
className={cn(
|
||||
'space-y-4 pl-6 border-l',
|
||||
!newBranchPerPr && '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 || !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={field.value}
|
||||
onCheckedChange={(val) => field.onChange(val)}
|
||||
disabled={!newBranchPerPr || !canUpdateGitHubConnection}
|
||||
/>
|
||||
</FormControl_Shadcn_>
|
||||
</FormItemLayout>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</Form_Shadcn_>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<SheetFooter>
|
||||
<Button
|
||||
type="default"
|
||||
onClick={() => sidePanelStateSnapshot.setGithubConnectionsOpen(false)}
|
||||
>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button
|
||||
type="primary"
|
||||
onClick={githubSettingsForm.handleSubmit(handleCreateOrUpdateConnection)}
|
||||
disabled={
|
||||
!canUpdateGitHubConnection ||
|
||||
isCheckingBranch ||
|
||||
isLoading ||
|
||||
!githubSettingsForm.formState.isDirty ||
|
||||
gitHubAuthorization === null
|
||||
}
|
||||
loading={isCheckingBranch || isLoading}
|
||||
>
|
||||
{isConnected ? 'Update integration' : 'Enable integration'}
|
||||
</Button>
|
||||
</SheetFooter>
|
||||
</SheetContent>
|
||||
</Sheet>
|
||||
|
||||
<ConfirmationModal
|
||||
variant="warning"
|
||||
visible={isConfirmingBranchChange}
|
||||
title="Changing production git branch"
|
||||
confirmLabel="Confirm"
|
||||
size="medium"
|
||||
onCancel={() => setIsConfirmingBranchChange(false)}
|
||||
onConfirm={onConfirmBranchChange}
|
||||
loading={isUpdatingConnection}
|
||||
>
|
||||
<p className="text-sm text-foreground-light">
|
||||
Open pull requests will only update your Supabase project on merge if the git base branch
|
||||
matches this new production git branch.
|
||||
</p>
|
||||
</ConfirmationModal>
|
||||
|
||||
<ConfirmationModal
|
||||
variant="warning"
|
||||
visible={isConfirmingRepoChange}
|
||||
title="Changing GitHub repository"
|
||||
confirmLabel="Change repository"
|
||||
size="medium"
|
||||
onCancel={() => setIsConfirmingRepoChange(false)}
|
||||
onConfirm={onConfirmRepoChange}
|
||||
loading={isLoading}
|
||||
>
|
||||
<div className="space-y-3">
|
||||
<p className="text-sm text-foreground-light">
|
||||
This will disconnect your current repository and create a new connection with the
|
||||
selected repository.
|
||||
</p>
|
||||
<p className="text-sm text-foreground-light">
|
||||
Your existing branch settings will be preserved, but make sure the new repository
|
||||
contains the specified branches.
|
||||
</p>
|
||||
</div>
|
||||
</ConfirmationModal>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default SidePanelGitHubRepoLinker
|
||||
@@ -1,6 +1,5 @@
|
||||
import Link from 'next/link'
|
||||
|
||||
import SidePanelGitHubRepoLinker from 'components/interfaces/Organization/IntegrationSettings/SidePanelGitHubRepoLinker'
|
||||
import SidePanelVercelProjectLinker from 'components/interfaces/Organization/IntegrationSettings/SidePanelVercelProjectLinker'
|
||||
import { ScaffoldContainer, ScaffoldDivider } from 'components/layouts/Scaffold'
|
||||
import { useProjectByRef, useSelectedProject } from 'hooks/misc/useSelectedProject'
|
||||
@@ -47,7 +46,6 @@ const IntegrationSettings = () => {
|
||||
<ScaffoldDivider />
|
||||
<VercelSection isProjectScoped={true} />
|
||||
<SidePanelVercelProjectLinker />
|
||||
<SidePanelGitHubRepoLinker />
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
-117
@@ -1,117 +0,0 @@
|
||||
import { Check, Loader2 } from 'lucide-react'
|
||||
|
||||
import {
|
||||
EmptyIntegrationConnection,
|
||||
IntegrationConnection,
|
||||
} from 'components/interfaces/Integrations/VercelGithub/IntegrationPanels'
|
||||
import type { GitHubConnection } from 'data/integrations/github-connections-query'
|
||||
import { useSidePanelsStateSnapshot } from 'state/side-panels'
|
||||
import {
|
||||
Badge,
|
||||
Button,
|
||||
FormControl_Shadcn_,
|
||||
FormField_Shadcn_,
|
||||
FormItem_Shadcn_,
|
||||
FormMessage_Shadcn_,
|
||||
Input_Shadcn_,
|
||||
Modal,
|
||||
} from 'ui'
|
||||
|
||||
interface GithubRepositorySelectionProps {
|
||||
form: any
|
||||
isChecking: boolean
|
||||
isValid: boolean
|
||||
githubConnection?: GitHubConnection
|
||||
}
|
||||
|
||||
const GithubRepositorySelection = ({
|
||||
form,
|
||||
isChecking,
|
||||
isValid,
|
||||
githubConnection,
|
||||
}: GithubRepositorySelectionProps) => {
|
||||
const sidePanels = useSidePanelsStateSnapshot()
|
||||
|
||||
function onSelectConnectRepo() {
|
||||
sidePanels.setGithubConnectionsOpen(true)
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="border-t border-b">
|
||||
<Modal.Content className="px-7">
|
||||
<div className="py-6">
|
||||
<div className="flex items-center space-x-2">
|
||||
<p>Git Connection</p>
|
||||
<Badge variant="warning">Required</Badge>
|
||||
</div>
|
||||
<p className="text-sm text-foreground-light !mb-4">
|
||||
{githubConnection !== undefined
|
||||
? 'Your database preview branches will be based on the branches in the following repository that your project is connected with:'
|
||||
: 'Your database preview branches will be based on the branches in the Git repository that your project is connected with.'}
|
||||
</p>
|
||||
|
||||
{githubConnection ? (
|
||||
<>
|
||||
<ul className="mb-3">
|
||||
<IntegrationConnection
|
||||
type={'GitHub'}
|
||||
connection={{
|
||||
id: String(githubConnection.id),
|
||||
added_by: {
|
||||
id: String(githubConnection.user?.id),
|
||||
primary_email: githubConnection.user?.primary_email ?? '',
|
||||
username: githubConnection.user?.username ?? '',
|
||||
},
|
||||
foreign_project_id: String(githubConnection.repository.id),
|
||||
supabase_project_ref: githubConnection.project.ref,
|
||||
organization_integration_id: 'unused',
|
||||
inserted_at: githubConnection.inserted_at,
|
||||
updated_at: githubConnection.updated_at,
|
||||
metadata: {
|
||||
name: githubConnection.repository.name,
|
||||
} as any,
|
||||
}}
|
||||
showNode={false}
|
||||
actions={
|
||||
<Button type="default" onClick={() => onSelectConnectRepo()}>
|
||||
Configure connection
|
||||
</Button>
|
||||
}
|
||||
orientation="horizontal"
|
||||
/>
|
||||
</ul>
|
||||
|
||||
<FormField_Shadcn_
|
||||
control={form.control}
|
||||
name="branchName"
|
||||
render={({ field }) => (
|
||||
<FormItem_Shadcn_ className="flex flex-col gap-y-1 relative">
|
||||
<label className="text-sm text-foreground-light">
|
||||
Choose your production branch
|
||||
</label>
|
||||
<FormControl_Shadcn_>
|
||||
<Input_Shadcn_ {...field} placeholder="e.g main" />
|
||||
</FormControl_Shadcn_>
|
||||
<div className="absolute top-9 right-3">
|
||||
{isChecking ? (
|
||||
<Loader2 size={14} className="animate-spin" />
|
||||
) : isValid ? (
|
||||
<Check size={14} className="text-brand" strokeWidth={2} />
|
||||
) : null}
|
||||
</div>
|
||||
|
||||
<FormMessage_Shadcn_ />
|
||||
</FormItem_Shadcn_>
|
||||
)}
|
||||
/>
|
||||
</>
|
||||
) : (
|
||||
<EmptyIntegrationConnection showNode={false} onClick={() => onSelectConnectRepo()} />
|
||||
)}
|
||||
</div>
|
||||
</Modal.Content>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default GithubRepositorySelection
|
||||
@@ -5,7 +5,6 @@ import { forwardRef, Fragment, PropsWithChildren, ReactNode, useEffect, useState
|
||||
|
||||
import { useParams } from 'common'
|
||||
import { CreateBranchModal } from 'components/interfaces/BranchManagement/CreateBranchModal'
|
||||
import SidePanelGitHubRepoLinker from 'components/interfaces/Organization/IntegrationSettings/SidePanelGitHubRepoLinker'
|
||||
import ProjectAPIDocs from 'components/interfaces/ProjectAPIDocs/ProjectAPIDocs'
|
||||
import { AIAssistant } from 'components/ui/AIAssistantPanel/AIAssistant'
|
||||
import { EditorPanel } from 'components/ui/EditorPanel/EditorPanel'
|
||||
@@ -259,7 +258,6 @@ const ProjectLayout = forwardRef<HTMLDivElement, PropsWithChildren<ProjectLayout
|
||||
</ResizablePanelGroup>
|
||||
</div>
|
||||
<CreateBranchModal />
|
||||
<SidePanelGitHubRepoLinker projectRef={projectRefForGitHubLinker} />
|
||||
<ProjectAPIDocs />
|
||||
<MobileSheetNav open={mobileMenuOpen} onOpenChange={setMobileMenuOpen}>
|
||||
{productMenu}
|
||||
|
||||
@@ -15,14 +15,6 @@ export const sidePanelsState = proxy({
|
||||
setVercelConnectionsIntegrationId: (id: string) => {
|
||||
sidePanelsState.vercelConnectionsIntegrationId = id
|
||||
},
|
||||
|
||||
/**
|
||||
* GitHub connections
|
||||
*/
|
||||
githubConnectionsOpen: false as boolean,
|
||||
setGithubConnectionsOpen: (bool: boolean) => {
|
||||
sidePanelsState.githubConnectionsOpen = bool
|
||||
},
|
||||
})
|
||||
|
||||
export const getSidePanelsState = () => snapshot(sidePanelsState)
|
||||
|
||||
Reference in new issue
Block a user