From 46f01e8c5807137502feaa1b2d0f489b927ae808 Mon Sep 17 00:00:00 2001 From: hatim dinia Date: Wed, 10 Jan 2024 11:36:26 +0100 Subject: [PATCH 1/6] feat(ui-common): add JSONEditor --- webapp/package-lock.json | 479 +++++++++++------- webapp/package.json | 2 + .../DebugView/StudyDataView/StudyJsonView.tsx | 53 +- .../DebugView/StudyDataView/style.ts | 2 +- .../components/common/JSONEditor/index.tsx | 46 ++ 5 files changed, 387 insertions(+), 195 deletions(-) create mode 100644 webapp/src/components/common/JSONEditor/index.tsx diff --git a/webapp/package-lock.json b/webapp/package-lock.json index 6ce088d97a..01efffd824 100644 --- a/webapp/package-lock.json +++ b/webapp/package-lock.json @@ -43,6 +43,7 @@ "i18next-xhr-backend": "3.2.2", "immer": "10.0.3", "js-cookie": "3.0.5", + "jsoneditor": "9.10.4", "jwt-decode": "3.1.2", "lodash": "4.17.21", "material-react-table": "2.0.5", @@ -85,6 +86,7 @@ "@total-typescript/ts-reset": "0.5.1", "@types/debug": "4.1.9", "@types/js-cookie": "3.0.4", + "@types/jsoneditor": "9.9.5", "@types/lodash": "4.14.199", "@types/ramda": "0.29.5", "@types/react-beautiful-dnd": "13.1.5", @@ -2736,6 +2738,26 @@ "url": "https://opencollective.com/eslint" } }, + "node_modules/@eslint/eslintrc/node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/@eslint/eslintrc/node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" + }, "node_modules/@eslint/js": { "version": "8.50.0", "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.50.0.tgz", @@ -4086,6 +4108,11 @@ "@sinonjs/commons": "^1.7.0" } }, + "node_modules/@sphinxxxx/color-conversion": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/@sphinxxxx/color-conversion/-/color-conversion-2.2.2.tgz", + "integrity": "sha512-XExJS3cLqgrmNBIP3bBw6+1oQ1ksGjFh0+oClDKFYpCCqx/hlqwWO5KO/S63fzUo67SxI9dMrF0y5T/Ey7h8Zw==" + }, "node_modules/@surma/rollup-plugin-off-main-thread": { "version": "2.2.3", "resolved": "https://registry.npmjs.org/@surma/rollup-plugin-off-main-thread/-/rollup-plugin-off-main-thread-2.2.3.tgz", @@ -4825,6 +4852,12 @@ "url": "https://opencollective.com/turf" } }, + "node_modules/@types/ace": { + "version": "0.0.52", + "resolved": "https://registry.npmjs.org/@types/ace/-/ace-0.0.52.tgz", + "integrity": "sha512-YPF9S7fzpuyrxru+sG/rrTpZkC6gpHBPF14W3x70kqVOD+ks6jkYLapk4yceh36xej7K4HYxcyz9ZDQ2lTvwgQ==", + "dev": true + }, "node_modules/@types/babel__core": { "version": "7.20.2", "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.2.tgz", @@ -5151,6 +5184,14 @@ "immutable": "~3.7.4" } }, + "node_modules/@types/draft-js/node_modules/immutable": { + "version": "3.7.6", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-3.7.6.tgz", + "integrity": "sha512-AizQPcaofEtO11RZhPPHBOJRdo/20MKQF9mBLnVkBoyHi1/zXK8fzVdnEpSV9gxqtnh6Qomfp3F0xT5qP/vThw==", + "engines": { + "node": ">=0.8.0" + } + }, "node_modules/@types/draftjs-to-html": { "version": "0.8.2", "resolved": "https://registry.npmjs.org/@types/draftjs-to-html/-/draftjs-to-html-0.8.2.tgz", @@ -5289,6 +5330,38 @@ "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==" }, + "node_modules/@types/jsoneditor": { + "version": "9.9.5", + "resolved": "https://registry.npmjs.org/@types/jsoneditor/-/jsoneditor-9.9.5.tgz", + "integrity": "sha512-+Wex7QCirPcG90WA8/CmvDO21KUjz63/G7Yk52Yx/NhWHw5DyeET/L+wjZHAeNeNCCnMOTEtVX5gc3F4UXwXMQ==", + "dev": true, + "dependencies": { + "@types/ace": "*", + "ajv": "^6.12.0" + } + }, + "node_modules/@types/jsoneditor/node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/@types/jsoneditor/node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, "node_modules/@types/lodash": { "version": "4.14.199", "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.199.tgz", @@ -5600,7 +5673,7 @@ "version": "6.7.3", "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.7.3.tgz", "integrity": "sha512-vntq452UHNltxsaaN+L9WyuMch8bMd9CqJ3zhzTPXXidwbf5mqqKCVXEuvRZUqLJSTLeWE65lQwyXsRGnXkCTA==", - "dev": true, + "devOptional": true, "dependencies": { "@eslint-community/regexpp": "^4.5.1", "@typescript-eslint/scope-manager": "6.7.3", @@ -5768,7 +5841,7 @@ "version": "6.7.3", "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.7.3.tgz", "integrity": "sha512-TlutE+iep2o7R8Lf+yoer3zU6/0EAUc8QIBB3GYBc1KGz4c4TRm83xwXUZVPlZ6YCLss4r77jbu6j3sendJoiQ==", - "dev": true, + "devOptional": true, "dependencies": { "@typescript-eslint/scope-manager": "6.7.3", "@typescript-eslint/types": "6.7.3", @@ -5796,7 +5869,7 @@ "version": "6.7.3", "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.7.3.tgz", "integrity": "sha512-wOlo0QnEou9cHO2TdkJmzF7DFGvAKEnB82PuPNHpT8ZKKaZu6Bm63ugOTn9fXNJtvuDPanBc78lGUGGytJoVzQ==", - "dev": true, + "devOptional": true, "dependencies": { "@typescript-eslint/types": "6.7.3", "@typescript-eslint/visitor-keys": "6.7.3" @@ -5813,7 +5886,7 @@ "version": "6.7.3", "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.7.3.tgz", "integrity": "sha512-Fc68K0aTDrKIBvLnKTZ5Pf3MXK495YErrbHb1R6aTpfK5OdSFj0rVN7ib6Tx6ePrZ2gsjLqr0s98NG7l96KSQw==", - "dev": true, + "devOptional": true, "dependencies": { "@typescript-eslint/typescript-estree": "6.7.3", "@typescript-eslint/utils": "6.7.3", @@ -5840,7 +5913,7 @@ "version": "6.7.3", "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.7.3.tgz", "integrity": "sha512-4g+de6roB2NFcfkZb439tigpAMnvEIg3rIjWQ+EM7IBaYt/CdJt6em9BJ4h4UpdgaBWdmx2iWsafHTrqmgIPNw==", - "dev": true, + "devOptional": true, "engines": { "node": "^16.0.0 || >=18.0.0" }, @@ -5853,7 +5926,7 @@ "version": "6.7.3", "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.7.3.tgz", "integrity": "sha512-YLQ3tJoS4VxLFYHTw21oe1/vIZPRqAO91z6Uv0Ss2BKm/Ag7/RVQBcXTGcXhgJMdA4U+HrKuY5gWlJlvoaKZ5g==", - "dev": true, + "devOptional": true, "dependencies": { "@typescript-eslint/types": "6.7.3", "@typescript-eslint/visitor-keys": "6.7.3", @@ -5880,7 +5953,7 @@ "version": "6.7.3", "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.7.3.tgz", "integrity": "sha512-vzLkVder21GpWRrmSR9JxGZ5+ibIUSudXlW52qeKpzUEQhRSmyZiVDDj3crAth7+5tmN1ulvgKaCU2f/bPRCzg==", - "dev": true, + "devOptional": true, "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", "@types/json-schema": "^7.0.12", @@ -5905,7 +5978,7 @@ "version": "6.7.3", "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.7.3.tgz", "integrity": "sha512-HEVXkU9IB+nk9o63CeICMHxFWbHWr3E1mpilIQBe9+7L/lH97rleFLVtYsfnWB+JVMaiFnEaxvknvmIzX+CqVg==", - "dev": true, + "devOptional": true, "dependencies": { "@typescript-eslint/types": "6.7.3", "eslint-visitor-keys": "^3.4.1" @@ -6091,6 +6164,11 @@ "node": ">= 0.6" } }, + "node_modules/ace-builds": { + "version": "1.32.2", + "resolved": "https://registry.npmjs.org/ace-builds/-/ace-builds-1.32.2.tgz", + "integrity": "sha512-mnJAc803p+7eeDt07r6XI7ufV7VdkpPq4gJZT8Jb3QsowkaBTVy4tdBgPrVT0WbXLm0toyEQXURKSVNj/7dfJQ==" + }, "node_modules/acorn": { "version": "8.10.0", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.10.0.tgz", @@ -6178,13 +6256,13 @@ } }, "node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "version": "8.12.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", + "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", "dependencies": { "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", "uri-js": "^4.2.2" }, "funding": { @@ -6208,34 +6286,6 @@ } } }, - "node_modules/ajv-formats/node_modules/ajv": { - "version": "8.12.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", - "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", - "dependencies": { - "fast-deep-equal": "^3.1.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/ajv-formats/node_modules/json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" - }, - "node_modules/ajv-keywords": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", - "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", - "peerDependencies": { - "ajv": "^6.9.1" - } - }, "node_modules/almost-equal": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/almost-equal/-/almost-equal-1.1.0.tgz", @@ -6702,6 +6752,34 @@ "webpack": ">=2" } }, + "node_modules/babel-loader/node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/babel-loader/node_modules/ajv-keywords": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "peerDependencies": { + "ajv": "^6.9.1" + } + }, + "node_modules/babel-loader/node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" + }, "node_modules/babel-loader/node_modules/schema-utils": { "version": "2.7.1", "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.1.tgz", @@ -8295,21 +8373,6 @@ } } }, - "node_modules/css-minimizer-webpack-plugin/node_modules/ajv": { - "version": "8.12.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", - "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", - "dependencies": { - "fast-deep-equal": "^3.1.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, "node_modules/css-minimizer-webpack-plugin/node_modules/ajv-keywords": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", @@ -8321,11 +8384,6 @@ "ajv": "^8.8.2" } }, - "node_modules/css-minimizer-webpack-plugin/node_modules/json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" - }, "node_modules/css-minimizer-webpack-plugin/node_modules/schema-utils": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.2.0.tgz", @@ -9472,6 +9530,14 @@ "react-dom": "^15.0.2 || ^16.0.0-rc || ^16.0.0 || ^17.0.0 || ^18.0.0" } }, + "node_modules/draft-convert/node_modules/immutable": { + "version": "3.7.6", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-3.7.6.tgz", + "integrity": "sha512-AizQPcaofEtO11RZhPPHBOJRdo/20MKQF9mBLnVkBoyHi1/zXK8fzVdnEpSV9gxqtnh6Qomfp3F0xT5qP/vThw==", + "engines": { + "node": ">=0.8.0" + } + }, "node_modules/draft-js": { "version": "0.11.7", "resolved": "https://registry.npmjs.org/draft-js/-/draft-js-0.11.7.tgz", @@ -9486,6 +9552,14 @@ "react-dom": ">=0.14.0" } }, + "node_modules/draft-js/node_modules/immutable": { + "version": "3.7.6", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-3.7.6.tgz", + "integrity": "sha512-AizQPcaofEtO11RZhPPHBOJRdo/20MKQF9mBLnVkBoyHi1/zXK8fzVdnEpSV9gxqtnh6Qomfp3F0xT5qP/vThw==", + "engines": { + "node": ">=0.8.0" + } + }, "node_modules/draftjs-to-html": { "version": "0.9.1", "resolved": "https://registry.npmjs.org/draftjs-to-html/-/draftjs-to-html-0.9.1.tgz", @@ -10723,21 +10797,6 @@ "webpack": "^5.0.0" } }, - "node_modules/eslint-webpack-plugin/node_modules/ajv": { - "version": "8.12.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", - "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", - "dependencies": { - "fast-deep-equal": "^3.1.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, "node_modules/eslint-webpack-plugin/node_modules/ajv-keywords": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", @@ -10762,11 +10821,6 @@ "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" } }, - "node_modules/eslint-webpack-plugin/node_modules/json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" - }, "node_modules/eslint-webpack-plugin/node_modules/schema-utils": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.2.0.tgz", @@ -10799,6 +10853,26 @@ "url": "https://github.com/chalk/supports-color?sponsor=1" } }, + "node_modules/eslint/node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/eslint/node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" + }, "node_modules/espree": { "version": "9.6.1", "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", @@ -11556,6 +11630,29 @@ } } }, + "node_modules/fork-ts-checker-webpack-plugin/node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/fork-ts-checker-webpack-plugin/node_modules/ajv-keywords": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "peerDependencies": { + "ajv": "^6.9.1" + } + }, "node_modules/fork-ts-checker-webpack-plugin/node_modules/cosmiconfig": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-6.0.0.tgz", @@ -11585,6 +11682,11 @@ "node": ">=10" } }, + "node_modules/fork-ts-checker-webpack-plugin/node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" + }, "node_modules/fork-ts-checker-webpack-plugin/node_modules/schema-utils": { "version": "2.7.0", "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.0.tgz", @@ -12888,12 +12990,10 @@ } }, "node_modules/immutable": { - "version": "3.7.6", - "resolved": "https://registry.npmjs.org/immutable/-/immutable-3.7.6.tgz", - "integrity": "sha512-AizQPcaofEtO11RZhPPHBOJRdo/20MKQF9mBLnVkBoyHi1/zXK8fzVdnEpSV9gxqtnh6Qomfp3F0xT5qP/vThw==", - "engines": { - "node": ">=0.8.0" - } + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.3.4.tgz", + "integrity": "sha512-fsXeu4J4i6WNWSikpI88v/PcVflZz+6kMhUfIwc5SY+poQRPnaf5V7qds6SUyUN3cVxEzuCab7QIoLOQ+DQ1wA==", + "peer": true }, "node_modules/import-fresh": { "version": "3.3.0", @@ -13685,6 +13785,11 @@ "node": ">=10" } }, + "node_modules/javascript-natural-sort": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/javascript-natural-sort/-/javascript-natural-sort-0.7.1.tgz", + "integrity": "sha512-nO6jcEfZWQXDhOiBtG2KvKyEptz7RVbpGP4vTD2hLBdmNQSsCiicO2Ioinv6UI4y9ukqnBpy+XZ9H6uLNgJTlw==" + }, "node_modules/jest": { "version": "27.5.1", "resolved": "https://registry.npmjs.org/jest/-/jest-27.5.1.tgz", @@ -14545,6 +14650,14 @@ "jiti": "bin/jiti.js" } }, + "node_modules/jmespath": { + "version": "0.16.0", + "resolved": "https://registry.npmjs.org/jmespath/-/jmespath-0.16.0.tgz", + "integrity": "sha512-9FzQjJ7MATs1tSpnco1K6ayiYE3figslrXA72G2HQ/n76RzvYlofyi5QM+iX4YRs/pu3yzxlVQSST23+dMDknw==", + "engines": { + "node": ">= 0.6.0" + } + }, "node_modules/js-cookie": { "version": "3.0.5", "resolved": "https://registry.npmjs.org/js-cookie/-/js-cookie-3.0.5.tgz", @@ -14683,9 +14796,14 @@ "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==" }, "node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" + }, + "node_modules/json-source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/json-source-map/-/json-source-map-0.6.1.tgz", + "integrity": "sha512-1QoztHPsMQqhDq0hlXY5ZqcEdUzxQEIxgFkKl4WUp2pgShObl+9ovi4kRh2TfvAfxAoHOJ9vIMEqk3k4iex7tg==" }, "node_modules/json-stable-stringify": { "version": "1.0.2", @@ -14714,6 +14832,42 @@ "node": ">=6" } }, + "node_modules/jsoneditor": { + "version": "9.10.4", + "resolved": "https://registry.npmjs.org/jsoneditor/-/jsoneditor-9.10.4.tgz", + "integrity": "sha512-tr7dSARLHM65OQTE81zo5fQAjLzijLl+u/z+pcJaeaFzgkey59Gi8TDCYIejQ/plvm6RLVmuEeqgDhsQdayhiQ==", + "dependencies": { + "ace-builds": "^1.31.1", + "ajv": "^6.12.6", + "javascript-natural-sort": "^0.7.1", + "jmespath": "^0.16.0", + "json-source-map": "^0.6.1", + "jsonrepair": "^3.4.0", + "mobius1-selectr": "^2.4.13", + "picomodal": "^3.0.0", + "vanilla-picker": "^2.12.2" + } + }, + "node_modules/jsoneditor/node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/jsoneditor/node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" + }, "node_modules/jsonfile": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", @@ -14868,6 +15022,14 @@ "node": ">=0.10.0" } }, + "node_modules/jsonrepair": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/jsonrepair/-/jsonrepair-3.5.0.tgz", + "integrity": "sha512-SavvDsUP9Xnqo2MoC6Wl6zNyX3f+I5199hRbXBtAITyP2NTPyAgyx5xM0bgcIljRjzsIvOBANbgfWe8XXlyeLA==", + "bin": { + "jsonrepair": "bin/cli.js" + } + }, "node_modules/jsx-ast-utils": { "version": "3.3.5", "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz", @@ -15396,21 +15558,6 @@ "webpack": "^5.0.0" } }, - "node_modules/mini-css-extract-plugin/node_modules/ajv": { - "version": "8.12.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", - "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", - "dependencies": { - "fast-deep-equal": "^3.1.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, "node_modules/mini-css-extract-plugin/node_modules/ajv-keywords": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", @@ -15422,11 +15569,6 @@ "ajv": "^8.8.2" } }, - "node_modules/mini-css-extract-plugin/node_modules/json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" - }, "node_modules/mini-css-extract-plugin/node_modules/schema-utils": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.2.0.tgz", @@ -15502,6 +15644,11 @@ "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==", "optional": true }, + "node_modules/mobius1-selectr": { + "version": "2.4.13", + "resolved": "https://registry.npmjs.org/mobius1-selectr/-/mobius1-selectr-2.4.13.tgz", + "integrity": "sha512-Mk9qDrvU44UUL0EBhbAA1phfQZ7aMZPjwtL7wkpiBzGh8dETGqfsh50mWoX9EkjDlkONlErWXArHCKfoxVg0Bw==" + }, "node_modules/moment": { "version": "2.29.4", "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.4.tgz", @@ -16476,6 +16623,11 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/picomodal": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/picomodal/-/picomodal-3.0.0.tgz", + "integrity": "sha512-FoR3TDfuLlqUvcEeK5ifpKSVVns6B4BQvc8SDF6THVMuadya6LLtji0QgUDSStw0ZR2J7I6UGi5V2V23rnPWTw==" + }, "node_modules/pify": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", @@ -19700,6 +19852,34 @@ "url": "https://opencollective.com/webpack" } }, + "node_modules/schema-utils/node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/schema-utils/node_modules/ajv-keywords": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "peerDependencies": { + "ajv": "^6.9.1" + } + }, + "node_modules/schema-utils/node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" + }, "node_modules/screenfull": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/screenfull/-/screenfull-5.2.0.tgz", @@ -21048,6 +21228,14 @@ "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.0.6.tgz", "integrity": "sha512-ilkD8YEnnGh1zJ240uJsW7AzE+2qpbOUYjacomn3AvJ6J4JhKGSZ2nh4wUIXPZrEPppaCLx5jFe8T89Rk8tQ7w==" }, + "node_modules/swagger-ui-react/node_modules/immutable": { + "version": "3.8.2", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-3.8.2.tgz", + "integrity": "sha512-15gZoQ38eYjEjxkorfbcgBKBL6R7T459OuK+CpcWt7O3KF4uPCx2tD0uFETlUDIyo+1789crbMhTvQBSR5yBMg==", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/symbol-tree": { "version": "3.2.4", "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", @@ -21555,7 +21743,7 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.0.3.tgz", "integrity": "sha512-wNMeqtMz5NtwpT/UZGY5alT+VoKdSsOOP/kqHFcUW1P/VRhH2wJ48+DN2WwUliNbQ976ETwDL0Ifd2VVvgonvg==", - "dev": true, + "devOptional": true, "engines": { "node": ">=16.13.0" }, @@ -21786,7 +21974,6 @@ "version": "5.2.2", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.2.2.tgz", "integrity": "sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==", - "dev": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -22151,6 +22338,14 @@ "node": ">= 8" } }, + "node_modules/vanilla-picker": { + "version": "2.12.2", + "resolved": "https://registry.npmjs.org/vanilla-picker/-/vanilla-picker-2.12.2.tgz", + "integrity": "sha512-dk0gNeNL9fQFGd1VEhNDQfFlbCqAiksRh1H2tVPlavkH88n/a/y30rXi9PPKrYPTK5kEfPO4xcldt4ts/1wIAg==", + "dependencies": { + "@sphinxxxx/color-conversion": "^2.2.2" + } + }, "node_modules/vary": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", @@ -22328,21 +22523,6 @@ "webpack": "^4.0.0 || ^5.0.0" } }, - "node_modules/webpack-dev-middleware/node_modules/ajv": { - "version": "8.12.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", - "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", - "dependencies": { - "fast-deep-equal": "^3.1.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, "node_modules/webpack-dev-middleware/node_modules/ajv-keywords": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", @@ -22354,11 +22534,6 @@ "ajv": "^8.8.2" } }, - "node_modules/webpack-dev-middleware/node_modules/json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" - }, "node_modules/webpack-dev-middleware/node_modules/schema-utils": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.2.0.tgz", @@ -22435,21 +22610,6 @@ } } }, - "node_modules/webpack-dev-server/node_modules/ajv": { - "version": "8.12.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", - "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", - "dependencies": { - "fast-deep-equal": "^3.1.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, "node_modules/webpack-dev-server/node_modules/ajv-keywords": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", @@ -22461,11 +22621,6 @@ "ajv": "^8.8.2" } }, - "node_modules/webpack-dev-server/node_modules/json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" - }, "node_modules/webpack-dev-server/node_modules/schema-utils": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.2.0.tgz", @@ -22778,21 +22933,6 @@ "node": ">=10.0.0" } }, - "node_modules/workbox-build/node_modules/ajv": { - "version": "8.12.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", - "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", - "dependencies": { - "fast-deep-equal": "^3.1.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, "node_modules/workbox-build/node_modules/fs-extra": { "version": "9.1.0", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", @@ -22807,11 +22947,6 @@ "node": ">=10" } }, - "node_modules/workbox-build/node_modules/json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" - }, "node_modules/workbox-build/node_modules/source-map": { "version": "0.8.0-beta.0", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.8.0-beta.0.tgz", diff --git a/webapp/package.json b/webapp/package.json index 6b687cb3ae..0a75cbf7ed 100644 --- a/webapp/package.json +++ b/webapp/package.json @@ -41,6 +41,7 @@ "i18next-xhr-backend": "3.2.2", "immer": "10.0.3", "js-cookie": "3.0.5", + "jsoneditor": "9.10.4", "jwt-decode": "3.1.2", "lodash": "4.17.21", "material-react-table": "2.0.5", @@ -105,6 +106,7 @@ "@total-typescript/ts-reset": "0.5.1", "@types/debug": "4.1.9", "@types/js-cookie": "3.0.4", + "@types/jsoneditor": "9.9.5", "@types/lodash": "4.14.199", "@types/ramda": "0.29.5", "@types/react-beautiful-dnd": "13.1.5", diff --git a/webapp/src/components/App/Singlestudy/explore/Modelization/DebugView/StudyDataView/StudyJsonView.tsx b/webapp/src/components/App/Singlestudy/explore/Modelization/DebugView/StudyDataView/StudyJsonView.tsx index 37f4ac97e8..bb84bc90c2 100644 --- a/webapp/src/components/App/Singlestudy/explore/Modelization/DebugView/StudyDataView/StudyJsonView.tsx +++ b/webapp/src/components/App/Singlestudy/explore/Modelization/DebugView/StudyDataView/StudyJsonView.tsx @@ -3,7 +3,6 @@ import { useState, useEffect } from "react"; import { AxiosError } from "axios"; import { useSnackbar } from "notistack"; import { useTranslation } from "react-i18next"; -import ReactJson from "react-json-view"; import SaveIcon from "@mui/icons-material/Save"; import { Box, Button, Typography } from "@mui/material"; import { @@ -11,8 +10,9 @@ import { getStudyData, } from "../../../../../../../services/api/study"; import useEnqueueErrorSnackbar from "../../../../../../../hooks/useEnqueueErrorSnackbar"; -import { Header, Root, Content } from "./style"; +import { Header, Root } from "./style"; import SimpleLoader from "../../../../../../common/loaders/SimpleLoader"; +import JSONEditor from "../../../../../../common/JSONEditor"; interface PropTypes { data: string; @@ -30,6 +30,12 @@ function StudyJsonView(props: PropTypes) { const [saveAllowed, setSaveAllowed] = useState(false); const [isEditable, setEditable] = useState(true); + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const handleJsonChange = (newJsonData: any) => { + setJsonData(newJsonData); + setSaveAllowed(true); + }; + const saveData = async () => { const tmpDataPath = data.split("/").filter((item) => item); const tmpPath = tmpDataPath.join("/"); @@ -88,27 +94,30 @@ function StudyJsonView(props: PropTypes) { )} - - {jsonData && ( - { - setJsonData(e.updated_src); - setSaveAllowed(true); - } - : undefined - } - theme="monokai" + {jsonData && ( + + console.log(json)} + modes={["tree", "code"]} + enableSort={false} + enableTransform={false} /> - )} - {!loaded && ( - - - - )} - + + )} + {!loaded && ( + + + + )} ); } diff --git a/webapp/src/components/App/Singlestudy/explore/Modelization/DebugView/StudyDataView/style.ts b/webapp/src/components/App/Singlestudy/explore/Modelization/DebugView/StudyDataView/style.ts index 0f2ace5de7..5d9106f03d 100644 --- a/webapp/src/components/App/Singlestudy/explore/Modelization/DebugView/StudyDataView/style.ts +++ b/webapp/src/components/App/Singlestudy/explore/Modelization/DebugView/StudyDataView/style.ts @@ -17,7 +17,7 @@ export const Header = styled(Box)(({ theme }) => ({ flexFlow: "row nowrap", justifyContent: "space-between", alignItems: "center", - padding: theme.spacing(0, 2), + marginBottom: theme.spacing(1), })); export const Content = styled(Paper)(({ theme }) => ({ diff --git a/webapp/src/components/common/JSONEditor/index.tsx b/webapp/src/components/common/JSONEditor/index.tsx new file mode 100644 index 0000000000..162e625384 --- /dev/null +++ b/webapp/src/components/common/JSONEditor/index.tsx @@ -0,0 +1,46 @@ +import JSONEditorLib, { JSONEditorOptions } from "jsoneditor"; +import { useEffect, useRef } from "react"; +import { useDeepCompareEffect, useMount } from "react-use"; +import "jsoneditor/dist/jsoneditor.min.css"; +import "./dark-theme.css"; + +interface JSONEditorProps extends JSONEditorOptions { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + json: any; +} + +function JSONEditor({ json, ...options }: JSONEditorProps) { + const ref = useRef(null); + const editorRef = useRef(); + + useEffect(() => { + if (editorRef.current) { + editorRef.current.expandAll(); + } + }, []); + + useMount(() => { + if (!ref.current) { + return; + } + + const editor = new JSONEditorLib(ref.current, options); + editor.set(json); + editorRef.current = editor; + + return () => editor.destroy(); + }); + + useDeepCompareEffect(() => { + if (!editorRef.current) { + return; + } + + editorRef.current.set(json); + editorRef.current.expandAll(); + }, [json]); + + return
; +} + +export default JSONEditor; From 522f4d957c850220df4d6366783e97603578fc78 Mon Sep 17 00:00:00 2001 From: hatim dinia Date: Fri, 22 Dec 2023 15:50:53 +0100 Subject: [PATCH 2/6] feat(ui-common): add dark theme for JSONEditor --- .../common/JSONEditor/dark-theme.css | 227 ++++++++++++++++++ .../components/common/JSONEditor/index.tsx | 8 +- 2 files changed, 228 insertions(+), 7 deletions(-) create mode 100644 webapp/src/components/common/JSONEditor/dark-theme.css diff --git a/webapp/src/components/common/JSONEditor/dark-theme.css b/webapp/src/components/common/JSONEditor/dark-theme.css new file mode 100644 index 0000000000..fb44f64326 --- /dev/null +++ b/webapp/src/components/common/JSONEditor/dark-theme.css @@ -0,0 +1,227 @@ +//////////////////////////////////////////////////////////////// +// JSONEditor +//////////////////////////////////////////////////////////////// + +div.jsoneditor { + border: 2px solid #212c38; /* Dark gray border for consistency */ + color: #ffffff; +} + +div.jsoneditor, +div.jsoneditor-menu { + border-color: #212c38; /* Dark gray border for consistency */ +} + +div.jsoneditor-menu { + background-color: #222333; /* Dark background for the menu */ +} + +div.jsoneditor-tree, +div.jsoneditor textarea.jsoneditor-text { + background-color: #222333; /* Dark background for the tree and text */ + color: #ffffff; +} + +div.jsoneditor-field, +div.jsoneditor-value { + color: #ffffff; /* White color for fields and values */ +} + +/* Search box styles */ +table.jsoneditor-search div.jsoneditor-frame { + background: #212c38; /* Dark background for the search box */ +} + +/* Highlight and selection styles */ +tr.jsoneditor-highlight, +tr.jsoneditor-selected { + background-color: #555666; /* Gray for highlighting and selection */ +} + +/* Focus and hover styles */ +div.jsoneditor-field[contenteditable="true"]:focus, +div.jsoneditor-field[contenteditable="true"]:hover, +div.jsoneditor-value[contenteditable="true"]:focus, +div.jsoneditor-value[contenteditable="true"]:hover, +div.jsoneditor-field.jsoneditor-highlight, +div.jsoneditor-value.jsoneditor-highlight { + background-color: #222333; + border-color: #ffb800; +} + +/* Active styles */ +div.jsoneditor-field.highlight-active, +div.jsoneditor-field.highlight-active:focus, +div.jsoneditor-field.highlight-active:hover, +div.jsoneditor-value.highlight-active, +div.jsoneditor-value.highlight-active:focus, +div.jsoneditor-value.highlight-active:hover { + background-color: #212c38; /* Darker background for active fields */ + border-color: #ffb800; +} + +div.jsoneditor-tree button:focus { + background-color: #00b2ff; /* Secondary main color for focused buttons */ +} + +div.jsoneditor-readonly { + color: #acacac; /* Gray color for readonly fields */ +} + +div.jsoneditor td.jsoneditor-separator { + color: #acacac; /* Gray color for separators */ +} + +/* Specific JSON data type colors based on GitHub Dark Theme */ +div.jsoneditor-value.jsoneditor-string { + color: #9ecbff; /* Light blue for strings */ +} + +div.jsoneditor-value.jsoneditor-object, +div.jsoneditor-value.jsoneditor-array { + color: #f97583; /* Soft red for objects and arrays */ +} + +div.jsoneditor-value.jsoneditor-number { + color: #f2c68a; /* Light orange for numbers */ +} + +div.jsoneditor-value.jsoneditor-boolean { + color: #e6c07b; /* Warm yellow for booleans */ +} + +div.jsoneditor-value.jsoneditor-null { + color: #c678dd; /* Purple for null */ +} + +div.jsoneditor-value.jsoneditor-invalid { + color: #ffffff; /* White color for invalid values */ +} + +div.jsoneditor-contextmenu .jsoneditor-menu button { + background-color: #222333; + color: #ffffff; /* White color for context menu items */ +} +.jsoneditor-contextmenu .jsoneditor-menu li button.jsoneditor-selected, +.jsoneditor-contextmenu .jsoneditor-menu li button.jsoneditor-selected:focus, +.jsoneditor-contextmenu .jsoneditor-menu li button.jsoneditor-selected:hover { + background-color: #222333; + color: #ffb800; /* Yellow color for active context menu item */ +} + +.jsoneditor-navigation-bar, +.jsoneditor-frame { + background-color: #555666; + color: #ffffff; +} + +.jsoneditor-search input[type="text"] { + background-color: #555666; + color: #ffffff; /* White color for search input */ +} + +//////////////////////////////////////////////////////////////// +// Ace editor +//////////////////////////////////////////////////////////////// + + +div.ace_jsoneditor .ace_editor { + background-color: #222333; + color: #ffffff; +} + +.ace-jsoneditor, +textarea.jsoneditor-text { + min-height: 70vh; +} + +div.ace_scroller { + background-color: #222333; + color: #ffffff; +} + +div.ace_content { + background-color: #222333; +} + +.jsoneditor-menu a.jsoneditor-poweredBy { + /* Hide the "powered by" link */ + color: #222333; + visibility: hidden; +} + +.ace-jsoneditor.ace_editor { + background-color: #222333; +} + +.ace-jsoneditor .ace_marker-layer .ace_active-line { + background: #333444; /* Background color for the active line */ +} + +.ace-jsoneditor .ace_gutter { + background: #222333; /* Dark background for the gutter */ + color: #ffffff; /* Color for gutter text */ +} + +.ace-jsoneditor .ace_gutter-active-line { + background: #333444; /* Darker background for the active line in gutter */ +} + +.ace-jsoneditor .ace_string { + /* Strings */ + color: #9ecbff; +} + +.ace-jsoneditor .ace_keyword { + /* Keywords, usually for booleans and null */ + color: #e6c07b; +} + +.ace-jsoneditor .ace_constant.ace_numeric { + /* Numbers */ + color: #f2c68a; +} + +.ace-jsoneditor .ace_constant.ace_language { + /* Constants, typically for null */ + color: #c678dd; +} + +.ace-jsoneditor .ace_invalid { + /* Invalid */ + color: white; + background-color: #f97583; +} + +.ace-jsoneditor .ace_comment { + /* Comments */ + color: #6a9955; +} + +.jsoneditor-statusbar { + background-color: #555666; /* Status bar */ + color: #ffffff; + opacity: 0.8; +} + +.ace_text-layer > .ace_line, +.ace_text-layer > .ace_line_group { + color: #ffffff; /* Brackets and commas color */ +} + +.ace-jsoneditor .ace_cursor { + border-left: 2px solid #ffb800; /*Cursor color */ +} + +.ace-jsoneditor .ace_variable { + color: #ffffff; /* White color for fields and values */ +} + +.ace-jsoneditor .ace_indent-guide { + border-right: 1px solid #555666; /* Indentation guides */ + opacity: 0.5; +} + +.ace-jsoneditor .ace_marker-layer .ace_selection { + background: #555666; /* Selection color */ +} diff --git a/webapp/src/components/common/JSONEditor/index.tsx b/webapp/src/components/common/JSONEditor/index.tsx index 162e625384..6c7a0c19b5 100644 --- a/webapp/src/components/common/JSONEditor/index.tsx +++ b/webapp/src/components/common/JSONEditor/index.tsx @@ -1,5 +1,5 @@ import JSONEditorLib, { JSONEditorOptions } from "jsoneditor"; -import { useEffect, useRef } from "react"; +import { useRef } from "react"; import { useDeepCompareEffect, useMount } from "react-use"; import "jsoneditor/dist/jsoneditor.min.css"; import "./dark-theme.css"; @@ -13,12 +13,6 @@ function JSONEditor({ json, ...options }: JSONEditorProps) { const ref = useRef(null); const editorRef = useRef(); - useEffect(() => { - if (editorRef.current) { - editorRef.current.expandAll(); - } - }, []); - useMount(() => { if (!ref.current) { return; From 3265ed94d018c8b78fa7ab656ad1d4f51bf54f3a Mon Sep 17 00:00:00 2001 From: hatim dinia Date: Tue, 9 Jan 2024 09:53:36 +0100 Subject: [PATCH 3/6] fix(ui-common): sanitize path for MatrixInput updates --- webapp/src/components/common/MatrixInput/index.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/webapp/src/components/common/MatrixInput/index.tsx b/webapp/src/components/common/MatrixInput/index.tsx index 2f4ab7ed89..31b4559fac 100644 --- a/webapp/src/components/common/MatrixInput/index.tsx +++ b/webapp/src/components/common/MatrixInput/index.tsx @@ -115,7 +115,8 @@ function MatrixInput({ if (source !== "loadData" && source !== "updateData") { try { if (change.length > 0) { - await editMatrix(study.id, url, change); + const sanitizedUrl = url.startsWith("/") ? url.substring(1) : url; + await editMatrix(study.id, sanitizedUrl, change); enqueueSnackbar(t("matrix.success.matrixUpdate"), { variant: "success", }); From b3473a4179e235c99f1e181e6e8faf1b16b65ee2 Mon Sep 17 00:00:00 2001 From: hatim dinia Date: Tue, 9 Jan 2024 20:27:33 +0100 Subject: [PATCH 4/6] feat(ui): move Debug to top level tabs --- .../components/App/Singlestudy/explore/Modelization/index.tsx | 4 ---- webapp/src/components/App/Singlestudy/index.tsx | 4 ++++ webapp/src/components/App/index.tsx | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/webapp/src/components/App/Singlestudy/explore/Modelization/index.tsx b/webapp/src/components/App/Singlestudy/explore/Modelization/index.tsx index 121c3fdc58..b034cc2c80 100644 --- a/webapp/src/components/App/Singlestudy/explore/Modelization/index.tsx +++ b/webapp/src/components/App/Singlestudy/explore/Modelization/index.tsx @@ -58,10 +58,6 @@ function Modelization() { label: t("study.bindingconstraints"), path: `${basePath}/bindingcontraint`, }, - { - label: t("study.debug"), - path: `${basePath}/debug`, - }, ]; }, [areaId, areas, dispatch, navigate, study?.id, t]); diff --git a/webapp/src/components/App/Singlestudy/index.tsx b/webapp/src/components/App/Singlestudy/index.tsx index 10f8b87f4f..9a76747915 100644 --- a/webapp/src/components/App/Singlestudy/index.tsx +++ b/webapp/src/components/App/Singlestudy/index.tsx @@ -66,6 +66,10 @@ function SingleStudy(props: Props) { label: t("study.results"), path: `/studies/${studyId}/explore/results`, }, + { + label: t("study.debug"), + path: `/studies/${studyId}/explore/debug`, + }, ], [studyId], ); diff --git a/webapp/src/components/App/index.tsx b/webapp/src/components/App/index.tsx index e34ca31a37..1c7824cfa5 100644 --- a/webapp/src/components/App/index.tsx +++ b/webapp/src/components/App/index.tsx @@ -24,7 +24,7 @@ import BindingConstraints from "./Singlestudy/explore/Modelization/BindingConstr import Links from "./Singlestudy/explore/Modelization/Links"; import Areas from "./Singlestudy/explore/Modelization/Areas"; import Map from "./Singlestudy/explore/Modelization/Map"; -import DebugView from "./Singlestudy/explore/Modelization/DebugView"; +import Debug from "./Singlestudy/explore/Modelization/Debug"; import Xpansion from "./Singlestudy/explore/Xpansion"; import Candidates from "./Singlestudy/explore/Xpansion/Candidates"; import XpansionSettings from "./Singlestudy/explore/Xpansion/Settings"; @@ -140,7 +140,6 @@ function App() { path="bindingcontraint" element={} /> - } /> } /> } /> @@ -165,6 +164,7 @@ function App() { } /> } /> + } /> }> } /> From 04d01b71676148500cc331bb1230da7d6c257ed9 Mon Sep 17 00:00:00 2001 From: hatim dinia Date: Wed, 10 Jan 2024 11:32:07 +0100 Subject: [PATCH 5/6] refactor(ui-debug): update Debug view --- .../explore/Modelization/Debug/Data/Json.tsx | 111 +++++++++ .../Modelization/Debug/Data/Matrix.tsx | 26 +++ .../explore/Modelization/Debug/Data/Text.tsx | 91 ++++++++ .../explore/Modelization/Debug/Data/index.tsx | 24 ++ .../StudyDataView => Debug/Data}/style.ts | 2 - .../Modelization/Debug/DebugContext.ts | 21 ++ .../Modelization/Debug/Tree/FileTreeItem.tsx | 64 ++++++ .../explore/Modelization/Debug/Tree/index.tsx | 28 +++ .../explore/Modelization/Debug/index.tsx | 94 ++++++++ .../explore/Modelization/Debug/utils.ts | 72 ++++++ .../DebugView/StudyDataView/StudyFileView.tsx | 122 ---------- .../DebugView/StudyDataView/StudyJsonView.tsx | 125 ---------- .../StudyMatrixView/StudyMatrixView.tsx | 217 ------------------ .../StudyDataView/StudyMatrixView/style.ts | 17 -- .../DebugView/StudyDataView/index.tsx | 66 ------ .../DebugView/StudyTreeView/index.tsx | 91 -------- .../DebugView/StudyTreeView/utils.ts | 39 ---- .../explore/Modelization/DebugView/index.tsx | 101 -------- .../common/JSONEditor/dark-theme.css | 10 +- 19 files changed, 532 insertions(+), 789 deletions(-) create mode 100644 webapp/src/components/App/Singlestudy/explore/Modelization/Debug/Data/Json.tsx create mode 100644 webapp/src/components/App/Singlestudy/explore/Modelization/Debug/Data/Matrix.tsx create mode 100644 webapp/src/components/App/Singlestudy/explore/Modelization/Debug/Data/Text.tsx create mode 100644 webapp/src/components/App/Singlestudy/explore/Modelization/Debug/Data/index.tsx rename webapp/src/components/App/Singlestudy/explore/Modelization/{DebugView/StudyDataView => Debug/Data}/style.ts (97%) create mode 100644 webapp/src/components/App/Singlestudy/explore/Modelization/Debug/DebugContext.ts create mode 100644 webapp/src/components/App/Singlestudy/explore/Modelization/Debug/Tree/FileTreeItem.tsx create mode 100644 webapp/src/components/App/Singlestudy/explore/Modelization/Debug/Tree/index.tsx create mode 100644 webapp/src/components/App/Singlestudy/explore/Modelization/Debug/index.tsx create mode 100644 webapp/src/components/App/Singlestudy/explore/Modelization/Debug/utils.ts delete mode 100644 webapp/src/components/App/Singlestudy/explore/Modelization/DebugView/StudyDataView/StudyFileView.tsx delete mode 100644 webapp/src/components/App/Singlestudy/explore/Modelization/DebugView/StudyDataView/StudyJsonView.tsx delete mode 100644 webapp/src/components/App/Singlestudy/explore/Modelization/DebugView/StudyDataView/StudyMatrixView/StudyMatrixView.tsx delete mode 100644 webapp/src/components/App/Singlestudy/explore/Modelization/DebugView/StudyDataView/StudyMatrixView/style.ts delete mode 100644 webapp/src/components/App/Singlestudy/explore/Modelization/DebugView/StudyDataView/index.tsx delete mode 100644 webapp/src/components/App/Singlestudy/explore/Modelization/DebugView/StudyTreeView/index.tsx delete mode 100644 webapp/src/components/App/Singlestudy/explore/Modelization/DebugView/StudyTreeView/utils.ts delete mode 100644 webapp/src/components/App/Singlestudy/explore/Modelization/DebugView/index.tsx diff --git a/webapp/src/components/App/Singlestudy/explore/Modelization/Debug/Data/Json.tsx b/webapp/src/components/App/Singlestudy/explore/Modelization/Debug/Data/Json.tsx new file mode 100644 index 0000000000..4dce33e2ed --- /dev/null +++ b/webapp/src/components/App/Singlestudy/explore/Modelization/Debug/Data/Json.tsx @@ -0,0 +1,111 @@ +import { useState } from "react"; +import { useTranslation } from "react-i18next"; +import { AxiosError } from "axios"; +import { useSnackbar } from "notistack"; +import SaveIcon from "@mui/icons-material/Save"; +import { Box, Button, Typography } from "@mui/material"; +import { useUpdateEffect } from "react-use"; +import { + editStudy, + getStudyData, +} from "../../../../../../../services/api/study"; +import { Header, Root } from "./style"; +import SimpleLoader from "../../../../../../common/loaders/SimpleLoader"; +import JSONEditor from "../../../../../../common/JSONEditor"; +import usePromiseWithSnackbarError from "../../../../../../../hooks/usePromiseWithSnackbarError"; +import UsePromiseCond from "../../../../../../common/utils/UsePromiseCond"; +import SimpleContent from "../../../../../../common/page/SimpleContent"; +import useEnqueueErrorSnackbar from "../../../../../../../hooks/useEnqueueErrorSnackbar"; + +interface Props { + path: string; + studyId: string; +} + +function Json({ path, studyId }: Props) { + const [t] = useTranslation(); + const { enqueueSnackbar } = useSnackbar(); + const enqueueErrorSnackbar = useEnqueueErrorSnackbar(); + const [jsonData, setJsonData] = useState(null); + const [isSaveAllowed, setSaveAllowed] = useState(false); + + const res = usePromiseWithSnackbarError( + () => getStudyData(studyId, path, -1), + { + errorMessage: t("studies.error.retrieveData"), + deps: [studyId, path], + }, + ); + + /* Reset save button when path changes */ + useUpdateEffect(() => { + setSaveAllowed(false); + }, [studyId, path]); + + //////////////////////////////////////////////////////////////// + // Event Handlers + //////////////////////////////////////////////////////////////// + + const handleSaveJson = async () => { + if (isSaveAllowed && jsonData) { + try { + await editStudy(jsonData, studyId, path); + enqueueSnackbar(t("studies.success.saveData"), { + variant: "success", + }); + setSaveAllowed(false); + } catch (e) { + enqueueErrorSnackbar(t("studies.error.saveData"), e as AxiosError); + } + } + }; + + const handleJsonChange = (newJson: string) => { + setJsonData(newJson); + setSaveAllowed(true); + }; + + //////////////////////////////////////////////////////////////// + // JSX + //////////////////////////////////////////////////////////////// + + return ( + +
+ +
+ } + ifResolved={(json) => ( + + + + )} + ifRejected={(error) => } + /> +
+ ); +} + +export default Json; diff --git a/webapp/src/components/App/Singlestudy/explore/Modelization/Debug/Data/Matrix.tsx b/webapp/src/components/App/Singlestudy/explore/Modelization/Debug/Data/Matrix.tsx new file mode 100644 index 0000000000..524c6b5117 --- /dev/null +++ b/webapp/src/components/App/Singlestudy/explore/Modelization/Debug/Data/Matrix.tsx @@ -0,0 +1,26 @@ +import { useOutletContext } from "react-router"; +import { MatrixStats, StudyMetadata } from "../../../../../../../common/types"; +import { Root, Content } from "./style"; +import MatrixInput from "../../../../../../common/MatrixInput"; + +interface Props { + path: string; +} + +function Matrix({ path }: Props) { + const { study } = useOutletContext<{ study: StudyMetadata }>(); + + //////////////////////////////////////////////////////////////// + // JSX + //////////////////////////////////////////////////////////////// + + return ( + + + + + + ); +} + +export default Matrix; diff --git a/webapp/src/components/App/Singlestudy/explore/Modelization/Debug/Data/Text.tsx b/webapp/src/components/App/Singlestudy/explore/Modelization/Debug/Data/Text.tsx new file mode 100644 index 0000000000..d4f00ac1fc --- /dev/null +++ b/webapp/src/components/App/Singlestudy/explore/Modelization/Debug/Data/Text.tsx @@ -0,0 +1,91 @@ +import { useState } from "react"; +import { AxiosError } from "axios"; +import { useSnackbar } from "notistack"; +import { useTranslation } from "react-i18next"; +import { Button } from "@mui/material"; +import UploadOutlinedIcon from "@mui/icons-material/UploadOutlined"; +import { + getStudyData, + importFile, +} from "../../../../../../../services/api/study"; +import { Content, Header, Root } from "./style"; +import useEnqueueErrorSnackbar from "../../../../../../../hooks/useEnqueueErrorSnackbar"; +import SimpleLoader from "../../../../../../common/loaders/SimpleLoader"; +import ImportDialog from "../../../../../../common/dialogs/ImportDialog"; +import usePromiseWithSnackbarError from "../../../../../../../hooks/usePromiseWithSnackbarError"; +import SimpleContent from "../../../../../../common/page/SimpleContent"; +import UsePromiseCond from "../../../../../../common/utils/UsePromiseCond"; +import { useDebugContext } from "../DebugContext"; + +interface Props { + studyId: string; + path: string; +} + +function Text({ studyId, path }: Props) { + const [t] = useTranslation(); + const { reloadTreeData } = useDebugContext(); + const { enqueueSnackbar } = useSnackbar(); + const enqueueErrorSnackbar = useEnqueueErrorSnackbar(); + const [openImportDialog, setOpenImportDialog] = useState(false); + + const res = usePromiseWithSnackbarError(() => getStudyData(studyId, path), { + errorMessage: t("studies.error.retrieveData"), + deps: [studyId, path], + }); + + //////////////////////////////////////////////////////////////// + // Event Handlers + //////////////////////////////////////////////////////////////// + + const handleImport = async (file: File) => { + try { + await importFile(file, studyId, path); + reloadTreeData(); + enqueueSnackbar(t("studies.success.saveData"), { + variant: "success", + }); + } catch (e) { + enqueueErrorSnackbar(t("studies.error.saveData"), e as AxiosError); + } + }; + + //////////////////////////////////////////////////////////////// + // JSX + //////////////////////////////////////////////////////////////// + + return ( + +
+ +
+ } + ifResolved={(data) => ( + + {data} + + )} + ifRejected={(error) => } + /> + {openImportDialog && ( + setOpenImportDialog(false)} + onImport={handleImport} + /> + )} +
+ ); +} + +export default Text; diff --git a/webapp/src/components/App/Singlestudy/explore/Modelization/Debug/Data/index.tsx b/webapp/src/components/App/Singlestudy/explore/Modelization/Debug/Data/index.tsx new file mode 100644 index 0000000000..0cb75a3eab --- /dev/null +++ b/webapp/src/components/App/Singlestudy/explore/Modelization/Debug/Data/index.tsx @@ -0,0 +1,24 @@ +import Text from "./Text"; +import Json from "./Json"; +import Matrix from "./Matrix"; +import { FileType } from "../utils"; + +interface Props { + studyId: string; + fileType: FileType; + filePath: string; +} + +const componentByFileType = { + matrix: Matrix, + json: Json, + file: Text, +} as const; + +function Data({ studyId, fileType, filePath }: Props) { + const DataViewer = componentByFileType[fileType]; + + return ; +} + +export default Data; diff --git a/webapp/src/components/App/Singlestudy/explore/Modelization/DebugView/StudyDataView/style.ts b/webapp/src/components/App/Singlestudy/explore/Modelization/Debug/Data/style.ts similarity index 97% rename from webapp/src/components/App/Singlestudy/explore/Modelization/DebugView/StudyDataView/style.ts rename to webapp/src/components/App/Singlestudy/explore/Modelization/Debug/Data/style.ts index 5d9106f03d..b0143fdec8 100644 --- a/webapp/src/components/App/Singlestudy/explore/Modelization/DebugView/StudyDataView/style.ts +++ b/webapp/src/components/App/Singlestudy/explore/Modelization/Debug/Data/style.ts @@ -32,5 +32,3 @@ export const Content = styled(Paper)(({ theme }) => ({ overflow: "auto", position: "relative", })); - -export default {}; diff --git a/webapp/src/components/App/Singlestudy/explore/Modelization/Debug/DebugContext.ts b/webapp/src/components/App/Singlestudy/explore/Modelization/Debug/DebugContext.ts new file mode 100644 index 0000000000..52e4c07d7f --- /dev/null +++ b/webapp/src/components/App/Singlestudy/explore/Modelization/Debug/DebugContext.ts @@ -0,0 +1,21 @@ +import { createContext, useContext } from "react"; +import { FileType, TreeData } from "./utils"; + +interface DebugContextProps { + treeData: TreeData; + onFileSelect: (fileType: FileType, filePath: string) => void; + reloadTreeData: () => void; +} + +const initialDebugContextValue: DebugContextProps = { + treeData: {}, + onFileSelect: () => {}, + reloadTreeData: () => {}, +}; + +const DebugContext = createContext(initialDebugContextValue); + +export const useDebugContext = (): DebugContextProps => + useContext(DebugContext); + +export default DebugContext; diff --git a/webapp/src/components/App/Singlestudy/explore/Modelization/Debug/Tree/FileTreeItem.tsx b/webapp/src/components/App/Singlestudy/explore/Modelization/Debug/Tree/FileTreeItem.tsx new file mode 100644 index 0000000000..5ae11cff9d --- /dev/null +++ b/webapp/src/components/App/Singlestudy/explore/Modelization/Debug/Tree/FileTreeItem.tsx @@ -0,0 +1,64 @@ +import { Box } from "@mui/material"; +import { TreeItem } from "@mui/x-tree-view"; +import { TreeData, determineFileType, getFileIcon } from "../utils"; +import { useDebugContext } from "../DebugContext"; + +interface Props { + name: string; + content: TreeData; + path: string; +} + +function FileTreeItem({ name, content, path }: Props) { + const { onFileSelect } = useDebugContext(); + const fullPath = `${path}/${name}`; + const fileType = determineFileType(content); + const FileIcon = getFileIcon(fileType); + const isFolderEmpty = !Object.keys(content).length; + + //////////////////////////////////////////////////////////////// + // Event handlers + //////////////////////////////////////////////////////////////// + + const handleClick = () => { + if (fileType !== "folder") { + onFileSelect(fileType, fullPath); + } + }; + + //////////////////////////////////////////////////////////////// + // JSX + //////////////////////////////////////////////////////////////// + + return ( + + + {name} + + } + > + {typeof content === "object" && + Object.keys(content).map((childName) => ( + + ))} + + ); +} + +export default FileTreeItem; diff --git a/webapp/src/components/App/Singlestudy/explore/Modelization/Debug/Tree/index.tsx b/webapp/src/components/App/Singlestudy/explore/Modelization/Debug/Tree/index.tsx new file mode 100644 index 0000000000..faa75ab045 --- /dev/null +++ b/webapp/src/components/App/Singlestudy/explore/Modelization/Debug/Tree/index.tsx @@ -0,0 +1,28 @@ +import { TreeView } from "@mui/x-tree-view"; +import ExpandMoreIcon from "@mui/icons-material/ExpandMore"; +import ChevronRightIcon from "@mui/icons-material/ChevronRight"; +import FileTreeItem from "./FileTreeItem"; +import { useDebugContext } from "../DebugContext"; + +function Tree() { + const { treeData } = useDebugContext(); + + //////////////////////////////////////////////////////////////// + // JSX + //////////////////////////////////////////////////////////////// + + return ( + } + defaultExpandIcon={} + > + {typeof treeData === "object" && + Object.keys(treeData).map((key) => ( + + ))} + + ); +} + +export default Tree; diff --git a/webapp/src/components/App/Singlestudy/explore/Modelization/Debug/index.tsx b/webapp/src/components/App/Singlestudy/explore/Modelization/Debug/index.tsx new file mode 100644 index 0000000000..414c004093 --- /dev/null +++ b/webapp/src/components/App/Singlestudy/explore/Modelization/Debug/index.tsx @@ -0,0 +1,94 @@ +import { useCallback, useMemo, useState } from "react"; +import { useTranslation } from "react-i18next"; +import { useOutletContext } from "react-router-dom"; +import { Box } from "@mui/material"; +import Tree from "./Tree"; +import Data from "./Data"; +import { StudyMetadata } from "../../../../../../common/types"; +import SimpleLoader from "../../../../../common/loaders/SimpleLoader"; +import UsePromiseCond from "../../../../../common/utils/UsePromiseCond"; +import SimpleContent from "../../../../../common/page/SimpleContent"; +import usePromiseWithSnackbarError from "../../../../../../hooks/usePromiseWithSnackbarError"; +import { getStudyData } from "../../../../../../services/api/study"; +import DebugContext from "./DebugContext"; +import { TreeData, filterTreeData, File } from "./utils"; + +function Debug() { + const [t] = useTranslation(); + const { study } = useOutletContext<{ study: StudyMetadata }>(); + const [selectedFile, setSelectedFile] = useState(); + + const studyTree = usePromiseWithSnackbarError( + async () => { + const treeData = await getStudyData(study.id, "", -1); + return filterTreeData(treeData); + }, + { + errorMessage: t("studies.error.retrieveData"), + deps: [study.id], + }, + ); + + const handleFileSelection = useCallback( + (fileType: File["fileType"], filePath: string) => { + setSelectedFile({ fileType, filePath }); + }, + [], + ); + + //////////////////////////////////////////////////////////////// + // Utils + //////////////////////////////////////////////////////////////// + + const contextValue = useMemo( + () => ({ + treeData: studyTree.data ?? {}, + onFileSelect: handleFileSelection, + reloadTreeData: studyTree.reload, + }), + [studyTree.data, studyTree.reload, handleFileSelection], + ); + + //////////////////////////////////////////////////////////////// + // JSX + //////////////////////////////////////////////////////////////// + + return ( + + + } + ifResolved={() => ( + <> + + + + + {selectedFile && ( + + )} + + + )} + ifRejected={(error) => } + /> + + + ); +} + +export default Debug; diff --git a/webapp/src/components/App/Singlestudy/explore/Modelization/Debug/utils.ts b/webapp/src/components/App/Singlestudy/explore/Modelization/Debug/utils.ts new file mode 100644 index 0000000000..7fc82f087e --- /dev/null +++ b/webapp/src/components/App/Singlestudy/explore/Modelization/Debug/utils.ts @@ -0,0 +1,72 @@ +import DataObjectIcon from "@mui/icons-material/DataObject"; +import TextSnippetIcon from "@mui/icons-material/TextSnippet"; +import FolderIcon from "@mui/icons-material/Folder"; +import DatasetIcon from "@mui/icons-material/Dataset"; +import { SvgIconComponent } from "@mui/icons-material"; + +//////////////////////////////////////////////////////////////// +// Types +//////////////////////////////////////////////////////////////// + +export type FileType = "json" | "file" | "matrix"; + +export interface File { + fileType: FileType; + filePath: string; +} + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export type TreeData = Record | string; + +//////////////////////////////////////////////////////////////// +// Utils +//////////////////////////////////////////////////////////////// + +/** + * Maps file types and folder to their corresponding icon components. + */ +const iconByFileType: Record = { + matrix: DatasetIcon, + json: DataObjectIcon, + folder: FolderIcon, + file: TextSnippetIcon, +} as const; + +/** + * Gets the icon component for a given file type or folder. + * @param {FileType | "folder"} type - The type of the file or "folder". + * @returns {SvgIconComponent} The corresponding icon component. + */ +export const getFileIcon = (type: FileType | "folder"): SvgIconComponent => { + return iconByFileType[type] || TextSnippetIcon; +}; + +/** + * Determines the file type based on the tree data. + * @param {TreeData} treeData - The data of the tree item. + * @returns {FileType | "folder"} The determined file type or "folder". + */ +export const determineFileType = (treeData: TreeData): FileType | "folder" => { + if (typeof treeData === "string") { + if (treeData.startsWith("matrix://")) { + return "matrix"; + } + if (treeData.startsWith("json://")) { + return "json"; + } + } + return typeof treeData === "object" ? "folder" : "file"; +}; + +/** + * Filters out specific keys from the tree data. + * @param {TreeData} data - The original tree data. + * @returns {TreeData} The filtered tree data. + */ +export const filterTreeData = (data: TreeData): TreeData => { + const excludedKeys = new Set(["Desktop", "study", "output", "logs"]); + + return Object.fromEntries( + Object.entries(data).filter(([key]) => !excludedKeys.has(key)), + ); +}; diff --git a/webapp/src/components/App/Singlestudy/explore/Modelization/DebugView/StudyDataView/StudyFileView.tsx b/webapp/src/components/App/Singlestudy/explore/Modelization/DebugView/StudyDataView/StudyFileView.tsx deleted file mode 100644 index 1a35c18add..0000000000 --- a/webapp/src/components/App/Singlestudy/explore/Modelization/DebugView/StudyDataView/StudyFileView.tsx +++ /dev/null @@ -1,122 +0,0 @@ -/* eslint-disable react-hooks/exhaustive-deps */ -import { useEffect, useState } from "react"; -import { AxiosError } from "axios"; -import debug from "debug"; -import { useSnackbar } from "notistack"; -import { useTranslation } from "react-i18next"; -import { Box, Button } from "@mui/material"; -import UploadOutlinedIcon from "@mui/icons-material/UploadOutlined"; -import { - getStudyData, - importFile, -} from "../../../../../../../services/api/study"; -import { Header, Root, Content } from "./style"; -import useEnqueueErrorSnackbar from "../../../../../../../hooks/useEnqueueErrorSnackbar"; -import SimpleLoader from "../../../../../../common/loaders/SimpleLoader"; -import ImportDialog from "../../../../../../common/dialogs/ImportDialog"; - -const logErr = debug("antares:createimportform:error"); - -interface PropTypes { - study: string; - url: string; - refreshView: () => void; - filterOut: Array; -} - -function StudyFileView(props: PropTypes) { - const { study, url, filterOut, refreshView } = props; - const { enqueueSnackbar } = useSnackbar(); - const enqueueErrorSnackbar = useEnqueueErrorSnackbar(); - const [t] = useTranslation(); - const [data, setData] = useState(); - const [loaded, setLoaded] = useState(false); - const [isEditable, setEditable] = useState(true); - const [formattedPath, setFormattedPath] = useState(""); - const [openImportDialog, setOpenImportDialog] = useState(false); - - const loadFileData = async () => { - setData(undefined); - setLoaded(false); - try { - const res = await getStudyData(study, url); - if (Array.isArray(res)) { - setData(res.join("\n")); - } else { - setData(res); - } - } catch (e) { - enqueueErrorSnackbar(t("studies.error.retrieveData"), e as AxiosError); - } finally { - setLoaded(true); - } - }; - - const onImport = async (file: File) => { - try { - await importFile(file, study, formattedPath); - } catch (e) { - logErr("Failed to import file", file, e); - enqueueErrorSnackbar(t("studies.error.saveData"), e as AxiosError); - } - refreshView(); - enqueueSnackbar(t("studies.success.saveData"), { - variant: "success", - }); - }; - - useEffect(() => { - const urlParts = url.split("/"); - const tmpUrl = urlParts.filter((item) => item); - setFormattedPath(tmpUrl.join("/")); - if (tmpUrl.length > 0) { - setEditable(!filterOut.includes(tmpUrl[0])); - } - if (urlParts.length < 2) { - enqueueSnackbar(t("studies.error.retrieveData"), { - variant: "error", - }); - return; - } - loadFileData(); - }, [url, filterOut]); - - return ( - <> - {data && ( - - {isEditable && ( -
- -
- )} - - {data} - -
- )} - {!loaded && ( - - - - )} - {openImportDialog && ( - setOpenImportDialog(false)} - onImport={onImport} - /> - )} - - ); -} - -export default StudyFileView; diff --git a/webapp/src/components/App/Singlestudy/explore/Modelization/DebugView/StudyDataView/StudyJsonView.tsx b/webapp/src/components/App/Singlestudy/explore/Modelization/DebugView/StudyDataView/StudyJsonView.tsx deleted file mode 100644 index bb84bc90c2..0000000000 --- a/webapp/src/components/App/Singlestudy/explore/Modelization/DebugView/StudyDataView/StudyJsonView.tsx +++ /dev/null @@ -1,125 +0,0 @@ -/* eslint-disable react-hooks/exhaustive-deps */ -import { useState, useEffect } from "react"; -import { AxiosError } from "axios"; -import { useSnackbar } from "notistack"; -import { useTranslation } from "react-i18next"; -import SaveIcon from "@mui/icons-material/Save"; -import { Box, Button, Typography } from "@mui/material"; -import { - editStudy, - getStudyData, -} from "../../../../../../../services/api/study"; -import useEnqueueErrorSnackbar from "../../../../../../../hooks/useEnqueueErrorSnackbar"; -import { Header, Root } from "./style"; -import SimpleLoader from "../../../../../../common/loaders/SimpleLoader"; -import JSONEditor from "../../../../../../common/JSONEditor"; - -interface PropTypes { - data: string; - study: string; - filterOut: Array; -} - -function StudyJsonView(props: PropTypes) { - const { data, study, filterOut } = props; - const { enqueueSnackbar } = useSnackbar(); - const enqueueErrorSnackbar = useEnqueueErrorSnackbar(); - const [t] = useTranslation(); - const [jsonData, setJsonData] = useState(); - const [loaded, setLoaded] = useState(false); - const [saveAllowed, setSaveAllowed] = useState(false); - const [isEditable, setEditable] = useState(true); - - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const handleJsonChange = (newJsonData: any) => { - setJsonData(newJsonData); - setSaveAllowed(true); - }; - - const saveData = async () => { - const tmpDataPath = data.split("/").filter((item) => item); - const tmpPath = tmpDataPath.join("/"); - if (loaded && jsonData) { - try { - await editStudy(jsonData, study, tmpPath); - enqueueSnackbar(t("studies.success.saveData"), { - variant: "success", - }); - setSaveAllowed(false); - } catch (e) { - enqueueErrorSnackbar(t("studies.error.saveData"), e as AxiosError); - } - } else { - enqueueSnackbar(t("studies.error.saveData"), { variant: "error" }); - } - }; - - useEffect(() => { - (async () => { - setJsonData(undefined); - setLoaded(false); - const tmpDataPath = data.split("/").filter((item) => item); - if (tmpDataPath.length > 0) { - setEditable(!filterOut.includes(tmpDataPath[0])); - } - try { - const res = await getStudyData(study, data, -1); - setJsonData(res); - setSaveAllowed(false); - } catch (e) { - enqueueErrorSnackbar(t("studies.error.retrieveData"), e as AxiosError); - } finally { - setLoaded(true); - } - })(); - }, [data, filterOut]); - - return ( - - {isEditable && ( -
- -
- )} - {jsonData && ( - - console.log(json)} - modes={["tree", "code"]} - enableSort={false} - enableTransform={false} - /> - - )} - {!loaded && ( - - - - )} -
- ); -} - -export default StudyJsonView; diff --git a/webapp/src/components/App/Singlestudy/explore/Modelization/DebugView/StudyDataView/StudyMatrixView/StudyMatrixView.tsx b/webapp/src/components/App/Singlestudy/explore/Modelization/DebugView/StudyDataView/StudyMatrixView/StudyMatrixView.tsx deleted file mode 100644 index 3c6f113dca..0000000000 --- a/webapp/src/components/App/Singlestudy/explore/Modelization/DebugView/StudyDataView/StudyMatrixView/StudyMatrixView.tsx +++ /dev/null @@ -1,217 +0,0 @@ -import { useEffect, useState } from "react"; -import { AxiosError } from "axios"; -import debug from "debug"; -import { useSnackbar } from "notistack"; -import { useTranslation } from "react-i18next"; -import { Box, Typography, Divider, ButtonGroup, Button } from "@mui/material"; -import TableViewIcon from "@mui/icons-material/TableView"; -import BarChartIcon from "@mui/icons-material/BarChart"; -import GetAppOutlinedIcon from "@mui/icons-material/GetAppOutlined"; -import { - getStudyData, - importFile, -} from "../../../../../../../../services/api/study"; -import { - MatrixType, - MatrixEditDTO, -} from "../../../../../../../../common/types"; -import { Header, Root, Content } from "../style"; -import usePromiseWithSnackbarError from "../../../../../../../../hooks/usePromiseWithSnackbarError"; -import { StyledButton } from "./style"; -import useEnqueueErrorSnackbar from "../../../../../../../../hooks/useEnqueueErrorSnackbar"; -import SimpleContent from "../../../../../../../common/page/SimpleContent"; -import ImportDialog from "../../../../../../../common/dialogs/ImportDialog"; -import SimpleLoader from "../../../../../../../common/loaders/SimpleLoader"; -import EditableMatrix from "../../../../../../../common/EditableMatrix"; -import { - editMatrix, - getStudyMatrixIndex, -} from "../../../../../../../../services/api/matrix"; - -const logErr = debug("antares:createimportform:error"); - -interface PropTypes { - study: string; - url: string; - filterOut: Array; -} - -function StudyMatrixView(props: PropTypes) { - const { study, url, filterOut } = props; - const { enqueueSnackbar } = useSnackbar(); - const enqueueErrorSnackbar = useEnqueueErrorSnackbar(); - const [t] = useTranslation(); - const [data, setData] = useState(); - const [loaded, setLoaded] = useState(false); - const [toggleView, setToggleView] = useState(true); - const [openImportDialog, setOpenImportDialog] = useState(false); - const [isEditable, setEditable] = useState(true); - const [formattedPath, setFormattedPath] = useState(""); - - const { data: matrixIndex } = usePromiseWithSnackbarError( - () => getStudyMatrixIndex(study, formattedPath), - { - errorMessage: t("matrix.error.failedToRetrieveIndex"), - deps: [study, formattedPath], - }, - ); - - //////////////////////////////////////////////////////////////// - // Utils - //////////////////////////////////////////////////////////////// - - const loadFileData = async () => { - setData(undefined); - setLoaded(false); - try { - const res = await getStudyData(study, url); - if (typeof res === "string") { - const fixed = res - .replace(/NaN/g, '"NaN"') - .replace(/Infinity/g, '"Infinity"'); - setData(JSON.parse(fixed)); - } else { - setData(res); - } - } catch (e) { - enqueueErrorSnackbar(t("data.error.matrix"), e as AxiosError); - } finally { - setLoaded(true); - } - }; - - //////////////////////////////////////////////////////////////// - // Event Handlers - //////////////////////////////////////////////////////////////// - - const handleUpdate = async (change: MatrixEditDTO[], source: string) => { - if (source !== "loadData" && source !== "updateData") { - try { - if (change.length > 0) { - await editMatrix(study, formattedPath, change); - enqueueSnackbar(t("matrix.success.matrixUpdate"), { - variant: "success", - }); - } - } catch (e) { - enqueueErrorSnackbar(t("matrix.error.matrixUpdate"), e as AxiosError); - } - } - }; - - const handleImport = async (file: File) => { - try { - await importFile(file, study, formattedPath); - } catch (e) { - logErr("Failed to import file", file, e); - enqueueErrorSnackbar(t("variants.error.import"), e as AxiosError); - } finally { - enqueueSnackbar(t("variants.success.import"), { - variant: "success", - }); - loadFileData(); - } - }; - - useEffect(() => { - const urlParts = url.split("/"); - const tmpUrl = urlParts.filter((item) => item); - setFormattedPath(tmpUrl.join("/")); - if (tmpUrl.length > 0) { - setEditable(!filterOut.includes(tmpUrl[0])); - } - if (urlParts.length < 2) { - enqueueSnackbar(t("studies.error.retrieveData"), { - variant: "error", - }); - return; - } - loadFileData(); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [url, filterOut, enqueueSnackbar, t]); - - //////////////////////////////////////////////////////////////// - // JSX - //////////////////////////////////////////////////////////////// - - return ( - - -
- - {t("xpansion.timeSeries")} - - - {loaded && data && data.columns?.length > 1 && ( - - setToggleView((prev) => !prev)}> - {toggleView ? ( - - ) : ( - - )} - - - )} - {isEditable && ( - - )} - -
- - {!loaded && } - {loaded && data && data.columns?.length >= 1 ? ( - - ) : ( - loaded && ( - } - onClick={() => setOpenImportDialog(true)} - > - {t("global.import")} - - } - /> - ) - )} -
- {openImportDialog && ( - setOpenImportDialog(false)} - onImport={handleImport} - /> - )} -
- ); -} - -export default StudyMatrixView; diff --git a/webapp/src/components/App/Singlestudy/explore/Modelization/DebugView/StudyDataView/StudyMatrixView/style.ts b/webapp/src/components/App/Singlestudy/explore/Modelization/DebugView/StudyDataView/StudyMatrixView/style.ts deleted file mode 100644 index 0e6eb5513b..0000000000 --- a/webapp/src/components/App/Singlestudy/explore/Modelization/DebugView/StudyDataView/StudyMatrixView/style.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { styled, Button } from "@mui/material"; - -export const StyledButton = styled(Button)(({ theme }) => ({ - backgroundColor: "rgba(180, 180, 180, 0.09)", - color: "white", - borderRight: "none !important", - "&:hover": { - color: "white", - backgroundColor: theme.palette.secondary.main, - }, - "&:disabled": { - backgroundColor: theme.palette.secondary.dark, - color: "white !important", - }, -})); - -export default {}; diff --git a/webapp/src/components/App/Singlestudy/explore/Modelization/DebugView/StudyDataView/index.tsx b/webapp/src/components/App/Singlestudy/explore/Modelization/DebugView/StudyDataView/index.tsx deleted file mode 100644 index 8cc91350af..0000000000 --- a/webapp/src/components/App/Singlestudy/explore/Modelization/DebugView/StudyDataView/index.tsx +++ /dev/null @@ -1,66 +0,0 @@ -/* eslint-disable @typescript-eslint/no-explicit-any */ -import { CSSProperties, ReactNode } from "react"; -import { Box } from "@mui/material"; -import StudyFileView from "./StudyFileView"; -import StudyJsonView from "./StudyJsonView"; -import StudyMatrixView from "./StudyMatrixView/StudyMatrixView"; -import { StudyDataType } from "../../../../../../../common/types"; - -interface PropTypes { - study: string; - type: StudyDataType; - data: string; - studyData: any; - setStudyData: (elm: any) => void; -} - -interface RenderData { - css: CSSProperties; - data: ReactNode; -} - -function StudyDataView(props: PropTypes) { - const { study, type, data, studyData, setStudyData } = props; - const filterOut = ["output", "logs", "Desktop"]; - - const refreshView = () => { - setStudyData({ ...studyData }); - }; - - const renderData = (): RenderData => { - if (type === "file") { - return { - css: { overflow: "auto" }, - data: ( - - ), - }; - } - if (type === "matrix" || type === "matrixfile") { - return { - css: { overflow: "auto" }, - data: ( - - ), - }; - } - return { - css: { overflow: "hidden", paddingTop: "0px" }, - data: , - }; - }; - - const rd = renderData(); - return ( - - {rd.data} - - ); -} - -export default StudyDataView; diff --git a/webapp/src/components/App/Singlestudy/explore/Modelization/DebugView/StudyTreeView/index.tsx b/webapp/src/components/App/Singlestudy/explore/Modelization/DebugView/StudyTreeView/index.tsx deleted file mode 100644 index fc7e2157e5..0000000000 --- a/webapp/src/components/App/Singlestudy/explore/Modelization/DebugView/StudyTreeView/index.tsx +++ /dev/null @@ -1,91 +0,0 @@ -/* eslint-disable jsx-a11y/interactive-supports-focus */ -/* eslint-disable jsx-a11y/click-events-have-key-events */ -import { Box } from "@mui/material"; -import { TreeItem, TreeView } from "@mui/lab"; -import ExpandMoreIcon from "@mui/icons-material/ExpandMore"; -import ChevronRightIcon from "@mui/icons-material/ChevronRight"; -import { StudyDataType } from "../../../../../../../common/types"; -import { getStudyParams } from "./utils"; - -interface ItemPropTypes { - itemkey: string; - // eslint-disable-next-line @typescript-eslint/no-explicit-any - data: any; - path?: string; - viewer: (type: StudyDataType, data: string) => void; -} - -function StudyTreeItem(props: ItemPropTypes) { - const { itemkey, data, path = "/", viewer } = props; - - // if not an object then it's a RawFileNode or MatrixNode - // here we have to decide which viewer to use - const params = getStudyParams(data, path, itemkey); - if (params) { - const FileIcon = params.icon; - return ( - viewer(params.type, params.data)} - > - - {itemkey} - - } - /> - ); - } - - // else this is a folder containing.. stuff (recursion) - return ( - - {Object.keys(data).map((childkey) => ( - - ))} - - ); -} - -interface PropTypes { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - data: any; - view: (type: StudyDataType, data: string) => void; -} - -function StudyTreeView(props: PropTypes) { - const { data, view } = props; - return ( - } - defaultExpandIcon={} - > - {Object.keys(data).map((key) => ( - - ))} - - ); -} - -StudyTreeItem.defaultProps = { - path: "/", -}; - -export default StudyTreeView; diff --git a/webapp/src/components/App/Singlestudy/explore/Modelization/DebugView/StudyTreeView/utils.ts b/webapp/src/components/App/Singlestudy/explore/Modelization/DebugView/StudyTreeView/utils.ts deleted file mode 100644 index b5395ccaba..0000000000 --- a/webapp/src/components/App/Singlestudy/explore/Modelization/DebugView/StudyTreeView/utils.ts +++ /dev/null @@ -1,39 +0,0 @@ -import IntegrationInstructionsIcon from "@mui/icons-material/IntegrationInstructions"; -import TextSnippetIcon from "@mui/icons-material/TextSnippet"; -import { SvgIconComponent } from "@mui/icons-material"; -import { StudyDataType } from "../../../../../../../common/types"; - -export interface StudyParams { - type: StudyDataType; - icon: SvgIconComponent; - data: string; -} - -export const getStudyParams = ( - // eslint-disable-next-line @typescript-eslint/no-explicit-any - data: any, - path: string, - itemkey: string, -): StudyParams | undefined => { - if (typeof data === "string") { - const tmp = data.split("://"); - if (tmp && tmp.length > 0) { - if (tmp[0] === "json" || tmp[1].endsWith(".json")) { - return { - type: "json", - data: `${path}/${itemkey}`, - icon: IntegrationInstructionsIcon, - }; - } - return { - type: tmp[0] as StudyDataType, - icon: TextSnippetIcon, - data: `${path}/${itemkey}`, - }; - } - return { type: "file", icon: TextSnippetIcon, data: `${path}/${itemkey}` }; - } - return undefined; -}; - -export default {}; diff --git a/webapp/src/components/App/Singlestudy/explore/Modelization/DebugView/index.tsx b/webapp/src/components/App/Singlestudy/explore/Modelization/DebugView/index.tsx deleted file mode 100644 index 3ba45612c8..0000000000 --- a/webapp/src/components/App/Singlestudy/explore/Modelization/DebugView/index.tsx +++ /dev/null @@ -1,101 +0,0 @@ -import { useEffect, useState, useCallback } from "react"; -import { AxiosError } from "axios"; -import debug from "debug"; -import { useTranslation } from "react-i18next"; -import { useOutletContext } from "react-router-dom"; -import { Box } from "@mui/material"; -import { getStudyData } from "../../../../../../services/api/study"; -import StudyTreeView from "./StudyTreeView"; -import StudyDataView from "./StudyDataView"; -import { StudyDataType, StudyMetadata } from "../../../../../../common/types"; -import useEnqueueErrorSnackbar from "../../../../../../hooks/useEnqueueErrorSnackbar"; -import SimpleLoader from "../../../../../common/loaders/SimpleLoader"; - -const logError = debug("antares:studyview:error"); - -interface ElementView { - type: StudyDataType; - data: string; -} - -function DebugView() { - const { study } = useOutletContext<{ study: StudyMetadata }>(); - const [t] = useTranslation(); - const enqueueErrorSnackbar = useEnqueueErrorSnackbar(); - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const [studyData, setStudyData] = useState(); - const [loaded, setLoaded] = useState(false); - const [elementView, setElementView] = useState(); - - const initStudyData = useCallback( - async (sid: string) => { - setLoaded(false); - try { - const data = await getStudyData(sid, "", -1); - setStudyData(data); - } catch (e) { - enqueueErrorSnackbar(t("studies.error.retrieveData"), e as AxiosError); - logError("Failed to fetch study data", sid, e); - } finally { - setLoaded(true); - } - }, - [enqueueErrorSnackbar, t], - ); - - useEffect(() => { - if (study) { - initStudyData(study.id); - } - }, [study, initStudyData]); - - return ( - - {study && studyData && ( - <> - - - {studyData && ( - setElementView({ type, data })} - /> - )} - - - - - {elementView && ( - - )} - - - - )} - {!loaded && studyData === undefined && } - - ); -} - -export default DebugView; diff --git a/webapp/src/components/common/JSONEditor/dark-theme.css b/webapp/src/components/common/JSONEditor/dark-theme.css index fb44f64326..18029e871d 100644 --- a/webapp/src/components/common/JSONEditor/dark-theme.css +++ b/webapp/src/components/common/JSONEditor/dark-theme.css @@ -1,7 +1,3 @@ -//////////////////////////////////////////////////////////////// -// JSONEditor -//////////////////////////////////////////////////////////////// - div.jsoneditor { border: 2px solid #212c38; /* Dark gray border for consistency */ color: #ffffff; @@ -102,6 +98,7 @@ div.jsoneditor-contextmenu .jsoneditor-menu button { background-color: #222333; color: #ffffff; /* White color for context menu items */ } + .jsoneditor-contextmenu .jsoneditor-menu li button.jsoneditor-selected, .jsoneditor-contextmenu .jsoneditor-menu li button.jsoneditor-selected:focus, .jsoneditor-contextmenu .jsoneditor-menu li button.jsoneditor-selected:hover { @@ -120,11 +117,6 @@ div.jsoneditor-contextmenu .jsoneditor-menu button { color: #ffffff; /* White color for search input */ } -//////////////////////////////////////////////////////////////// -// Ace editor -//////////////////////////////////////////////////////////////// - - div.ace_jsoneditor .ace_editor { background-color: #222333; color: #ffffff; From b54aa65adb035434ec5bb42785af001e4920e784 Mon Sep 17 00:00:00 2001 From: hatim dinia Date: Tue, 9 Jan 2024 10:24:05 +0100 Subject: [PATCH 6/6] fix(npm): add missing devDependencies --- webapp/package-lock.json | 72 ++++++++++++++++++++++++---------------- webapp/package.json | 2 ++ 2 files changed, 46 insertions(+), 28 deletions(-) diff --git a/webapp/package-lock.json b/webapp/package-lock.json index 01efffd824..a93cc98b8a 100644 --- a/webapp/package-lock.json +++ b/webapp/package-lock.json @@ -83,6 +83,7 @@ "xml-js": "1.6.11" }, "devDependencies": { + "@babel/plugin-proposal-private-property-in-object": "7.21.11", "@total-typescript/ts-reset": "0.5.1", "@types/debug": "4.1.9", "@types/js-cookie": "3.0.4", @@ -109,6 +110,7 @@ "eslint-plugin-react": "7.33.2", "eslint-plugin-react-hooks": "4.6.0", "husky": "8.0.3", + "immutable": "3.8.2", "jest-sonar-reporter": "2.0.0", "prettier": "3.0.3", "process": "0.11.10", @@ -877,9 +879,17 @@ } }, "node_modules/@babel/plugin-proposal-private-property-in-object": { - "version": "7.21.0-placeholder-for-preset-env.2", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0-placeholder-for-preset-env.2.tgz", - "integrity": "sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w==", + "version": "7.21.11", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.11.tgz", + "integrity": "sha512-0QZ8qP/3RLDVBwBFoWAwCtgcDZJVwA5LUJRZU8x2YFfKNuFq161wK3cuGrALu5yiPu+vzwTAg/sMWVNeWeNyaw==", + "deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-private-property-in-object instead.", + "dev": true, + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.18.6", + "@babel/helper-create-class-features-plugin": "^7.21.0", + "@babel/helper-plugin-utils": "^7.20.2", + "@babel/plugin-syntax-private-property-in-object": "^7.14.5" + }, "engines": { "node": ">=6.9.0" }, @@ -1729,12 +1739,12 @@ } }, "node_modules/@babel/plugin-transform-private-property-in-object": { - "version": "7.22.11", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.22.11.tgz", - "integrity": "sha512-sSCbqZDBKHetvjSwpyWzhuHkmW5RummxJBVbYLkGkaiTOWGxml7SXt0iWa03bzxFIx7wOj3g/ILRd0RcJKBeSQ==", + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.23.4.tgz", + "integrity": "sha512-9G3K1YqTq3F4Vt88Djx1UZ79PDyj+yKRnUy7cZGSMe+a7jkwD259uKKuUzQlPkGam7R+8RJwh5z4xO27fA1o2A==", "dependencies": { "@babel/helper-annotate-as-pure": "^7.22.5", - "@babel/helper-create-class-features-plugin": "^7.22.11", + "@babel/helper-create-class-features-plugin": "^7.22.15", "@babel/helper-plugin-utils": "^7.22.5", "@babel/plugin-syntax-private-property-in-object": "^7.14.5" }, @@ -2130,6 +2140,17 @@ "@babel/core": "^7.0.0-0" } }, + "node_modules/@babel/preset-env/node_modules/@babel/plugin-proposal-private-property-in-object": { + "version": "7.21.0-placeholder-for-preset-env.2", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0-placeholder-for-preset-env.2.tgz", + "integrity": "sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w==", + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, "node_modules/@babel/preset-env/node_modules/semver": { "version": "6.3.1", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", @@ -5673,7 +5694,7 @@ "version": "6.7.3", "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.7.3.tgz", "integrity": "sha512-vntq452UHNltxsaaN+L9WyuMch8bMd9CqJ3zhzTPXXidwbf5mqqKCVXEuvRZUqLJSTLeWE65lQwyXsRGnXkCTA==", - "devOptional": true, + "dev": true, "dependencies": { "@eslint-community/regexpp": "^4.5.1", "@typescript-eslint/scope-manager": "6.7.3", @@ -5841,7 +5862,7 @@ "version": "6.7.3", "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.7.3.tgz", "integrity": "sha512-TlutE+iep2o7R8Lf+yoer3zU6/0EAUc8QIBB3GYBc1KGz4c4TRm83xwXUZVPlZ6YCLss4r77jbu6j3sendJoiQ==", - "devOptional": true, + "dev": true, "dependencies": { "@typescript-eslint/scope-manager": "6.7.3", "@typescript-eslint/types": "6.7.3", @@ -5869,7 +5890,7 @@ "version": "6.7.3", "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.7.3.tgz", "integrity": "sha512-wOlo0QnEou9cHO2TdkJmzF7DFGvAKEnB82PuPNHpT8ZKKaZu6Bm63ugOTn9fXNJtvuDPanBc78lGUGGytJoVzQ==", - "devOptional": true, + "dev": true, "dependencies": { "@typescript-eslint/types": "6.7.3", "@typescript-eslint/visitor-keys": "6.7.3" @@ -5886,7 +5907,7 @@ "version": "6.7.3", "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.7.3.tgz", "integrity": "sha512-Fc68K0aTDrKIBvLnKTZ5Pf3MXK495YErrbHb1R6aTpfK5OdSFj0rVN7ib6Tx6ePrZ2gsjLqr0s98NG7l96KSQw==", - "devOptional": true, + "dev": true, "dependencies": { "@typescript-eslint/typescript-estree": "6.7.3", "@typescript-eslint/utils": "6.7.3", @@ -5913,7 +5934,7 @@ "version": "6.7.3", "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.7.3.tgz", "integrity": "sha512-4g+de6roB2NFcfkZb439tigpAMnvEIg3rIjWQ+EM7IBaYt/CdJt6em9BJ4h4UpdgaBWdmx2iWsafHTrqmgIPNw==", - "devOptional": true, + "dev": true, "engines": { "node": "^16.0.0 || >=18.0.0" }, @@ -5926,7 +5947,7 @@ "version": "6.7.3", "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.7.3.tgz", "integrity": "sha512-YLQ3tJoS4VxLFYHTw21oe1/vIZPRqAO91z6Uv0Ss2BKm/Ag7/RVQBcXTGcXhgJMdA4U+HrKuY5gWlJlvoaKZ5g==", - "devOptional": true, + "dev": true, "dependencies": { "@typescript-eslint/types": "6.7.3", "@typescript-eslint/visitor-keys": "6.7.3", @@ -5953,7 +5974,7 @@ "version": "6.7.3", "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.7.3.tgz", "integrity": "sha512-vzLkVder21GpWRrmSR9JxGZ5+ibIUSudXlW52qeKpzUEQhRSmyZiVDDj3crAth7+5tmN1ulvgKaCU2f/bPRCzg==", - "devOptional": true, + "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", "@types/json-schema": "^7.0.12", @@ -5978,7 +5999,7 @@ "version": "6.7.3", "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.7.3.tgz", "integrity": "sha512-HEVXkU9IB+nk9o63CeICMHxFWbHWr3E1mpilIQBe9+7L/lH97rleFLVtYsfnWB+JVMaiFnEaxvknvmIzX+CqVg==", - "devOptional": true, + "dev": true, "dependencies": { "@typescript-eslint/types": "6.7.3", "eslint-visitor-keys": "^3.4.1" @@ -12990,10 +13011,12 @@ } }, "node_modules/immutable": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.3.4.tgz", - "integrity": "sha512-fsXeu4J4i6WNWSikpI88v/PcVflZz+6kMhUfIwc5SY+poQRPnaf5V7qds6SUyUN3cVxEzuCab7QIoLOQ+DQ1wA==", - "peer": true + "version": "3.8.2", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-3.8.2.tgz", + "integrity": "sha512-15gZoQ38eYjEjxkorfbcgBKBL6R7T459OuK+CpcWt7O3KF4uPCx2tD0uFETlUDIyo+1789crbMhTvQBSR5yBMg==", + "engines": { + "node": ">=0.10.0" + } }, "node_modules/import-fresh": { "version": "3.3.0", @@ -21228,14 +21251,6 @@ "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.0.6.tgz", "integrity": "sha512-ilkD8YEnnGh1zJ240uJsW7AzE+2qpbOUYjacomn3AvJ6J4JhKGSZ2nh4wUIXPZrEPppaCLx5jFe8T89Rk8tQ7w==" }, - "node_modules/swagger-ui-react/node_modules/immutable": { - "version": "3.8.2", - "resolved": "https://registry.npmjs.org/immutable/-/immutable-3.8.2.tgz", - "integrity": "sha512-15gZoQ38eYjEjxkorfbcgBKBL6R7T459OuK+CpcWt7O3KF4uPCx2tD0uFETlUDIyo+1789crbMhTvQBSR5yBMg==", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/symbol-tree": { "version": "3.2.4", "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", @@ -21743,7 +21758,7 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.0.3.tgz", "integrity": "sha512-wNMeqtMz5NtwpT/UZGY5alT+VoKdSsOOP/kqHFcUW1P/VRhH2wJ48+DN2WwUliNbQ976ETwDL0Ifd2VVvgonvg==", - "devOptional": true, + "dev": true, "engines": { "node": ">=16.13.0" }, @@ -21974,6 +21989,7 @@ "version": "5.2.2", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.2.2.tgz", "integrity": "sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==", + "dev": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" diff --git a/webapp/package.json b/webapp/package.json index 0a75cbf7ed..ec4839cff3 100644 --- a/webapp/package.json +++ b/webapp/package.json @@ -103,6 +103,7 @@ "proxy": "http://localhost:8080", "homepage": "/", "devDependencies": { + "@babel/plugin-proposal-private-property-in-object": "7.21.11", "@total-typescript/ts-reset": "0.5.1", "@types/debug": "4.1.9", "@types/js-cookie": "3.0.4", @@ -129,6 +130,7 @@ "eslint-plugin-react": "7.33.2", "eslint-plugin-react-hooks": "4.6.0", "husky": "8.0.3", + "immutable": "3.8.2", "jest-sonar-reporter": "2.0.0", "prettier": "3.0.3", "process": "0.11.10",