AWSPrivateLinkForm.tsx237 lines · main
1import Link from 'next/link'
2import { useEffect } from 'react'
3import { useForm } from 'react-hook-form'
4import { toast } from 'sonner'
5import {
6 Badge,
7 Button,
8 Form,
9 FormControl,
10 FormField,
11 Input,
12 Sheet,
13 SheetContent,
14 SheetDescription,
15 SheetFooter,
16 SheetHeader,
17 SheetSection,
18 SheetTitle,
19} from 'ui'
20import { Admonition } from 'ui-patterns'
21import { FormItemLayout } from 'ui-patterns/form/FormItemLayout/FormItemLayout'
22
23import { InlineLink } from '@/components/ui/InlineLink'
24import { useAWSAccountCreateMutation } from '@/data/aws-accounts/aws-account-create-mutation'
25import type { AWSAccount } from '@/data/aws-accounts/aws-accounts-query'
26import { useSelectedProjectQuery } from '@/hooks/misc/useSelectedProject'
27import { DOCS_URL } from '@/lib/constants'
28
29interface AWSPrivateLinkFormProps {
30 account?: AWSAccount
31 open: boolean
32 onOpenChange: (open: boolean) => void
33}
34
35interface FormValues {
36 awsAccountId: string
37 accountName: string
38}
39
40export const AWSPrivateLinkForm = ({ account, open, onOpenChange }: AWSPrivateLinkFormProps) => {
41 const isNew = !account
42 const { data: project } = useSelectedProjectQuery()
43 const { mutate: createAccount, isPending } = useAWSAccountCreateMutation()
44
45 const form = useForm<FormValues>({
46 defaultValues: {
47 awsAccountId: account?.aws_account_id ?? '',
48 accountName: account?.account_name ?? '',
49 },
50 })
51
52 const title =
53 account?.status === 'ASSOCIATION_ACCEPTED'
54 ? 'This connection is active'
55 : account?.status === 'READY'
56 ? 'Connection is ready'
57 : account?.status === 'CREATING'
58 ? 'This account connection is being created'
59 : account?.status === 'DELETING'
60 ? 'This account is being deleted'
61 : account?.status === 'ASSOCIATION_REQUEST_EXPIRED'
62 ? 'Account acceptance request has expired'
63 : account?.status === 'CREATION_FAILED'
64 ? 'Failed to create account'
65 : 'This account needs to be accepted by the AWS account owner.'
66
67 const description =
68 account?.status === 'ASSOCIATION_ACCEPTED'
69 ? 'The resource share has been accepted by the AWS account owner and the connection is established.'
70 : account?.status === 'READY'
71 ? 'It may be waiting acceptance from the AWS account owner. Association requests are automatically deleted if not accepted within 12 hours.'
72 : account?.status === 'ASSOCIATION_REQUEST_EXPIRED'
73 ? 'Reconnect this account to initiate a new connection request'
74 : account?.status === 'CREATION_FAILED'
75 ? 'Reconnect this account to initiate a new connection request'
76 : ''
77
78 const onSubmit = (values: FormValues) => {
79 if (!project) return
80 if (isNew) {
81 createAccount(
82 {
83 projectRef: project.ref,
84 awsAccountId: values.awsAccountId,
85 accountName: values.accountName,
86 },
87 {
88 onSuccess: () => {
89 toast.success('Successfully added AWS account')
90 onOpenChange(false)
91 },
92 }
93 )
94 }
95 }
96
97 // Reset form when account changes
98 useEffect(() => {
99 form.reset({
100 awsAccountId: account?.aws_account_id ?? '',
101 accountName: account?.account_name ?? '',
102 })
103 }, [account, form])
104
105 return (
106 <Sheet open={open} onOpenChange={onOpenChange}>
107 <SheetContent className="flex flex-col gap-0">
108 <SheetHeader>
109 <SheetTitle>{isNew ? 'Add AWS Account' : 'AWS Account Details'}</SheetTitle>
110 <SheetDescription>
111 Connect to your Briven project from your AWS VPC using AWS PrivateLink.{' '}
112 <InlineLink href={`${DOCS_URL}/guides/platform/privatelink`}>Learn more</InlineLink>
113 </SheetDescription>
114 </SheetHeader>
115 <Form {...form}>
116 <form onSubmit={form.handleSubmit(onSubmit)} className="flex flex-col flex-1">
117 <SheetSection className="space-y-4 flex-1">
118 {!isNew && account && (
119 <>
120 <Admonition
121 showIcon={false}
122 type="default"
123 childProps={{ title: { className: 'flex-row gap-x-2 items-center' } }}
124 // @ts-ignore
125 title={
126 <>
127 <span>{title}</span>
128 <Badge
129 variant={
130 account.status === 'ASSOCIATION_ACCEPTED'
131 ? 'success'
132 : account.status === 'READY'
133 ? 'success'
134 : account.status === 'CREATING'
135 ? 'warning'
136 : account.status === 'CREATION_FAILED' ||
137 account.status === 'ASSOCIATION_REQUEST_EXPIRED'
138 ? 'destructive'
139 : 'warning'
140 }
141 >
142 {account.status === 'ASSOCIATION_ACCEPTED'
143 ? 'Connected'
144 : account.status === 'READY'
145 ? 'Ready'
146 : account.status === 'CREATING'
147 ? 'Creating'
148 : account.status === 'CREATION_FAILED'
149 ? 'Failed'
150 : account.status === 'ASSOCIATION_REQUEST_EXPIRED'
151 ? 'Expired'
152 : account.status === 'DELETING'
153 ? 'Deleting'
154 : 'Unknown'}
155 </Badge>
156 </>
157 }
158 description={description}
159 actions={
160 account.status === 'READY' && (
161 <Button type="default" className="w-min mt-2">
162 <Link
163 target="_blank"
164 rel="noopener noreferrer"
165 href={`${DOCS_URL}/guides/platform/privatelink#step-2-accept-resource-share`}
166 >
167 How to accept?
168 </Link>
169 </Button>
170 )
171 }
172 />
173 </>
174 )}
175 <FormField
176 control={form.control}
177 name="awsAccountId"
178 render={({ field }) => (
179 <FormItemLayout
180 label="AWS Account ID"
181 description="The ID of the AWS account you want to connect to."
182 >
183 <FormControl>
184 <Input
185 {...field}
186 readOnly={!isNew}
187 autoFocus={isNew}
188 onFocus={(e) => {
189 if (!isNew) {
190 e.target.blur()
191 }
192 }}
193 />
194 </FormControl>
195 </FormItemLayout>
196 )}
197 />
198 <FormField
199 control={form.control}
200 name="accountName"
201 render={({ field }) => (
202 <FormItemLayout
203 label="Account Name"
204 description="A name for this account connection."
205 >
206 <FormControl>
207 <Input
208 {...field}
209 readOnly={!isNew}
210 onFocus={(e) => {
211 if (!isNew) {
212 e.target.blur()
213 }
214 }}
215 />
216 </FormControl>
217 </FormItemLayout>
218 )}
219 />
220 </SheetSection>
221
222 <SheetFooter>
223 <Button type="default" disabled={isPending} onClick={() => onOpenChange(false)}>
224 Cancel
225 </Button>
226 {isNew && (
227 <Button htmlType="submit" loading={isPending}>
228 Add Account
229 </Button>
230 )}
231 </SheetFooter>
232 </form>
233 </Form>
234 </SheetContent>
235 </Sheet>
236 )
237}