IntegrationSettings.tsx209 lines · main
1// @ts-nocheck
2import { PermissionAction } from '@supabase/shared-types/out/constants'
3import { useRouter } from 'next/router'
4import { useCallback } from 'react'
5import { toast } from 'sonner'
6import { GenericSkeletonLoader } from 'ui-patterns'
7
8import { IntegrationConnectionItem } from '../../Integrations/VercelGithub/IntegrationConnection'
9import SidePanelVercelProjectLinker from './SidePanelVercelProjectLinker'
10import { EmptyIntegrationConnection } from '@/components/interfaces/Integrations/VercelGithub/IntegrationPanels'
11import { IntegrationImageHandler } from '@/components/interfaces/Settings/Integrations/IntegrationsSettings'
12import { VercelSection } from '@/components/interfaces/Settings/Integrations/VercelIntegration/VercelSection'
13import {
14 ScaffoldContainer,
15 ScaffoldContainerLegacy,
16 ScaffoldDivider,
17 ScaffoldSection,
18 ScaffoldSectionContent,
19 ScaffoldSectionDetail,
20 ScaffoldTitle,
21} from '@/components/layouts/Scaffold'
22import { InlineLink } from '@/components/ui/InlineLink'
23import NoPermission from '@/components/ui/NoPermission'
24import { useGitHubAuthorizationQuery } from '@/data/integrations/github-authorization-query'
25import { useGitHubConnectionDeleteMutation } from '@/data/integrations/github-connection-delete-mutation'
26import {
27 useGitHubConnectionsQuery,
28 type GitHubConnection,
29} from '@/data/integrations/github-connections-query'
30import type { IntegrationProjectConnection } from '@/data/integrations/integrations.types'
31import { useAsyncCheckPermissions } from '@/hooks/misc/useCheckPermissions'
32import { useIsFeatureEnabled } from '@/hooks/misc/useIsFeatureEnabled'
33import { useSelectedOrganizationQuery } from '@/hooks/misc/useSelectedOrganization'
34import {
35 GITHUB_INTEGRATION_INSTALLATION_URL,
36 GITHUB_INTEGRATION_REVOKE_AUTHORIZATION_URL,
37} from '@/lib/github'
38
39type GitHubSectionProps = {
40 canCreateGitHubConnection: boolean
41 canReadGithubConnection: boolean
42 canUpdateGitHubConnection: boolean
43 connections?: GitHubConnection[]
44 isGitHubAuthorized: boolean
45 isLoadingPermissions: boolean
46 onAddGitHubConnection: () => void
47 onDeleteGitHubConnection: (connection: IntegrationProjectConnection) => void | Promise<void>
48}
49
50const GitHubSection = ({
51 canCreateGitHubConnection,
52 canReadGithubConnection,
53 canUpdateGitHubConnection,
54 connections,
55 isGitHubAuthorized,
56 isLoadingPermissions,
57 onAddGitHubConnection,
58 onDeleteGitHubConnection,
59}: GitHubSectionProps) => (
60 <ScaffoldContainer>
61 <ScaffoldSection className="py-12">
62 <ScaffoldSectionDetail title="GitHub Connections">
63 <p>Connect any of your GitHub repositories to a project.</p>
64 <IntegrationImageHandler title="github" />
65 </ScaffoldSectionDetail>
66 <ScaffoldSectionContent>
67 {isLoadingPermissions ? (
68 <GenericSkeletonLoader />
69 ) : !canReadGithubConnection ? (
70 <NoPermission resourceText="view this organization's GitHub connections" />
71 ) : (
72 <div className="space-y-6">
73 <div className="space-y-1">
74 <h3 className="text-sm font-medium text-foreground">
75 How do GitHub connections work?
76 </h3>
77 <p className="text-sm text-foreground-light">
78 Connect a GitHub repository to a Briven project. The GitHub app watches file,
79 branch, and pull request activity in your repository.
80 </p>
81 </div>
82
83 <div>
84 <ul className="flex flex-col gap-y-2">
85 {connections?.map((connection) => (
86 <IntegrationConnectionItem
87 key={connection.id}
88 disabled={!canUpdateGitHubConnection}
89 connection={{
90 id: String(connection.id),
91 added_by: {
92 id: String(connection.user?.id),
93 primary_email: connection.user?.primary_email ?? '',
94 username: connection.user?.username ?? '',
95 },
96 foreign_project_id: String(connection.repository.id),
97 briven_project_ref: connection.project.ref,
98 organization_integration_id: 'unused',
99 inserted_at: connection.inserted_at,
100 updated_at: connection.updated_at,
101 metadata: {
102 name: connection.repository.name,
103 } as IntegrationProjectConnection['metadata'],
104 }}
105 type="GitHub"
106 onDeleteConnection={onDeleteGitHubConnection}
107 />
108 ))}
109 </ul>
110
111 <EmptyIntegrationConnection
112 onClick={onAddGitHubConnection}
113 showNode={false}
114 disabled={!canCreateGitHubConnection}
115 >
116 Add new project connection
117 </EmptyIntegrationConnection>
118 </div>
119
120 {isGitHubAuthorized && (
121 <p className="text-sm text-foreground-light">
122 You are authorized with the Briven GitHub App. You can configure your{' '}
123 <InlineLink href={GITHUB_INTEGRATION_INSTALLATION_URL}>
124 GitHub App installations and repository access
125 </InlineLink>
126 , or{' '}
127 <InlineLink href={GITHUB_INTEGRATION_REVOKE_AUTHORIZATION_URL}>
128 revoke your authorization
129 </InlineLink>
130 .
131 </p>
132 )}
133 </div>
134 )}
135 </ScaffoldSectionContent>
136 </ScaffoldSection>
137 </ScaffoldContainer>
138)
139
140export const IntegrationSettings = () => {
141 const router = useRouter()
142 const { data: org } = useSelectedOrganizationQuery()
143
144 const showVercelIntegration = useIsFeatureEnabled('integrations:vercel')
145
146 const { can: canReadGithubConnection, isLoading: isLoadingPermissions } =
147 useAsyncCheckPermissions(PermissionAction.READ, 'integrations.github_connections')
148 const { can: canCreateGitHubConnection } = useAsyncCheckPermissions(
149 PermissionAction.CREATE,
150 'integrations.github_connections'
151 )
152 const { can: canUpdateGitHubConnection } = useAsyncCheckPermissions(
153 PermissionAction.UPDATE,
154 'integrations.github_connections'
155 )
156
157 const { data: gitHubAuthorization } = useGitHubAuthorizationQuery()
158 const { data: connections } = useGitHubConnectionsQuery({ organizationId: org?.id })
159
160 const { mutate: deleteGitHubConnection } = useGitHubConnectionDeleteMutation({
161 onSuccess: () => {
162 toast.success('Successfully deleted GitHub connection')
163 },
164 })
165
166 const onAddGitHubConnection = useCallback(() => {
167 router.push('/project/_/settings/integrations')
168 }, [router])
169
170 const onDeleteGitHubConnection = useCallback(
171 async (connection: IntegrationProjectConnection) => {
172 if (!org?.id) {
173 toast.error('Organization not found')
174 return
175 }
176
177 deleteGitHubConnection({
178 connectionId: connection.id,
179 organizationId: org.id,
180 })
181 },
182 [deleteGitHubConnection, org?.id]
183 )
184
185 return (
186 <>
187 <ScaffoldContainerLegacy>
188 <ScaffoldTitle>Integrations</ScaffoldTitle>
189 </ScaffoldContainerLegacy>
190 <GitHubSection
191 canCreateGitHubConnection={canCreateGitHubConnection}
192 canReadGithubConnection={canReadGithubConnection}
193 canUpdateGitHubConnection={canUpdateGitHubConnection}
194 connections={connections}
195 isGitHubAuthorized={Boolean(gitHubAuthorization)}
196 isLoadingPermissions={isLoadingPermissions}
197 onAddGitHubConnection={onAddGitHubConnection}
198 onDeleteGitHubConnection={onDeleteGitHubConnection}
199 />
200 {showVercelIntegration && (
201 <>
202 <ScaffoldDivider />
203 <VercelSection isProjectScoped={false} />
204 <SidePanelVercelProjectLinker />
205 </>
206 )}
207 </>
208 )
209}