CreateClerkAuthDialog.tsx149 lines · main
1import { zodResolver } from '@hookform/resolvers/zod'
2import { useParams } from 'common'
3import { useEffect } from 'react'
4import { SubmitHandler, useForm } from 'react-hook-form'
5import { toast } from 'sonner'
6import {
7 Button,
8 Dialog,
9 DialogContent,
10 DialogFooter,
11 DialogHeader,
12 DialogSection,
13 DialogTitle,
14 Form,
15 FormControl,
16 FormField,
17 Input,
18 Separator,
19} from 'ui'
20import { FormItemLayout } from 'ui-patterns/form/FormItemLayout/FormItemLayout'
21import * as z from 'zod'
22
23import { InlineLink } from '@/components/ui/InlineLink'
24import { useCreateThirdPartyAuthIntegrationMutation } from '@/data/third-party-auth/integration-create-mutation'
25
26interface CreateClerkAuthIntegrationProps {
27 visible: boolean
28 prod?: boolean
29 onClose: () => void
30 // TODO: Remove this if this Dialog is only used for creating.
31 onDelete: () => void
32}
33
34const FORM_ID = 'create-firebase-auth-integration-form'
35
36const FormSchema = z
37 .object({
38 enabled: z.boolean(),
39 domain: z.string(),
40 })
41 .superRefine((val, ctx) => {
42 if (
43 !val.domain.match(/https:\/\/clerk([.][a-z0-9-]+){2,}\/?/) &&
44 !val.domain.match(/https:\/\/[a-z0-9-]+[.]clerk[.]accounts[.]dev\/?$/)
45 ) {
46 ctx.addIssue({
47 code: z.ZodIssueCode.invalid_string,
48 path: ['domain'],
49 message:
50 'Production Clerk domains use HTTPS and start with the clerk subdomain (https://clerk.example.com). Development Clerk domains use HTTPS and end with .clerk.accounts.dev (https://example.clerk.accounts.dev).',
51 validation: 'regex',
52 })
53 }
54 })
55
56export const CreateClerkAuthIntegrationDialog = ({
57 visible,
58 onClose,
59}: CreateClerkAuthIntegrationProps) => {
60 const { ref: projectRef } = useParams()
61 const { mutate: createAuthIntegration, isPending } = useCreateThirdPartyAuthIntegrationMutation({
62 onSuccess: () => {
63 toast.success(`Successfully created a new Clerk integration.`)
64 onClose()
65 },
66 })
67
68 const form = useForm<z.infer<typeof FormSchema>>({
69 resolver: zodResolver(FormSchema as any),
70 defaultValues: {
71 enabled: true,
72 domain: '',
73 },
74 })
75
76 useEffect(() => {
77 if (visible) {
78 form.reset({
79 enabled: true,
80 domain: '',
81 })
82 // the form input doesn't exist when the form is reset
83 setTimeout(() => {
84 form.setFocus('domain')
85 }, 25)
86 }
87 }, [visible])
88
89 const onSubmit: SubmitHandler<z.infer<typeof FormSchema>> = async (values) => {
90 createAuthIntegration({
91 projectRef: projectRef!,
92 oidcIssuerUrl: values.domain,
93 })
94 }
95
96 return (
97 <Dialog open={visible} onOpenChange={() => onClose()}>
98 <DialogContent>
99 <DialogHeader>
100 <DialogTitle className="truncate">Add new Clerk connection</DialogTitle>
101 </DialogHeader>
102
103 <Separator />
104 <DialogSection>
105 <Form {...form}>
106 <form id={FORM_ID} onSubmit={form.handleSubmit(onSubmit)} className="space-y-4">
107 <p className="text-sm text-foreground-light">
108 Register your Clerk domain. Visit{' '}
109 <InlineLink
110 href="https://dashboard.clerk.com/setup/briven"
111 target="_blank"
112 rel="noopener"
113 >
114 Clerk's Connect with Briven page
115 </InlineLink>{' '}
116 to configure your Clerk instance.
117 </p>
118 <FormField
119 key="domain"
120 control={form.control}
121 name="domain"
122 render={({ field }) => (
123 <FormItemLayout label="Clerk Domain">
124 <FormControl>
125 <Input
126 {...field}
127 placeholder={
128 'https://clerk.example.com or https://example.clerk.accounts.dev'
129 }
130 />
131 </FormControl>
132 </FormItemLayout>
133 )}
134 />
135 </form>
136 </Form>
137 </DialogSection>
138 <DialogFooter>
139 <Button disabled={isPending} type="default" onClick={() => onClose()}>
140 Cancel
141 </Button>
142 <Button form={FORM_ID} htmlType="submit" disabled={isPending} loading={isPending}>
143 Create connection
144 </Button>
145 </DialogFooter>
146 </DialogContent>
147 </Dialog>
148 )
149}