@@ -11,9 +11,12 @@ import {
1111 FormMessage ,
1212} from "@/components/ui/form" ;
1313import { Input } from "@/components/ui/input" ;
14+ import { Label } from "@/components/ui/label" ;
15+ import { Switch } from "@/components/ui/switch" ;
1416import { cn } from "@/lib/utils" ;
1517import { zodResolver } from "@hookform/resolvers/zod" ;
16- import { useForm } from "react-hook-form" ;
18+ import { PlusIcon , Trash2Icon } from "lucide-react" ;
19+ import { useFieldArray , useForm } from "react-hook-form" ;
1720import { toast } from "sonner" ;
1821import type { z } from "zod" ;
1922import type { Ecosystem , Partner } from "../../../../../types" ;
@@ -31,12 +34,22 @@ export function UpdatePartnerForm({
3134 onSuccess : ( ) => void ;
3235 authToken : string ;
3336} ) {
34- const form = useForm < z . input < typeof partnerFormSchema > > ( {
37+ // Check if partner has accessControl and serverVerifier
38+ const hasAccessControl = ! ! partner . accessControl ;
39+ const hasServerVerifier =
40+ hasAccessControl && ! ! partner . accessControl ?. serverVerifier ;
41+
42+ const form = useForm < z . infer < typeof partnerFormSchema > > ( {
3543 resolver : zodResolver ( partnerFormSchema ) ,
3644 defaultValues : {
3745 name : partner . name ,
3846 domains : partner . allowlistedDomains . join ( "," ) ,
3947 bundleIds : partner . allowlistedBundleIds . join ( "," ) ,
48+ // Set the actual accessControl data if it exists
49+ accessControl : partner . accessControl ,
50+ // Set the UI control properties based on existing data
51+ accessControlEnabled : hasAccessControl ,
52+ serverVerifierEnabled : hasServerVerifier ,
4053 } ,
4154 } ) ;
4255
@@ -59,10 +72,34 @@ export function UpdatePartnerForm({
5972 } ,
6073 ) ;
6174
75+ // Watch the boolean flags for UI state
76+ const accessControlEnabled = form . watch ( "accessControlEnabled" ) ;
77+ const serverVerifierEnabled = form . watch ( "serverVerifierEnabled" ) ;
78+
79+ // Setup field array for headers
80+ const customHeaderFields = useFieldArray ( {
81+ control : form . control ,
82+ name : "accessControl.serverVerifier.headers" ,
83+ } ) ;
84+
6285 return (
6386 < Form { ...form } >
6487 < form
6588 onSubmit = { form . handleSubmit ( ( values ) => {
89+ // Construct the final accessControl object based on the enabled flags
90+ let finalAccessControl : Partner [ "accessControl" ] | null = null ;
91+
92+ if ( values . accessControlEnabled ) {
93+ finalAccessControl = { } as Partner [ "accessControl" ] ;
94+
95+ if ( finalAccessControl && values . serverVerifierEnabled ) {
96+ finalAccessControl . serverVerifier = {
97+ url : values . accessControl ?. serverVerifier ?. url || "" ,
98+ headers : values . accessControl ?. serverVerifier ?. headers || [ ] ,
99+ } ;
100+ }
101+ }
102+
66103 updatePartner ( {
67104 ecosystem,
68105 partnerId : partner . id ,
@@ -73,6 +110,7 @@ export function UpdatePartnerForm({
73110 allowlistedBundleIds : values . bundleIds
74111 . split ( / , | / )
75112 . filter ( ( d ) => d . length > 0 ) ,
113+ accessControl : finalAccessControl ,
76114 } ) ;
77115 } ) }
78116 className = "flex flex-col gap-4"
@@ -81,7 +119,7 @@ export function UpdatePartnerForm({
81119 < FormField
82120 control = { form . control }
83121 name = "name"
84- defaultValue = "" // Note: you *must* provide a default value here or the field won't reset
122+ defaultValue = ""
85123 render = { ( { field } ) => (
86124 < FormItem className = "col-span-4" >
87125 < FormLabel > Name </ FormLabel >
@@ -108,7 +146,7 @@ export function UpdatePartnerForm({
108146 < FormField
109147 control = { form . control }
110148 name = "domains"
111- defaultValue = "" // Note: you *must* provide a default value here or the field won't reset
149+ defaultValue = ""
112150 render = { ( { field } ) => (
113151 < FormItem className = "col-span-4 lg:col-span-4" >
114152 < FormLabel > Domains </ FormLabel >
@@ -119,7 +157,7 @@ export function UpdatePartnerForm({
119157 className = { cn (
120158 "block text-xs" ,
121159 form . formState . errors . domains ?. message &&
122- "block translate-y-0 text-destructive opacity-100" , // If there are errors show them rather than the tip
160+ "block translate-y-0 text-destructive opacity-100" ,
123161 ) }
124162 >
125163 { form . formState . errors . domains ?. message ??
@@ -131,7 +169,7 @@ export function UpdatePartnerForm({
131169 < FormField
132170 control = { form . control }
133171 name = "bundleIds"
134- defaultValue = "" // Note: you *must* provide a default value here or the field won't reset
172+ defaultValue = ""
135173 render = { ( { field } ) => (
136174 < FormItem className = "col-span-4" >
137175 < FormLabel > Bundle ID </ FormLabel >
@@ -152,6 +190,144 @@ export function UpdatePartnerForm({
152190 </ FormItem >
153191 ) }
154192 />
193+
194+ { /* Access Control Section */ }
195+ < div className = "mb-4 flex items-center justify-between gap-6" >
196+ < div >
197+ < Label htmlFor = "access-control-switch" className = "text-base" >
198+ Access Control
199+ </ Label >
200+ < p className = "mt-0.5 text-muted-foreground text-xs" >
201+ Enable access control for this partner
202+ </ p >
203+ </ div >
204+ < Switch
205+ id = "access-control-switch"
206+ checked = { accessControlEnabled }
207+ onCheckedChange = { ( checked ) => {
208+ form . setValue ( "accessControlEnabled" , checked ) ;
209+ // If disabling access control, also disable server verifier
210+ if ( ! checked ) {
211+ form . setValue ( "serverVerifierEnabled" , false ) ;
212+ }
213+ } }
214+ />
215+ </ div >
216+
217+ { accessControlEnabled && (
218+ < div className = "rounded-lg border border-border p-4" >
219+ < div className = "mb-4 flex items-center justify-between gap-6" >
220+ < div >
221+ < Label htmlFor = "server-verifier-switch" className = "text-base" >
222+ Server Verifier
223+ </ Label >
224+ < p className = "mt-0.5 text-muted-foreground text-xs" >
225+ Configure a server verifier for access control
226+ </ p >
227+ </ div >
228+ < Switch
229+ id = "server-verifier-switch"
230+ checked = { serverVerifierEnabled }
231+ onCheckedChange = { ( checked ) => {
232+ form . setValue ( "serverVerifierEnabled" , checked ) ;
233+
234+ // Initialize serverVerifier fields if enabling
235+ if (
236+ checked &&
237+ ! form . getValues ( "accessControl.serverVerifier" )
238+ ) {
239+ form . setValue ( "accessControl.serverVerifier" , {
240+ url : "" ,
241+ headers : [ ] ,
242+ } ) ;
243+ }
244+ } }
245+ />
246+ </ div >
247+
248+ { serverVerifierEnabled && (
249+ < div className = "mt-4 grid grid-cols-1 gap-6" >
250+ < FormField
251+ control = { form . control }
252+ name = "accessControl.serverVerifier.url"
253+ render = { ( { field } ) => (
254+ < FormItem >
255+ < FormLabel > Server Verifier URL</ FormLabel >
256+ < FormControl >
257+ < Input
258+ { ...field }
259+ placeholder = "https://example.com/your-verifier"
260+ />
261+ </ FormControl >
262+ < FormDescription className = "text-xs" >
263+ Enter the URL of your server where verification
264+ requests will be sent
265+ </ FormDescription >
266+ < FormMessage />
267+ </ FormItem >
268+ ) }
269+ />
270+
271+ < div >
272+ < Label className = "mb-3 inline-block" > Custom Headers</ Label >
273+ < div className = "flex flex-col gap-4" >
274+ { customHeaderFields . fields . map ( ( field , headerIdx ) => {
275+ return (
276+ < div className = "flex gap-4" key = { field . id } >
277+ < Input
278+ placeholder = "Name"
279+ type = "text"
280+ { ...form . register (
281+ `accessControl.serverVerifier.headers.${ headerIdx } .key` ,
282+ ) }
283+ />
284+ < Input
285+ placeholder = "Value"
286+ type = "text"
287+ { ...form . register (
288+ `accessControl.serverVerifier.headers.${ headerIdx } .value` ,
289+ ) }
290+ />
291+ < Button
292+ variant = "outline"
293+ aria-label = "Remove header"
294+ onClick = { ( ) => {
295+ customHeaderFields . remove ( headerIdx ) ;
296+ } }
297+ className = "!w-auto px-3"
298+ type = "button"
299+ >
300+ < Trash2Icon className = "size-4 shrink-0 text-destructive-text" />
301+ </ Button >
302+ </ div >
303+ ) ;
304+ } ) }
305+
306+ < Button
307+ variant = "outline"
308+ className = "w-full gap-2 bg-background"
309+ onClick = { ( ) => {
310+ customHeaderFields . append ( {
311+ key : "" ,
312+ value : "" ,
313+ } ) ;
314+ } }
315+ type = "button"
316+ >
317+ < PlusIcon className = "size-4" />
318+ Add header
319+ </ Button >
320+ </ div >
321+
322+ < p className = "mt-3 text-muted-foreground text-xs" >
323+ Set custom headers to be sent along with verification
324+ requests
325+ </ p >
326+ </ div >
327+ </ div >
328+ ) }
329+ </ div >
330+ ) }
155331 </ div >
156332
157333 < Button
0 commit comments