From 692dee183e5235b00a0ed09d2a4a25ef2a0db378 Mon Sep 17 00:00:00 2001 From: Vyacheslav Matyukhin Date: Wed, 27 Apr 2022 23:22:11 +0400 Subject: [PATCH 1/4] feat: reenable question page links --- src/web/display/DisplayQuestion/index.tsx | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/src/web/display/DisplayQuestion/index.tsx b/src/web/display/DisplayQuestion/index.tsx index b117207..3bb582f 100644 --- a/src/web/display/DisplayQuestion/index.tsx +++ b/src/web/display/DisplayQuestion/index.tsx @@ -146,16 +146,14 @@ export const DisplayQuestion: React.FC = ({ ) : null}
- {process.env.NEXT_PUBLIC_ENABLE_QUESTION_PAGES ? ( - - - - - - ) : null} + + + + + Date: Wed, 27 Apr 2022 23:51:00 +0400 Subject: [PATCH 2/4] feat: chats (merge code from #63) --- package-lock.json | 1001 ++++++++++++++++- package.json | 3 +- src/pages/secretEmbed.tsx | 3 +- src/web/dashboards/queries.generated.tsx | 2 +- .../display/DisplayOneQuestionForCapture.tsx | 2 +- .../DisplayQuestion/QuestionFooter.tsx | 2 +- src/web/display/DisplayQuestion/index.tsx | 21 +- src/web/display/DisplayQuestions.tsx | 2 +- src/web/fragments.generated.tsx | 9 + src/web/fragments.graphql | 38 + src/web/questions/components/HistoryChart.tsx | 207 ++++ .../questions/components/QuestionOptions.tsx | 2 +- src/web/questions/pages/QuestionPage.tsx | 17 +- src/web/questions/queries.generated.tsx | 8 +- src/web/questions/queries.graphql | 4 +- src/web/search/CommonDisplay.tsx | 3 +- src/web/search/anySearchPage.tsx | 3 +- src/web/search/queries.generated.tsx | 5 +- src/web/search/queries.graphql | 28 - src/web/utils.ts | 18 + 20 files changed, 1305 insertions(+), 73 deletions(-) create mode 100644 src/web/fragments.generated.tsx create mode 100644 src/web/fragments.graphql create mode 100644 src/web/questions/components/HistoryChart.tsx diff --git a/package-lock.json b/package-lock.json index 5a9d7cd..36b267e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -71,7 +71,8 @@ "ts-node": "^10.7.0", "tunnel": "^0.0.6", "urql": "^2.2.0", - "urql-custom-scalars-exchange": "^0.1.5" + "urql-custom-scalars-exchange": "^0.1.5", + "victory": "^36.3.2" }, "devDependencies": { "@graphql-codegen/cli": "^2.6.2", @@ -5095,6 +5096,89 @@ "internmap": "^1.0.0" } }, + "node_modules/d3-collection": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/d3-collection/-/d3-collection-1.0.7.tgz", + "integrity": "sha512-ii0/r5f4sjKNTfh84Di+DpztYwqKhEyUlKoPrzUFfeSkWxjW49xU2QzO9qrPrNkpdI0XJkfzvmTu8V2Zylln6A==" + }, + "node_modules/d3-color": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-1.4.1.tgz", + "integrity": "sha512-p2sTHSLCJI2QKunbGb7ocOh7DgTAn8IrLx21QRc/BSnodXM4sv6aLQlnfpvehFMLZEfBc6g9pH9SWQccFYfJ9Q==" + }, + "node_modules/d3-ease": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-1.0.7.tgz", + "integrity": "sha512-lx14ZPYkhNx0s/2HX5sLFUI3mbasHjSSpwO/KaaNACweVwxUruKyWVcb293wMv1RqTPZyZ8kSZ2NogUZNcLOFQ==" + }, + "node_modules/d3-format": { + "version": "1.4.5", + "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-1.4.5.tgz", + "integrity": "sha512-J0piedu6Z8iB6TbIGfZgDzfXxUFN3qQRMofy2oPdXzQibYGqPB/9iMcxr/TGalU+2RsyDO+U4f33id8tbnSRMQ==" + }, + "node_modules/d3-interpolate": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-1.4.0.tgz", + "integrity": "sha512-V9znK0zc3jOPV4VD2zZn0sDhZU3WAE2bmlxdIwwQPPzPjvyLkd8B3JUVdS1IDUFDkWZ72c9qnv1GK2ZagTZ8EA==", + "dependencies": { + "d3-color": "1" + } + }, + "node_modules/d3-path": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-1.0.9.tgz", + "integrity": "sha512-VLaYcn81dtHVTjEHd8B+pbe9yHWpXKZUC87PzoFmsFrJqgFwDe/qxfp5MlfsfM1V5E/iVt0MmEbWQ7FVIXh/bg==" + }, + "node_modules/d3-scale": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-1.0.7.tgz", + "integrity": "sha512-KvU92czp2/qse5tUfGms6Kjig0AhHOwkzXG0+PqIJB3ke0WUv088AHMZI0OssO9NCkXt4RP8yju9rpH8aGB7Lw==", + "dependencies": { + "d3-array": "^1.2.0", + "d3-collection": "1", + "d3-color": "1", + "d3-format": "1", + "d3-interpolate": "1", + "d3-time": "1", + "d3-time-format": "2" + } + }, + "node_modules/d3-scale/node_modules/d3-array": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-1.2.4.tgz", + "integrity": "sha512-KHW6M86R+FUPYGb3R5XiYjXPq7VzwxZ22buHhAEVG5ztoEcZZMLov530mmccaqA1GghZArjQV46fuc8kUqhhHw==" + }, + "node_modules/d3-shape": { + "version": "1.3.7", + "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-1.3.7.tgz", + "integrity": "sha512-EUkvKjqPFUAZyOlhY5gzCxCeI0Aep04LwIRpsZ/mLFelJiUfnK56jo5JMDSE7yyP2kLSb6LtF+S5chMk7uqPqw==", + "dependencies": { + "d3-path": "1" + } + }, + "node_modules/d3-time": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-1.1.0.tgz", + "integrity": "sha512-Xh0isrZ5rPYYdqhAVk8VLnMEidhz5aP7htAADH6MfzgmmicPkTo8LhkLxci61/lCB7n7UmE3bN0leRt+qvkLxA==" + }, + "node_modules/d3-time-format": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-2.3.0.tgz", + "integrity": "sha512-guv6b2H37s2Uq/GefleCDtbe0XZAuy7Wa49VGkPVPMfLL9qObgBST3lEHJBMUp8S7NdLQAGIvr2KXk8Hc98iKQ==", + "dependencies": { + "d3-time": "1" + } + }, + "node_modules/d3-timer": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-1.0.10.tgz", + "integrity": "sha512-B1JDm0XDaQC+uvo4DT79H0XmBskgS3l6Ve+1SBCfxgmtIb1AVrPIoqd+nPSv+loMX8szQ0sVUhGngL7D5QPiXw==" + }, + "node_modules/d3-voronoi": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/d3-voronoi/-/d3-voronoi-1.1.4.tgz", + "integrity": "sha512-dArJ32hchFsrQ8uMiTBLq256MpnZjeuBtdHpaDlYuQyjU0CVzCJl/BVW+SkszaAeH95D/8gxqAhgx0ouAWAfRg==" + }, "node_modules/dashify": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/dashify/-/dashify-2.0.0.tgz", @@ -5318,6 +5402,19 @@ "dev": true, "license": "MIT" }, + "node_modules/delaunator": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/delaunator/-/delaunator-4.0.1.tgz", + "integrity": "sha512-WNPWi1IRKZfCt/qIDMfERkDp93+iZEmOxN2yy4Jg+Xhv8SLk2UTqqbe1sfiipn0and9QrE914/ihdx82Y/Giag==" + }, + "node_modules/delaunay-find": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/delaunay-find/-/delaunay-find-0.0.6.tgz", + "integrity": "sha512-1+almjfrnR7ZamBk0q3Nhg6lqSe6Le4vL0WJDSMx4IDbQwTpUTXPjxC00lqLBT8MYsJpPCbI16sIkw9cPsbi7Q==", + "dependencies": { + "delaunator": "^4.0.0" + } + }, "node_modules/delayed-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", @@ -7512,6 +7609,11 @@ "jsonify": "~0.0.0" } }, + "node_modules/json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=" + }, "node_modules/json-to-pretty-yaml": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/json-to-pretty-yaml/-/json-to-pretty-yaml-1.2.2.tgz", @@ -37319,6 +37421,11 @@ "react-dom": "^0.14.7 || ^15.0.0-0 || ^16.0.0 || ^17.0.0" } }, + "node_modules/react-fast-compare": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-2.0.4.tgz", + "integrity": "sha512-suNP+J1VU1MWFKcyt7RtjiSWUjvidmQSlqu+eHslq+342xCbGTYmC0mEhPCOHxlW0CywylOC1u2DFAT+bv4dBw==" + }, "node_modules/react-hook-form": { "version": "7.28.0", "resolved": "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.28.0.tgz", @@ -39252,6 +39359,438 @@ "url": "https://opencollective.com/unified" } }, + "node_modules/victory": { + "version": "36.3.2", + "resolved": "https://registry.npmjs.org/victory/-/victory-36.3.2.tgz", + "integrity": "sha512-++LY8hFzhxsWCErN8SMz0WcrhCeueHQz22H3ELzQl4lcDxC1+hyQ1eLm6yGVpfqPG6HROKCRVpxZhE1PiDQFNQ==", + "dependencies": { + "victory-area": "^36.3.2", + "victory-axis": "^36.3.2", + "victory-bar": "^36.3.2", + "victory-box-plot": "^36.3.2", + "victory-brush-container": "^36.3.2", + "victory-brush-line": "^36.3.2", + "victory-candlestick": "^36.3.2", + "victory-canvas": "^36.3.2", + "victory-chart": "^36.3.2", + "victory-core": "^36.3.2", + "victory-create-container": "^36.3.2", + "victory-cursor-container": "^36.3.2", + "victory-errorbar": "^36.3.2", + "victory-group": "^36.3.2", + "victory-histogram": "^36.3.2", + "victory-legend": "^36.3.2", + "victory-line": "^36.3.2", + "victory-pie": "^36.3.2", + "victory-polar-axis": "^36.3.2", + "victory-scatter": "^36.3.2", + "victory-selection-container": "^36.3.2", + "victory-shared-events": "^36.3.2", + "victory-stack": "^36.3.2", + "victory-tooltip": "^36.3.2", + "victory-voronoi": "^36.3.2", + "victory-voronoi-container": "^36.3.2", + "victory-zoom-container": "^36.3.2" + }, + "peerDependencies": { + "react": "^16.6.0 || ^17.0.0" + } + }, + "node_modules/victory-area": { + "version": "36.3.2", + "resolved": "https://registry.npmjs.org/victory-area/-/victory-area-36.3.2.tgz", + "integrity": "sha512-Cc5eu1LUDQdYMATNX8u6K+Dlbcr83bqXP1dHsKz2i2fib2cus491YnBbxvS39kW8bt7hCKjdmX0uEFdFyo1KSg==", + "dependencies": { + "d3-shape": "^1.2.0", + "lodash": "^4.17.19", + "prop-types": "^15.5.8", + "victory-core": "^36.3.2" + }, + "peerDependencies": { + "react": "^16.6.0 || ^17.0.0" + } + }, + "node_modules/victory-axis": { + "version": "36.3.2", + "resolved": "https://registry.npmjs.org/victory-axis/-/victory-axis-36.3.2.tgz", + "integrity": "sha512-uhCRiM3VPrJvSGQq3qaneC2906rFbUyndpQkoNLgY7Igp5kQaHMRFwp6xMjhay58Etr7qbmEFslpmQ0E3nedlQ==", + "dependencies": { + "lodash": "^4.17.19", + "prop-types": "^15.5.8", + "victory-core": "^36.3.2" + }, + "peerDependencies": { + "react": "^16.6.0 || ^17.0.0" + } + }, + "node_modules/victory-bar": { + "version": "36.3.2", + "resolved": "https://registry.npmjs.org/victory-bar/-/victory-bar-36.3.2.tgz", + "integrity": "sha512-fM9M05UAukwMKGyKwALB43T3xbIwT/9HR3FsdGr7LLEk+LWXxM4oBchyCE9TaCf9/6EvW8fmu6TEjcCr1Vd/tw==", + "dependencies": { + "d3-shape": "^1.2.0", + "lodash": "^4.17.19", + "prop-types": "^15.5.8", + "victory-core": "^36.3.2" + }, + "peerDependencies": { + "react": "^16.6.0 || ^17.0.0" + } + }, + "node_modules/victory-box-plot": { + "version": "36.3.2", + "resolved": "https://registry.npmjs.org/victory-box-plot/-/victory-box-plot-36.3.2.tgz", + "integrity": "sha512-kC9iXh21+UfxNfNMkjx986b3FXpvIQ06c/OLX/qVzowXUgPySMnk/tSkybFKg1R4ncZuAxD0Xl03fAoysYeCZw==", + "dependencies": { + "d3-array": "^1.2.0", + "lodash": "^4.17.19", + "prop-types": "^15.5.8", + "victory-core": "^36.3.2" + }, + "peerDependencies": { + "react": "^16.6.0 || ^17.0.0" + } + }, + "node_modules/victory-box-plot/node_modules/d3-array": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-1.2.4.tgz", + "integrity": "sha512-KHW6M86R+FUPYGb3R5XiYjXPq7VzwxZ22buHhAEVG5ztoEcZZMLov530mmccaqA1GghZArjQV46fuc8kUqhhHw==" + }, + "node_modules/victory-brush-container": { + "version": "36.3.2", + "resolved": "https://registry.npmjs.org/victory-brush-container/-/victory-brush-container-36.3.2.tgz", + "integrity": "sha512-n8gDLtGbil1ylk3vELX0ZicNcyI1mFEzt1+JtjZzMhPlu45XOF2O492rMksD5d69cDgXzQ87QUOF0HhTP6UOTg==", + "dependencies": { + "lodash": "^4.17.19", + "prop-types": "^15.5.8", + "react-fast-compare": "^2.0.0", + "victory-core": "^36.3.2" + }, + "peerDependencies": { + "react": "^16.6.0 || ^17.0.0" + } + }, + "node_modules/victory-brush-line": { + "version": "36.3.2", + "resolved": "https://registry.npmjs.org/victory-brush-line/-/victory-brush-line-36.3.2.tgz", + "integrity": "sha512-aYTBcJV94A1p35ZExuUWnptg7KiLwGSU9cVU7SJTRZ06L2VyhvH8UTP+3m0Z/liHeSBV0WWJ+tltcDBufCjpHQ==", + "dependencies": { + "lodash": "^4.17.19", + "prop-types": "^15.5.8", + "react-fast-compare": "^2.0.0", + "victory-core": "^36.3.2" + }, + "peerDependencies": { + "react": "^16.6.0 || ^17.0.0" + } + }, + "node_modules/victory-candlestick": { + "version": "36.3.2", + "resolved": "https://registry.npmjs.org/victory-candlestick/-/victory-candlestick-36.3.2.tgz", + "integrity": "sha512-jrr8OSJnoIDCLshXXGg43GVhn5f5FrgFvSFMZYdZ2zKByF02Zxi5A4v6aKYMZ+V4hvoAlqtE+AsQgqzIJiKY8g==", + "dependencies": { + "lodash": "^4.17.19", + "prop-types": "^15.5.8", + "victory-core": "^36.3.2" + }, + "peerDependencies": { + "react": "^16.6.0 || ^17.0.0" + } + }, + "node_modules/victory-canvas": { + "version": "36.3.2", + "resolved": "https://registry.npmjs.org/victory-canvas/-/victory-canvas-36.3.2.tgz", + "integrity": "sha512-j2nONFCrEzhGD+7KKzfLmBwI5PcQ+WXNiTbqTxnIuYad1c68inQjRByI4703HiYNIIPb1FryJ47sQgfLMich8w==", + "dependencies": { + "lodash": "^4.17.19", + "prop-types": "^15.5.8", + "victory-core": "^36.3.2" + }, + "peerDependencies": { + "react": "^16.6.0 || ^17.0.0" + } + }, + "node_modules/victory-chart": { + "version": "36.3.2", + "resolved": "https://registry.npmjs.org/victory-chart/-/victory-chart-36.3.2.tgz", + "integrity": "sha512-tSyf1wTVmVoXPnKlXCoK9ldOQ6WRtoJ052fDLFa/Ws12dy7X9Oxz6JlZa968nsjyLxs0bkJeA74sjvJAzhFFCQ==", + "dependencies": { + "lodash": "^4.17.19", + "prop-types": "^15.5.8", + "react-fast-compare": "^2.0.0", + "victory-axis": "^36.3.2", + "victory-core": "^36.3.2", + "victory-polar-axis": "^36.3.2", + "victory-shared-events": "^36.3.2" + }, + "peerDependencies": { + "react": "^16.6.0 || ^17.0.0" + } + }, + "node_modules/victory-core": { + "version": "36.3.2", + "resolved": "https://registry.npmjs.org/victory-core/-/victory-core-36.3.2.tgz", + "integrity": "sha512-p7SRqW7KqRJb+/mgjaTtZFSjM86llH0+vnabnUkVE7YE7ZwOiB5vhC1iPMv2d9BeNwyUs9ahO+TD7en3Mg3UUg==", + "dependencies": { + "d3-ease": "^1.0.0", + "d3-interpolate": "^1.1.1", + "d3-scale": "^1.0.0", + "d3-shape": "^1.2.0", + "d3-timer": "^1.0.0", + "lodash": "^4.17.21", + "prop-types": "^15.5.8", + "react-fast-compare": "^2.0.0" + }, + "peerDependencies": { + "react": "^16.6.0 || ^17.0.0" + } + }, + "node_modules/victory-create-container": { + "version": "36.3.2", + "resolved": "https://registry.npmjs.org/victory-create-container/-/victory-create-container-36.3.2.tgz", + "integrity": "sha512-4BE269o9ch6AUv9wyaKx7usaUPXx5prB7nCbmkNTkxUKFHyk7FC/JuXwhfSjebiAuoU/4KWWhd0pzLLtW9XLsw==", + "dependencies": { + "lodash": "^4.17.19", + "victory-brush-container": "^36.3.2", + "victory-core": "^36.3.2", + "victory-cursor-container": "^36.3.2", + "victory-selection-container": "^36.3.2", + "victory-voronoi-container": "^36.3.2", + "victory-zoom-container": "^36.3.2" + }, + "peerDependencies": { + "react": "^16.6.0 || ^17.0.0" + } + }, + "node_modules/victory-cursor-container": { + "version": "36.3.2", + "resolved": "https://registry.npmjs.org/victory-cursor-container/-/victory-cursor-container-36.3.2.tgz", + "integrity": "sha512-XCIJ1pOuMltjn4LD73RcykctuvyEHH9oPV5qwN85GUv1QNXuYgtBJ+8DtqM8hqnPYK4TuTqBbYb3UFnmqoKi8g==", + "dependencies": { + "lodash": "^4.17.19", + "prop-types": "^15.5.8", + "victory-core": "^36.3.2" + }, + "peerDependencies": { + "react": "^16.6.0 || ^17.0.0" + } + }, + "node_modules/victory-errorbar": { + "version": "36.3.2", + "resolved": "https://registry.npmjs.org/victory-errorbar/-/victory-errorbar-36.3.2.tgz", + "integrity": "sha512-eA52LHYA8hnP3BydmZbnjogkmzmG8BWZU45iRXvA53XjLwd4/W2hIJ36ZBByW9ut1f9oxUijSvf0yMmolsD5tA==", + "dependencies": { + "lodash": "^4.17.19", + "prop-types": "^15.5.8", + "victory-core": "^36.3.2" + }, + "peerDependencies": { + "react": "^16.6.0 || ^17.0.0" + } + }, + "node_modules/victory-group": { + "version": "36.3.2", + "resolved": "https://registry.npmjs.org/victory-group/-/victory-group-36.3.2.tgz", + "integrity": "sha512-P7iu63VZzXmpOGEPwHRvY8k5lx1EgTFssh0rlSGOgzTM2I6V3LAR9hIzXs9I4iQfrkEU8W1DAOEkT3IDTNfeyQ==", + "dependencies": { + "lodash": "^4.17.19", + "prop-types": "^15.5.8", + "react-fast-compare": "^2.0.0", + "victory-core": "^36.3.2", + "victory-shared-events": "^36.3.2" + }, + "peerDependencies": { + "react": "^16.6.0 || ^17.0.0" + } + }, + "node_modules/victory-histogram": { + "version": "36.3.2", + "resolved": "https://registry.npmjs.org/victory-histogram/-/victory-histogram-36.3.2.tgz", + "integrity": "sha512-dN2V/jCOUrQy/XHe5HnJ6BgH6S3LT5eDniY+EPiMtbXNBPWkFRiL2YjR28c0Bn2pVfHvamiMti7AhQA7/P6kVw==", + "dependencies": { + "d3-array": "~2.3.0", + "d3-scale": "^1.0.0", + "lodash": "^4.17.19", + "prop-types": "^15.5.8", + "react-fast-compare": "^2.0.0", + "victory-bar": "^36.3.2", + "victory-core": "^36.3.2" + }, + "peerDependencies": { + "react": "^16.6.0 || ^17.0.0" + } + }, + "node_modules/victory-histogram/node_modules/d3-array": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-2.3.3.tgz", + "integrity": "sha512-syv3wp0U5aB6toP2zb2OdBkhTy1MWDsCAaYk6OXJZv+G4u7bSWEmYgxLoFyc88RQUhZYGCebW9a9UD1gFi5+MQ==" + }, + "node_modules/victory-legend": { + "version": "36.3.2", + "resolved": "https://registry.npmjs.org/victory-legend/-/victory-legend-36.3.2.tgz", + "integrity": "sha512-hnHuwFyaLBbG3AsAt7vhp9GfSSut2Q77WX/AYYifsyOkECtpH7p8/oW+sYmDp51STbeOoOmgBQvRz9rqhjRkhQ==", + "dependencies": { + "lodash": "^4.17.19", + "prop-types": "^15.5.8", + "victory-core": "^36.3.2" + }, + "peerDependencies": { + "react": "^16.6.0 || ^17.0.0" + } + }, + "node_modules/victory-line": { + "version": "36.3.2", + "resolved": "https://registry.npmjs.org/victory-line/-/victory-line-36.3.2.tgz", + "integrity": "sha512-ZiZgpbzomv5oqxeNNXIoDtUvwUDhYMZQRT/pi1+UplqcPyN5VWy78cdgtE7uJeIpk+phoOxGRD1VYPiNeVpuqg==", + "dependencies": { + "d3-shape": "^1.2.0", + "lodash": "^4.17.19", + "prop-types": "^15.5.8", + "victory-core": "^36.3.2" + }, + "peerDependencies": { + "react": "^16.6.0 || ^17.0.0" + } + }, + "node_modules/victory-pie": { + "version": "36.3.2", + "resolved": "https://registry.npmjs.org/victory-pie/-/victory-pie-36.3.2.tgz", + "integrity": "sha512-Au45U+i3KwVZLDe4qydT9OpAZBr7Kln50SZEGFX3QVQPw4fqDZnOHt1cw9dIL81DcZyM8ZNFgg1Q6AM4q6mdcg==", + "dependencies": { + "d3-shape": "^1.0.0", + "lodash": "^4.17.19", + "prop-types": "^15.5.8", + "victory-core": "^36.3.2" + }, + "peerDependencies": { + "react": "^16.6.0 || ^17.0.0" + } + }, + "node_modules/victory-polar-axis": { + "version": "36.3.2", + "resolved": "https://registry.npmjs.org/victory-polar-axis/-/victory-polar-axis-36.3.2.tgz", + "integrity": "sha512-HC1U3Zzl9lfi0eHREuunmYTJTe+CLiSXo2nF09t1ksMnRmklJg15/+Zo870OzihWujGiJf+6nODjnD+6MsMnCQ==", + "dependencies": { + "lodash": "^4.17.19", + "prop-types": "^15.5.8", + "victory-core": "^36.3.2" + }, + "peerDependencies": { + "react": "^16.6.0 || ^17.0.0" + } + }, + "node_modules/victory-scatter": { + "version": "36.3.2", + "resolved": "https://registry.npmjs.org/victory-scatter/-/victory-scatter-36.3.2.tgz", + "integrity": "sha512-DY6pMccY8OX5ijjTyTkJbudKlcFe34ECiHdEH+SHYJgvQQL5/9IJ5hcFOeyyieUxZiu4nFT1P14+Y436YZCykA==", + "dependencies": { + "lodash": "^4.17.19", + "prop-types": "^15.5.8", + "victory-core": "^36.3.2" + }, + "peerDependencies": { + "react": "^16.6.0 || ^17.0.0" + } + }, + "node_modules/victory-selection-container": { + "version": "36.3.2", + "resolved": "https://registry.npmjs.org/victory-selection-container/-/victory-selection-container-36.3.2.tgz", + "integrity": "sha512-upKgAMwIZIME6VHEeZZhd7KYp4XVis4tST0+6Pevpt4RIO6R+6Rl3bV9t8EfVVE2SdqWL5vra0+HtaVVRpqAIQ==", + "dependencies": { + "lodash": "^4.17.19", + "prop-types": "^15.5.8", + "victory-core": "^36.3.2" + }, + "peerDependencies": { + "react": "^16.6.0 || ^17.0.0" + } + }, + "node_modules/victory-shared-events": { + "version": "36.3.2", + "resolved": "https://registry.npmjs.org/victory-shared-events/-/victory-shared-events-36.3.2.tgz", + "integrity": "sha512-8ME0LRT2B7j4VGqYhKtq1A8Npnkn65r2K9VpIFulePpmA2+KlEHey2lxqjns4lW4DoaeAMBF45kJUzwwa5twtQ==", + "dependencies": { + "json-stringify-safe": "^5.0.1", + "lodash": "^4.17.19", + "prop-types": "^15.5.8", + "react-fast-compare": "^2.0.0", + "victory-core": "^36.3.2" + }, + "peerDependencies": { + "react": "^16.6.0 || ^17.0.0" + } + }, + "node_modules/victory-stack": { + "version": "36.3.2", + "resolved": "https://registry.npmjs.org/victory-stack/-/victory-stack-36.3.2.tgz", + "integrity": "sha512-ySxzU9H7peNyVgYdkvHgqN63A4PV9efHxq6SnMvxc36B4VZTwT3N4Qrbi+MGsJ5LhQ7R2Q2eH3mBqXPYrNfiEg==", + "dependencies": { + "lodash": "^4.17.19", + "prop-types": "^15.5.8", + "react-fast-compare": "^2.0.0", + "victory-core": "^36.3.2", + "victory-shared-events": "^36.3.2" + }, + "peerDependencies": { + "react": "^16.6.0 || ^17.0.0" + } + }, + "node_modules/victory-tooltip": { + "version": "36.3.2", + "resolved": "https://registry.npmjs.org/victory-tooltip/-/victory-tooltip-36.3.2.tgz", + "integrity": "sha512-wH+3Qpg/MMLeJ0Z+/GJpSOPldxczP5g3VcdE/p9JAxUwlL3bC6oWA//HoZO+TqlQSaIg8dGcaKgFoh7c8avCQQ==", + "dependencies": { + "lodash": "^4.17.19", + "prop-types": "^15.5.8", + "victory-core": "^36.3.2" + }, + "peerDependencies": { + "react": "^16.6.0 || ^17.0.0" + } + }, + "node_modules/victory-voronoi": { + "version": "36.3.2", + "resolved": "https://registry.npmjs.org/victory-voronoi/-/victory-voronoi-36.3.2.tgz", + "integrity": "sha512-x3Nzk2wwyJixDon1Cw/TXdIKxdAliErlyglgMo+/nBFEulMTl0rDc6zWuyNWCD/MelkQ3O0jQqTORtpBKcJ86w==", + "dependencies": { + "d3-voronoi": "^1.1.2", + "lodash": "^4.17.19", + "prop-types": "^15.5.8", + "victory-core": "^36.3.2" + }, + "peerDependencies": { + "react": "^16.6.0 || ^17.0.0" + } + }, + "node_modules/victory-voronoi-container": { + "version": "36.3.2", + "resolved": "https://registry.npmjs.org/victory-voronoi-container/-/victory-voronoi-container-36.3.2.tgz", + "integrity": "sha512-lV7fx3NWiHa40wY0TIKgIlrmj+KIsI9HeYpO7D13F2ooGTPvUlNE+7ZkBzmDT2KQ5wmXMV2u3lcaAWWVlLrc4Q==", + "dependencies": { + "delaunay-find": "0.0.6", + "lodash": "^4.17.19", + "prop-types": "^15.5.8", + "react-fast-compare": "^2.0.0", + "victory-core": "^36.3.2", + "victory-tooltip": "^36.3.2" + }, + "peerDependencies": { + "react": "^16.6.0 || ^17.0.0" + } + }, + "node_modules/victory-zoom-container": { + "version": "36.3.2", + "resolved": "https://registry.npmjs.org/victory-zoom-container/-/victory-zoom-container-36.3.2.tgz", + "integrity": "sha512-xE0ds97gS07iKXaEDCiMWVE/IyxK4fjhaY7Mgr928qNVABk7e8VATguNstQKgu7UaoS5tI8tRxNLaXhHfBHoQQ==", + "dependencies": { + "lodash": "^4.17.19", + "prop-types": "^15.5.8", + "victory-core": "^36.3.2" + }, + "peerDependencies": { + "react": "^16.6.0 || ^17.0.0" + } + }, "node_modules/w3c-hr-time": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz", @@ -43247,6 +43786,91 @@ "internmap": "^1.0.0" } }, + "d3-collection": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/d3-collection/-/d3-collection-1.0.7.tgz", + "integrity": "sha512-ii0/r5f4sjKNTfh84Di+DpztYwqKhEyUlKoPrzUFfeSkWxjW49xU2QzO9qrPrNkpdI0XJkfzvmTu8V2Zylln6A==" + }, + "d3-color": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-1.4.1.tgz", + "integrity": "sha512-p2sTHSLCJI2QKunbGb7ocOh7DgTAn8IrLx21QRc/BSnodXM4sv6aLQlnfpvehFMLZEfBc6g9pH9SWQccFYfJ9Q==" + }, + "d3-ease": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-1.0.7.tgz", + "integrity": "sha512-lx14ZPYkhNx0s/2HX5sLFUI3mbasHjSSpwO/KaaNACweVwxUruKyWVcb293wMv1RqTPZyZ8kSZ2NogUZNcLOFQ==" + }, + "d3-format": { + "version": "1.4.5", + "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-1.4.5.tgz", + "integrity": "sha512-J0piedu6Z8iB6TbIGfZgDzfXxUFN3qQRMofy2oPdXzQibYGqPB/9iMcxr/TGalU+2RsyDO+U4f33id8tbnSRMQ==" + }, + "d3-interpolate": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-1.4.0.tgz", + "integrity": "sha512-V9znK0zc3jOPV4VD2zZn0sDhZU3WAE2bmlxdIwwQPPzPjvyLkd8B3JUVdS1IDUFDkWZ72c9qnv1GK2ZagTZ8EA==", + "requires": { + "d3-color": "1" + } + }, + "d3-path": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-1.0.9.tgz", + "integrity": "sha512-VLaYcn81dtHVTjEHd8B+pbe9yHWpXKZUC87PzoFmsFrJqgFwDe/qxfp5MlfsfM1V5E/iVt0MmEbWQ7FVIXh/bg==" + }, + "d3-scale": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-1.0.7.tgz", + "integrity": "sha512-KvU92czp2/qse5tUfGms6Kjig0AhHOwkzXG0+PqIJB3ke0WUv088AHMZI0OssO9NCkXt4RP8yju9rpH8aGB7Lw==", + "requires": { + "d3-array": "^1.2.0", + "d3-collection": "1", + "d3-color": "1", + "d3-format": "1", + "d3-interpolate": "1", + "d3-time": "1", + "d3-time-format": "2" + }, + "dependencies": { + "d3-array": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-1.2.4.tgz", + "integrity": "sha512-KHW6M86R+FUPYGb3R5XiYjXPq7VzwxZ22buHhAEVG5ztoEcZZMLov530mmccaqA1GghZArjQV46fuc8kUqhhHw==" + } + } + }, + "d3-shape": { + "version": "1.3.7", + "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-1.3.7.tgz", + "integrity": "sha512-EUkvKjqPFUAZyOlhY5gzCxCeI0Aep04LwIRpsZ/mLFelJiUfnK56jo5JMDSE7yyP2kLSb6LtF+S5chMk7uqPqw==", + "requires": { + "d3-path": "1" + } + }, + "d3-time": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-1.1.0.tgz", + "integrity": "sha512-Xh0isrZ5rPYYdqhAVk8VLnMEidhz5aP7htAADH6MfzgmmicPkTo8LhkLxci61/lCB7n7UmE3bN0leRt+qvkLxA==" + }, + "d3-time-format": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-2.3.0.tgz", + "integrity": "sha512-guv6b2H37s2Uq/GefleCDtbe0XZAuy7Wa49VGkPVPMfLL9qObgBST3lEHJBMUp8S7NdLQAGIvr2KXk8Hc98iKQ==", + "requires": { + "d3-time": "1" + } + }, + "d3-timer": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-1.0.10.tgz", + "integrity": "sha512-B1JDm0XDaQC+uvo4DT79H0XmBskgS3l6Ve+1SBCfxgmtIb1AVrPIoqd+nPSv+loMX8szQ0sVUhGngL7D5QPiXw==" + }, + "d3-voronoi": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/d3-voronoi/-/d3-voronoi-1.1.4.tgz", + "integrity": "sha512-dArJ32hchFsrQ8uMiTBLq256MpnZjeuBtdHpaDlYuQyjU0CVzCJl/BVW+SkszaAeH95D/8gxqAhgx0ouAWAfRg==" + }, "dashify": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/dashify/-/dashify-2.0.0.tgz", @@ -43408,6 +44032,19 @@ "integrity": "sha512-EPS1carKg+dkEVy3qNTqIdp2qV7mUP08nIsupfwQpz++slCVRw7qbQyWvSTig+kFPwz2XXp5/kIIkH+CwrJKkQ==", "dev": true }, + "delaunator": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/delaunator/-/delaunator-4.0.1.tgz", + "integrity": "sha512-WNPWi1IRKZfCt/qIDMfERkDp93+iZEmOxN2yy4Jg+Xhv8SLk2UTqqbe1sfiipn0and9QrE914/ihdx82Y/Giag==" + }, + "delaunay-find": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/delaunay-find/-/delaunay-find-0.0.6.tgz", + "integrity": "sha512-1+almjfrnR7ZamBk0q3Nhg6lqSe6Le4vL0WJDSMx4IDbQwTpUTXPjxC00lqLBT8MYsJpPCbI16sIkw9cPsbi7Q==", + "requires": { + "delaunator": "^4.0.0" + } + }, "delayed-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", @@ -44974,6 +45611,11 @@ "jsonify": "~0.0.0" } }, + "json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=" + }, "json-to-pretty-yaml": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/json-to-pretty-yaml/-/json-to-pretty-yaml-1.2.2.tgz", @@ -67344,6 +67986,11 @@ "classnames": "^2.2.3" } }, + "react-fast-compare": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-2.0.4.tgz", + "integrity": "sha512-suNP+J1VU1MWFKcyt7RtjiSWUjvidmQSlqu+eHslq+342xCbGTYmC0mEhPCOHxlW0CywylOC1u2DFAT+bv4dBw==" + }, "react-hook-form": { "version": "7.28.0", "resolved": "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.28.0.tgz", @@ -68706,6 +69353,358 @@ "unist-util-stringify-position": "^3.0.0" } }, + "victory": { + "version": "36.3.2", + "resolved": "https://registry.npmjs.org/victory/-/victory-36.3.2.tgz", + "integrity": "sha512-++LY8hFzhxsWCErN8SMz0WcrhCeueHQz22H3ELzQl4lcDxC1+hyQ1eLm6yGVpfqPG6HROKCRVpxZhE1PiDQFNQ==", + "requires": { + "victory-area": "^36.3.2", + "victory-axis": "^36.3.2", + "victory-bar": "^36.3.2", + "victory-box-plot": "^36.3.2", + "victory-brush-container": "^36.3.2", + "victory-brush-line": "^36.3.2", + "victory-candlestick": "^36.3.2", + "victory-canvas": "^36.3.2", + "victory-chart": "^36.3.2", + "victory-core": "^36.3.2", + "victory-create-container": "^36.3.2", + "victory-cursor-container": "^36.3.2", + "victory-errorbar": "^36.3.2", + "victory-group": "^36.3.2", + "victory-histogram": "^36.3.2", + "victory-legend": "^36.3.2", + "victory-line": "^36.3.2", + "victory-pie": "^36.3.2", + "victory-polar-axis": "^36.3.2", + "victory-scatter": "^36.3.2", + "victory-selection-container": "^36.3.2", + "victory-shared-events": "^36.3.2", + "victory-stack": "^36.3.2", + "victory-tooltip": "^36.3.2", + "victory-voronoi": "^36.3.2", + "victory-voronoi-container": "^36.3.2", + "victory-zoom-container": "^36.3.2" + } + }, + "victory-area": { + "version": "36.3.2", + "resolved": "https://registry.npmjs.org/victory-area/-/victory-area-36.3.2.tgz", + "integrity": "sha512-Cc5eu1LUDQdYMATNX8u6K+Dlbcr83bqXP1dHsKz2i2fib2cus491YnBbxvS39kW8bt7hCKjdmX0uEFdFyo1KSg==", + "requires": { + "d3-shape": "^1.2.0", + "lodash": "^4.17.19", + "prop-types": "^15.5.8", + "victory-core": "^36.3.2" + } + }, + "victory-axis": { + "version": "36.3.2", + "resolved": "https://registry.npmjs.org/victory-axis/-/victory-axis-36.3.2.tgz", + "integrity": "sha512-uhCRiM3VPrJvSGQq3qaneC2906rFbUyndpQkoNLgY7Igp5kQaHMRFwp6xMjhay58Etr7qbmEFslpmQ0E3nedlQ==", + "requires": { + "lodash": "^4.17.19", + "prop-types": "^15.5.8", + "victory-core": "^36.3.2" + } + }, + "victory-bar": { + "version": "36.3.2", + "resolved": "https://registry.npmjs.org/victory-bar/-/victory-bar-36.3.2.tgz", + "integrity": "sha512-fM9M05UAukwMKGyKwALB43T3xbIwT/9HR3FsdGr7LLEk+LWXxM4oBchyCE9TaCf9/6EvW8fmu6TEjcCr1Vd/tw==", + "requires": { + "d3-shape": "^1.2.0", + "lodash": "^4.17.19", + "prop-types": "^15.5.8", + "victory-core": "^36.3.2" + } + }, + "victory-box-plot": { + "version": "36.3.2", + "resolved": "https://registry.npmjs.org/victory-box-plot/-/victory-box-plot-36.3.2.tgz", + "integrity": "sha512-kC9iXh21+UfxNfNMkjx986b3FXpvIQ06c/OLX/qVzowXUgPySMnk/tSkybFKg1R4ncZuAxD0Xl03fAoysYeCZw==", + "requires": { + "d3-array": "^1.2.0", + "lodash": "^4.17.19", + "prop-types": "^15.5.8", + "victory-core": "^36.3.2" + }, + "dependencies": { + "d3-array": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-1.2.4.tgz", + "integrity": "sha512-KHW6M86R+FUPYGb3R5XiYjXPq7VzwxZ22buHhAEVG5ztoEcZZMLov530mmccaqA1GghZArjQV46fuc8kUqhhHw==" + } + } + }, + "victory-brush-container": { + "version": "36.3.2", + "resolved": "https://registry.npmjs.org/victory-brush-container/-/victory-brush-container-36.3.2.tgz", + "integrity": "sha512-n8gDLtGbil1ylk3vELX0ZicNcyI1mFEzt1+JtjZzMhPlu45XOF2O492rMksD5d69cDgXzQ87QUOF0HhTP6UOTg==", + "requires": { + "lodash": "^4.17.19", + "prop-types": "^15.5.8", + "react-fast-compare": "^2.0.0", + "victory-core": "^36.3.2" + } + }, + "victory-brush-line": { + "version": "36.3.2", + "resolved": "https://registry.npmjs.org/victory-brush-line/-/victory-brush-line-36.3.2.tgz", + "integrity": "sha512-aYTBcJV94A1p35ZExuUWnptg7KiLwGSU9cVU7SJTRZ06L2VyhvH8UTP+3m0Z/liHeSBV0WWJ+tltcDBufCjpHQ==", + "requires": { + "lodash": "^4.17.19", + "prop-types": "^15.5.8", + "react-fast-compare": "^2.0.0", + "victory-core": "^36.3.2" + } + }, + "victory-candlestick": { + "version": "36.3.2", + "resolved": "https://registry.npmjs.org/victory-candlestick/-/victory-candlestick-36.3.2.tgz", + "integrity": "sha512-jrr8OSJnoIDCLshXXGg43GVhn5f5FrgFvSFMZYdZ2zKByF02Zxi5A4v6aKYMZ+V4hvoAlqtE+AsQgqzIJiKY8g==", + "requires": { + "lodash": "^4.17.19", + "prop-types": "^15.5.8", + "victory-core": "^36.3.2" + } + }, + "victory-canvas": { + "version": "36.3.2", + "resolved": "https://registry.npmjs.org/victory-canvas/-/victory-canvas-36.3.2.tgz", + "integrity": "sha512-j2nONFCrEzhGD+7KKzfLmBwI5PcQ+WXNiTbqTxnIuYad1c68inQjRByI4703HiYNIIPb1FryJ47sQgfLMich8w==", + "requires": { + "lodash": "^4.17.19", + "prop-types": "^15.5.8", + "victory-core": "^36.3.2" + } + }, + "victory-chart": { + "version": "36.3.2", + "resolved": "https://registry.npmjs.org/victory-chart/-/victory-chart-36.3.2.tgz", + "integrity": "sha512-tSyf1wTVmVoXPnKlXCoK9ldOQ6WRtoJ052fDLFa/Ws12dy7X9Oxz6JlZa968nsjyLxs0bkJeA74sjvJAzhFFCQ==", + "requires": { + "lodash": "^4.17.19", + "prop-types": "^15.5.8", + "react-fast-compare": "^2.0.0", + "victory-axis": "^36.3.2", + "victory-core": "^36.3.2", + "victory-polar-axis": "^36.3.2", + "victory-shared-events": "^36.3.2" + } + }, + "victory-core": { + "version": "36.3.2", + "resolved": "https://registry.npmjs.org/victory-core/-/victory-core-36.3.2.tgz", + "integrity": "sha512-p7SRqW7KqRJb+/mgjaTtZFSjM86llH0+vnabnUkVE7YE7ZwOiB5vhC1iPMv2d9BeNwyUs9ahO+TD7en3Mg3UUg==", + "requires": { + "d3-ease": "^1.0.0", + "d3-interpolate": "^1.1.1", + "d3-scale": "^1.0.0", + "d3-shape": "^1.2.0", + "d3-timer": "^1.0.0", + "lodash": "^4.17.21", + "prop-types": "^15.5.8", + "react-fast-compare": "^2.0.0" + } + }, + "victory-create-container": { + "version": "36.3.2", + "resolved": "https://registry.npmjs.org/victory-create-container/-/victory-create-container-36.3.2.tgz", + "integrity": "sha512-4BE269o9ch6AUv9wyaKx7usaUPXx5prB7nCbmkNTkxUKFHyk7FC/JuXwhfSjebiAuoU/4KWWhd0pzLLtW9XLsw==", + "requires": { + "lodash": "^4.17.19", + "victory-brush-container": "^36.3.2", + "victory-core": "^36.3.2", + "victory-cursor-container": "^36.3.2", + "victory-selection-container": "^36.3.2", + "victory-voronoi-container": "^36.3.2", + "victory-zoom-container": "^36.3.2" + } + }, + "victory-cursor-container": { + "version": "36.3.2", + "resolved": "https://registry.npmjs.org/victory-cursor-container/-/victory-cursor-container-36.3.2.tgz", + "integrity": "sha512-XCIJ1pOuMltjn4LD73RcykctuvyEHH9oPV5qwN85GUv1QNXuYgtBJ+8DtqM8hqnPYK4TuTqBbYb3UFnmqoKi8g==", + "requires": { + "lodash": "^4.17.19", + "prop-types": "^15.5.8", + "victory-core": "^36.3.2" + } + }, + "victory-errorbar": { + "version": "36.3.2", + "resolved": "https://registry.npmjs.org/victory-errorbar/-/victory-errorbar-36.3.2.tgz", + "integrity": "sha512-eA52LHYA8hnP3BydmZbnjogkmzmG8BWZU45iRXvA53XjLwd4/W2hIJ36ZBByW9ut1f9oxUijSvf0yMmolsD5tA==", + "requires": { + "lodash": "^4.17.19", + "prop-types": "^15.5.8", + "victory-core": "^36.3.2" + } + }, + "victory-group": { + "version": "36.3.2", + "resolved": "https://registry.npmjs.org/victory-group/-/victory-group-36.3.2.tgz", + "integrity": "sha512-P7iu63VZzXmpOGEPwHRvY8k5lx1EgTFssh0rlSGOgzTM2I6V3LAR9hIzXs9I4iQfrkEU8W1DAOEkT3IDTNfeyQ==", + "requires": { + "lodash": "^4.17.19", + "prop-types": "^15.5.8", + "react-fast-compare": "^2.0.0", + "victory-core": "^36.3.2", + "victory-shared-events": "^36.3.2" + } + }, + "victory-histogram": { + "version": "36.3.2", + "resolved": "https://registry.npmjs.org/victory-histogram/-/victory-histogram-36.3.2.tgz", + "integrity": "sha512-dN2V/jCOUrQy/XHe5HnJ6BgH6S3LT5eDniY+EPiMtbXNBPWkFRiL2YjR28c0Bn2pVfHvamiMti7AhQA7/P6kVw==", + "requires": { + "d3-array": "~2.3.0", + "d3-scale": "^1.0.0", + "lodash": "^4.17.19", + "prop-types": "^15.5.8", + "react-fast-compare": "^2.0.0", + "victory-bar": "^36.3.2", + "victory-core": "^36.3.2" + }, + "dependencies": { + "d3-array": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-2.3.3.tgz", + "integrity": "sha512-syv3wp0U5aB6toP2zb2OdBkhTy1MWDsCAaYk6OXJZv+G4u7bSWEmYgxLoFyc88RQUhZYGCebW9a9UD1gFi5+MQ==" + } + } + }, + "victory-legend": { + "version": "36.3.2", + "resolved": "https://registry.npmjs.org/victory-legend/-/victory-legend-36.3.2.tgz", + "integrity": "sha512-hnHuwFyaLBbG3AsAt7vhp9GfSSut2Q77WX/AYYifsyOkECtpH7p8/oW+sYmDp51STbeOoOmgBQvRz9rqhjRkhQ==", + "requires": { + "lodash": "^4.17.19", + "prop-types": "^15.5.8", + "victory-core": "^36.3.2" + } + }, + "victory-line": { + "version": "36.3.2", + "resolved": "https://registry.npmjs.org/victory-line/-/victory-line-36.3.2.tgz", + "integrity": "sha512-ZiZgpbzomv5oqxeNNXIoDtUvwUDhYMZQRT/pi1+UplqcPyN5VWy78cdgtE7uJeIpk+phoOxGRD1VYPiNeVpuqg==", + "requires": { + "d3-shape": "^1.2.0", + "lodash": "^4.17.19", + "prop-types": "^15.5.8", + "victory-core": "^36.3.2" + } + }, + "victory-pie": { + "version": "36.3.2", + "resolved": "https://registry.npmjs.org/victory-pie/-/victory-pie-36.3.2.tgz", + "integrity": "sha512-Au45U+i3KwVZLDe4qydT9OpAZBr7Kln50SZEGFX3QVQPw4fqDZnOHt1cw9dIL81DcZyM8ZNFgg1Q6AM4q6mdcg==", + "requires": { + "d3-shape": "^1.0.0", + "lodash": "^4.17.19", + "prop-types": "^15.5.8", + "victory-core": "^36.3.2" + } + }, + "victory-polar-axis": { + "version": "36.3.2", + "resolved": "https://registry.npmjs.org/victory-polar-axis/-/victory-polar-axis-36.3.2.tgz", + "integrity": "sha512-HC1U3Zzl9lfi0eHREuunmYTJTe+CLiSXo2nF09t1ksMnRmklJg15/+Zo870OzihWujGiJf+6nODjnD+6MsMnCQ==", + "requires": { + "lodash": "^4.17.19", + "prop-types": "^15.5.8", + "victory-core": "^36.3.2" + } + }, + "victory-scatter": { + "version": "36.3.2", + "resolved": "https://registry.npmjs.org/victory-scatter/-/victory-scatter-36.3.2.tgz", + "integrity": "sha512-DY6pMccY8OX5ijjTyTkJbudKlcFe34ECiHdEH+SHYJgvQQL5/9IJ5hcFOeyyieUxZiu4nFT1P14+Y436YZCykA==", + "requires": { + "lodash": "^4.17.19", + "prop-types": "^15.5.8", + "victory-core": "^36.3.2" + } + }, + "victory-selection-container": { + "version": "36.3.2", + "resolved": "https://registry.npmjs.org/victory-selection-container/-/victory-selection-container-36.3.2.tgz", + "integrity": "sha512-upKgAMwIZIME6VHEeZZhd7KYp4XVis4tST0+6Pevpt4RIO6R+6Rl3bV9t8EfVVE2SdqWL5vra0+HtaVVRpqAIQ==", + "requires": { + "lodash": "^4.17.19", + "prop-types": "^15.5.8", + "victory-core": "^36.3.2" + } + }, + "victory-shared-events": { + "version": "36.3.2", + "resolved": "https://registry.npmjs.org/victory-shared-events/-/victory-shared-events-36.3.2.tgz", + "integrity": "sha512-8ME0LRT2B7j4VGqYhKtq1A8Npnkn65r2K9VpIFulePpmA2+KlEHey2lxqjns4lW4DoaeAMBF45kJUzwwa5twtQ==", + "requires": { + "json-stringify-safe": "^5.0.1", + "lodash": "^4.17.19", + "prop-types": "^15.5.8", + "react-fast-compare": "^2.0.0", + "victory-core": "^36.3.2" + } + }, + "victory-stack": { + "version": "36.3.2", + "resolved": "https://registry.npmjs.org/victory-stack/-/victory-stack-36.3.2.tgz", + "integrity": "sha512-ySxzU9H7peNyVgYdkvHgqN63A4PV9efHxq6SnMvxc36B4VZTwT3N4Qrbi+MGsJ5LhQ7R2Q2eH3mBqXPYrNfiEg==", + "requires": { + "lodash": "^4.17.19", + "prop-types": "^15.5.8", + "react-fast-compare": "^2.0.0", + "victory-core": "^36.3.2", + "victory-shared-events": "^36.3.2" + } + }, + "victory-tooltip": { + "version": "36.3.2", + "resolved": "https://registry.npmjs.org/victory-tooltip/-/victory-tooltip-36.3.2.tgz", + "integrity": "sha512-wH+3Qpg/MMLeJ0Z+/GJpSOPldxczP5g3VcdE/p9JAxUwlL3bC6oWA//HoZO+TqlQSaIg8dGcaKgFoh7c8avCQQ==", + "requires": { + "lodash": "^4.17.19", + "prop-types": "^15.5.8", + "victory-core": "^36.3.2" + } + }, + "victory-voronoi": { + "version": "36.3.2", + "resolved": "https://registry.npmjs.org/victory-voronoi/-/victory-voronoi-36.3.2.tgz", + "integrity": "sha512-x3Nzk2wwyJixDon1Cw/TXdIKxdAliErlyglgMo+/nBFEulMTl0rDc6zWuyNWCD/MelkQ3O0jQqTORtpBKcJ86w==", + "requires": { + "d3-voronoi": "^1.1.2", + "lodash": "^4.17.19", + "prop-types": "^15.5.8", + "victory-core": "^36.3.2" + } + }, + "victory-voronoi-container": { + "version": "36.3.2", + "resolved": "https://registry.npmjs.org/victory-voronoi-container/-/victory-voronoi-container-36.3.2.tgz", + "integrity": "sha512-lV7fx3NWiHa40wY0TIKgIlrmj+KIsI9HeYpO7D13F2ooGTPvUlNE+7ZkBzmDT2KQ5wmXMV2u3lcaAWWVlLrc4Q==", + "requires": { + "delaunay-find": "0.0.6", + "lodash": "^4.17.19", + "prop-types": "^15.5.8", + "react-fast-compare": "^2.0.0", + "victory-core": "^36.3.2", + "victory-tooltip": "^36.3.2" + } + }, + "victory-zoom-container": { + "version": "36.3.2", + "resolved": "https://registry.npmjs.org/victory-zoom-container/-/victory-zoom-container-36.3.2.tgz", + "integrity": "sha512-xE0ds97gS07iKXaEDCiMWVE/IyxK4fjhaY7Mgr928qNVABk7e8VATguNstQKgu7UaoS5tI8tRxNLaXhHfBHoQQ==", + "requires": { + "lodash": "^4.17.19", + "prop-types": "^15.5.8", + "victory-core": "^36.3.2" + } + }, "w3c-hr-time": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz", diff --git a/package.json b/package.json index ccb9dbc..5cd4b0a 100644 --- a/package.json +++ b/package.json @@ -89,7 +89,8 @@ "ts-node": "^10.7.0", "tunnel": "^0.0.6", "urql": "^2.2.0", - "urql-custom-scalars-exchange": "^0.1.5" + "urql-custom-scalars-exchange": "^0.1.5", + "victory": "^36.3.2" }, "devDependencies": { "@graphql-codegen/cli": "^2.6.2", diff --git a/src/pages/secretEmbed.tsx b/src/pages/secretEmbed.tsx index 893db6f..1c3cba5 100644 --- a/src/pages/secretEmbed.tsx +++ b/src/pages/secretEmbed.tsx @@ -5,7 +5,8 @@ import React from "react"; import { platforms } from "../backend/platforms"; import { DisplayQuestion } from "../web/display/DisplayQuestion"; -import { QuestionFragment, SearchDocument } from "../web/search/queries.generated"; +import { QuestionFragment } from "../web/fragments.generated"; +import { SearchDocument } from "../web/search/queries.generated"; import { ssrUrql } from "../web/urql"; interface Props { diff --git a/src/web/dashboards/queries.generated.tsx b/src/web/dashboards/queries.generated.tsx index 7560a0f..481f49c 100644 --- a/src/web/dashboards/queries.generated.tsx +++ b/src/web/dashboards/queries.generated.tsx @@ -1,7 +1,7 @@ import * as Types from '../../graphql/types.generated'; import { TypedDocumentNode as DocumentNode } from '@graphql-typed-document-node/core'; -import { QuestionFragmentDoc } from '../search/queries.generated'; +import { QuestionFragmentDoc } from '../fragments.generated'; export type DashboardFragment = { __typename?: 'Dashboard', id: string, title: string, description: string, creator: string, questions: Array<{ __typename?: 'Question', id: string, url: string, title: string, description: string, timestamp: number, visualization?: string | null, options: Array<{ __typename?: 'ProbabilityOption', name?: string | null, probability?: number | null }>, platform: { __typename?: 'Platform', id: string, label: string }, qualityIndicators: { __typename?: 'QualityIndicators', stars: number, numForecasts?: number | null, numForecasters?: number | null, volume?: number | null, spread?: number | null, sharesVolume?: number | null, openInterest?: number | null, liquidity?: number | null, tradeVolume?: number | null } }> }; export type DashboardByIdQueryVariables = Types.Exact<{ diff --git a/src/web/display/DisplayOneQuestionForCapture.tsx b/src/web/display/DisplayOneQuestionForCapture.tsx index 4b199a5..adcad5e 100644 --- a/src/web/display/DisplayOneQuestionForCapture.tsx +++ b/src/web/display/DisplayOneQuestionForCapture.tsx @@ -2,7 +2,7 @@ import domtoimage from "dom-to-image"; // https://github.com/tsayen/dom-to-image import { useEffect, useRef, useState } from "react"; import { CopyToClipboard } from "react-copy-to-clipboard"; -import { QuestionFragment } from "../search/queries.generated"; +import { QuestionFragment } from "../fragments.generated"; import { uploadToImgur } from "../worker/uploadToImgur"; import { DisplayQuestion } from "./DisplayQuestion"; diff --git a/src/web/display/DisplayQuestion/QuestionFooter.tsx b/src/web/display/DisplayQuestion/QuestionFooter.tsx index beebe2b..ecd37da 100644 --- a/src/web/display/DisplayQuestion/QuestionFooter.tsx +++ b/src/web/display/DisplayQuestion/QuestionFooter.tsx @@ -1,4 +1,4 @@ -import { QuestionFragment } from "../../search/queries.generated"; +import { QuestionFragment } from "../../fragments.generated"; type QualityIndicator = QuestionFragment["qualityIndicators"]; type IndicatorName = keyof QualityIndicator; diff --git a/src/web/display/DisplayQuestion/index.tsx b/src/web/display/DisplayQuestion/index.tsx index 3bb582f..dd48001 100644 --- a/src/web/display/DisplayQuestion/index.tsx +++ b/src/web/display/DisplayQuestion/index.tsx @@ -3,8 +3,9 @@ import { FaExpand } from "react-icons/fa"; import ReactMarkdown from "react-markdown"; import { CopyText } from "../../common/CopyText"; +import { QuestionFragment } from "../../fragments.generated"; import { QuestionOptions } from "../../questions/components/QuestionOptions"; -import { QuestionFragment } from "../../search/queries.generated"; +import { cleanText } from "../../utils"; import { Card } from "../Card"; import { QuestionFooter } from "./QuestionFooter"; @@ -68,24 +69,6 @@ if (!String.prototype.replaceAll) { }; } -const cleanText = (text: string): string => { - // Note: should no longer be necessary - let textString = !!text ? text : ""; - textString = textString - .replaceAll("] (", "](") - .replaceAll(") )", "))") - .replaceAll("( [", "([") - .replaceAll(") ,", "),") - .replaceAll("==", "") // Denotes a title in markdown - .replaceAll("Background\n", "") - .replaceAll("Context\n", "") - .replaceAll("--- \n", "- ") - .replaceAll(/\[(.*?)\]\(.*?\)/g, "$1"); - textString = textString.slice(0, 1) == "=" ? textString.slice(1) : textString; - //console.log(textString) - return textString; -}; - // Auxiliary components const DisplayMarkdown: React.FC<{ description: string }> = ({ diff --git a/src/web/display/DisplayQuestions.tsx b/src/web/display/DisplayQuestions.tsx index 4f012c6..289f58a 100644 --- a/src/web/display/DisplayQuestions.tsx +++ b/src/web/display/DisplayQuestions.tsx @@ -1,6 +1,6 @@ import React from "react"; -import { QuestionFragment } from "../search/queries.generated"; +import { QuestionFragment } from "../fragments.generated"; import { DisplayQuestion } from "./DisplayQuestion"; interface Props { diff --git a/src/web/fragments.generated.tsx b/src/web/fragments.generated.tsx new file mode 100644 index 0000000..9229e76 --- /dev/null +++ b/src/web/fragments.generated.tsx @@ -0,0 +1,9 @@ +import * as Types from '../graphql/types.generated'; + +import { TypedDocumentNode as DocumentNode } from '@graphql-typed-document-node/core'; +export type QuestionFragment = { __typename?: 'Question', id: string, url: string, title: string, description: string, timestamp: number, visualization?: string | null, options: Array<{ __typename?: 'ProbabilityOption', name?: string | null, probability?: number | null }>, platform: { __typename?: 'Platform', id: string, label: string }, qualityIndicators: { __typename?: 'QualityIndicators', stars: number, numForecasts?: number | null, numForecasters?: number | null, volume?: number | null, spread?: number | null, sharesVolume?: number | null, openInterest?: number | null, liquidity?: number | null, tradeVolume?: number | null } }; + +export type QuestionWithHistoryFragment = { __typename?: 'Question', id: string, url: string, title: string, description: string, timestamp: number, visualization?: string | null, history: Array<{ __typename?: 'History', timestamp: number, options: Array<{ __typename?: 'ProbabilityOption', name?: string | null, probability?: number | null }> }>, options: Array<{ __typename?: 'ProbabilityOption', name?: string | null, probability?: number | null }>, platform: { __typename?: 'Platform', id: string, label: string }, qualityIndicators: { __typename?: 'QualityIndicators', stars: number, numForecasts?: number | null, numForecasters?: number | null, volume?: number | null, spread?: number | null, sharesVolume?: number | null, openInterest?: number | null, liquidity?: number | null, tradeVolume?: number | null } }; + +export const QuestionFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"Question"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Question"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"url"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"timestamp"}},{"kind":"Field","name":{"kind":"Name","value":"options"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"probability"}}]}},{"kind":"Field","name":{"kind":"Name","value":"platform"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"label"}}]}},{"kind":"Field","name":{"kind":"Name","value":"qualityIndicators"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"stars"}},{"kind":"Field","name":{"kind":"Name","value":"numForecasts"}},{"kind":"Field","name":{"kind":"Name","value":"numForecasters"}},{"kind":"Field","name":{"kind":"Name","value":"volume"}},{"kind":"Field","name":{"kind":"Name","value":"spread"}},{"kind":"Field","name":{"kind":"Name","value":"sharesVolume"}},{"kind":"Field","name":{"kind":"Name","value":"openInterest"}},{"kind":"Field","name":{"kind":"Name","value":"liquidity"}},{"kind":"Field","name":{"kind":"Name","value":"tradeVolume"}}]}},{"kind":"Field","name":{"kind":"Name","value":"visualization"}}]}}]} as unknown as DocumentNode; +export const QuestionWithHistoryFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"QuestionWithHistory"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Question"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"Question"}},{"kind":"Field","name":{"kind":"Name","value":"history"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"timestamp"}},{"kind":"Field","name":{"kind":"Name","value":"options"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"probability"}}]}}]}}]}},...QuestionFragmentDoc.definitions]} as unknown as DocumentNode; \ No newline at end of file diff --git a/src/web/fragments.graphql b/src/web/fragments.graphql new file mode 100644 index 0000000..859ab71 --- /dev/null +++ b/src/web/fragments.graphql @@ -0,0 +1,38 @@ +fragment Question on Question { + id + url + title + description + timestamp + options { + name + probability + } + platform { + id + label + } + qualityIndicators { + stars + numForecasts + numForecasters + volume + spread + sharesVolume + openInterest + liquidity + tradeVolume + } + visualization +} + +fragment QuestionWithHistory on Question { + ...Question + history { + timestamp + options { + name + probability + } + } +} diff --git a/src/web/questions/components/HistoryChart.tsx b/src/web/questions/components/HistoryChart.tsx new file mode 100644 index 0000000..fa33703 --- /dev/null +++ b/src/web/questions/components/HistoryChart.tsx @@ -0,0 +1,207 @@ +import React from "react"; +import { + VictoryAxis, VictoryChart, VictoryGroup, VictoryLabel, VictoryLegend, VictoryScatter, + VictoryTheme, VictoryTooltip, VictoryVoronoiContainer +} from "victory"; + +import { QuestionWithHistoryFragment } from "../../fragments.generated"; + +interface Props { + question: QuestionWithHistoryFragment; +} + +const buildDataset = (n, fn) => { + return Array.from(Array(n).keys()).map((x) => ({ + date: x, + probability: fn(x), + })); +}; + +let getDate0 = (x) => { + // for fake data + let date = new Date(x); + return date.toISOString().slice(5, 10).replaceAll("-", "/"); +}; + +let timestampToString = (x) => { + // for real timestamps + console.log(x); + let date = new Date(Date.parse(x)); + let dayOfMonth = date.getDate(); + let month = date.getMonth() + 1; + let year = date.getFullYear(); + let dateString = `${("0" + dayOfMonth).slice(-2)}/${("0" + month).slice( + -2 + )}/${year.toString().slice(-2)}`; + console.log(dateString); + return dateString; +}; + +let dataAsXy = (data) => + data.map((datum) => ({ + x: timestampToString(datum.date), //getDate(datum.date * (1000 * 60 * 60 * 24)), + y: datum.probability, + })); + +const colors = ["dodgerblue", "crimson", "seagreen", "darkviolet", "turquoise"]; +const getVictoryGroup = (data, i) => { + return ( + + (active ? 3.75 : 3)} + //labels={() => null} + //labelComponent={} + /> + + {/* Doesn't work well with tooltips + null} + //labelComponent={} + /> + */} + + ); +}; + +export const HistoryChart: React.FC = ({ question }) => { + let height = 400; + let width = 500; + let padding = { top: 20, bottom: 50, left: 50, right: 100 }; + // let dataSetsNames = ["Yes", "No", "Maybe", "Perhaps", "Possibly"]; + let dataSetsNames = []; + question.history.forEach((item) => { + let optionNames = item.options.map((option) => option.name); + dataSetsNames.push(...optionNames); + }); + dataSetsNames = [...new Set(dataSetsNames)].slice(0, 5); // take the first 5 + let dataSets = []; + dataSetsNames.forEach((name) => { + let newDataset = []; + question.history.forEach((item) => { + let relevantItemsArray = item.options.filter((x) => x.name == name); + let date = new Date(item.timestamp * 1000); + if (relevantItemsArray.length == 1) { + let relevantItem = relevantItemsArray[0]; + // if (relevantItem.type == "PROBABILITY") { + let result = { + date, + probability: relevantItem.probability, + }; + newDataset.push(result); + // } + } + }); + dataSets.push(newDataset); + }); + + let dataSetsLength = dataSets.length; + + return ( +
+ +

+ {question.title} +

+
+ `${datum.x}: ${Math.round(datum.y * 100)}%`} + labelComponent={ + + } + voronoiBlacklist={ + ["line0", "line1", "line2", "line3", "line4"] + + //Array.from(Array(5).keys()).map((x, i) => `line${i}`) + // see: https://github.com/FormidableLabs/victory/issues/545 + } + /> + } + domain={{ + y: [0, 1], + }} + > + ({ + name: dataSetsNames[i], + symbol: { fill: colors[i] }, + })) + /*[ + { name: "One", symbol: { fill: "tomato", type: "star" } }, + { name: "Two", symbol: { fill: "orange" } }, + { name: "Three", symbol: { fill: "gold" } }, + ]*/ + } + /> + + {dataSets.slice(0, 5).map((dataset, i) => getVictoryGroup(dataset, i))} + datum.x)} + // tickFormat={dataAsXy.map((datum) => datum.x)} + tickCount={7} + style={{ + grid: { stroke: null, strokeWidth: 0.5 }, + }} + //axisLabelComponent={ + // + //} + // label="Date (dd/mm/yy)" + tickLabelComponent={ + + } + /> + `${x * 100}%`} + style={{ + grid: { stroke: "#D3D3D3", strokeWidth: 0.5 }, + }} + tickLabelComponent={ + + } + /> + +
+ ); +}; diff --git a/src/web/questions/components/QuestionOptions.tsx b/src/web/questions/components/QuestionOptions.tsx index e200018..d984747 100644 --- a/src/web/questions/components/QuestionOptions.tsx +++ b/src/web/questions/components/QuestionOptions.tsx @@ -1,4 +1,4 @@ -import { QuestionFragment } from "../../search/queries.generated"; +import { QuestionFragment } from "../../fragments.generated"; import { formatProbability } from "../utils"; type Option = QuestionFragment["options"][0]; diff --git a/src/web/questions/pages/QuestionPage.tsx b/src/web/questions/pages/QuestionPage.tsx index c36ca11..4b2b926 100644 --- a/src/web/questions/pages/QuestionPage.tsx +++ b/src/web/questions/pages/QuestionPage.tsx @@ -5,10 +5,11 @@ import { Query } from "../../common/Query"; import { Card } from "../../display/Card"; import { QuestionFooter } from "../../display/DisplayQuestion/QuestionFooter"; import { Layout } from "../../display/Layout"; -import { QuestionFragment } from "../../search/queries.generated"; +import { QuestionWithHistoryFragment } from "../../fragments.generated"; import { ssrUrql } from "../../urql"; +import { HistoryChart } from "../components/HistoryChart"; import { QuestionOptions } from "../components/QuestionOptions"; -import { QuestionByIdDocument } from "../queries.generated"; +import { QuestionPageDocument } from "../queries.generated"; interface Props { id: string; @@ -21,7 +22,7 @@ export const getServerSideProps: GetServerSideProps = async ( const id = context.query.id as string; const question = - (await client.query(QuestionByIdDocument, { id }).toPromise()).data + (await client.query(QuestionPageDocument, { id }).toPromise()).data ?.result || null; if (!question) { @@ -36,9 +37,9 @@ export const getServerSideProps: GetServerSideProps = async ( }; }; -const QuestionCardContents: React.FC<{ question: QuestionFragment }> = ({ - question, -}) => ( +const QuestionCardContents: React.FC<{ + question: QuestionWithHistoryFragment; +}> = ({ question }) => ( ); @@ -63,7 +66,7 @@ const QuestionPage: NextPage = ({ id }) => {
- + {({ data }) => } diff --git a/src/web/questions/queries.generated.tsx b/src/web/questions/queries.generated.tsx index 04ee65a..f03c9b4 100644 --- a/src/web/questions/queries.generated.tsx +++ b/src/web/questions/queries.generated.tsx @@ -1,13 +1,13 @@ import * as Types from '../../graphql/types.generated'; import { TypedDocumentNode as DocumentNode } from '@graphql-typed-document-node/core'; -import { QuestionFragmentDoc } from '../search/queries.generated'; -export type QuestionByIdQueryVariables = Types.Exact<{ +import { QuestionWithHistoryFragmentDoc } from '../fragments.generated'; +export type QuestionPageQueryVariables = Types.Exact<{ id: Types.Scalars['ID']; }>; -export type QuestionByIdQuery = { __typename?: 'Query', result: { __typename?: 'Question', id: string, url: string, title: string, description: string, timestamp: number, visualization?: string | null, options: Array<{ __typename?: 'ProbabilityOption', name?: string | null, probability?: number | null }>, platform: { __typename?: 'Platform', id: string, label: string }, qualityIndicators: { __typename?: 'QualityIndicators', stars: number, numForecasts?: number | null, numForecasters?: number | null, volume?: number | null, spread?: number | null, sharesVolume?: number | null, openInterest?: number | null, liquidity?: number | null, tradeVolume?: number | null } } }; +export type QuestionPageQuery = { __typename?: 'Query', result: { __typename?: 'Question', id: string, url: string, title: string, description: string, timestamp: number, visualization?: string | null, history: Array<{ __typename?: 'History', timestamp: number, options: Array<{ __typename?: 'ProbabilityOption', name?: string | null, probability?: number | null }> }>, options: Array<{ __typename?: 'ProbabilityOption', name?: string | null, probability?: number | null }>, platform: { __typename?: 'Platform', id: string, label: string }, qualityIndicators: { __typename?: 'QualityIndicators', stars: number, numForecasts?: number | null, numForecasters?: number | null, volume?: number | null, spread?: number | null, sharesVolume?: number | null, openInterest?: number | null, liquidity?: number | null, tradeVolume?: number | null } } }; -export const QuestionByIdDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"QuestionById"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"id"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","alias":{"kind":"Name","value":"result"},"name":{"kind":"Name","value":"question"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"id"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"Question"}}]}}]}},...QuestionFragmentDoc.definitions]} as unknown as DocumentNode; \ No newline at end of file +export const QuestionPageDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"QuestionPage"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"id"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","alias":{"kind":"Name","value":"result"},"name":{"kind":"Name","value":"question"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"id"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"QuestionWithHistory"}}]}}]}},...QuestionWithHistoryFragmentDoc.definitions]} as unknown as DocumentNode; \ No newline at end of file diff --git a/src/web/questions/queries.graphql b/src/web/questions/queries.graphql index a06beb4..7bc64c1 100644 --- a/src/web/questions/queries.graphql +++ b/src/web/questions/queries.graphql @@ -1,5 +1,5 @@ -query QuestionById($id: ID!) { +query QuestionPage($id: ID!) { result: question(id: $id) { - ...Question + ...QuestionWithHistory } } diff --git a/src/web/search/CommonDisplay.tsx b/src/web/search/CommonDisplay.tsx index bf60989..c3e2e1d 100644 --- a/src/web/search/CommonDisplay.tsx +++ b/src/web/search/CommonDisplay.tsx @@ -6,9 +6,10 @@ import { ButtonsForStars } from "../display/ButtonsForStars"; import { MultiSelectPlatform } from "../display/MultiSelectPlatform"; import { QueryForm } from "../display/QueryForm"; import { SliderElement } from "../display/SliderElement"; +import { QuestionFragment } from "../fragments.generated"; import { useIsFirstRender, useNoInitialEffect } from "../hooks"; import { Props as AnySearchPageProps, QueryParameters } from "./anySearchPage"; -import { QuestionFragment, SearchDocument } from "./queries.generated"; +import { SearchDocument } from "./queries.generated"; interface Props extends AnySearchPageProps { hasSearchbar: boolean; diff --git a/src/web/search/anySearchPage.tsx b/src/web/search/anySearchPage.tsx index 03f61cd..5402664 100644 --- a/src/web/search/anySearchPage.tsx +++ b/src/web/search/anySearchPage.tsx @@ -1,8 +1,9 @@ import { GetServerSideProps } from "next"; import { getPlatformsConfig, PlatformConfig, platforms } from "../../backend/platforms"; +import { QuestionFragment } from "../fragments.generated"; import { ssrUrql } from "../urql"; -import { FrontpageDocument, QuestionFragment, SearchDocument } from "./queries.generated"; +import { FrontpageDocument, SearchDocument } from "./queries.generated"; /* Common code for / and /capture */ diff --git a/src/web/search/queries.generated.tsx b/src/web/search/queries.generated.tsx index d765346..60998f2 100644 --- a/src/web/search/queries.generated.tsx +++ b/src/web/search/queries.generated.tsx @@ -1,8 +1,7 @@ import * as Types from '../../graphql/types.generated'; import { TypedDocumentNode as DocumentNode } from '@graphql-typed-document-node/core'; -export type QuestionFragment = { __typename?: 'Question', id: string, url: string, title: string, description: string, timestamp: number, visualization?: string | null, options: Array<{ __typename?: 'ProbabilityOption', name?: string | null, probability?: number | null }>, platform: { __typename?: 'Platform', id: string, label: string }, qualityIndicators: { __typename?: 'QualityIndicators', stars: number, numForecasts?: number | null, numForecasters?: number | null, volume?: number | null, spread?: number | null, sharesVolume?: number | null, openInterest?: number | null, liquidity?: number | null, tradeVolume?: number | null } }; - +import { QuestionFragmentDoc } from '../fragments.generated'; export type FrontpageQueryVariables = Types.Exact<{ [key: string]: never; }>; @@ -15,6 +14,6 @@ export type SearchQueryVariables = Types.Exact<{ export type SearchQuery = { __typename?: 'Query', result: Array<{ __typename?: 'Question', id: string, url: string, title: string, description: string, timestamp: number, visualization?: string | null, options: Array<{ __typename?: 'ProbabilityOption', name?: string | null, probability?: number | null }>, platform: { __typename?: 'Platform', id: string, label: string }, qualityIndicators: { __typename?: 'QualityIndicators', stars: number, numForecasts?: number | null, numForecasters?: number | null, volume?: number | null, spread?: number | null, sharesVolume?: number | null, openInterest?: number | null, liquidity?: number | null, tradeVolume?: number | null } }> }; -export const QuestionFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"Question"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Question"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"url"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"timestamp"}},{"kind":"Field","name":{"kind":"Name","value":"options"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"probability"}}]}},{"kind":"Field","name":{"kind":"Name","value":"platform"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"label"}}]}},{"kind":"Field","name":{"kind":"Name","value":"qualityIndicators"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"stars"}},{"kind":"Field","name":{"kind":"Name","value":"numForecasts"}},{"kind":"Field","name":{"kind":"Name","value":"numForecasters"}},{"kind":"Field","name":{"kind":"Name","value":"volume"}},{"kind":"Field","name":{"kind":"Name","value":"spread"}},{"kind":"Field","name":{"kind":"Name","value":"sharesVolume"}},{"kind":"Field","name":{"kind":"Name","value":"openInterest"}},{"kind":"Field","name":{"kind":"Name","value":"liquidity"}},{"kind":"Field","name":{"kind":"Name","value":"tradeVolume"}}]}},{"kind":"Field","name":{"kind":"Name","value":"visualization"}}]}}]} as unknown as DocumentNode; + export const FrontpageDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"Frontpage"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","alias":{"kind":"Name","value":"result"},"name":{"kind":"Name","value":"frontpage"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"Question"}}]}}]}},...QuestionFragmentDoc.definitions]} as unknown as DocumentNode; export const SearchDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"Search"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"SearchInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","alias":{"kind":"Name","value":"result"},"name":{"kind":"Name","value":"searchQuestions"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"Question"}}]}}]}},...QuestionFragmentDoc.definitions]} as unknown as DocumentNode; \ No newline at end of file diff --git a/src/web/search/queries.graphql b/src/web/search/queries.graphql index 38895a3..8b62a0e 100644 --- a/src/web/search/queries.graphql +++ b/src/web/search/queries.graphql @@ -1,31 +1,3 @@ -fragment Question on Question { - id - url - title - description - timestamp - options { - name - probability - } - platform { - id - label - } - qualityIndicators { - stars - numForecasts - numForecasters - volume - spread - sharesVolume - openInterest - liquidity - tradeVolume - } - visualization -} - query Frontpage { result: frontpage { ...Question diff --git a/src/web/utils.ts b/src/web/utils.ts index a588542..9196286 100644 --- a/src/web/utils.ts +++ b/src/web/utils.ts @@ -10,3 +10,21 @@ export const getBasePath = () => { return "http://localhost:3000"; }; + +export const cleanText = (text: string): string => { + // Note: should no longer be necessary + let textString = !!text ? text : ""; + textString = textString + .replaceAll("] (", "](") + .replaceAll(") )", "))") + .replaceAll("( [", "([") + .replaceAll(") ,", "),") + .replaceAll("==", "") // Denotes a title in markdown + .replaceAll("Background\n", "") + .replaceAll("Context\n", "") + .replaceAll("--- \n", "- ") + .replaceAll(/\[(.*?)\]\(.*?\)/g, "$1"); + textString = textString.slice(0, 1) == "=" ? textString.slice(1) : textString; + //console.log(textString) + return textString; +}; From 340d99b4852cd48c15a6b3c375358fcbccb157df Mon Sep 17 00:00:00 2001 From: NunoSempere Date: Thu, 28 Apr 2022 15:22:22 -0400 Subject: [PATCH 3/4] tweak: Charts Add line, reorganize display Also fix nasty bug where probabilities are inverted in frontpage --- .eslintrc | 6 + src/web/questions/components/HistoryChart.tsx | 249 ++++++++++-------- .../questions/components/QuestionOptions.tsx | 11 +- src/web/questions/pages/QuestionPage.tsx | 24 +- 4 files changed, 174 insertions(+), 116 deletions(-) create mode 100644 .eslintrc diff --git a/.eslintrc b/.eslintrc new file mode 100644 index 0000000..8def063 --- /dev/null +++ b/.eslintrc @@ -0,0 +1,6 @@ +{ + "extends": ["next", "prettier"], + "rules": { + "next/no-document-import-in-page": "off" + } +} diff --git a/src/web/questions/components/HistoryChart.tsx b/src/web/questions/components/HistoryChart.tsx index fa33703..cd6a404 100644 --- a/src/web/questions/components/HistoryChart.tsx +++ b/src/web/questions/components/HistoryChart.tsx @@ -1,7 +1,15 @@ import React from "react"; import { - VictoryAxis, VictoryChart, VictoryGroup, VictoryLabel, VictoryLegend, VictoryScatter, - VictoryTheme, VictoryTooltip, VictoryVoronoiContainer + VictoryAxis, + VictoryChart, + VictoryGroup, + VictoryLabel, + VictoryLegend, + VictoryScatter, + VictoryLine, + VictoryTheme, + VictoryTooltip, + VictoryVoronoiContainer, } from "victory"; import { QuestionWithHistoryFragment } from "../../fragments.generated"; @@ -48,26 +56,25 @@ const getVictoryGroup = (data, i) => { return ( (active ? 3.75 : 3)} //labels={() => null} //labelComponent={} /> - {/* Doesn't work well with tooltips - null} - //labelComponent={} - /> - */} + null} + //labelComponent={} + /> ); }; export const HistoryChart: React.FC = ({ question }) => { - let height = 400; + let height = 300; let width = 500; let padding = { top: 20, bottom: 50, left: 50, right: 100 }; // let dataSetsNames = ["Yes", "No", "Maybe", "Perhaps", "Possibly"]; @@ -78,6 +85,7 @@ export const HistoryChart: React.FC = ({ question }) => { }); dataSetsNames = [...new Set(dataSetsNames)].slice(0, 5); // take the first 5 let dataSets = []; + /* dataSetsNames.forEach((name) => { let newDataset = []; question.history.forEach((item) => { @@ -96,112 +104,145 @@ export const HistoryChart: React.FC = ({ question }) => { }); dataSets.push(newDataset); }); + */ + + dataSetsNames.forEach((name) => { + let newDataset = []; + let previousDate = -Infinity; + for (let item of question.history) { + let relevantItemsArray = item.options.filter((x) => x.name == name); + let date = new Date(item.timestamp * 1000); + if ( + relevantItemsArray.length == 1 && + item.timestamp - previousDate > 12 * 60 * 60 + ) { + let relevantItem = relevantItemsArray[0]; + // if (relevantItem.type == "PROBABILITY") { + let result = { + date, + probability: relevantItem.probability, + }; + newDataset.push(result); + // } + previousDate = item.timestamp; + } + } + dataSets.push(newDataset); + }); let dataSetsLength = dataSets.length; return ( -
- +
+
+ + {/*

{question.title}

-
- `${datum.x}: ${Math.round(datum.y * 100)}%`} - labelComponent={ - - } - voronoiBlacklist={ - ["line0", "line1", "line2", "line3", "line4"] + */} + + + `${datum.x}: ${Math.round(datum.y * 100)}%` + } + labelComponent={ + + } + voronoiBlacklist={ + ["line-0", "line-1", "line-2", "line-3", "line-4"] - //Array.from(Array(5).keys()).map((x, i) => `line${i}`) - // see: https://github.com/FormidableLabs/victory/issues/545 - } - /> - } - domain={{ - y: [0, 1], - }} - > - ({ - name: dataSetsNames[i], - symbol: { fill: colors[i] }, - })) - /*[ + //Array.from(Array(5).keys()).map((x, i) => `line${i}`) + // see: https://github.com/FormidableLabs/victory/issues/545 + } + /> + } + domain={{ + y: [0, 1], + }} + > + ({ + name: dataSetsNames[i], + symbol: { fill: colors[i] }, + })) + /*[ { name: "One", symbol: { fill: "tomato", type: "star" } }, { name: "Two", symbol: { fill: "orange" } }, { name: "Three", symbol: { fill: "gold" } }, ]*/ - } - /> + } + /> - {dataSets.slice(0, 5).map((dataset, i) => getVictoryGroup(dataset, i))} - datum.x)} - // tickFormat={dataAsXy.map((datum) => datum.x)} - tickCount={7} - style={{ - grid: { stroke: null, strokeWidth: 0.5 }, - }} - //axisLabelComponent={ - // - //} - // label="Date (dd/mm/yy)" - tickLabelComponent={ - - } - /> - `${x * 100}%`} - style={{ - grid: { stroke: "#D3D3D3", strokeWidth: 0.5 }, - }} - tickLabelComponent={ - - } - /> - + {dataSets + .slice(0, 5) + .map((dataset, i) => getVictoryGroup(dataset, i))} + datum.x)} + // tickFormat={dataAsXy.map((datum) => datum.x)} + tickCount={7} + style={{ + grid: { stroke: null, strokeWidth: 0.5 }, + }} + //axisLabelComponent={ + // + //} + // label="Date (dd/mm/yy)" + tickLabelComponent={ + + } + /> + `${x * 100}%`} + style={{ + grid: { stroke: "#D3D3D3", strokeWidth: 0.5 }, + }} + tickLabelComponent={ + + } + /> + +
); }; diff --git a/src/web/questions/components/QuestionOptions.tsx b/src/web/questions/components/QuestionOptions.tsx index d984747..4a5ff9b 100644 --- a/src/web/questions/components/QuestionOptions.tsx +++ b/src/web/questions/components/QuestionOptions.tsx @@ -112,7 +112,8 @@ export const QuestionOptions: React.FC<{ options: Option[] }> = ({ const isBinary = options.length === 2 && (options[0].name === "Yes" || options[0].name === "No"); - + const getYesOption = (options) => + options.find((option) => option.name == "Yes"); const optionsSorted = options.sort((a, b) => b.probability - a.probability); const optionsMax5 = !!optionsSorted.slice ? optionsSorted.slice(0, 5) : []; // display max 5 options. @@ -121,17 +122,17 @@ export const QuestionOptions: React.FC<{ options: Option[] }> = ({
- {formatProbability(options[0].probability)} + {formatProbability(getYesOption(options).probability)} - {primaryEstimateAsText(options[0].probability)} + {primaryEstimateAsText(getYesOption(options).probability)}
); diff --git a/src/web/questions/pages/QuestionPage.tsx b/src/web/questions/pages/QuestionPage.tsx index 4b2b926..739c251 100644 --- a/src/web/questions/pages/QuestionPage.tsx +++ b/src/web/questions/pages/QuestionPage.tsx @@ -40,8 +40,8 @@ export const getServerSideProps: GetServerSideProps = async ( const QuestionCardContents: React.FC<{ question: QuestionWithHistoryFragment; }> = ({ question }) => ( -
-

+ ); const QuestionPage: NextPage = ({ id }) => { return ( -
+
{({ data }) => } From bbe0fd59828ea8aa0e2409eb2b863ac13c8112a2 Mon Sep 17 00:00:00 2001 From: NunoSempere Date: Thu, 28 Apr 2022 17:45:39 -0400 Subject: [PATCH 4/4] fix: further chart tweaks --- src/web/questions/components/HistoryChart.tsx | 119 ++++----- .../components/QuestionIndicators.tsx | 227 ++++++++++++++++++ src/web/questions/pages/QuestionPage.tsx | 2 +- 3 files changed, 290 insertions(+), 58 deletions(-) create mode 100644 src/web/questions/components/QuestionIndicators.tsx diff --git a/src/web/questions/components/HistoryChart.tsx b/src/web/questions/components/HistoryChart.tsx index cd6a404..ad04e10 100644 --- a/src/web/questions/components/HistoryChart.tsx +++ b/src/web/questions/components/HistoryChart.tsx @@ -31,6 +31,25 @@ let getDate0 = (x) => { return date.toISOString().slice(5, 10).replaceAll("-", "/"); }; +let formatOptionName = (name) => { + return name.length > 10 ? name.slice(0, 8) + "..." : name; +}; + +let getLength = (str) => { + let capitalLetterLengthMultiplier = 1.25; + let smallLetterMultiplier = 0.8; + let numUpper = (str.match(/[A-Z]/g) || []).length; + let numSmallLetters = (str.match(/[fijlrt]/g) || []).length; + let numSpaces = (str.match(/[\s]/g) || []).length; + let length = + str.length + + -numUpper - + numSmallLetters + + numUpper * capitalLetterLengthMultiplier + + (numSmallLetters + numSpaces) * smallLetterMultiplier; + return length; +}; + let timestampToString = (x) => { // for real timestamps console.log(x); @@ -49,6 +68,7 @@ let dataAsXy = (data) => data.map((datum) => ({ x: timestampToString(datum.date), //getDate(datum.date * (1000 * 60 * 60 * 24)), y: datum.probability, + name: datum.name, })); const colors = ["dodgerblue", "crimson", "seagreen", "darkviolet", "turquoise"]; @@ -74,10 +94,6 @@ const getVictoryGroup = (data, i) => { }; export const HistoryChart: React.FC = ({ question }) => { - let height = 300; - let width = 500; - let padding = { top: 20, bottom: 50, left: 50, right: 100 }; - // let dataSetsNames = ["Yes", "No", "Maybe", "Perhaps", "Possibly"]; let dataSetsNames = []; question.history.forEach((item) => { let optionNames = item.options.map((option) => option.name); @@ -85,28 +101,10 @@ export const HistoryChart: React.FC = ({ question }) => { }); dataSetsNames = [...new Set(dataSetsNames)].slice(0, 5); // take the first 5 let dataSets = []; - /* - dataSetsNames.forEach((name) => { - let newDataset = []; - question.history.forEach((item) => { - let relevantItemsArray = item.options.filter((x) => x.name == name); - let date = new Date(item.timestamp * 1000); - if (relevantItemsArray.length == 1) { - let relevantItem = relevantItemsArray[0]; - // if (relevantItem.type == "PROBABILITY") { - let result = { - date, - probability: relevantItem.probability, - }; - newDataset.push(result); - // } - } - }); - dataSets.push(newDataset); - }); - */ + let maxProbability = 0; + let longestNameLength = 0; - dataSetsNames.forEach((name) => { + for (let name of dataSetsNames) { let newDataset = []; let previousDate = -Infinity; for (let item of question.history) { @@ -121,31 +119,46 @@ export const HistoryChart: React.FC = ({ question }) => { let result = { date, probability: relevantItem.probability, + name: relevantItem.name, }; + maxProbability = + relevantItem.probability > maxProbability + ? relevantItem.probability + : maxProbability; + let length = getLength(relevantItem.name); + longestNameLength = + length > longestNameLength ? length : longestNameLength; newDataset.push(result); // } previousDate = item.timestamp; } } dataSets.push(newDataset); - }); + } + let letterLength = 7; + let labelLegendStart = 45; + let domainMax = + maxProbability < 0.5 ? Math.round(10 * (maxProbability + 0.05)) / 10 : 1; let dataSetsLength = dataSets.length; + let goldenRatio = (1 + Math.sqrt(5)) / 2; + let width = 750; + let height = width / goldenRatio; + let padding = { + top: 20, + bottom: 50, + left: 0, + right: labelLegendStart + letterLength * longestNameLength, + }; return (
-
+
- {/* -

- {question.title} -

- */} -
+ > = ({ question }) => { width={width} containerComponent={ - `${datum.x}: ${Math.round(datum.y * 100)}%` - } + labels={({ datum }) => `Not shown`} labelComponent={ + `${datum.name}: ${Math.round(datum.y * 100)}%` + } style={{ - fontSize: 10, + fontSize: 15, fill: "black", strokeWidth: 0.05, }} @@ -170,40 +184,31 @@ export const HistoryChart: React.FC = ({ question }) => { stroke: "black", fill: "white", }} - flyoutWidth={80} cornerRadius={0} flyoutPadding={7} /> } voronoiBlacklist={ ["line-0", "line-1", "line-2", "line-3", "line-4"] - //Array.from(Array(5).keys()).map((x, i) => `line${i}`) // see: https://github.com/FormidableLabs/victory/issues/545 } /> } domain={{ - y: [0, 1], + y: [0, domainMax], }} > ({ - name: dataSetsNames[i], - symbol: { fill: colors[i] }, - })) - /*[ - { name: "One", symbol: { fill: "tomato", type: "star" } }, - { name: "Two", symbol: { fill: "orange" } }, - { name: "Three", symbol: { fill: "gold" } }, - ]*/ - } + style={{ border: { stroke: "black" }, labels: { fontSize: 15 } }} + data={Array.from(Array(dataSetsLength).keys()).map((i) => ({ + name: dataSetsNames[i], + symbol: { fill: colors[i] }, + }))} /> {dataSets @@ -224,9 +229,9 @@ export const HistoryChart: React.FC = ({ question }) => { // label="Date (dd/mm/yy)" tickLabelComponent={ } /> @@ -238,7 +243,7 @@ export const HistoryChart: React.FC = ({ question }) => { grid: { stroke: "#D3D3D3", strokeWidth: 0.5 }, }} tickLabelComponent={ - + } /> diff --git a/src/web/questions/components/QuestionIndicators.tsx b/src/web/questions/components/QuestionIndicators.tsx new file mode 100644 index 0000000..ecd37da --- /dev/null +++ b/src/web/questions/components/QuestionIndicators.tsx @@ -0,0 +1,227 @@ +import { QuestionFragment } from "../../fragments.generated"; + +type QualityIndicator = QuestionFragment["qualityIndicators"]; +type IndicatorName = keyof QualityIndicator; + +// this duplication can probably be simplified with typescript magic, but this is good enough for now +type UsedIndicatorName = + | "volume" + | "numForecasters" + | "spread" + | "sharesVolume" + | "liquidity" + | "tradeVolume" + | "openInterest"; + +const qualityIndicatorLabels: { [k in UsedIndicatorName]: string } = { + // numForecasts: null, + // stars: null, + // yesBid: "Yes bid", + // yesAsk: "Yes ask", + volume: "Volume", + numForecasters: "Forecasters", + spread: "Spread", + sharesVolume: "Shares vol.", + liquidity: "Liquidity", + tradeVolume: "Volume", + openInterest: "Interest", +}; + +const formatNumber = (num) => { + if (Number(num) < 1000) { + return Number(num).toFixed(0); + } else if (num < 10000) { + return (Number(num) / 1000).toFixed(1) + "k"; + } else { + return (Number(num) / 1000).toFixed(0) + "k"; + } +}; + +/* Display functions*/ + +const getPercentageSymbolIfNeeded = ({ + indicator, + platform, +}: { + indicator: UsedIndicatorName; + platform: string; +}) => { + let indicatorsWhichNeedPercentageSymbol: IndicatorName[] = ["spread"]; + if (indicatorsWhichNeedPercentageSymbol.includes(indicator)) { + return "%"; + } else { + return ""; + } +}; + +const getCurrencySymbolIfNeeded = ({ + indicator, + platform, +}: { + indicator: UsedIndicatorName; + platform: string; +}) => { + const indicatorsWhichNeedCurrencySymbol: IndicatorName[] = [ + "volume", + "tradeVolume", + "openInterest", + "liquidity", + ]; + let dollarPlatforms = ["predictit", "kalshi", "polymarket"]; + if (indicatorsWhichNeedCurrencySymbol.includes(indicator)) { + if (dollarPlatforms.includes(platform)) { + return "$"; + } else { + return "£"; + } + } else { + return ""; + } +}; + +const FirstQualityIndicator: React.FC<{ + question: QuestionFragment; +}> = ({ question }) => { + if (question.qualityIndicators.numForecasts) { + return ( +
+ Forecasts:  + + {Number(question.qualityIndicators.numForecasts).toFixed(0)} + +
+ ); + } else { + return null; + } +}; + +const QualityIndicatorsList: React.FC<{ + question: QuestionFragment; +}> = ({ question }) => { + return ( +
+ + {Object.entries(question.qualityIndicators).map((entry, i) => { + const indicatorLabel = qualityIndicatorLabels[entry[0]]; + if (!indicatorLabel || entry[1] === null) return; + const indicator = entry[0] as UsedIndicatorName; // guaranteed by the previous line + const value = entry[1]; + + return ( +
+ {indicatorLabel}:  + + {`${getCurrencySymbolIfNeeded({ + indicator, + platform: question.platform.id, + })}${formatNumber(value)}${getPercentageSymbolIfNeeded({ + indicator, + platform: question.platform.id, + })}`} + +
+ ); + })} +
+ ); +}; + +// Database-like functions +export function getstars(numstars: number) { + let stars = "★★☆☆☆"; + switch (numstars) { + case 0: + stars = "☆☆☆☆☆"; + break; + case 1: + stars = "★☆☆☆☆"; + break; + case 2: + stars = "★★☆☆☆"; + break; + case 3: + stars = "★★★☆☆"; + break; + case 4: + stars = "★★★★☆"; + break; + case 5: + stars = "★★★★★"; + break; + default: + stars = "★★☆☆☆"; + } + return stars; +} + +function getStarsColor(numstars: number) { + let color = "text-yellow-400"; + switch (numstars) { + case 0: + color = "text-red-400"; + break; + case 1: + color = "text-red-400"; + break; + case 2: + color = "text-orange-400"; + break; + case 3: + color = "text-yellow-400"; + break; + case 4: + color = "text-green-400"; + break; + case 5: + color = "text-blue-400"; + break; + default: + color = "text-yellow-400"; + } + return color; +} + +interface Props { + question: QuestionFragment; + expandFooterToFullWidth: boolean; +} + +export const QuestionFooter: React.FC = ({ + question, + expandFooterToFullWidth, +}) => { + return ( +
+
+ {getstars(question.qualityIndicators.stars)} +
+
+ {question.platform.label + .replace("Good Judgment Open", "GJOpen") + .replace(/ /g, "\u00a0")} +
+
+ +
+
+ ); +}; diff --git a/src/web/questions/pages/QuestionPage.tsx b/src/web/questions/pages/QuestionPage.tsx index 739c251..50eace2 100644 --- a/src/web/questions/pages/QuestionPage.tsx +++ b/src/web/questions/pages/QuestionPage.tsx @@ -61,7 +61,7 @@ const QuestionCardContents: React.FC<{ */} -

+

{"Question description"}