diff --git a/assets/icons/jsonarrayicon.svg b/assets/icons/jsonarrayicon.svg
new file mode 100644
index 0000000..1866bc2
--- /dev/null
+++ b/assets/icons/jsonarrayicon.svg
@@ -0,0 +1,17 @@
+
+
+
diff --git a/assets/icons/jsonboolicon.svg b/assets/icons/jsonboolicon.svg
new file mode 100644
index 0000000..5b639b1
--- /dev/null
+++ b/assets/icons/jsonboolicon.svg
@@ -0,0 +1,19 @@
+
+
+
diff --git a/assets/icons/jsonnumbericon.svg b/assets/icons/jsonnumbericon.svg
new file mode 100644
index 0000000..c76184e
--- /dev/null
+++ b/assets/icons/jsonnumbericon.svg
@@ -0,0 +1,7 @@
+
+
+
diff --git a/assets/icons/jsonobjecticon.svg b/assets/icons/jsonobjecticon.svg
new file mode 100644
index 0000000..0563426
--- /dev/null
+++ b/assets/icons/jsonobjecticon.svg
@@ -0,0 +1,10 @@
+
+
+
diff --git a/assets/icons/jsonstringicon.svg b/assets/icons/jsonstringicon.svg
new file mode 100644
index 0000000..eeeccd1
--- /dev/null
+++ b/assets/icons/jsonstringicon.svg
@@ -0,0 +1,10 @@
+
+
+
diff --git a/design_files/jsonarrayicon.afdesign b/design_files/jsonarrayicon.afdesign
new file mode 100644
index 0000000..6297f80
Binary files /dev/null and b/design_files/jsonarrayicon.afdesign differ
diff --git a/design_files/jsonboolicon.afdesign b/design_files/jsonboolicon.afdesign
new file mode 100644
index 0000000..6d77c0e
Binary files /dev/null and b/design_files/jsonboolicon.afdesign differ
diff --git a/design_files/jsonnumbericon.afdesign b/design_files/jsonnumbericon.afdesign
new file mode 100644
index 0000000..6da729d
Binary files /dev/null and b/design_files/jsonnumbericon.afdesign differ
diff --git a/design_files/jsonobjecticon.afdesign b/design_files/jsonobjecticon.afdesign
new file mode 100644
index 0000000..f64067e
Binary files /dev/null and b/design_files/jsonobjecticon.afdesign differ
diff --git a/design_files/jsonstringicon.afdesign b/design_files/jsonstringicon.afdesign
new file mode 100644
index 0000000..cc0d809
Binary files /dev/null and b/design_files/jsonstringicon.afdesign differ
diff --git a/src/components/Dashboard/common/DataTree.jsx b/src/components/Dashboard/common/DataTree.jsx
new file mode 100644
index 0000000..aa47749
--- /dev/null
+++ b/src/components/Dashboard/common/DataTree.jsx
@@ -0,0 +1,238 @@
+import PropTypes from 'prop-types'
+import { Tree, Typography, Space, Tag } from 'antd'
+import { useState, useMemo } from 'react'
+import XMarkIcon from '../../Icons/XMarkIcon'
+import QuestionCircleIcon from '../../Icons/QuestionCircleIcon'
+import JsonStringIcon from '../../Icons/JsonStringIcon'
+import JsonArrayIcon from '../../Icons/JsonArrayIcon'
+import JsonObjectIcon from '../../Icons/JsonObjectIcon'
+import JsonBoolIcon from '../../Icons/JsonBoolIcon'
+import JsonNumberIcon from '../../Icons/JsonNumberIcon'
+import CopyButton from './CopyButton'
+
+const { Text } = Typography
+
+const DataTree = ({
+ data,
+ showLine = true,
+ showValueCopy = true,
+ showKeyCopy = false,
+ defaultExpandAll = false,
+ onNodeSelect,
+ style = {}
+}) => {
+ const [expandedKeys, setExpandedKeys] = useState([])
+ const [selectedKeys, setSelectedKeys] = useState([])
+
+ // Function to get data type and format value
+ const getDataTypeInfo = (value) => {
+ if (value === null)
+ return { type: 'null', color: 'default', icon: }
+ if (value === undefined)
+ return {
+ type: 'undefined',
+ color: 'default',
+ icon:
+ }
+ if (typeof value === 'boolean')
+ return { type: 'boolean', color: 'blue', icon: }
+ if (typeof value === 'number')
+ return { type: 'number', color: 'green', icon: }
+ if (typeof value === 'string')
+ return { type: 'string', color: 'orange', icon: }
+ if (Array.isArray(value))
+ return { type: 'array', color: 'purple', icon: }
+ if (typeof value === 'object')
+ return { type: 'object', color: 'cyan', icon: }
+ return { type: 'unknown', color: 'default', icon: }
+ }
+
+ // Function to format value for display
+ const formatValue = (value) => {
+ if (value === null) return 'null'
+ if (value === undefined) return 'undefined'
+ if (typeof value === 'boolean') return value.toString()
+ if (typeof value === 'number') return value.toString()
+ if (typeof value === 'string') {
+ // Truncate long strings
+ return value.length > 50 ? `${value.substring(0, 50)}...` : value
+ }
+ if (Array.isArray(value)) return `Array (${value.length} items)`
+ if (typeof value === 'object') {
+ const keys = Object.keys(value)
+ return `Object (${keys.length} properties)`
+ }
+ return String(value)
+ }
+
+ // Function to get raw value for copying
+ const getCopyValue = (value) => {
+ if (value === null) return 'null'
+ if (value === undefined) return 'undefined'
+ if (typeof value === 'boolean') return value.toString()
+ if (typeof value === 'number') return value.toString()
+ if (typeof value === 'string') return value
+ if (Array.isArray(value)) return JSON.stringify(value, null, 2)
+ if (typeof value === 'object') return JSON.stringify(value, null, 2)
+ return String(value)
+ }
+
+ // Recursive function to convert JSON to tree data
+ const convertToTreeData = (obj, key = 'root', path = '') => {
+ const currentPath = path ? `${path}.${key}` : key
+ const dataInfo = getDataTypeInfo(obj)
+
+ const node = {
+ title: (
+
+
+ {dataInfo.icon}
+
+ {key}
+ {showKeyCopy && (
+
+ )}
+ ({dataInfo.type})
+ {dataInfo.type !== 'object' && dataInfo.type !== 'array' && (
+ <>
+ {formatValue(obj)}
+ {showValueCopy && (
+
+ )}
+ >
+ )}
+ {(dataInfo.type === 'object' || dataInfo.type === 'array') &&
+ showValueCopy && (
+
+ )}
+
+ ),
+ key: currentPath,
+ value: obj,
+ path: currentPath
+ }
+
+ // Add children for objects and arrays
+ if (typeof obj === 'object' && obj !== null) {
+ if (Array.isArray(obj)) {
+ node.children = obj.map((item, index) =>
+ convertToTreeData(item, `[${index}]`, currentPath)
+ )
+ } else {
+ node.children = Object.entries(obj).map(([childKey, childValue]) =>
+ convertToTreeData(childValue, childKey, currentPath)
+ )
+ }
+ }
+
+ return node
+ }
+
+ // Convert data to tree structure
+ const treeData = useMemo(() => {
+ if (!data) return []
+
+ if (typeof data === 'object' && data !== null) {
+ if (Array.isArray(data)) {
+ return [convertToTreeData(data, 'root')]
+ } else {
+ return [convertToTreeData(data, 'root')]
+ }
+ } else {
+ // Handle primitive values
+ const dataInfo = getDataTypeInfo(data)
+ return [
+ {
+ title: (
+
+
+ {dataInfo.icon}
+
+ Value
+ ({dataInfo.type})
+ {formatValue(data)}
+ {showValueCopy && (
+
+ )}
+
+ ),
+ key: 'root',
+ value: data,
+ path: 'root'
+ }
+ ]
+ }
+ }, [data])
+
+ // Handle node selection
+ const handleSelect = (selectedKeys, { selected, selectedNodes }) => {
+ setSelectedKeys(selectedKeys)
+ if (onNodeSelect && selected && selectedNodes.length > 0) {
+ const node = selectedNodes[0]
+ onNodeSelect({
+ key: node.key,
+ value: node.value,
+ path: node.path
+ })
+ }
+ }
+
+ // Handle expand/collapse
+ const handleExpand = (keys) => {
+ setExpandedKeys(keys)
+ }
+
+ // Auto-expand all if requested
+ const getExpandedKeys = () => {
+ if (defaultExpandAll) {
+ return treeData.length > 0 ? getAllKeys(treeData[0]) : []
+ }
+ return expandedKeys
+ }
+
+ // Helper function to get all keys for auto-expand
+ const getAllKeys = (node) => {
+ let keys = [node.key]
+ if (node.children) {
+ node.children.forEach((child) => {
+ keys = keys.concat(getAllKeys(child))
+ })
+ }
+ return keys
+ }
+
+ return (
+
+ )
+}
+
+DataTree.propTypes = {
+ data: PropTypes.any.isRequired,
+ showLine: PropTypes.bool,
+ showValueCopy: PropTypes.bool,
+ showKeyCopy: PropTypes.bool,
+ defaultExpandAll: PropTypes.bool,
+ onNodeSelect: PropTypes.func,
+ style: PropTypes.object
+}
+
+export default DataTree