CreateFirebaseAuthDialog.tsx164 lines · main
1import { zodResolver } from '@hookform/resolvers/zod'
2import { useParams } from 'common'
3import { Trash } from 'lucide-react'
4import { useEffect } from 'react'
5import { SubmitHandler, useForm } from 'react-hook-form'
6import { toast } from 'sonner'
7import {
8 Button,
9 Dialog,
10 DialogContent,
11 DialogFooter,
12 DialogHeader,
13 DialogSection,
14 DialogTitle,
15 Form,
16 FormControl,
17 FormField,
18 Input,
19 Separator,
20} from 'ui'
21import { FormItemLayout } from 'ui-patterns/form/FormItemLayout/FormItemLayout'
22import * as z from 'zod'
23
24import { useCreateThirdPartyAuthIntegrationMutation } from '@/data/third-party-auth/integration-create-mutation'
25
26interface CreateFirebaseAuthIntegrationProps {
27 visible: boolean
28 onClose: () => void
29 // TODO: Remove this if this Dialog is only used for creating.
30 onDelete: () => void
31}
32
33const FORM_ID = 'create-firebase-auth-integration-form'
34
35const FormSchema = z.object({
36 enabled: z.boolean(),
37 firebaseProjectId: z
38 .string()
39 .trim()
40 .min(1)
41 .regex(/^[A-Za-z0-9-]+$/, 'The project ID contains invalid characters.'), // Only allow alphanumeric characters and hyphens.
42})
43
44export const CreateFirebaseAuthIntegrationDialog = ({
45 visible,
46 onClose,
47 onDelete,
48}: CreateFirebaseAuthIntegrationProps) => {
49 // TODO: Remove this if this Dialog is only used for creating.
50 const isCreating = true
51
52 const { ref: projectRef } = useParams()
53 const { mutate: createAuthIntegration, isPending } = useCreateThirdPartyAuthIntegrationMutation({
54 onSuccess: () => {
55 toast.success(`Successfully created a new Firebase Auth integration.`)
56 onClose()
57 },
58 })
59
60 const form = useForm<z.infer<typeof FormSchema>>({
61 resolver: zodResolver(FormSchema as any),
62 defaultValues: {
63 enabled: true,
64 firebaseProjectId: '',
65 },
66 })
67
68 useEffect(() => {
69 if (visible) {
70 form.reset({
71 enabled: true,
72 firebaseProjectId: '',
73 })
74 // the form input doesn't exist when the form is reset
75 setTimeout(() => {
76 form.setFocus('firebaseProjectId')
77 }, 25)
78 }
79 }, [visible])
80
81 const onSubmit: SubmitHandler<z.infer<typeof FormSchema>> = async (values) => {
82 createAuthIntegration({
83 projectRef: projectRef!,
84 oidcIssuerUrl: `https://securetoken.google.com/${values.firebaseProjectId}`,
85 })
86 }
87
88 return (
89 <Dialog open={visible} onOpenChange={() => onClose()}>
90 <DialogContent>
91 <DialogHeader>
92 <DialogTitle className="truncate">
93 {isCreating
94 ? `Add new Firebase Auth connection`
95 : `Update existing Firebase Auth connection`}
96 </DialogTitle>
97 </DialogHeader>
98
99 <Separator />
100 <DialogSection>
101 <Form {...form}>
102 <form id={FORM_ID} onSubmit={form.handleSubmit(onSubmit)} className="space-y-4">
103 {/* Enabled flag can't be changed for now because there's no update API call for integrations */}
104 {/* <FormField
105 key="enabled"
106 control={form.control}
107 name="enabled"
108 render={({ field }) => (
109 <FormItemLayout
110 className="px-8"
111 label={`Enable Firebase Auth Connection`}
112 layout="flex"
113 >
114 <FormControl>
115 <Switch
116 checked={field.value}
117 onCheckedChange={field.onChange}
118 disabled={field.disabled}
119 />
120 </FormControl>
121 </FormItemLayout>
122 )}
123 />
124 <Separator /> */}
125
126 <p className="text-sm text-foreground-light">
127 This will enable a JWT token from a specific Firebase project to access data from
128 this Briven project.
129 </p>
130 <FormField
131 key="firebaseProjectId"
132 control={form.control}
133 name="firebaseProjectId"
134 render={({ field }) => (
135 <FormItemLayout label="Firebase Auth Project ID">
136 <FormControl>
137 <Input {...field} />
138 </FormControl>
139 </FormItemLayout>
140 )}
141 />
142 </form>
143 </Form>
144 </DialogSection>
145 <DialogFooter>
146 {!isCreating && (
147 <div className="flex-1">
148 <Button type="danger" onClick={() => onDelete()} icon={<Trash />}>
149 Remove connection
150 </Button>
151 </div>
152 )}
153
154 <Button disabled={isPending} type="default" onClick={() => onClose()}>
155 Cancel
156 </Button>
157 <Button form={FORM_ID} htmlType="submit" disabled={isPending} loading={isPending}>
158 {isCreating ? 'Create connection' : 'Update connection'}
159 </Button>
160 </DialogFooter>
161 </DialogContent>
162 </Dialog>
163 )
164}