2024-09-30 07:12:01 -07:00
import { getClient } from "@/api/AxiosClient" ;
2024-11-11 09:50:18 -08:00
import {
CreateTaskRequest ,
OrganizationApiResponse ,
ProxyLocation ,
} from "@/api/types" ;
2024-09-30 07:12:01 -07:00
import { AutoResizingTextarea } from "@/components/AutoResizingTextarea/AutoResizingTextarea" ;
2024-04-01 21:34:52 +03:00
import { Button } from "@/components/ui/button" ;
import {
Form ,
FormControl ,
FormField ,
FormItem ,
FormLabel ,
FormMessage ,
} from "@/components/ui/form" ;
import { Input } from "@/components/ui/input" ;
2024-09-30 09:08:27 -07:00
import { Separator } from "@/components/ui/separator" ;
2024-12-05 11:56:09 -08:00
import { toast } from "@/components/ui/use-toast" ;
2024-05-14 05:02:12 -07:00
import { useApiCredential } from "@/hooks/useApiCredential" ;
2024-09-30 07:12:01 -07:00
import { useCredentialGetter } from "@/hooks/useCredentialGetter" ;
import { CodeEditor } from "@/routes/workflows/components/CodeEditor" ;
import { apiBaseUrl } from "@/util/env" ;
2025-06-09 21:01:45 -04:00
import { CopyApiCommandDropdown } from "@/components/CopyApiCommandDropdown" ;
import { type ApiCommandOptions } from "@/util/apiCommands" ;
2024-09-30 07:12:01 -07:00
import { zodResolver } from "@hookform/resolvers/zod" ;
2025-06-09 21:01:45 -04:00
import { PlayIcon , ReloadIcon } from "@radix-ui/react-icons" ;
2024-09-30 07:12:01 -07:00
import { ToastAction } from "@radix-ui/react-toast" ;
2024-10-08 11:36:41 -07:00
import { useMutation , useQuery , useQueryClient } from "@tanstack/react-query" ;
2024-05-29 09:34:58 -07:00
import { AxiosError } from "axios" ;
2024-09-30 07:12:01 -07:00
import { useState } from "react" ;
import { useForm , useFormState } from "react-hook-form" ;
import { Link } from "react-router-dom" ;
2024-06-21 13:08:00 -07:00
import { MAX_STEPS_DEFAULT } from "../constants" ;
2024-09-30 07:12:01 -07:00
import { TaskFormSection } from "./TaskFormSection" ;
2024-09-30 09:08:27 -07:00
import {
createNewTaskFormSchema ,
CreateNewTaskFormValues ,
} from "./taskFormTypes" ;
2024-11-11 09:50:18 -08:00
import { ProxySelector } from "@/components/ProxySelector" ;
2025-05-15 08:18:24 -07:00
import { Switch } from "@/components/ui/switch" ;
2025-06-13 23:59:50 -07:00
import { MAX_SCREENSHOT_SCROLLING_TIMES_DEFAULT } from "@/routes/workflows/editor/nodes/Taskv2Node/types" ;
2024-04-01 21:34:52 +03:00
type Props = {
initialValues : CreateNewTaskFormValues ;
} ;
2024-09-30 09:08:27 -07:00
function transform < T > ( value : T ) : T | null {
2024-05-20 13:50:21 -07:00
return value === "" ? null : value ;
}
2024-09-30 09:08:27 -07:00
function createTaskRequestObject (
formValues : CreateNewTaskFormValues ,
) : CreateTaskRequest {
2024-06-24 05:07:21 -07:00
let extractedInformationSchema = null ;
if ( formValues . extractedInformationSchema ) {
try {
extractedInformationSchema = JSON . parse (
formValues . extractedInformationSchema ,
) ;
} catch ( e ) {
extractedInformationSchema = formValues . extractedInformationSchema ;
}
}
2024-09-30 09:08:27 -07:00
let errorCodeMapping = null ;
if ( formValues . errorCodeMapping ) {
try {
errorCodeMapping = JSON . parse ( formValues . errorCodeMapping ) ;
} catch ( e ) {
errorCodeMapping = formValues . errorCodeMapping ;
}
}
2024-06-24 05:07:21 -07:00
2024-04-01 21:34:52 +03:00
return {
2024-09-30 09:08:27 -07:00
title : null ,
2024-04-01 21:34:52 +03:00
url : formValues.url ,
2024-05-20 13:50:21 -07:00
webhook_callback_url : transform ( formValues . webhookCallbackUrl ) ,
navigation_goal : transform ( formValues . navigationGoal ) ,
data_extraction_goal : transform ( formValues . dataExtractionGoal ) ,
2024-11-11 09:50:18 -08:00
proxy_location : formValues.proxyLocation ? ? ProxyLocation . Residential ,
2024-05-20 13:50:21 -07:00
navigation_payload : transform ( formValues . navigationPayload ) ,
2024-06-24 05:07:21 -07:00
extracted_information_schema : extractedInformationSchema ,
2024-09-30 07:12:01 -07:00
totp_identifier : transform ( formValues . totpIdentifier ) ,
2024-09-30 09:08:27 -07:00
error_code_mapping : errorCodeMapping ,
2025-06-13 23:59:50 -07:00
max_screenshot_scrolling_times : formValues.maxScreenshotScrollingTimes ,
2025-05-15 08:18:24 -07:00
include_action_history_in_verification :
formValues . includeActionHistoryInVerification ,
2024-04-01 21:34:52 +03:00
} ;
}
2024-10-02 12:07:06 -07:00
type Section = "base" | "extraction" | "advanced" ;
2024-04-01 21:34:52 +03:00
function CreateNewTaskForm ( { initialValues } : Props ) {
const queryClient = useQueryClient ( ) ;
2024-05-07 11:31:05 -07:00
const credentialGetter = useCredentialGetter ( ) ;
2024-05-14 05:02:12 -07:00
const apiCredential = useApiCredential ( ) ;
2024-10-02 12:07:06 -07:00
const [ activeSections , setActiveSections ] = useState < Array < Section > > ( [
2024-09-30 08:19:03 -07:00
"base" ,
2024-10-02 12:07:06 -07:00
] ) ;
2024-10-02 07:45:05 -07:00
const [ showAdvancedBaseContent , setShowAdvancedBaseContent ] = useState ( false ) ;
2024-06-20 09:24:39 -07:00
2024-10-08 11:36:41 -07:00
const { data : organizations } = useQuery < Array < OrganizationApiResponse > > ( {
queryKey : [ "organizations" ] ,
queryFn : async ( ) = > {
const client = await getClient ( credentialGetter ) ;
return await client
. get ( "/organizations" )
. then ( ( response ) = > response . data . organizations ) ;
} ,
} ) ;
const organization = organizations ? . [ 0 ] ;
2024-04-01 21:34:52 +03:00
const form = useForm < CreateNewTaskFormValues > ( {
resolver : zodResolver ( createNewTaskFormSchema ) ,
2024-11-11 09:50:18 -08:00
defaultValues : {
2024-06-20 09:24:39 -07:00
. . . initialValues ,
2024-11-11 09:50:18 -08:00
maxStepsOverride : initialValues.maxStepsOverride ? ? null ,
proxyLocation : initialValues.proxyLocation ? ? ProxyLocation . Residential ,
2025-06-13 23:59:50 -07:00
maxScreenshotScrollingTimes :
initialValues . maxScreenshotScrollingTimes ? ? null ,
2024-06-20 09:24:39 -07:00
} ,
2024-04-01 21:34:52 +03:00
} ) ;
2024-09-30 07:12:01 -07:00
const { errors } = useFormState ( { control : form.control } ) ;
2024-04-01 21:34:52 +03:00
const mutation = useMutation ( {
2024-05-07 11:31:05 -07:00
mutationFn : async ( formValues : CreateNewTaskFormValues ) = > {
2024-04-01 21:34:52 +03:00
const taskRequest = createTaskRequestObject ( formValues ) ;
2024-05-07 11:31:05 -07:00
const client = await getClient ( credentialGetter ) ;
2024-06-20 09:24:39 -07:00
const includeOverrideHeader =
2024-10-08 11:36:41 -07:00
formValues . maxStepsOverride !== null &&
2024-06-20 09:24:39 -07:00
formValues . maxStepsOverride !== MAX_STEPS_DEFAULT ;
2024-04-01 21:34:52 +03:00
return client . post <
ReturnType < typeof createTaskRequestObject > ,
{ data : { task_id : string } }
2024-06-20 09:24:39 -07:00
> ( "/tasks" , taskRequest , {
. . . ( includeOverrideHeader && {
headers : {
2024-10-08 11:36:41 -07:00
"x-max-steps-override" : formValues . maxStepsOverride ,
2024-06-20 09:24:39 -07:00
} ,
} ) ,
} ) ;
2024-04-01 21:34:52 +03:00
} ,
2024-05-29 09:34:58 -07:00
onError : ( error : AxiosError ) = > {
if ( error . response ? . status === 402 ) {
toast ( {
variant : "destructive" ,
title : "Failed to create task" ,
description :
"You don't have enough credits to run this task. Go to billing to see your credit balance." ,
action : (
< ToastAction altText = "Go to Billing" >
< Button asChild >
< Link to = "billing" > Go to Billing < / Link >
< / Button >
< / ToastAction >
) ,
} ) ;
return ;
}
2024-04-01 21:34:52 +03:00
toast ( {
variant : "destructive" ,
2024-05-20 08:27:36 -07:00
title : "There was an error creating the task." ,
2024-04-01 21:34:52 +03:00
description : error.message ,
} ) ;
} ,
onSuccess : ( response ) = > {
toast ( {
2024-05-29 09:34:58 -07:00
variant : "success" ,
2024-04-01 21:34:52 +03:00
title : "Task Created" ,
description : ` ${ response . data . task_id } created successfully. ` ,
action : (
< ToastAction altText = "View" >
< Button asChild >
< Link to = { ` /tasks/ ${ response . data . task_id } ` } > View < / Link >
< / Button >
< / ToastAction >
) ,
} ) ;
queryClient . invalidateQueries ( {
queryKey : [ "tasks" ] ,
} ) ;
2025-02-25 12:59:26 -08:00
queryClient . invalidateQueries ( {
queryKey : [ "runs" ] ,
} ) ;
2024-04-01 21:34:52 +03:00
} ,
} ) ;
function onSubmit ( values : CreateNewTaskFormValues ) {
mutation . mutate ( values ) ;
}
2024-10-02 12:07:06 -07:00
function isActive ( section : Section ) {
return activeSections . includes ( section ) ;
}
function toggleSection ( section : Section ) {
if ( isActive ( section ) ) {
setActiveSections ( activeSections . filter ( ( s ) = > s !== section ) ) ;
} else {
setActiveSections ( [ . . . activeSections , section ] ) ;
}
}
2024-04-01 21:34:52 +03:00
return (
< Form { ...form } >
2024-09-30 07:12:01 -07:00
< form onSubmit = { form . handleSubmit ( onSubmit ) } className = "space-y-4" >
< TaskFormSection
index = { 1 }
title = "Base Content"
2024-10-02 12:07:06 -07:00
active = { isActive ( "base" ) }
2024-09-30 07:12:01 -07:00
onClick = { ( ) = > {
2024-10-02 12:07:06 -07:00
toggleSection ( "base" ) ;
2024-09-30 07:12:01 -07:00
} }
hasError = {
typeof errors . url !== "undefined" ||
typeof errors . navigationGoal !== "undefined"
}
>
2024-10-02 12:07:06 -07:00
{ isActive ( "base" ) && (
2024-09-30 07:12:01 -07:00
< div className = "space-y-6" >
< div className = "space-y-4" >
< FormField
control = { form . control }
name = "url"
render = { ( { field } ) = > (
< FormItem >
< div className = "flex gap-16" >
< FormLabel >
< div className = "w-72" >
< h1 className = "text-lg" > URL < / h1 >
< h2 className = "text-base text-slate-400" >
The starting URL for the task
< / h2 >
< / div >
< / FormLabel >
< div className = "w-full" >
< FormControl >
< Input placeholder = "https://" { ...field } / >
< / FormControl >
< FormMessage / >
< / div >
< / div >
< / FormItem >
) }
/ >
< FormField
control = { form . control }
name = "navigationGoal"
render = { ( { field } ) = > (
< FormItem >
< div className = "flex gap-16" >
< FormLabel >
< div className = "w-72" >
< h1 className = "text-lg" > Navigation Goal < / h1 >
< h2 className = "text-base text-slate-400" >
Where should Skyvern go and what should Skyvern
do ?
< / h2 >
< / div >
< / FormLabel >
< div className = "w-full" >
< FormControl >
< AutoResizingTextarea
{ . . . field }
2024-10-08 11:36:41 -07:00
placeholder = "Tell Skyvern what to do."
2024-09-30 07:12:01 -07:00
value = { field . value === null ? "" : field . value }
/ >
< / FormControl >
< FormMessage / >
< / div >
< / div >
< / FormItem >
) }
2024-05-20 08:27:36 -07:00
/ >
2024-10-02 07:45:05 -07:00
{ showAdvancedBaseContent ? (
< div className = "border-t border-dashed pt-4" >
< FormField
control = { form . control }
name = "navigationPayload"
render = { ( { field } ) = > (
< FormItem >
< div className = "flex gap-16" >
< FormLabel >
< div className = "w-72" >
< h1 className = "text-lg" > Navigation Payload < / h1 >
< h2 className = "text-base text-slate-400" >
Specify important parameters , routes , or
states
< / h2 >
< / div >
< Button
className = "mt-4"
type = "button"
variant = "tertiary"
onClick = { ( ) = > {
setShowAdvancedBaseContent ( false ) ;
} }
size = "sm"
>
Hide Advanced Settings
< / Button >
< / FormLabel >
< div className = "w-full" >
< FormControl >
< CodeEditor
{ . . . field }
language = "json"
minHeight = "96px"
2024-10-09 08:01:24 -07:00
maxHeight = "500px"
2024-10-02 07:45:05 -07:00
value = {
field . value === null ? "" : field . value
}
/ >
< / FormControl >
< FormMessage / >
< / div >
< / div >
< / FormItem >
) }
/ >
< / div >
) : (
< div >
< Button
type = "button"
variant = "tertiary"
onClick = { ( ) = > {
setShowAdvancedBaseContent ( true ) ;
} }
size = "sm"
>
Show Advanced Settings
< / Button >
< / div >
) }
2024-09-30 07:12:01 -07:00
< / div >
< / div >
2024-04-01 21:34:52 +03:00
) }
2024-09-30 07:12:01 -07:00
< / TaskFormSection >
< TaskFormSection
index = { 2 }
title = "Extraction"
2024-10-02 12:07:06 -07:00
active = { isActive ( "extraction" ) }
2024-09-30 07:12:01 -07:00
onClick = { ( ) = > {
2024-10-02 12:07:06 -07:00
toggleSection ( "extraction" ) ;
2024-09-30 07:12:01 -07:00
} }
hasError = {
typeof errors . dataExtractionGoal !== "undefined" ||
typeof errors . extractedInformationSchema !== "undefined"
}
>
2024-10-02 12:07:06 -07:00
{ isActive ( "extraction" ) && (
2024-09-30 07:12:01 -07:00
< div className = "space-y-6" >
< div className = "space-y-4" >
< FormField
control = { form . control }
name = "dataExtractionGoal"
render = { ( { field } ) = > (
< FormItem >
< div className = "flex gap-16" >
< FormLabel >
< div className = "w-72" >
< h1 className = "text-lg" > Data Extraction Goal < / h1 >
< h2 className = "text-base text-slate-400" >
What outputs are you looking to get ?
< / h2 >
< / div >
< / FormLabel >
< div className = "w-full" >
< FormControl >
< AutoResizingTextarea
{ . . . field }
2024-10-08 11:36:41 -07:00
placeholder = "What data do you need to extract?"
2024-09-30 07:12:01 -07:00
value = { field . value === null ? "" : field . value }
/ >
< / FormControl >
< FormMessage / >
< / div >
< / div >
< / FormItem >
) }
/ >
< FormField
control = { form . control }
name = "extractedInformationSchema"
render = { ( { field } ) = > (
< FormItem >
< div className = "flex gap-16" >
< FormLabel >
< div className = "w-72" >
< h1 className = "text-lg" > Data Schema < / h1 >
< h2 className = "text-base text-slate-400" >
Specify the output format in JSON
< / h2 >
< / div >
< / FormLabel >
< div className = "w-full" >
< FormControl >
< CodeEditor
{ . . . field }
language = "json"
minHeight = "96px"
2024-10-09 08:01:24 -07:00
maxHeight = "500px"
2024-09-30 09:08:27 -07:00
value = { field . value === null ? "" : field . value }
2024-09-30 07:12:01 -07:00
/ >
< / FormControl >
< FormMessage / >
< / div >
< / div >
< / FormItem >
) }
2024-04-01 21:34:52 +03:00
/ >
2024-09-30 07:12:01 -07:00
< / div >
< / div >
2024-04-01 21:34:52 +03:00
) }
2024-09-30 07:12:01 -07:00
< / TaskFormSection >
< TaskFormSection
index = { 3 }
title = "Advanced Settings"
2024-10-02 12:07:06 -07:00
active = { isActive ( "advanced" ) }
2024-09-30 07:12:01 -07:00
onClick = { ( ) = > {
2024-10-02 12:07:06 -07:00
toggleSection ( "advanced" ) ;
2024-09-30 07:12:01 -07:00
} }
hasError = {
typeof errors . navigationPayload !== "undefined" ||
typeof errors . maxStepsOverride !== "undefined" ||
2024-09-30 09:08:27 -07:00
typeof errors . webhookCallbackUrl !== "undefined" ||
typeof errors . errorCodeMapping !== "undefined"
2024-09-30 07:12:01 -07:00
}
>
2024-10-02 12:07:06 -07:00
{ isActive ( "advanced" ) && (
2024-09-30 07:12:01 -07:00
< div className = "space-y-6" >
< div className = "space-y-4" >
2025-05-15 08:18:24 -07:00
< FormField
control = { form . control }
name = "includeActionHistoryInVerification"
render = { ( { field } ) = > (
< FormItem >
< div className = "flex gap-16" >
< FormLabel >
< div className = "w-72" >
< h1 className = "text-lg" > Include Action History < / h1 >
< h2 className = "text-base text-slate-400" >
Whether to include action history when verifying
the task completion .
< / h2 >
< / div >
< / FormLabel >
< div className = "w-full" >
< FormControl >
< Switch
checked = { field . value ? ? false }
onCheckedChange = { ( checked ) = > {
field . onChange ( checked ) ;
} }
/ >
< / FormControl >
< FormMessage / >
< / div >
< / div >
< / FormItem >
) }
/ >
2024-09-30 07:12:01 -07:00
< FormField
control = { form . control }
name = "maxStepsOverride"
render = { ( { field } ) = > (
< FormItem >
< div className = "flex gap-16" >
< FormLabel >
< div className = "w-72" >
< h1 className = "text-lg" > Max Steps Override < / h1 >
< h2 className = "text-base text-slate-400" >
Want to allow this task to execute more or less
steps than the default ?
< / h2 >
< / div >
< / FormLabel >
< div className = "w-full" >
< FormControl >
< Input
{ . . . field }
type = "number"
min = { 1 }
2024-10-08 11:36:41 -07:00
value = { field . value ? ? "" }
placeholder = { ` Default: ${ organization ? . max_steps_per_run ? ? MAX_STEPS_DEFAULT } ` }
2024-09-30 07:12:01 -07:00
onChange = { ( event ) = > {
2024-10-08 11:36:41 -07:00
const value =
event . target . value === ""
? null
: Number ( event . target . value ) ;
field . onChange ( value ) ;
2024-09-30 07:12:01 -07:00
} }
/ >
< / FormControl >
< FormMessage / >
< / div >
2024-06-19 19:15:11 +03:00
< / div >
2024-09-30 07:12:01 -07:00
< / FormItem >
) }
/ >
< FormField
control = { form . control }
name = "webhookCallbackUrl"
render = { ( { field } ) = > (
< FormItem >
< div className = "flex gap-16" >
< FormLabel >
< div className = "w-72" >
< h1 className = "text-lg" > Webhook Callback URL < / h1 >
< h2 className = "text-base text-slate-400" >
The URL of a webhook endpoint to send the
extracted information
< / h2 >
< / div >
< / FormLabel >
< div className = "w-full" >
< FormControl >
< Input
{ . . . field }
2024-10-08 11:36:41 -07:00
placeholder = "https://"
2024-09-30 07:12:01 -07:00
value = { field . value === null ? "" : field . value }
/ >
< / FormControl >
< FormMessage / >
< / div >
2024-06-19 19:15:11 +03:00
< / div >
2024-09-30 07:12:01 -07:00
< / FormItem >
) }
/ >
2024-11-11 09:50:18 -08:00
< FormField
control = { form . control }
name = "proxyLocation"
render = { ( { field } ) = > {
return (
< FormItem >
< div className = "flex gap-16" >
< FormLabel >
< div className = "w-72" >
< div className = "flex items-center gap-2 text-lg" >
Proxy Location
< / div >
< h2 className = "text-sm text-slate-400" >
Route Skyvern through one of our available
proxies .
< / h2 >
< / div >
< / FormLabel >
< div className = "w-full space-y-2" >
< FormControl >
< ProxySelector
value = { field . value }
onChange = { field . onChange }
2024-11-26 10:41:18 -08:00
className = "w-48"
2024-11-11 09:50:18 -08:00
/ >
< / FormControl >
< FormMessage / >
< / div >
< / div >
< / FormItem >
) ;
} }
/ >
2025-06-13 23:59:50 -07:00
< FormField
control = { form . control }
name = "maxScreenshotScrollingTimes"
render = { ( { field } ) = > (
< FormItem >
< div className = "flex gap-16" >
< FormLabel >
< div className = "w-72" >
< h1 className = "text-lg" >
Max Scrolling Screenshots
< / h1 >
< h2 className = "text-base text-slate-400" >
{ ` The maximum number of times to scroll down the page to take merged screenshots after action. Default is ${ MAX_SCREENSHOT_SCROLLING_TIMES_DEFAULT } . If it's set to 0, it will take the current viewport screenshot. ` }
< / h2 >
< / div >
< / FormLabel >
< div className = "w-full" >
< FormControl >
< Input
{ . . . field }
type = "number"
min = { 0 }
value = { field . value ? ? "" }
placeholder = { ` Default: ${ MAX_SCREENSHOT_SCROLLING_TIMES_DEFAULT } ` }
onChange = { ( event ) = > {
const value =
event . target . value === ""
? null
: Number ( event . target . value ) ;
field . onChange ( value ) ;
} }
/ >
< / FormControl >
< FormMessage / >
< / div >
< / div >
< / FormItem >
) }
/ >
2024-09-30 09:08:27 -07:00
< Separator / >
< FormField
control = { form . control }
name = "errorCodeMapping"
render = { ( { field } ) = > (
< FormItem >
< div className = "flex gap-16" >
< FormLabel >
< div className = "w-72" >
< h1 className = "text-lg" > Error Messages < / h1 >
< h2 className = "text-base text-slate-400" >
Specify any error outputs you would like to be
notified about
< / h2 >
< / div >
< / FormLabel >
< div className = "w-full" >
< FormControl >
< CodeEditor
{ . . . field }
language = "json"
minHeight = "96px"
2024-10-09 08:01:24 -07:00
maxHeight = "500px"
2024-09-30 09:08:27 -07:00
value = { field . value === null ? "" : field . value }
/ >
< / FormControl >
< FormMessage / >
< / div >
< / div >
< / FormItem >
) }
/ >
< Separator / >
2024-09-30 07:12:01 -07:00
< FormField
control = { form . control }
name = "totpIdentifier"
render = { ( { field } ) = > (
< FormItem >
< div className = "flex gap-16" >
< FormLabel >
< div className = "w-72" >
2024-10-10 06:16:47 -07:00
< h1 className = "text-lg" > 2 FA Identifier < / h1 >
2024-09-30 07:12:01 -07:00
< h2 className = "text-base text-slate-400" > < / h2 >
< / div >
< / FormLabel >
< div className = "w-full" >
< FormControl >
< Input
{ . . . field }
2024-10-08 11:36:41 -07:00
placeholder = "Add an ID that links your TOTP to the task"
2024-09-30 07:12:01 -07:00
value = { field . value === null ? "" : field . value }
/ >
< / FormControl >
< FormMessage / >
< / div >
< / div >
< / FormItem >
) }
/ >
< / div >
< / div >
) }
< / TaskFormSection >
2024-06-19 19:15:11 +03:00
2024-04-01 21:34:52 +03:00
< div className = "flex justify-end gap-3" >
2025-06-09 21:01:45 -04:00
< CopyApiCommandDropdown
getOptions = { ( ) = >
( {
2025-06-05 06:47:05 -04:00
method : "POST" ,
url : ` ${ apiBaseUrl } /tasks ` ,
body : createTaskRequestObject ( form . getValues ( ) ) ,
headers : {
"Content-Type" : "application/json" ,
"x-api-key" : apiCredential ? ? "<your-api-key>" ,
} ,
2025-06-09 21:01:45 -04:00
} ) satisfies ApiCommandOptions
}
/ >
2024-05-20 08:27:36 -07:00
< Button type = "submit" disabled = { mutation . isPending } >
{ mutation . isPending && (
< ReloadIcon className = "mr-2 h-4 w-4 animate-spin" / >
) }
2024-10-11 10:12:08 -07:00
< PlayIcon className = "mr-2 h-4 w-4" / >
Run
2024-05-20 08:27:36 -07:00
< / Button >
2024-04-01 21:34:52 +03:00
< / div >
< / form >
< / Form >
) ;
}
export { CreateNewTaskForm } ;