import { type FormEventHandler, useEffect, useMemo, useState } from "react"; import { useMutation } from "@tanstack/react-query"; import { getClient } from "@/api/AxiosClient"; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; import { useToast } from "@/components/ui/use-toast"; import { AutoResizingTextarea } from "@/components/AutoResizingTextarea/AutoResizingTextarea"; import { useCredentialGetter } from "@/hooks/useCredentialGetter"; import { cn } from "@/util/utils"; type Props = { className?: string; defaultIdentifier?: string | null; defaultWorkflowRunId?: string | null; defaultWorkflowId?: string | null; defaultTaskId?: string | null; showAdvancedFields?: boolean; onSuccess?: () => void; }; type SendTotpCodeRequest = { totp_identifier: string; content: string; workflow_run_id?: string; workflow_id?: string; task_id?: string; source?: string; }; function PushTotpCodeForm({ className, defaultIdentifier, defaultWorkflowRunId, defaultWorkflowId, defaultTaskId, showAdvancedFields = false, onSuccess, }: Props) { const [identifier, setIdentifier] = useState(defaultIdentifier?.trim() ?? ""); const [content, setContent] = useState(""); const [workflowRunId, setWorkflowRunId] = useState( defaultWorkflowRunId?.trim() ?? "", ); const [workflowId, setWorkflowId] = useState(defaultWorkflowId?.trim() ?? ""); const [taskId, setTaskId] = useState(defaultTaskId?.trim() ?? ""); const [advancedOpen, setAdvancedOpen] = useState(false); const credentialGetter = useCredentialGetter(); const { toast } = useToast(); useEffect(() => { if ( typeof defaultIdentifier === "string" && defaultIdentifier.trim() !== "" && identifier.trim() === "" ) { setIdentifier(defaultIdentifier.trim()); } }, [defaultIdentifier, identifier]); useEffect(() => { if ( typeof defaultWorkflowRunId === "string" && defaultWorkflowRunId.trim() !== "" && workflowRunId.trim() === "" ) { setWorkflowRunId(defaultWorkflowRunId.trim()); } }, [defaultWorkflowRunId, workflowRunId]); useEffect(() => { if ( typeof defaultWorkflowId === "string" && defaultWorkflowId.trim() !== "" && workflowId.trim() === "" ) { setWorkflowId(defaultWorkflowId.trim()); } }, [defaultWorkflowId, workflowId]); useEffect(() => { if ( typeof defaultTaskId === "string" && defaultTaskId.trim() !== "" && taskId.trim() === "" ) { setTaskId(defaultTaskId.trim()); } }, [defaultTaskId, taskId]); const trimmedIdentifier = useMemo(() => identifier.trim(), [identifier]); const trimmedContent = useMemo(() => content.trim(), [content]); const trimmedWorkflowRunId = useMemo( () => workflowRunId.trim(), [workflowRunId], ); const trimmedWorkflowId = useMemo(() => workflowId.trim(), [workflowId]); const trimmedTaskId = useMemo(() => taskId.trim(), [taskId]); const canSubmit = trimmedIdentifier !== "" && trimmedContent !== ""; const mutation = useMutation({ mutationFn: async (payload: SendTotpCodeRequest) => { const client = await getClient(credentialGetter, "sans-api-v1"); return client.post("/credentials/totp", payload); }, onSuccess: () => { toast({ title: "2FA code sent", description: "Skyvern will process it shortly.", }); setContent(""); onSuccess?.(); }, onError: () => { toast({ variant: "destructive", title: "Failed to send code", description: "Check the identifier and message format, then retry.", }); }, }); const handleSubmit: FormEventHandler = (event) => { event.preventDefault(); if (!canSubmit || mutation.isPending) { return; } const payload: SendTotpCodeRequest = { totp_identifier: trimmedIdentifier, content: trimmedContent, source: "manual_ui", }; if (trimmedWorkflowRunId !== "") { payload.workflow_run_id = trimmedWorkflowRunId; } if (trimmedWorkflowId !== "") { payload.workflow_id = trimmedWorkflowId; } if (trimmedTaskId !== "") { payload.task_id = trimmedTaskId; } mutation.mutate(payload); }; return (
setIdentifier(event.target.value)} disabled={mutation.isPending} />
setContent(event.target.value)} readOnly={mutation.isPending} className="min-h-[4.5rem]" />

We only store this to help the current login. Avoid pasting unrelated sensitive data.

{showAdvancedFields && (
{advancedOpen && (
setWorkflowRunId(event.target.value)} disabled={mutation.isPending} />
setWorkflowId(event.target.value)} disabled={mutation.isPending} />
setTaskId(event.target.value)} disabled={mutation.isPending} />
)}
)}
); } export { PushTotpCodeForm };