Skip to main content
Glama
FinalTransformCard.tsx19.6 kB
import JsonSchemaEditor from "@/src/components/editors/JsonSchemaEditor"; import { Button } from "@/src/components/ui/button"; import { Card } from "@/src/components/ui/card"; import { Tabs, TabsContent, TabsList, TabsTrigger, } from "@/src/components/ui/tabs"; import { HelpTooltip } from "@/src/components/utils/HelpTooltip"; import { downloadJson } from "@/src/lib/download-utils"; import { isEmptyData, } from "@/src/lib/general-utils"; import { Code2, Download, FileBracesCorner, FileInput, FilePlay, Loader2, Play, Wand2, X } from "lucide-react"; import { useEffect, useState } from "react"; import { JavaScriptCodeEditor } from "../../editors/JavaScriptCodeEditor"; import { JsonCodeEditor } from "../../editors/JsonCodeEditor"; import { useDataProcessor } from "../hooks/use-data-processor"; import { CopyButton } from "../shared/CopyButton"; export const FinalTransformMiniStepCard = ({ transform, responseSchema, onTransformChange, onResponseSchemaChange, readOnly, onExecuteTransform, onFixTransform, isRunningTransform, isFixingTransform, canExecute, transformResult, transformError, stepInputs, hasTransformCompleted }: { transform?: string; responseSchema?: string; onTransformChange?: (value: string) => void; onResponseSchemaChange?: (value: string) => void; readOnly?: boolean; onExecuteTransform?: (schema: string, transform: string) => void; onFixTransform?: (schema: string, transform: string) => void; isRunningTransform?: boolean; isFixingTransform?: boolean; canExecute?: boolean; transformResult?: any; transformError?: string | null; stepInputs?: any; hasTransformCompleted?: boolean; }) => { const [activeTab, setActiveTab] = useState("transform"); const [localTransform, setLocalTransform] = useState(transform || ""); const [localSchema, setLocalSchema] = useState(responseSchema || ""); const [inputViewMode, setInputViewMode] = useState<"preview" | "schema">( "preview" ); const [outputViewMode, setOutputViewMode] = useState<"preview" | "schema">( "preview" ); const [schemaInitialized, setSchemaInitialized] = useState(false); useEffect(() => { setLocalTransform(transform || ""); }, [transform]); useEffect(() => { if (!schemaInitialized) { setLocalSchema(responseSchema || ""); setSchemaInitialized(true); } }, [responseSchema, schemaInitialized]); useEffect(() => { if (hasTransformCompleted) { setActiveTab("output"); } }, [hasTransformCompleted]); const inputProcessor = useDataProcessor( stepInputs, activeTab === "inputs" ); const outputProcessor = useDataProcessor( transformResult, activeTab === "output" ); // Re-trigger schema computation when data changes and we're viewing schema useEffect(() => { if (activeTab === "output" && outputViewMode === "schema" && transformResult) { outputProcessor.computeSchema(); } }, [transformResult, outputViewMode, activeTab, outputProcessor]); useEffect(() => { if (activeTab === "inputs" && inputViewMode === "schema" && stepInputs) { inputProcessor.computeSchema(); } }, [stepInputs, inputViewMode, activeTab, inputProcessor]); const inputData = { displayString: inputViewMode === "schema" ? inputProcessor.schema?.displayString || "" : inputProcessor.preview?.displayString || "", truncated: inputViewMode === "schema" ? inputProcessor.schema?.truncated || false : inputProcessor.preview?.truncated || false, bytes: inputProcessor.bytes, }; const outputData = { displayString: outputViewMode === "schema" ? outputProcessor.schema?.displayString || "" : outputProcessor.preview?.displayString || "", truncated: outputViewMode === "schema" ? outputProcessor.schema?.truncated || false : outputProcessor.preview?.truncated || false, bytes: outputProcessor.bytes, }; // Trigger schema computation when switching to schema view const handleInputViewModeChange = (mode: "preview" | "schema") => { setInputViewMode(mode); if (mode === "schema") { inputProcessor.computeSchema(); } }; const handleOutputViewModeChange = (mode: "preview" | "schema") => { setOutputViewMode(mode); if (mode === "schema") { outputProcessor.computeSchema(); } }; function handleTransformChange(value: string): void { setLocalTransform(value); if (onTransformChange) onTransformChange(value); } function handleSchemaChange(value: string | null): void { if (value === null || value === "") { setLocalSchema(""); if (onResponseSchemaChange) onResponseSchemaChange(""); } else { setLocalSchema(value); if (onResponseSchemaChange) onResponseSchemaChange(value); } } function handleExecuteTransform(): void { if (onExecuteTransform) { onExecuteTransform(localSchema, localTransform); setActiveTab("output"); } } function handleFixTransform(): void { if (onFixTransform) { onFixTransform(localSchema, localTransform); setActiveTab("output"); } } return ( <Card className="w-full max-w-6xl mx-auto shadow-md border dark:border-border/50"> <div className="p-3"> <div className="flex items-center justify-between mb-3"> <div className="flex items-center gap-2"> <FilePlay className="h-4 w-4 text-muted-foreground" /> <h3 className="text-lg font-semibold">Tool Result</h3> </div> <div className="flex items-center gap-2"> {!readOnly && (onExecuteTransform || onFixTransform) && ( <> {onExecuteTransform && ( <span title={ !canExecute ? "Execute all steps first" : isRunningTransform ? "Transform is running..." : "Test final transform" } > <Button variant="ghost" onClick={handleExecuteTransform} disabled={!canExecute || isRunningTransform || isFixingTransform} className="h-8 px-3 gap-2" > {isRunningTransform ? ( <div className="h-3 w-3 animate-spin rounded-full border-2 border-current border-t-transparent" /> ) : ( <Play className="h-3 w-3" /> )} <span className="font-medium text-[13px]"> Run Transform </span> </Button> </span> )} {onFixTransform && ( <span title={ !canExecute ? "Execute all steps first" : isFixingTransform ? "Fixing transform..." : "Fix transform with auto-repair" } > <Button variant="ghost" onClick={handleFixTransform} disabled={!canExecute || isRunningTransform || isFixingTransform} className="h-8 px-3 gap-2" > {isFixingTransform ? ( <div className="h-3 w-3 animate-spin rounded-full border-2 border-current border-t-transparent" /> ) : ( <Wand2 className="h-3 w-3" /> )} <span className="font-medium text-[13px]"> Fix Transform </span> </Button> </span> )} <HelpTooltip text="Run Transform: executes the transform code with step results. Fix Transform: uses auto-repair to automatically fix transform errors and update the code." /> </> )} </div> </div> <Tabs value={activeTab} onValueChange={setActiveTab}> <TabsList className="h-9 p-1 rounded-md mb-3"> <TabsTrigger value="inputs" className="h-full px-3 text-xs flex items-center gap-1 rounded-sm data-[state=active]:rounded-sm" > <FileInput className="h-4 w-4" /> Step Input </TabsTrigger> <TabsTrigger value="transform" className="h-full px-3 text-xs flex items-center gap-1 rounded-sm data-[state=active]:rounded-sm" > <Code2 className="h-4 w-4" /> Transform Code </TabsTrigger> <TabsTrigger value="schema" className="h-full px-3 text-xs flex items-center gap-1 rounded-sm data-[state=active]:rounded-sm" > <FileBracesCorner className="h-4 w-4" /> Result Schema </TabsTrigger> <TabsTrigger value="output" className="h-full px-3 text-xs flex items-center gap-1 rounded-sm data-[state=active]:rounded-sm" style={hasTransformCompleted && activeTab === "output" ? { backgroundColor: "#FFA500", color: "#000" } : undefined} > {(isRunningTransform || isFixingTransform) ? ( <Loader2 className="h-4 w-4 animate-spin" /> ) : ( <FilePlay className="h-4 w-4" /> )} Tool Result </TabsTrigger> </TabsList> <TabsContent value="inputs" className="mt-2"> {!canExecute ? ( <div className="flex flex-col items-center justify-center py-8 text-muted-foreground border rounded-md bg-muted/5"> <div className="text-xs mb-1">No input yet</div> <p className="text-[10px]">Run all previous steps to see inputs</p> </div> ) : ( <> <JsonCodeEditor value={inputData.displayString} readOnly={true} minHeight="150px" maxHeight="250px" resizable={true} overlay={ <div className="flex items-center gap-2"> {(inputProcessor.isComputingPreview || inputProcessor.isComputingSchema) && ( <Loader2 className="h-4 w-4 animate-spin text-muted-foreground" /> )} <Tabs value={inputViewMode} onValueChange={(v) => handleInputViewModeChange(v as "preview" | "schema") } className="w-auto" > <TabsList className="h-6 rounded-md"> <TabsTrigger value="preview" className="h-5 px-2 text-[11px] rounded-md data-[state=active]:rounded-md" > Preview </TabsTrigger> <TabsTrigger value="schema" className="h-5 px-2 text-[11px] rounded-md data-[state=active]:rounded-md" > Schema </TabsTrigger> </TabsList> </Tabs> <span className="text-[10px] text-muted-foreground"> {inputData.bytes.toLocaleString()} bytes </span> <CopyButton text={inputData.displayString} /> <Button variant="ghost" size="icon" className="h-6 w-6" onClick={() => downloadJson(stepInputs, "transform_step_inputs.json") } title="Download transform inputs as JSON" > <Download className="h-3 w-3" /> </Button> </div> } /> {inputData.truncated && inputViewMode === "preview" && ( <div className="mt-1 text-[10px] text-amber-600 dark:text-amber-300 px-2"> Preview truncated for display performance </div> )} </> )} </TabsContent> <TabsContent value="transform" className="mt-2"> <JavaScriptCodeEditor value={localTransform} onChange={handleTransformChange} readOnly={readOnly} minHeight="150px" maxHeight="250px" resizable={true} isTransformEditor={true} /> </TabsContent> <TabsContent value="schema" className="mt-2"> <div className="space-y-3"> <JsonSchemaEditor value={localSchema || ""} onChange={handleSchemaChange} isOptional={true} showModeToggle={true} /> </div> </TabsContent> <TabsContent value="output" className="mt-2"> <> {(isRunningTransform || isFixingTransform) ? ( <div className="flex flex-col items-center justify-center py-12 text-muted-foreground border rounded-lg"> <Loader2 className="h-8 w-8 mb-2 animate-spin" /> <p className="text-sm"> {isFixingTransform ? "Fixing transform..." : "Running transform..."} </p> <p className="text-xs mt-1"> Please wait while the transform executes </p> </div> ) : transformError ? ( <div className="flex flex-col items-start justify-start p-4 border rounded-lg bg-muted/30 border-border"> <div className="flex items-center gap-2 mb-2"> <X className="h-4 w-4 text-red-500 dark:text-red-400" /> <p className="text-sm font-semibold text-red-500 dark:text-red-400">Transform Error</p> </div> <pre className="text-xs whitespace-pre-wrap font-mono w-full overflow-x-auto"> {transformError} </pre> <p className="text-xs text-muted-foreground mt-2"> Use the "Fix Transform" button above to automatically repair the transform code. </p> </div> ) : transformResult === undefined ? ( <div className="flex flex-col items-center justify-center py-12 text-muted-foreground border rounded-lg"> <FilePlay className="h-8 w-8 mb-2 opacity-50" /> <p className="text-sm">No result yet</p> <p className="text-xs mt-1"> Run the tool or test the transform to see results </p> </div> ) : ( <> <JsonCodeEditor value={outputData.displayString} readOnly minHeight="150px" maxHeight="250px" resizable={true} overlay={ <div className="flex items-center gap-2"> {(outputProcessor.isComputingPreview || outputProcessor.isComputingSchema) && ( <Loader2 className="h-4 w-4 animate-spin text-muted-foreground" /> )} <Tabs value={outputViewMode} onValueChange={(v) => handleOutputViewModeChange(v as "preview" | "schema") } className="w-auto" > <TabsList className="h-6 rounded-md"> <TabsTrigger value="preview" className="h-5 px-2 text-[11px] rounded-md data-[state=active]:rounded-md" > Preview </TabsTrigger> <TabsTrigger value="schema" className="h-5 px-2 text-[11px] rounded-md data-[state=active]:rounded-md" > Schema </TabsTrigger> </TabsList> </Tabs> <span className="text-[10px] text-muted-foreground"> {outputData.bytes.toLocaleString()} bytes </span> <CopyButton text={outputData.displayString} /> <Button variant="ghost" size="icon" className="h-6 w-6" onClick={() => downloadJson( transformResult, "tool_result.json" ) } title="Download tool result as JSON" > <Download className="h-3 w-3" /> </Button> </div> } /> {isEmptyData(outputData.displayString) && ( <div className="mt-2 text-xs text-amber-700 dark:text-amber-300"> ⚠ No data returned. Is this expected? </div> )} {outputData.truncated && outputViewMode === "preview" && ( <div className="mt-2 text-xs text-amber-600 dark:text-amber-300"> Preview truncated for display performance. Use download button to get full data. </div> )} </> )} </> </TabsContent> </Tabs> </div> </Card> ); };

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/superglue-ai/superglue'

If you have feedback or need assistance with the MCP directory API, please join our Discord server