IntegrationConnection.tsx169 lines · main
1// @ts-nocheck
2import { ChevronDown, Loader2, RefreshCw, Trash } from 'lucide-react'
3import Link from 'next/link'
4import { useRouter } from 'next/router'
5import { forwardRef, useCallback, useState } from 'react'
6import { toast } from 'sonner'
7import {
8 Button,
9 DropdownMenu,
10 DropdownMenuContent,
11 DropdownMenuItem,
12 DropdownMenuSeparator,
13 DropdownMenuTrigger,
14} from 'ui'
15import ConfirmationModal from 'ui-patterns/Dialogs/ConfirmationModal'
16
17import {
18 IntegrationConnection,
19 IntegrationConnectionProps,
20} from '@/components/interfaces/Integrations/VercelGithub/IntegrationPanels'
21import { ButtonTooltip } from '@/components/ui/ButtonTooltip'
22import { useIntegrationsVercelConnectionSyncEnvsMutation } from '@/data/integrations/integrations-vercel-connection-sync-envs-mutation'
23import type { IntegrationProjectConnection } from '@/data/integrations/integrations.types'
24import { useProjectDetailQuery } from '@/data/projects/project-detail-query'
25import { useSelectedOrganizationQuery } from '@/hooks/misc/useSelectedOrganization'
26
27interface IntegrationConnectionItemProps extends IntegrationConnectionProps {
28 disabled?: boolean
29 onDeleteConnection: (connection: IntegrationProjectConnection) => void | Promise<void>
30}
31
32export const IntegrationConnectionItem = forwardRef<HTMLLIElement, IntegrationConnectionItemProps>(
33 ({ disabled, onDeleteConnection, ...props }, _ref) => {
34 const router = useRouter()
35 const { data: org } = useSelectedOrganizationQuery()
36
37 const { type, connection } = props
38 const { data: project } = useProjectDetailQuery({ ref: connection.briven_project_ref })
39 const isBranchingEnabled = project?.is_branch_enabled === true
40
41 const [isOpen, setIsOpen] = useState(false)
42 const [isDeleting, setIsDeleting] = useState(false)
43 const [dropdownVisible, setDropdownVisible] = useState(false)
44
45 const onConfirm = useCallback(async () => {
46 try {
47 setIsDeleting(true)
48 await onDeleteConnection(connection)
49 } catch (error) {
50 // [Joshen] No need for error handler
51 } finally {
52 setIsDeleting(false)
53 setIsOpen(false)
54 }
55 }, [connection, onDeleteConnection])
56
57 const onCancel = useCallback(() => {
58 setIsOpen(false)
59 }, [])
60
61 const { mutate: syncEnvs, isPending: isSyncEnvLoading } =
62 useIntegrationsVercelConnectionSyncEnvsMutation({
63 onSuccess: () => {
64 toast.success('Successfully synced environment variables')
65 setDropdownVisible(false)
66 },
67 })
68
69 const onReSyncEnvVars = useCallback(async () => {
70 syncEnvs({ connectionId: connection.id })
71 }, [connection, syncEnvs])
72
73 const projectIntegrationUrl = `/project/[ref]/settings/integrations`
74
75 return (
76 <>
77 <IntegrationConnection
78 showNode={false}
79 actions={
80 disabled ? (
81 <ButtonTooltip
82 disabled
83 iconRight={<ChevronDown size={14} />}
84 type="default"
85 tooltip={{
86 content: {
87 side: 'bottom',
88 text: 'You need additional permissions to manage this connection',
89 },
90 }}
91 >
92 Manage
93 </ButtonTooltip>
94 ) : (
95 <DropdownMenu
96 open={dropdownVisible}
97 onOpenChange={() => setDropdownVisible(!dropdownVisible)}
98 modal={false}
99 >
100 <DropdownMenuTrigger asChild>
101 <Button iconRight={<ChevronDown size={14} />} type="default">
102 <span>Manage</span>
103 </Button>
104 </DropdownMenuTrigger>
105 <DropdownMenuContent side="bottom" align="end">
106 {router.pathname !== projectIntegrationUrl && (
107 <DropdownMenuItem asChild>
108 <Link
109 href={projectIntegrationUrl.replace(
110 '[ref]',
111 connection.briven_project_ref
112 )}
113 >
114 Configure connection
115 </Link>
116 </DropdownMenuItem>
117 )}
118 {type === 'Vercel' && org?.managed_by !== 'vercel-marketplace' && (
119 <DropdownMenuItem
120 className="space-x-2"
121 onSelect={(event) => {
122 event.preventDefault()
123 onReSyncEnvVars()
124 }}
125 disabled={isSyncEnvLoading}
126 >
127 {isSyncEnvLoading ? (
128 <Loader2 className="animate-spin" size={14} />
129 ) : (
130 <RefreshCw size={14} />
131 )}
132 <p>Resync environment variables</p>
133 </DropdownMenuItem>
134 )}
135 {((type === 'Vercel' && org?.managed_by !== 'vercel-marketplace') ||
136 router.pathname !== projectIntegrationUrl) && <DropdownMenuSeparator />}
137 <DropdownMenuItem className="space-x-2" onSelect={() => setIsOpen(true)}>
138 <Trash size={14} />
139 <p>Delete connection</p>
140 </DropdownMenuItem>
141 </DropdownMenuContent>
142 </DropdownMenu>
143 )
144 }
145 {...props}
146 />
147
148 <ConfirmationModal
149 variant="destructive"
150 size={type === 'GitHub' && isBranchingEnabled ? 'medium' : 'small'}
151 visible={isOpen}
152 title={`Confirm to delete ${type} connection`}
153 confirmLabel="Delete connection"
154 onCancel={onCancel}
155 onConfirm={onConfirm}
156 loading={isDeleting}
157 >
158 <p className="text-sm text-foreground-light">
159 {type === 'Vercel'
160 ? 'Deleting this Vercel connection will stop syncing environment variables to your Vercel project. Existing environment variables will remain unchanged.'
161 : 'Deleting this GitHub connection will stop automatic creation and merging of preview branches. Existing preview branches will remain unchanged.'}
162 </p>
163 </ConfirmationModal>
164 </>
165 )
166 }
167)
168
169IntegrationConnectionItem.displayName = 'IntegrationConnectionItem'