Files
supabase/apps/studio/components/interfaces/Settings/Integrations/GithubIntegration/GithubSection.tsx
T
df52ea7ee0 feat: Replace all toasts with sonner (#28250)
* Update the design of the sonner toasts. Add the close button by default.

* Migrate studio and www apps to use the SonnerToaster.

* Migrate all toasts from studio.

* Migrate all leftover toasts in studio.

* Add a new toast component with progress. Use it in studio.

* Migrate the design-system app.

* Refactor the consent toast to use sonner.

* Switch docs to use the new sonner toasts.

* Remove toast examples from the design-system app.

* Remove all toast-related components and old code.

* Fix the progress bar in the toast progress component. Also make the bottom components vertically centered.

* Fix the width of the toast progress.

* Use text-foreground-lighter instead of muted for ToastProgress text

* Rename ToastProgress to SonnerProgress.

* Shorten the text in sonner progress.

* Use the correct classes for the close button. Add a const var for the default toast duration. Remove the custom width class from sonner.

* Set the position for all progress toasts to bottom right. Set the duration for all toasts to the default (when reusing a toast id from loading/progress toast, the duration is set to infinity).

* Fix the playwright tests.

* Refactor imports to use ui instead of @ui.

* Change all imports of react-hot-toast with sonner. These components were merged since the last commit to this branch.

* Remove react-hot-toast lib.

---------

Co-authored-by: Joshen Lim <joshenlimek@gmail.com>
Co-authored-by: Jonathan Summers-Muir <MildTomato@users.noreply.github.com>
2024-08-31 07:50:51 +08:00

203 lines
8.0 KiB
TypeScript

import { PermissionAction } from '@supabase/shared-types/out/constants'
import { useCallback } from 'react'
import { toast } from 'sonner'
import { useParams } from 'common'
import { IntegrationConnectionItem } from 'components/interfaces/Integrations/IntegrationConnection'
import { EmptyIntegrationConnection } from 'components/interfaces/Integrations/IntegrationPanels'
import { Markdown } from 'components/interfaces/Markdown'
import {
ScaffoldContainer,
ScaffoldSection,
ScaffoldSectionContent,
ScaffoldSectionDetail,
} from 'components/layouts/Scaffold'
import NoPermission from 'components/ui/NoPermission'
import { useGitHubConnectionDeleteMutation } from 'data/integrations/github-connection-delete-mutation'
import { useGitHubConnectionsQuery } from 'data/integrations/github-connections-query'
import type {
IntegrationName,
IntegrationProjectConnection,
} from 'data/integrations/integrations.types'
import { useCheckPermissions } from 'hooks/misc/useCheckPermissions'
import { useSelectedOrganization } from 'hooks/misc/useSelectedOrganization'
import { useSelectedProject } from 'hooks/misc/useSelectedProject'
import { useFlag } from 'hooks/ui/useFlag'
import { useSidePanelsStateSnapshot } from 'state/side-panels'
import { IntegrationImageHandler } from '../IntegrationsSettings'
import GitHubIntegrationConnectionForm from './GitHubIntegrationConnectionForm'
const GitHubTitle = `GitHub Connections`
const GitHubDetailsSection = `
Connect any of your GitHub repositories to a project.
`
const GitHubContentSectionTop = `
### How will GitHub connections work?
You will be able to connect a GitHub repository to a Supabase project.
The GitHub app will watch for changes in your repository such as file changes, branch changes as well as pull request activity.
`
const GitHubSection = () => {
const { ref: projectRef } = useParams()
const project = useSelectedProject()
const org = useSelectedOrganization()
const sidePanelsStateSnapshot = useSidePanelsStateSnapshot()
const canReadGitHubConnection = useCheckPermissions(
PermissionAction.READ,
'integrations.github_connections'
)
const canCreateGitHubConnection = useCheckPermissions(
PermissionAction.CREATE,
'integrations.github_connections'
)
const canUpdateGitHubConnection = useCheckPermissions(
PermissionAction.UPDATE,
'integrations.github_connections'
)
const { data: allConnections } = useGitHubConnectionsQuery({ organizationId: org?.id })
const { mutateAsync: deleteGitHubConnection } = useGitHubConnectionDeleteMutation({
onSuccess: () => {
toast.success('Successfully deleted Github connection')
},
})
const hasAccessToBranching = useFlag<boolean>('branchManagement')
const isBranch = project?.parent_project_ref !== undefined
const connections =
allConnections?.filter((connection) =>
isBranch
? connection.project.ref === project.parent_project_ref
: connection.project.ref === projectRef
) ?? []
const onAddGitHubConnection = useCallback(() => {
sidePanelsStateSnapshot.setGithubConnectionsOpen(true)
}, [sidePanelsStateSnapshot])
const onDeleteGitHubConnection = useCallback(
async (connection: IntegrationProjectConnection) => {
if (!org?.id) {
toast.error('Organization not found')
return
}
await deleteGitHubConnection({ connectionId: connection.id, organizationId: org.id })
},
[deleteGitHubConnection, org?.id]
)
return (
<ScaffoldContainer>
<ScaffoldSection>
<ScaffoldSectionDetail title={GitHubTitle}>
<Markdown content={GitHubDetailsSection} />
<IntegrationImageHandler title="github" />
</ScaffoldSectionDetail>
<ScaffoldSectionContent>
{!canReadGitHubConnection ? (
<NoPermission resourceText="view this organization's GitHub connections" />
) : (
<>
<Markdown content={GitHubContentSectionTop} />
{connections.length > 0 ? (
<ul className="flex flex-col">
{connections.map((connection) => (
<div
key={connection.id}
className="relative flex flex-col -gap-[1px] [&>li]:pb-0"
>
<IntegrationConnectionItem
showNode={false}
disabled={isBranch || !canUpdateGitHubConnection}
key={connection.id}
connection={{
id: String(connection.id),
added_by: {
id: String(connection.user?.id),
primary_email: connection.user?.primary_email ?? '',
username: connection.user?.username ?? '',
},
foreign_project_id: String(connection.repository.id),
supabase_project_ref: connection.project.ref,
organization_integration_id: 'unused',
inserted_at: connection.inserted_at,
updated_at: connection.updated_at,
metadata: {
name: connection.repository.name,
} as any,
}}
type={'GitHub' as IntegrationName}
onDeleteConnection={onDeleteGitHubConnection}
className="!rounded-b-none !mb-0"
/>
<div className="border-b border-l border-r rounded-b-lg">
<GitHubIntegrationConnectionForm
disabled={!canUpdateGitHubConnection}
connection={{
id: String(connection.id),
added_by: {
id: String(connection.user?.id),
primary_email: connection.user?.primary_email ?? '',
username: connection.user?.username ?? '',
},
foreign_project_id: String(connection.repository.id),
supabase_project_ref: connection.project.ref,
organization_integration_id: 'unused',
inserted_at: connection.inserted_at,
updated_at: connection.updated_at,
metadata: {
name: connection.repository.name,
supabaseConfig: {
supabaseDirectory: connection.workdir,
supabaseChangesOnly: connection.supabase_changes_only,
branchLimit: connection.branch_limit,
},
} as any,
}}
/>
</div>
</div>
))}
</ul>
) : hasAccessToBranching ? (
<EmptyIntegrationConnection
onClick={onAddGitHubConnection}
orgSlug={org?.slug}
showNode={false}
disabled={!canCreateGitHubConnection}
>
Add new project connection
</EmptyIntegrationConnection>
) : (
<p className="text-sm text-foreground-light">
Access to{' '}
<a
href="https://supabase.com/docs/guides/platform/branching"
target="_blank"
rel="noreferrer"
className="text-foreground"
>
branching
</a>{' '}
is required to add GitHub connections.
</p>
)}
</>
)}
</ScaffoldSectionContent>
</ScaffoldSection>
</ScaffoldContainer>
)
}
export default GitHubSection