SidePanelVercelProjectLinker.tsx162 lines · main
1import { useParams } from 'common'
2import { keyBy } from 'lodash'
3import { useCallback, useMemo } from 'react'
4import { toast } from 'sonner'
5import { SidePanel } from 'ui'
6
7import { ENV_VAR_RAW_KEYS } from '@/components/interfaces/Integrations/Vercel/Integrations-Vercel.constants'
8import ProjectLinker, {
9 ForeignProject,
10} from '@/components/interfaces/Integrations/VercelGithub/ProjectLinker'
11import { Markdown } from '@/components/interfaces/Markdown'
12import { vercelIcon } from '@/components/to-be-cleaned/ListIcons'
13import { useOrgIntegrationsQuery } from '@/data/integrations/integrations-query-org-only'
14import { useIntegrationVercelConnectionsCreateMutation } from '@/data/integrations/integrations-vercel-connections-create-mutation'
15import { useVercelProjectsQuery } from '@/data/integrations/integrations-vercel-projects-query'
16import { useSelectedOrganizationQuery } from '@/hooks/misc/useSelectedOrganization'
17import { BASE_PATH } from '@/lib/constants'
18import { EMPTY_ARR } from '@/lib/void'
19import { useSidePanelsStateSnapshot } from '@/state/side-panels'
20
21const VERCEL_ICON = (
22 <svg xmlns="http://www.w3.org/2000/svg" fill="white" viewBox="0 0 512 512" className="w-6">
23 <path fillRule="evenodd" d="M256,48,496,464H16Z" />
24 </svg>
25)
26
27export const SidePanelVercelProjectLinker = () => {
28 const { ref } = useParams()
29 const { data: selectedOrganization } = useSelectedOrganizationQuery()
30 const sidePanelStateSnapshot = useSidePanelsStateSnapshot()
31 const organizationIntegrationId = sidePanelStateSnapshot.vercelConnectionsIntegrationId
32
33 const { data: integrationData } = useOrgIntegrationsQuery({
34 orgSlug: selectedOrganization?.slug,
35 })
36 const vercelIntegrations = integrationData?.filter(
37 (integration) => integration.integration.name === 'Vercel'
38 ) // vercel
39
40 /**
41 * Find the right integration
42 *
43 * we use the snapshot.organizationIntegrationId which should be set whenever this sidepanel is opened
44 */
45 const selectedIntegration = vercelIntegrations?.find((x) => x.id === organizationIntegrationId)
46
47 const { data: vercelProjectsData } = useVercelProjectsQuery(
48 {
49 organization_integration_id: organizationIntegrationId,
50 },
51 { enabled: organizationIntegrationId !== undefined }
52 )
53
54 const vercelProjects = useMemo(() => vercelProjectsData ?? EMPTY_ARR, [vercelProjectsData])
55 const vercelProjectsById = useMemo(() => keyBy(vercelProjects, 'id'), [vercelProjects])
56
57 const getForeignProjectIcon = useCallback(
58 (_project: ForeignProject) => {
59 const project = vercelProjectsById[_project.id]
60
61 return !project?.framework ? (
62 vercelIcon
63 ) : (
64 <img
65 src={`${BASE_PATH}/img/icons/frameworks/${project.framework}.svg`}
66 width={21}
67 height={21}
68 alt={`icon`}
69 />
70 )
71 },
72 [vercelProjectsById]
73 )
74
75 const { mutate: createConnections, isPending: isCreatingConnection } =
76 useIntegrationVercelConnectionsCreateMutation({
77 async onSuccess({ env_sync_error: envSyncError }) {
78 if (envSyncError) {
79 toast.error(
80 `Failed to sync environment variables: ${envSyncError.message}. Please try re-syncing manually from settings.`
81 )
82 }
83
84 sidePanelStateSnapshot.setVercelConnectionsOpen(false)
85 },
86 })
87
88 const onCreateConnections = useCallback(
89 (vars: any) => {
90 createConnections({
91 ...vars,
92 connection: {
93 ...vars.connection,
94 metadata: {
95 ...vars.connection.metadata,
96 brivenConfig: {
97 projectEnvVars: {
98 write: true,
99 },
100 },
101 },
102 },
103 })
104 },
105 [createConnections]
106 )
107
108 return (
109 <SidePanel
110 hideFooter
111 size="large"
112 header="Add new Vercel project connection"
113 visible={sidePanelStateSnapshot.vercelConnectionsOpen}
114 onCancel={() => sidePanelStateSnapshot.setVercelConnectionsOpen(false)}
115 >
116 <div className="py-6 flex flex-col gap-6 bg-studio h-full">
117 <SidePanel.Content>
118 <Markdown
119 content={`
120### Choose repository to connect to
121
122Check the details below before proceeding
123 `}
124 />
125 </SidePanel.Content>
126 <SidePanel.Content className="flex flex-col gap-2">
127 <ProjectLinker
128 slug={selectedOrganization?.slug}
129 defaultBrivenProjectRef={ref}
130 organizationIntegrationId={selectedIntegration?.id}
131 foreignProjects={vercelProjects}
132 onCreateConnections={onCreateConnections}
133 installedConnections={selectedIntegration?.connections}
134 isLoading={isCreatingConnection}
135 integrationIcon={VERCEL_ICON}
136 getForeignProjectIcon={getForeignProjectIcon}
137 choosePrompt="Choose Vercel Project"
138 mode="Vercel"
139 />
140 <Markdown
141 content={`
142The following environment variables will be added:
143
144${ENV_VAR_RAW_KEYS.map((x) => {
145 return `\n - \`${x}\``
146})}
147`}
148 />
149 </SidePanel.Content>
150 <SidePanel.Content>
151 <ul>
152 <li className="border px-10">
153 {/* <IntegrationConnectionOption connection={githubIntegrations[0]} /> */}
154 </li>
155 </ul>
156 </SidePanel.Content>
157 </div>
158 </SidePanel>
159 )
160}
161
162export default SidePanelVercelProjectLinker