@@ -9,6 +9,13 @@ import { Button } from "@/components/ui/button";
99import { CodeClient } from "@/components/ui/code/code.client" ;
1010import { Form , FormField , FormItem , FormMessage } from "@/components/ui/form" ;
1111import { Input } from "@/components/ui/input" ;
12+ import {
13+ Select ,
14+ SelectContent ,
15+ SelectItem ,
16+ SelectTrigger ,
17+ SelectValue ,
18+ } from "@/components/ui/select" ;
1219import { Separator } from "@/components/ui/separator" ;
1320import { ToolTipLabel } from "@/components/ui/tooltip" ;
1421import { cn } from "@/lib/utils" ;
@@ -27,7 +34,11 @@ import {
2734import Link from "next/link" ;
2835import type { OpenAPIV3 } from "openapi-types" ;
2936import { useEffect , useMemo , useState } from "react" ;
30- import { type UseFormReturn , useForm } from "react-hook-form" ;
37+ import {
38+ type ControllerRenderProps ,
39+ type UseFormReturn ,
40+ useForm ,
41+ } from "react-hook-form" ;
3142import { z } from "zod" ;
3243import { useTrack } from "../../../../../../hooks/analytics/useTrack" ;
3344import { getVercelEnv } from "../../../../../../lib/vercel-utils" ;
@@ -412,14 +423,20 @@ function RequestConfigSection(props: {
412423 supportedChainIds : number [ ] ;
413424} ) {
414425 const pathVariables = props . parameters . filter ( ( param ) => param . in === "path" ) ;
415-
416426 const queryParams = props . parameters . filter ( ( param ) => param . in === "query" ) ;
427+ const showError =
428+ ! props . form . formState . isValid &&
429+ props . form . formState . isDirty &&
430+ props . form . formState . isSubmitted ;
417431
418432 return (
419433 < div className = "flex grow flex-col overflow-hidden" >
420- < div className = "flex min-h-[60px] items-center gap-2 border-b p-4 text-sm" >
421- < ArrowUpRightIcon className = "size-5" />
422- Request
434+ < div className = "flex min-h-[60px] items-center justify-between gap-2 border-b p-4 text-sm" >
435+ < div className = "flex items-center gap-2" >
436+ < ArrowUpRightIcon className = "size-5" />
437+ Request
438+ </ div >
439+ { showError && < Badge variant = "destructive" > Invalid Request</ Badge > }
423440 </ div >
424441
425442 < ScrollShadow className = "flex-1" scrollableClassName = "max-h-full" >
@@ -474,7 +491,25 @@ function ParameterSection(props: {
474491 ? param . schema . description
475492 : undefined ;
476493
494+ const example =
495+ param . schema && "type" in param . schema
496+ ? param . schema . example
497+ : undefined ;
498+ const exampleToShow =
499+ typeof example === "string" || typeof example === "number"
500+ ? example
501+ : undefined ;
502+
503+ const showTip = description !== undefined || example !== undefined ;
504+
477505 const hasError = ! ! props . form . formState . errors [ param . name ] ;
506+
507+ const placeholder = url . includes ( `{${ param . name } }` )
508+ ? `{${ param . name } }`
509+ : url . includes ( `:${ param . name } ` )
510+ ? `:${ param . name } `
511+ : "Value" ;
512+
478513 return (
479514 < FormField
480515 key = { param . name }
@@ -530,23 +565,39 @@ function ParameterSection(props: {
530565 />
531566 ) : (
532567 < >
533- < Input
534- { ...field }
535- className = { cn (
536- "h-auto truncate rounded-none border-0 bg-transparent py-3 font-mono text-sm focus-visible:ring-0 focus-visible:ring-offset-0" ,
537- description && "lg:pr-10" ,
538- hasError && "text-destructive-text" ,
539- ) }
540- placeholder = {
541- url . includes ( `{${ param . name } }` )
542- ? `{${ param . name } }`
543- : url . includes ( `:${ param . name } ` )
544- ? `:${ param . name } `
545- : "Value"
546- }
568+ < ParameterInput
569+ param = { param }
570+ field = { field }
571+ showTip = { showTip }
572+ hasError = { hasError }
573+ placeholder = { placeholder }
547574 />
548- { description && (
549- < ToolTipLabel label = { description } >
575+
576+ { showTip && (
577+ < ToolTipLabel
578+ hoverable
579+ contentClassName = "max-w-[100vw] break-all"
580+ label = {
581+ < div className = "flex flex-col gap-2" >
582+ { description && (
583+ < p className = "text-foreground" >
584+ { description }
585+ </ p >
586+ ) }
587+
588+ { exampleToShow !== undefined && (
589+ < div >
590+ < p className = "mb-1 text-muted-foreground" >
591+ Example:{ " " }
592+ < span className = "font-mono" >
593+ { exampleToShow }
594+ </ span >
595+ </ p >
596+ </ div >
597+ ) }
598+ </ div >
599+ }
600+ >
550601 < Button
551602 asChild
552603 variant = "ghost"
@@ -573,6 +624,66 @@ function ParameterSection(props: {
573624 ) ;
574625}
575626
627+ function ParameterInput ( props : {
628+ param : OpenAPIV3 . ParameterObject ;
629+ field : ControllerRenderProps <
630+ {
631+ [ x : string ] : string | number ;
632+ } ,
633+ string
634+ > ;
635+ showTip : boolean ;
636+ hasError : boolean ;
637+ placeholder : string ;
638+ } ) {
639+ const { param, field, showTip, hasError, placeholder } = props ;
640+
641+ if ( param . schema && "type" in param . schema && param . schema . enum ) {
642+ const { value, onChange, ...restField } = field ;
643+ return (
644+ < Select
645+ { ...restField }
646+ value = { value . toString ( ) }
647+ onValueChange = { ( v ) => {
648+ onChange ( { target : { value : v } } ) ;
649+ } }
650+ >
651+ < SelectTrigger
652+ className = { cn (
653+ "border-none bg-transparent pr-10 font-mono focus:ring-0 focus:ring-offset-0" ,
654+ value === "" && "text-muted-foreground" ,
655+ ) }
656+ chevronClassName = "hidden"
657+ >
658+ < SelectValue placeholder = "Select" />
659+ </ SelectTrigger >
660+
661+ < SelectContent className = "font-mono" >
662+ { param . schema . enum . map ( ( val ) => {
663+ return (
664+ < SelectItem value = { val } key = { val } >
665+ { val }
666+ </ SelectItem >
667+ ) ;
668+ } ) }
669+ </ SelectContent >
670+ </ Select >
671+ ) ;
672+ }
673+
674+ return (
675+ < Input
676+ { ...field }
677+ className = { cn (
678+ "h-auto truncate rounded-none border-0 bg-transparent py-3 font-mono text-sm focus-visible:ring-0 focus-visible:ring-offset-0" ,
679+ showTip && "lg:pr-10" ,
680+ hasError && "text-destructive-text" ,
681+ ) }
682+ placeholder = { placeholder }
683+ />
684+ ) ;
685+ }
686+
576687function formatMilliseconds ( ms : number ) {
577688 if ( ms < 1000 ) {
578689 return `${ Math . round ( ms ) } ms` ;
@@ -677,6 +788,21 @@ function openAPIV3ParamToZodFormSchema(param: BlueprintParameter) {
677788 return ;
678789 }
679790
791+ // if enum values
792+ const enumValues = param . schema . enum ;
793+ if ( enumValues ) {
794+ const enumSchema = z . enum (
795+ // @ts -expect-error - Its correct
796+ enumValues ,
797+ ) ;
798+
799+ if ( param . required ) {
800+ return enumSchema ;
801+ }
802+
803+ return enumSchema . or ( z . literal ( "" ) ) ;
804+ }
805+
680806 switch ( param . schema . type ) {
681807 case "integer" : {
682808 const intSchema = z . coerce
0 commit comments