diff --git a/client/src/components/DynamicJsonForm.tsx b/client/src/components/DynamicJsonForm.tsx index 8b339980..efa4d2ad 100644 --- a/client/src/components/DynamicJsonForm.tsx +++ b/client/src/components/DynamicJsonForm.tsx @@ -12,6 +12,8 @@ import JsonEditor from "./JsonEditor"; import { updateValueAtPath } from "@/utils/jsonUtils"; import { generateDefaultValue } from "@/utils/schemaUtils"; import type { JsonValue, JsonSchemaType } from "@/utils/jsonUtils"; +import { useToast } from "@/lib/hooks/useToast"; +import { CheckCheck, Copy } from "lucide-react"; interface DynamicJsonFormProps { schema: JsonSchemaType; @@ -67,6 +69,9 @@ const DynamicJsonForm = forwardRef( const isOnlyJSON = !isSimpleObject(schema); const [isJsonMode, setIsJsonMode] = useState(isOnlyJSON); const [jsonError, setJsonError] = useState(); + const [copiedJson, setCopiedJson] = useState(false); + const { toast } = useToast(); + // Store the raw JSON string to allow immediate feedback during typing // while deferring parsing until the user stops typing const [rawJsonValue, setRawJsonValue] = useState( @@ -160,6 +165,31 @@ const DynamicJsonForm = forwardRef( } }; + const handleCopyJson = useCallback(async () => { + try { + await navigator.clipboard.writeText( + JSON.stringify(value, null, 2) ?? "[]", + ); + setCopiedJson(true); + + toast({ + title: "JSON copied", + description: + "The JSON data has been successfully copied to your clipboard.", + }); + + setTimeout(() => { + setCopiedJson(false); + }, 2000); + } catch (error) { + toast({ + title: "Error", + description: `Failed to copy JSON: ${error instanceof Error ? error.message : String(error)}`, + variant: "destructive", + }); + } + }, [toast, value]); + useImperativeHandle(ref, () => ({ validateJson, })); @@ -432,14 +462,29 @@ const DynamicJsonForm = forwardRef(
{isJsonMode && ( - + <> + + + )} {!isOnlyJSON && (