diff --git a/.storybook/preview-head.html b/.storybook/preview-head.html index 7585a60e3..51c6b0baf 100644 --- a/.storybook/preview-head.html +++ b/.storybook/preview-head.html @@ -5,4 +5,8 @@ font-weight: 500; font-style: normal; } + + #storybook-root { + height: 100%; + } diff --git a/package-lock.json b/package-lock.json index 19a2cb70c..9fe7e28ae 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,12 +11,15 @@ "dependencies": { "@floating-ui/react": "^0.21.0", "@tanstack/react-table": "^8.7.8", + "allotment": "^1.19.0", "copy-to-clipboard": "^3.3.3", "date-fns": "^2.29.3", "react": "^18.2.0", "react-cool-dimensions": "^3.0.1", "react-dom": "^18.2.0", + "react-scrollbar-size": "^5.0.0", "react-transition-group": "^4.4.5", + "recharts": "^2.6.2", "styled-components": "^5.3.6" }, "devDependencies": { @@ -2832,8 +2835,7 @@ "node_modules/@juggle/resize-observer": { "version": "3.4.0", "resolved": "https://registry.npmjs.org/@juggle/resize-observer/-/resize-observer-3.4.0.tgz", - "integrity": "sha512-dfLbk+PwWvFzSxwk3n5ySL0hfBog779o8h68wK/7/APo/7cgyWp5jcXockbxdk5kFRkbeXWm4Fbi9FrdN381sA==", - "dev": true + "integrity": "sha512-dfLbk+PwWvFzSxwk3n5ySL0hfBog779o8h68wK/7/APo/7cgyWp5jcXockbxdk5kFRkbeXWm4Fbi9FrdN381sA==" }, "node_modules/@mdx-js/react": { "version": "2.3.0", @@ -4970,6 +4972,60 @@ "@types/node": "*" } }, + "node_modules/@types/d3-array": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/d3-array/-/d3-array-3.0.4.tgz", + "integrity": "sha512-nwvEkG9vYOc0Ic7G7kwgviY4AQlTfYGIZ0fqB7CQHXGyYM6nO7kJh5EguSNA3jfh4rq7Sb7eMVq8isuvg2/miQ==" + }, + "node_modules/@types/d3-color": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@types/d3-color/-/d3-color-3.1.0.tgz", + "integrity": "sha512-HKuicPHJuvPgCD+np6Se9MQvS6OCbJmOjGvylzMJRlDwUXjKTTXs6Pwgk79O09Vj/ho3u1ofXnhFOaEWWPrlwA==" + }, + "node_modules/@types/d3-ease": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/d3-ease/-/d3-ease-3.0.0.tgz", + "integrity": "sha512-aMo4eaAOijJjA6uU+GIeW018dvy9+oH5Y2VPPzjjfxevvGQ/oRDs+tfYC9b50Q4BygRR8yE2QCLsrT0WtAVseA==" + }, + "node_modules/@types/d3-interpolate": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@types/d3-interpolate/-/d3-interpolate-3.0.1.tgz", + "integrity": "sha512-jx5leotSeac3jr0RePOH1KdR9rISG91QIE4Q2PYTu4OymLTZfA3SrnURSLzKH48HmXVUru50b8nje4E79oQSQw==", + "dependencies": { + "@types/d3-color": "*" + } + }, + "node_modules/@types/d3-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/d3-path/-/d3-path-3.0.0.tgz", + "integrity": "sha512-0g/A+mZXgFkQxN3HniRDbXMN79K3CdTpLsevj+PXiTcb2hVyvkZUBg37StmgCQkaD84cUJ4uaDAWq7UJOQy2Tg==" + }, + "node_modules/@types/d3-scale": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/@types/d3-scale/-/d3-scale-4.0.3.tgz", + "integrity": "sha512-PATBiMCpvHJSMtZAMEhc2WyL+hnzarKzI6wAHYjhsonjWJYGq5BXTzQjv4l8m2jO183/4wZ90rKvSeT7o72xNQ==", + "dependencies": { + "@types/d3-time": "*" + } + }, + "node_modules/@types/d3-shape": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@types/d3-shape/-/d3-shape-3.1.1.tgz", + "integrity": "sha512-6Uh86YFF7LGg4PQkuO2oG6EMBRLuW9cbavUW46zkIO5kuS2PfTqo2o9SkgtQzguBHbLgNnU90UNsITpsX1My+A==", + "dependencies": { + "@types/d3-path": "*" + } + }, + "node_modules/@types/d3-time": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/d3-time/-/d3-time-3.0.0.tgz", + "integrity": "sha512-sZLCdHvBUcNby1cB6Fd3ZBrABbjz3v1Vm90nysCQ6Vt7vd6e/h9Lt7SiJUoEX0l4Dzc7P5llKyhqSi1ycSf1Hg==" + }, + "node_modules/@types/d3-timer": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/d3-timer/-/d3-timer-3.0.0.tgz", + "integrity": "sha512-HNB/9GHqu7Fo8AQiugyJbv6ZxYz58wef0esl4Mv828w1ZKpAshw/uFWVDUcIB9KKFeFKoxS3cHY07FFgtTRZ1g==" + }, "node_modules/@types/detect-port": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/@types/detect-port/-/detect-port-1.3.2.tgz", @@ -5874,6 +5930,28 @@ "ajv": "^6.9.1" } }, + "node_modules/allotment": { + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/allotment/-/allotment-1.19.0.tgz", + "integrity": "sha512-qL/1faHUoCOvMstCFGkaGSK/nVoSAVN6NLFuIW4P6D5fvKdgxV8/KNfMFeuQUXReodILp4I6z+5a9zmuqK5t4g==", + "dependencies": { + "classnames": "^2.3.0", + "eventemitter3": "^5.0.0", + "lodash.clamp": "^4.0.0", + "lodash.debounce": "^4.0.0", + "lodash.isequal": "^4.5.0", + "use-resize-observer": "^9.0.0" + }, + "peerDependencies": { + "react": "^17.0.0 || ^18.0.0", + "react-dom": "^17.0.0 || ^18.0.0" + } + }, + "node_modules/allotment/node_modules/eventemitter3": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz", + "integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==" + }, "node_modules/ansi-align": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.1.tgz", @@ -6904,6 +6982,11 @@ "node": ">=8" } }, + "node_modules/classnames": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.3.2.tgz", + "integrity": "sha512-CSbhY4cFEJRe6/GQzIk5qXZ4Jeg5pcsP7b5peFSDpffpe1cqjASH/n9UTjBwOp6XpMSTwQ8Za2K5V02ueA7Tmw==" + }, "node_modules/clean-css": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-5.3.2.tgz", @@ -7616,6 +7699,11 @@ "postcss-value-parser": "^4.0.2" } }, + "node_modules/css-unit-converter": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/css-unit-converter/-/css-unit-converter-1.1.2.tgz", + "integrity": "sha512-IiJwMC8rdZE0+xiEZHeru6YoONC4rfPMqGm2W85jMIbkFvv5nFTwJVFHam2eFrN6txmoUYFAFXiv8ICVeTO0MA==" + }, "node_modules/css-what": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz", @@ -7645,6 +7733,116 @@ "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.1.tgz", "integrity": "sha512-DJR/VvkAvSZW9bTouZue2sSxDwdTN92uHjqeKVm+0dAqdfNykRzQ95tay8aXMBAAPpUiq4Qcug2L7neoRh2Egw==" }, + "node_modules/d3-array": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-3.2.3.tgz", + "integrity": "sha512-JRHwbQQ84XuAESWhvIPaUV4/1UYTBOLiOPGWqgFDHZS1D5QN9c57FbH3QpEnQMYiOXNzKUQyGTZf+EVO7RT5TQ==", + "dependencies": { + "internmap": "1 - 2" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-color": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-3.1.0.tgz", + "integrity": "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-ease": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-3.0.1.tgz", + "integrity": "sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-format": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-3.1.0.tgz", + "integrity": "sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-interpolate": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-3.0.1.tgz", + "integrity": "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==", + "dependencies": { + "d3-color": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-path": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-3.1.0.tgz", + "integrity": "sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-scale": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-4.0.2.tgz", + "integrity": "sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==", + "dependencies": { + "d3-array": "2.10.0 - 3", + "d3-format": "1 - 3", + "d3-interpolate": "1.2.0 - 3", + "d3-time": "2.1.1 - 3", + "d3-time-format": "2 - 4" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-shape": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-3.2.0.tgz", + "integrity": "sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==", + "dependencies": { + "d3-path": "^3.1.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-time": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-3.1.0.tgz", + "integrity": "sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==", + "dependencies": { + "d3-array": "2 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-time-format": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-4.1.0.tgz", + "integrity": "sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==", + "dependencies": { + "d3-time": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-timer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-3.0.1.tgz", + "integrity": "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==", + "engines": { + "node": ">=12" + } + }, "node_modules/date-fns": { "version": "2.29.3", "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.29.3.tgz", @@ -7707,6 +7905,11 @@ "node": ">=0.10.0" } }, + "node_modules/decimal.js-light": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/decimal.js-light/-/decimal.js-light-2.5.1.tgz", + "integrity": "sha512-qIMFpTMZmny+MMIitAB6D7iVPEorVw6YQRWkvarTkT4tBeSLLiHzcwj6q0MmYSFCiVpiqPJTJEYIrpcPzVEIvg==" + }, "node_modules/dedent": { "version": "0.7.0", "resolved": "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz", @@ -8951,6 +9154,11 @@ "node": ">= 0.6" } }, + "node_modules/eventemitter3": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", + "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==" + }, "node_modules/events": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", @@ -9097,6 +9305,11 @@ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", "dev": true }, + "node_modules/fast-equals": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/fast-equals/-/fast-equals-4.0.3.tgz", + "integrity": "sha512-G3BSX9cfKttjr+2o1O22tYMLq0DPluZnYtq1rXumE1SpL/F/SLIfHx08WYQoWSIpeMYf8sRbJ8++71+v6Pnxfg==" + }, "node_modules/fast-glob": { "version": "3.2.12", "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz", @@ -10300,6 +10513,14 @@ "node": ">= 0.4" } }, + "node_modules/internmap": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/internmap/-/internmap-2.0.3.tgz", + "integrity": "sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==", + "engines": { + "node": ">=12" + } + }, "node_modules/interpret": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/interpret/-/interpret-3.1.1.tgz", @@ -11552,11 +11773,20 @@ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" }, + "node_modules/lodash.clamp": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/lodash.clamp/-/lodash.clamp-4.0.3.tgz", + "integrity": "sha512-HvzRFWjtcguTW7yd8NJBshuNaCa8aqNFtnswdT7f/cMd/1YKy5Zzoq4W/Oxvnx9l7aeY258uSdDfM793+eLsVg==" + }, "node_modules/lodash.debounce": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", - "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==", - "dev": true + "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==" + }, + "node_modules/lodash.isequal": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz", + "integrity": "sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==" }, "node_modules/lodash.merge": { "version": "4.6.2", @@ -13323,6 +13553,11 @@ "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" }, + "node_modules/react-lifecycles-compat": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz", + "integrity": "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==" + }, "node_modules/react-refresh": { "version": "0.11.0", "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.11.0.tgz", @@ -13332,6 +13567,66 @@ "node": ">=0.10.0" } }, + "node_modules/react-resize-detector": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/react-resize-detector/-/react-resize-detector-8.1.0.tgz", + "integrity": "sha512-S7szxlaIuiy5UqLhLL1KY3aoyGHbZzsTpYal9eYMwCyKqoqoVLCmIgAgNyIM1FhnP2KyBygASJxdhejrzjMb+w==", + "dependencies": { + "lodash": "^4.17.21" + }, + "peerDependencies": { + "react": "^16.0.0 || ^17.0.0 || ^18.0.0", + "react-dom": "^16.0.0 || ^17.0.0 || ^18.0.0" + } + }, + "node_modules/react-scrollbar-size": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/react-scrollbar-size/-/react-scrollbar-size-5.0.0.tgz", + "integrity": "sha512-Ly3OuRMz4yDFViTh+ANH6TrG8EqrgjC1uxxm2a/95+2Ijy3XT+bWtzm4QmgZUcUVg+8BCKzmPMM7z39ZtucDIQ==", + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.1 || ^18.0.0" + } + }, + "node_modules/react-smooth": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/react-smooth/-/react-smooth-2.0.2.tgz", + "integrity": "sha512-pgqSp1q8rAGtF1bXQE0m3CHGLNfZZh5oA5o1tsPLXRHnKtkujMIJ8Ws5nO1mTySZf1c4vgwlEk+pHi3Ln6eYLw==", + "dependencies": { + "fast-equals": "^4.0.3", + "react-transition-group": "2.9.0" + }, + "peerDependencies": { + "prop-types": "^15.6.0", + "react": "^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0", + "react-dom": "^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0" + } + }, + "node_modules/react-smooth/node_modules/dom-helpers": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-3.4.0.tgz", + "integrity": "sha512-LnuPJ+dwqKDIyotW1VzmOZ5TONUN7CwkCR5hrgawTUbkBGYdeoNLZo6nNfGkCrjtE1nXXaj7iMMpDa8/d9WoIA==", + "dependencies": { + "@babel/runtime": "^7.1.2" + } + }, + "node_modules/react-smooth/node_modules/react-transition-group": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-2.9.0.tgz", + "integrity": "sha512-+HzNTCHpeQyl4MJ/bdE0u6XRMe9+XG/+aL4mCxVN4DnPBQ0/5bfHWPDuOZUzYdMj94daZaZdCCc1Dzt9R/xSSg==", + "dependencies": { + "dom-helpers": "^3.4.0", + "loose-envify": "^1.4.0", + "prop-types": "^15.6.2", + "react-lifecycles-compat": "^3.0.4" + }, + "peerDependencies": { + "react": ">=15.0.0", + "react-dom": ">=15.0.0" + } + }, "node_modules/react-transition-group": { "version": "4.4.5", "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz", @@ -13478,6 +13773,38 @@ "node": ">=4" } }, + "node_modules/recharts": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/recharts/-/recharts-2.6.2.tgz", + "integrity": "sha512-dVhNfgI21LlF+4AesO3mj+i+9YdAAjoGaDWIctUgH/G2iy14YVtb/DSUeic77xr19rbKCiq+pQGfeg2kJQDHig==", + "dependencies": { + "classnames": "^2.2.5", + "eventemitter3": "^4.0.1", + "lodash": "^4.17.19", + "react-is": "^16.10.2", + "react-resize-detector": "^8.0.4", + "react-smooth": "^2.0.2", + "recharts-scale": "^0.4.4", + "reduce-css-calc": "^2.1.8", + "victory-vendor": "^36.6.8" + }, + "engines": { + "node": ">=12" + }, + "peerDependencies": { + "prop-types": "^15.6.0", + "react": "^16.0.0 || ^17.0.0 || ^18.0.0", + "react-dom": "^16.0.0 || ^17.0.0 || ^18.0.0" + } + }, + "node_modules/recharts-scale": { + "version": "0.4.5", + "resolved": "https://registry.npmjs.org/recharts-scale/-/recharts-scale-0.4.5.tgz", + "integrity": "sha512-kivNFO+0OcUNu7jQquLXAxz1FIwZj8nrj+YkOKc5694NbjCvcT6aSZiIzNzd2Kul4o4rTto8QVR9lMNtxD4G1w==", + "dependencies": { + "decimal.js-light": "^2.4.1" + } + }, "node_modules/rechoir": { "version": "0.8.0", "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.8.0.tgz", @@ -13503,6 +13830,20 @@ "node": ">=8" } }, + "node_modules/reduce-css-calc": { + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/reduce-css-calc/-/reduce-css-calc-2.1.8.tgz", + "integrity": "sha512-8liAVezDmUcH+tdzoEGrhfbGcP7nOV4NkGE3a74+qqvE7nt9i4sKLGBuZNOnpI4WiGksiNPklZxva80061QiPg==", + "dependencies": { + "css-unit-converter": "^1.1.1", + "postcss-value-parser": "^3.3.0" + } + }, + "node_modules/reduce-css-calc/node_modules/postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==" + }, "node_modules/regenerate": { "version": "1.4.2", "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", @@ -15484,7 +15825,6 @@ "version": "9.1.0", "resolved": "https://registry.npmjs.org/use-resize-observer/-/use-resize-observer-9.1.0.tgz", "integrity": "sha512-R25VqO9Wb3asSD4eqtcxk8sJalvIOYBqS8MNZlpDSQ4l4xMQxC/J7Id9HoTqPq8FwULIn0PVW+OAqF2dyYbjow==", - "dev": true, "dependencies": { "@juggle/resize-observer": "^3.3.1" }, @@ -15578,6 +15918,27 @@ "node": ">= 0.8" } }, + "node_modules/victory-vendor": { + "version": "36.6.10", + "resolved": "https://registry.npmjs.org/victory-vendor/-/victory-vendor-36.6.10.tgz", + "integrity": "sha512-7YqYGtsA4mByokBhCjk+ewwPhUfzhR1I3Da6/ZsZUv/31ceT77RKoaqrxRq5Ki+9we4uzf7+A+7aG2sfYhm7nA==", + "dependencies": { + "@types/d3-array": "^3.0.3", + "@types/d3-ease": "^3.0.0", + "@types/d3-interpolate": "^3.0.1", + "@types/d3-scale": "^4.0.2", + "@types/d3-shape": "^3.1.0", + "@types/d3-time": "^3.0.0", + "@types/d3-timer": "^3.0.0", + "d3-array": "^3.1.6", + "d3-ease": "^3.0.1", + "d3-interpolate": "^3.0.1", + "d3-scale": "^4.0.2", + "d3-shape": "^3.1.0", + "d3-time": "^3.0.0", + "d3-timer": "^3.0.1" + } + }, "node_modules/walker": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", @@ -17988,8 +18349,7 @@ "@juggle/resize-observer": { "version": "3.4.0", "resolved": "https://registry.npmjs.org/@juggle/resize-observer/-/resize-observer-3.4.0.tgz", - "integrity": "sha512-dfLbk+PwWvFzSxwk3n5ySL0hfBog779o8h68wK/7/APo/7cgyWp5jcXockbxdk5kFRkbeXWm4Fbi9FrdN381sA==", - "dev": true + "integrity": "sha512-dfLbk+PwWvFzSxwk3n5ySL0hfBog779o8h68wK/7/APo/7cgyWp5jcXockbxdk5kFRkbeXWm4Fbi9FrdN381sA==" }, "@mdx-js/react": { "version": "2.3.0", @@ -19486,6 +19846,60 @@ "@types/node": "*" } }, + "@types/d3-array": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/d3-array/-/d3-array-3.0.4.tgz", + "integrity": "sha512-nwvEkG9vYOc0Ic7G7kwgviY4AQlTfYGIZ0fqB7CQHXGyYM6nO7kJh5EguSNA3jfh4rq7Sb7eMVq8isuvg2/miQ==" + }, + "@types/d3-color": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@types/d3-color/-/d3-color-3.1.0.tgz", + "integrity": "sha512-HKuicPHJuvPgCD+np6Se9MQvS6OCbJmOjGvylzMJRlDwUXjKTTXs6Pwgk79O09Vj/ho3u1ofXnhFOaEWWPrlwA==" + }, + "@types/d3-ease": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/d3-ease/-/d3-ease-3.0.0.tgz", + "integrity": "sha512-aMo4eaAOijJjA6uU+GIeW018dvy9+oH5Y2VPPzjjfxevvGQ/oRDs+tfYC9b50Q4BygRR8yE2QCLsrT0WtAVseA==" + }, + "@types/d3-interpolate": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@types/d3-interpolate/-/d3-interpolate-3.0.1.tgz", + "integrity": "sha512-jx5leotSeac3jr0RePOH1KdR9rISG91QIE4Q2PYTu4OymLTZfA3SrnURSLzKH48HmXVUru50b8nje4E79oQSQw==", + "requires": { + "@types/d3-color": "*" + } + }, + "@types/d3-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/d3-path/-/d3-path-3.0.0.tgz", + "integrity": "sha512-0g/A+mZXgFkQxN3HniRDbXMN79K3CdTpLsevj+PXiTcb2hVyvkZUBg37StmgCQkaD84cUJ4uaDAWq7UJOQy2Tg==" + }, + "@types/d3-scale": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/@types/d3-scale/-/d3-scale-4.0.3.tgz", + "integrity": "sha512-PATBiMCpvHJSMtZAMEhc2WyL+hnzarKzI6wAHYjhsonjWJYGq5BXTzQjv4l8m2jO183/4wZ90rKvSeT7o72xNQ==", + "requires": { + "@types/d3-time": "*" + } + }, + "@types/d3-shape": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@types/d3-shape/-/d3-shape-3.1.1.tgz", + "integrity": "sha512-6Uh86YFF7LGg4PQkuO2oG6EMBRLuW9cbavUW46zkIO5kuS2PfTqo2o9SkgtQzguBHbLgNnU90UNsITpsX1My+A==", + "requires": { + "@types/d3-path": "*" + } + }, + "@types/d3-time": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/d3-time/-/d3-time-3.0.0.tgz", + "integrity": "sha512-sZLCdHvBUcNby1cB6Fd3ZBrABbjz3v1Vm90nysCQ6Vt7vd6e/h9Lt7SiJUoEX0l4Dzc7P5llKyhqSi1ycSf1Hg==" + }, + "@types/d3-timer": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/d3-timer/-/d3-timer-3.0.0.tgz", + "integrity": "sha512-HNB/9GHqu7Fo8AQiugyJbv6ZxYz58wef0esl4Mv828w1ZKpAshw/uFWVDUcIB9KKFeFKoxS3cHY07FFgtTRZ1g==" + }, "@types/detect-port": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/@types/detect-port/-/detect-port-1.3.2.tgz", @@ -20224,6 +20638,26 @@ "dev": true, "requires": {} }, + "allotment": { + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/allotment/-/allotment-1.19.0.tgz", + "integrity": "sha512-qL/1faHUoCOvMstCFGkaGSK/nVoSAVN6NLFuIW4P6D5fvKdgxV8/KNfMFeuQUXReodILp4I6z+5a9zmuqK5t4g==", + "requires": { + "classnames": "^2.3.0", + "eventemitter3": "^5.0.0", + "lodash.clamp": "^4.0.0", + "lodash.debounce": "^4.0.0", + "lodash.isequal": "^4.5.0", + "use-resize-observer": "^9.0.0" + }, + "dependencies": { + "eventemitter3": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz", + "integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==" + } + } + }, "ansi-align": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.1.tgz", @@ -20975,6 +21409,11 @@ "integrity": "sha512-eXTggHWSooYhq49F2opQhuHWgzucfF2YgODK4e1566GQs5BIfP30B0oenwBJHfWxAs2fyPB1s7Mg949zLf61Yw==", "dev": true }, + "classnames": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.3.2.tgz", + "integrity": "sha512-CSbhY4cFEJRe6/GQzIk5qXZ4Jeg5pcsP7b5peFSDpffpe1cqjASH/n9UTjBwOp6XpMSTwQ8Za2K5V02ueA7Tmw==" + }, "clean-css": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-5.3.2.tgz", @@ -21518,6 +21957,11 @@ "postcss-value-parser": "^4.0.2" } }, + "css-unit-converter": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/css-unit-converter/-/css-unit-converter-1.1.2.tgz", + "integrity": "sha512-IiJwMC8rdZE0+xiEZHeru6YoONC4rfPMqGm2W85jMIbkFvv5nFTwJVFHam2eFrN6txmoUYFAFXiv8ICVeTO0MA==" + }, "css-what": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz", @@ -21535,6 +21979,83 @@ "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.1.tgz", "integrity": "sha512-DJR/VvkAvSZW9bTouZue2sSxDwdTN92uHjqeKVm+0dAqdfNykRzQ95tay8aXMBAAPpUiq4Qcug2L7neoRh2Egw==" }, + "d3-array": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-3.2.3.tgz", + "integrity": "sha512-JRHwbQQ84XuAESWhvIPaUV4/1UYTBOLiOPGWqgFDHZS1D5QN9c57FbH3QpEnQMYiOXNzKUQyGTZf+EVO7RT5TQ==", + "requires": { + "internmap": "1 - 2" + } + }, + "d3-color": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-3.1.0.tgz", + "integrity": "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==" + }, + "d3-ease": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-3.0.1.tgz", + "integrity": "sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==" + }, + "d3-format": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-3.1.0.tgz", + "integrity": "sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA==" + }, + "d3-interpolate": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-3.0.1.tgz", + "integrity": "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==", + "requires": { + "d3-color": "1 - 3" + } + }, + "d3-path": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-3.1.0.tgz", + "integrity": "sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==" + }, + "d3-scale": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-4.0.2.tgz", + "integrity": "sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==", + "requires": { + "d3-array": "2.10.0 - 3", + "d3-format": "1 - 3", + "d3-interpolate": "1.2.0 - 3", + "d3-time": "2.1.1 - 3", + "d3-time-format": "2 - 4" + } + }, + "d3-shape": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-3.2.0.tgz", + "integrity": "sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==", + "requires": { + "d3-path": "^3.1.0" + } + }, + "d3-time": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-3.1.0.tgz", + "integrity": "sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==", + "requires": { + "d3-array": "2 - 3" + } + }, + "d3-time-format": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-4.1.0.tgz", + "integrity": "sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==", + "requires": { + "d3-time": "1 - 3" + } + }, + "d3-timer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-3.0.1.tgz", + "integrity": "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==" + }, "date-fns": { "version": "2.29.3", "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.29.3.tgz", @@ -21572,6 +22093,11 @@ } } }, + "decimal.js-light": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/decimal.js-light/-/decimal.js-light-2.5.1.tgz", + "integrity": "sha512-qIMFpTMZmny+MMIitAB6D7iVPEorVw6YQRWkvarTkT4tBeSLLiHzcwj6q0MmYSFCiVpiqPJTJEYIrpcPzVEIvg==" + }, "dedent": { "version": "0.7.0", "resolved": "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz", @@ -22532,6 +23058,11 @@ "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", "dev": true }, + "eventemitter3": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", + "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==" + }, "events": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", @@ -22661,6 +23192,11 @@ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", "dev": true }, + "fast-equals": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/fast-equals/-/fast-equals-4.0.3.tgz", + "integrity": "sha512-G3BSX9cfKttjr+2o1O22tYMLq0DPluZnYtq1rXumE1SpL/F/SLIfHx08WYQoWSIpeMYf8sRbJ8++71+v6Pnxfg==" + }, "fast-glob": { "version": "3.2.12", "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz", @@ -23560,6 +24096,11 @@ "side-channel": "^1.0.4" } }, + "internmap": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/internmap/-/internmap-2.0.3.tgz", + "integrity": "sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==" + }, "interpret": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/interpret/-/interpret-3.1.1.tgz", @@ -24461,11 +25002,20 @@ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" }, + "lodash.clamp": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/lodash.clamp/-/lodash.clamp-4.0.3.tgz", + "integrity": "sha512-HvzRFWjtcguTW7yd8NJBshuNaCa8aqNFtnswdT7f/cMd/1YKy5Zzoq4W/Oxvnx9l7aeY258uSdDfM793+eLsVg==" + }, "lodash.debounce": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", - "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==", - "dev": true + "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==" + }, + "lodash.isequal": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz", + "integrity": "sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==" }, "lodash.merge": { "version": "4.6.2", @@ -25766,12 +26316,61 @@ "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" }, + "react-lifecycles-compat": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz", + "integrity": "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==" + }, "react-refresh": { "version": "0.11.0", "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.11.0.tgz", "integrity": "sha512-F27qZr8uUqwhWZboondsPx8tnC3Ct3SxZA3V5WyEvujRyyNv0VYPhoBg1gZ8/MV5tubQp76Trw8lTv9hzRBa+A==", "dev": true }, + "react-resize-detector": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/react-resize-detector/-/react-resize-detector-8.1.0.tgz", + "integrity": "sha512-S7szxlaIuiy5UqLhLL1KY3aoyGHbZzsTpYal9eYMwCyKqoqoVLCmIgAgNyIM1FhnP2KyBygASJxdhejrzjMb+w==", + "requires": { + "lodash": "^4.17.21" + } + }, + "react-scrollbar-size": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/react-scrollbar-size/-/react-scrollbar-size-5.0.0.tgz", + "integrity": "sha512-Ly3OuRMz4yDFViTh+ANH6TrG8EqrgjC1uxxm2a/95+2Ijy3XT+bWtzm4QmgZUcUVg+8BCKzmPMM7z39ZtucDIQ==", + "requires": {} + }, + "react-smooth": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/react-smooth/-/react-smooth-2.0.2.tgz", + "integrity": "sha512-pgqSp1q8rAGtF1bXQE0m3CHGLNfZZh5oA5o1tsPLXRHnKtkujMIJ8Ws5nO1mTySZf1c4vgwlEk+pHi3Ln6eYLw==", + "requires": { + "fast-equals": "^4.0.3", + "react-transition-group": "2.9.0" + }, + "dependencies": { + "dom-helpers": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-3.4.0.tgz", + "integrity": "sha512-LnuPJ+dwqKDIyotW1VzmOZ5TONUN7CwkCR5hrgawTUbkBGYdeoNLZo6nNfGkCrjtE1nXXaj7iMMpDa8/d9WoIA==", + "requires": { + "@babel/runtime": "^7.1.2" + } + }, + "react-transition-group": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-2.9.0.tgz", + "integrity": "sha512-+HzNTCHpeQyl4MJ/bdE0u6XRMe9+XG/+aL4mCxVN4DnPBQ0/5bfHWPDuOZUzYdMj94daZaZdCCc1Dzt9R/xSSg==", + "requires": { + "dom-helpers": "^3.4.0", + "loose-envify": "^1.4.0", + "prop-types": "^15.6.2", + "react-lifecycles-compat": "^3.0.4" + } + } + } + }, "react-transition-group": { "version": "4.4.5", "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz", @@ -25890,6 +26489,30 @@ } } }, + "recharts": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/recharts/-/recharts-2.6.2.tgz", + "integrity": "sha512-dVhNfgI21LlF+4AesO3mj+i+9YdAAjoGaDWIctUgH/G2iy14YVtb/DSUeic77xr19rbKCiq+pQGfeg2kJQDHig==", + "requires": { + "classnames": "^2.2.5", + "eventemitter3": "^4.0.1", + "lodash": "^4.17.19", + "react-is": "^16.10.2", + "react-resize-detector": "^8.0.4", + "react-smooth": "^2.0.2", + "recharts-scale": "^0.4.4", + "reduce-css-calc": "^2.1.8", + "victory-vendor": "^36.6.8" + } + }, + "recharts-scale": { + "version": "0.4.5", + "resolved": "https://registry.npmjs.org/recharts-scale/-/recharts-scale-0.4.5.tgz", + "integrity": "sha512-kivNFO+0OcUNu7jQquLXAxz1FIwZj8nrj+YkOKc5694NbjCvcT6aSZiIzNzd2Kul4o4rTto8QVR9lMNtxD4G1w==", + "requires": { + "decimal.js-light": "^2.4.1" + } + }, "rechoir": { "version": "0.8.0", "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.8.0.tgz", @@ -25909,6 +26532,22 @@ "strip-indent": "^3.0.0" } }, + "reduce-css-calc": { + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/reduce-css-calc/-/reduce-css-calc-2.1.8.tgz", + "integrity": "sha512-8liAVezDmUcH+tdzoEGrhfbGcP7nOV4NkGE3a74+qqvE7nt9i4sKLGBuZNOnpI4WiGksiNPklZxva80061QiPg==", + "requires": { + "css-unit-converter": "^1.1.1", + "postcss-value-parser": "^3.3.0" + }, + "dependencies": { + "postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==" + } + } + }, "regenerate": { "version": "1.4.2", "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", @@ -27396,7 +28035,6 @@ "version": "9.1.0", "resolved": "https://registry.npmjs.org/use-resize-observer/-/use-resize-observer-9.1.0.tgz", "integrity": "sha512-R25VqO9Wb3asSD4eqtcxk8sJalvIOYBqS8MNZlpDSQ4l4xMQxC/J7Id9HoTqPq8FwULIn0PVW+OAqF2dyYbjow==", - "dev": true, "requires": { "@juggle/resize-observer": "^3.3.1" } @@ -27477,6 +28115,27 @@ "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", "dev": true }, + "victory-vendor": { + "version": "36.6.10", + "resolved": "https://registry.npmjs.org/victory-vendor/-/victory-vendor-36.6.10.tgz", + "integrity": "sha512-7YqYGtsA4mByokBhCjk+ewwPhUfzhR1I3Da6/ZsZUv/31ceT77RKoaqrxRq5Ki+9we4uzf7+A+7aG2sfYhm7nA==", + "requires": { + "@types/d3-array": "^3.0.3", + "@types/d3-ease": "^3.0.0", + "@types/d3-interpolate": "^3.0.1", + "@types/d3-scale": "^4.0.2", + "@types/d3-shape": "^3.1.0", + "@types/d3-time": "^3.0.0", + "@types/d3-timer": "^3.0.0", + "d3-array": "^3.1.6", + "d3-ease": "^3.0.1", + "d3-interpolate": "^3.0.1", + "d3-scale": "^4.0.2", + "d3-shape": "^3.1.0", + "d3-time": "^3.0.0", + "d3-timer": "^3.0.1" + } + }, "walker": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", diff --git a/package.json b/package.json index 1d5be982d..0d623602b 100644 --- a/package.json +++ b/package.json @@ -79,12 +79,15 @@ "dependencies": { "@floating-ui/react": "^0.21.0", "@tanstack/react-table": "^8.7.8", + "allotment": "^1.19.0", "copy-to-clipboard": "^3.3.3", "date-fns": "^2.29.3", "react": "^18.2.0", "react-cool-dimensions": "^3.0.1", "react-dom": "^18.2.0", + "react-scrollbar-size": "^5.0.0", "react-transition-group": "^4.4.5", + "recharts": "^2.6.2", "styled-components": "^5.3.6" } } diff --git a/src/components/Assets/index.tsx b/src/components/Assets/index.tsx index 1d2148997..585330ed0 100644 --- a/src/components/Assets/index.tsx +++ b/src/components/Assets/index.tsx @@ -1,5 +1,6 @@ import { useEffect, useMemo, useState } from "react"; import { dispatcher } from "../../dispatcher"; +import { isNumber } from "../../typeGuards/isNumber"; import { addPrefix } from "../../utils/addPrefix"; import { groupBy } from "../../utils/groupBy"; import { AssetList } from "./AssetList"; @@ -13,10 +14,9 @@ import { GroupedAssetEntries } from "./types"; -const REFRESH_INTERVAL = - typeof window.assetsRefreshInterval === "number" - ? window.assetsRefreshInterval - : 10 * 1000; // in milliseconds +const REFRESH_INTERVAL = isNumber(window.assetsRefreshInterval) + ? window.assetsRefreshInterval + : 10 * 1000; // in milliseconds const ACTION_PREFIX = "ASSETS"; diff --git a/src/components/Assets/types.ts b/src/components/Assets/types.ts index 5917075bc..fffa5a3f7 100644 --- a/src/components/Assets/types.ts +++ b/src/components/Assets/types.ts @@ -1,4 +1,8 @@ -import { Duration } from "../../globals"; +import { + DurationPercentileWithChange, + SpanInfo, + SpanInstanceInfo +} from "../../types"; export interface AssetsProps { data?: AssetsData; @@ -25,38 +29,19 @@ export interface Insight { }; } -export interface DurationPercentiles { - percentile: number; - currentDuration: Duration; - previousDuration: Duration | null; - changeTime: string | null; - changeVerified: boolean | null; - traceIds: string[]; +export interface AssetEntrySpanInfo extends SpanInfo { + classification: string; + role: string; } export interface AssetEntry { - span: { - classification: string; - role: string; - name: string; - displayName: string; - instrumentationLibrary: string; - methodCodeObjectId: string; - spanCodeObjectId: string; - kind: string; - codeObjectId: string; - }; + span: AssetEntrySpanInfo; assetType: string; serviceName: string; endpointCodeObjectId: string | null; - durationPercentiles: DurationPercentiles[]; + durationPercentiles: DurationPercentileWithChange[]; insights: Insight[]; - lastSpanInstanceInfo: { - traceId: string; - spanId: string; - startTime: string; - duration: Duration; - }; + lastSpanInstanceInfo: SpanInstanceInfo; firstDataSeenTime: string; } diff --git a/src/components/InstallationWizard/Step/index.tsx b/src/components/InstallationWizard/Step/index.tsx index 32f0182a4..824b6a271 100644 --- a/src/components/InstallationWizard/Step/index.tsx +++ b/src/components/InstallationWizard/Step/index.tsx @@ -2,6 +2,7 @@ import { useRef } from "react"; import useDimensions from "react-cool-dimensions"; import { CSSTransition } from "react-transition-group"; import { useTheme } from "styled-components"; +import { isNumber } from "../../../typeGuards/isNumber"; import { CheckmarkCircleInvertedIcon } from "../../common/icons/CheckmarkCircleInvertedIcon"; import * as s from "./styles"; import { StepProps } from "./types"; @@ -13,10 +14,9 @@ const DEFAULT_TRANSITION_DURATION = 300; // in milliseconds export const Step = (props: StepProps) => { const theme = useTheme(); - const transitionDuration = - typeof props.transitionDuration === "number" - ? props.transitionDuration - : DEFAULT_TRANSITION_DURATION; + const transitionDuration = isNumber(props.transitionDuration) + ? props.transitionDuration + : DEFAULT_TRANSITION_DURATION; const containerRef = useRef(null); const numberRef = useRef(null); diff --git a/src/components/InstallationWizard/index.tsx b/src/components/InstallationWizard/index.tsx index f3f47fa61..5a29c6ee5 100644 --- a/src/components/InstallationWizard/index.tsx +++ b/src/components/InstallationWizard/index.tsx @@ -6,6 +6,7 @@ import { IDE } from "../../globals"; import { useDebounce } from "../../hooks/useDebounce"; import { usePrevious } from "../../hooks/usePrevious"; import { ide } from "../../platform"; +import { isString } from "../../typeGuards/isString"; import { addPrefix } from "../../utils/addPrefix"; import { actions as globalActions } from "../common/App"; import { getThemeKind } from "../common/App/styles"; @@ -77,8 +78,7 @@ const firstStep = window.wizardSkipInstallationStep === true ? 1 : 0; const preselectedIsObservabilityEnabled = window.isObservabilityEnabled === true; -const preselectedEmail = - typeof window.userEmail === "string" ? window.userEmail : ""; +const preselectedEmail = isString(window.userEmail) ? window.userEmail : ""; // TO DO: // add environment variable for presetting the correct installation type diff --git a/src/components/RecentActivity/LiveView/LiveView.stories.tsx b/src/components/RecentActivity/LiveView/LiveView.stories.tsx new file mode 100644 index 000000000..17e7542f1 --- /dev/null +++ b/src/components/RecentActivity/LiveView/LiveView.stories.tsx @@ -0,0 +1,34 @@ +import { Meta, StoryObj } from "@storybook/react"; +import { LiveView } from "."; +import { mockData } from "./mockData"; + +// More on how to set up stories at: https://storybook.js.org/docs/react/writing-stories/introduction +const meta: Meta = { + title: "Recent Activity/LiveView", + component: LiveView, + parameters: { + // More on how to position stories at: https://storybook.js.org/docs/react/configure/story-layout + layout: "fullscreen" + } +}; + +export default meta; + +type Story = StoryObj; + +// More on writing stories with args: https://storybook.js.org/docs/react/writing-stories/args + +export const Empty: Story = { + args: { + data: { + ...mockData, + liveDataRecords: [] + } + } +}; + +export const WithData: Story = { + args: { + data: mockData + } +}; diff --git a/src/components/RecentActivity/LiveView/TooltipContent/index.tsx b/src/components/RecentActivity/LiveView/TooltipContent/index.tsx new file mode 100644 index 000000000..5fd7d738b --- /dev/null +++ b/src/components/RecentActivity/LiveView/TooltipContent/index.tsx @@ -0,0 +1,6 @@ +import * as s from "./styles"; +import { TooltipContentProps } from "./types"; + +export const TooltipContent = (props: TooltipContentProps) => ( + {props.children} +); diff --git a/src/components/RecentActivity/LiveView/TooltipContent/styles.ts b/src/components/RecentActivity/LiveView/TooltipContent/styles.ts new file mode 100644 index 000000000..07d472d38 --- /dev/null +++ b/src/components/RecentActivity/LiveView/TooltipContent/styles.ts @@ -0,0 +1,30 @@ +import styled from "styled-components"; + +export const Container = styled.div` + display: flex; + flex-direction: column; + border-radius: 4px; + padding: 8px; + gap: 4px; + box-shadow: 0px 0px 6px rgba(0, 0, 0, 0.15); + + color: ${({ theme }) => { + switch (theme.mode) { + case "light": + return "#4d668a"; + case "dark": + case "dark-jetbrains": + return "#dadada"; + } + }}; + + background: ${({ theme }) => { + switch (theme.mode) { + case "light": + return "#fbfdff"; + case "dark": + case "dark-jetbrains": + return "#2e2e2e"; + } + }}; +`; diff --git a/src/components/RecentActivity/LiveView/TooltipContent/types.ts b/src/components/RecentActivity/LiveView/TooltipContent/types.ts new file mode 100644 index 000000000..de529b53b --- /dev/null +++ b/src/components/RecentActivity/LiveView/TooltipContent/types.ts @@ -0,0 +1,5 @@ +import { ReactNode } from "react"; + +export interface TooltipContentProps { + children: ReactNode; +} diff --git a/src/components/RecentActivity/LiveView/index.tsx b/src/components/RecentActivity/LiveView/index.tsx new file mode 100644 index 000000000..3ac6a5817 --- /dev/null +++ b/src/components/RecentActivity/LiveView/index.tsx @@ -0,0 +1,499 @@ +import { format } from "date-fns"; +import { useEffect, useRef, useState } from "react"; +import useDimensions from "react-cool-dimensions"; +import useScrollbarSize from "react-scrollbar-size"; + +import { + Area, + CartesianGrid, + ComposedChart, + Line, + ResponsiveContainer, + Tooltip, + XAxis, + YAxis +} from "recharts"; +import { ChartOffset } from "recharts/types/util/types"; +import { DefaultTheme, useTheme } from "styled-components"; +import { usePrevious } from "../../../hooks/usePrevious"; +import { roundTo } from "../../../utils/roundTo"; +import { ChartIcon } from "../../common/icons/ChartIcon"; +import { CrossIcon } from "../../common/icons/CrossIcon"; +import { DoubleCircleIcon } from "../../common/icons/DoubleCircleIcon"; +import { EndpointIcon } from "../../common/icons/EndpointIcon"; +import { MinusIcon } from "../../common/icons/MinusIcon"; +import { PlusIcon } from "../../common/icons/PlusIcon"; +import { TooltipContent } from "./TooltipContent"; +import * as s from "./styles"; +import { + Coordinates, + DotTooltipProps, + ExtendedLiveDataRecord, + LiveViewProps, + PercentileInfo +} from "./types"; + +const ZOOM_FACTOR = 1.2; + +const PERCENTILES = [ + { label: "Median", percentile: 0.5 }, + { label: "Slowest 5%", percentile: 0.95 } +]; + +const getSpanIconColor = (theme: DefaultTheme) => { + switch (theme.mode) { + case "light": + return "#4d668a"; + case "dark": + case "dark-jetbrains": + return "#7891d0"; + } +}; + +const getLiveIconColor = (theme: DefaultTheme) => { + switch (theme.mode) { + case "light": + return "#426dda"; + case "dark": + case "dark-jetbrains": + return "#b9c2eb"; + } +}; + +const getCloseIconColor = (theme: DefaultTheme) => { + switch (theme.mode) { + case "light": + return "#6e6e6e"; + case "dark": + case "dark-jetbrains": + return "#afb1b3"; + } +}; + +const getLineColor = (theme: DefaultTheme) => { + switch (theme.mode) { + case "light": + return "#426dda"; + case "dark": + case "dark-jetbrains": + return "#b9c2eb"; + } +}; + +const getAxisColor = (theme: DefaultTheme) => { + switch (theme.mode) { + case "light": + return "#b9c0d4"; + case "dark": + case "dark-jetbrains": + return "#49494d"; + } +}; + +const getTickLabelColor = (theme: DefaultTheme) => { + switch (theme.mode) { + case "light": + return "#828797"; + case "dark": + case "dark-jetbrains": + return "#9b9b9b"; + } +}; + +const getAreaColor = (theme: DefaultTheme) => { + switch (theme.mode) { + case "light": + return "#7891d0"; + case "dark": + case "dark-jetbrains": + return "#5154ec"; + } +}; + +const getZoomButtonIconColor = (theme: DefaultTheme) => { + switch (theme.mode) { + case "light": + return "#7891d0"; + case "dark": + case "dark-jetbrains": + return "#e2e7ff"; + } +}; + +const convertTo = (nanoseconds: number, unit: string) => { + const milliseconds = nanoseconds / 1000 / 1000; + + switch (unit) { + case "ms": + return milliseconds; + case "sec": + return milliseconds / 1000; + case "min": + default: + return milliseconds / 1000 / 60; + } +}; + +const getMaxDuration = ( + records: ExtendedLiveDataRecord[] +): ExtendedLiveDataRecord | undefined => + records.reduce( + (max: ExtendedLiveDataRecord | undefined, curr) => + max && max.duration.raw > curr.duration.raw ? max : curr, + undefined + ); + +const formatDate = (dateTime: number): string => + format(new Date(dateTime), "HH:mm MM/dd/yyyy"); + +export const LiveView = (props: LiveViewProps) => { + const theme = useTheme(); + const lineColor = getLineColor(theme); + const axisColor = getAxisColor(theme); + const areaColor = getAreaColor(theme); + const tickLabelColor = getTickLabelColor(theme); + const zoomButtonIconColor = getZoomButtonIconColor(theme); + const { observe, width } = useDimensions(); + const previousWidth = usePrevious(width); + const [containerInitialWidth, setContainerInitialWidth] = + useState(width); + const containerRef = useRef(null); + const [chartWidth, setChartWidth] = useState(width); + const previousChartWidth = usePrevious(chartWidth); + const [isZoomed, setIsZoomed] = useState(false); + const [areaTooltip, setAreaTooltip] = useState(); + const [dotToolTip, setDotTooltip] = useState(); + const [scrollPercentagePosition, setScrollPercentagePosition] = useState(1); + const scrollbar = useScrollbarSize(); + + useEffect(() => { + observe(containerRef.current); + }, [observe, containerRef.current]); + + useEffect(() => { + if (previousWidth !== width) { + setContainerInitialWidth(width); + if (width > chartWidth) { + setChartWidth(width); + } + } + }, [width, previousWidth, chartWidth]); + + useEffect(() => { + const containerEl = containerRef.current; + if (containerEl && previousChartWidth !== chartWidth) { + containerEl.scrollTo({ + left: + scrollPercentagePosition * containerEl.scrollWidth - + containerEl.clientWidth + }); + } + }, [previousChartWidth, chartWidth, scrollPercentagePosition]); + + const persistScrollPosition = () => { + const el = containerRef.current; + if (el) { + if (el.clientWidth === el.scrollWidth) { + setScrollPercentagePosition(1); + } else { + setScrollPercentagePosition( + (el.scrollLeft + el.clientWidth) / el.scrollWidth + ); + } + } + }; + + const handleCloseButtonClick = () => { + props.onClose(props.data.durationData.codeObjectId); + }; + + const handleZoomOutButtonClick = () => { + persistScrollPosition(); + + if (chartWidth > containerInitialWidth) { + const newWidth = Math.round(chartWidth / ZOOM_FACTOR); + + if (newWidth > containerInitialWidth) { + setChartWidth(newWidth); + setIsZoomed(true); + } else { + setChartWidth(containerInitialWidth); + setIsZoomed(false); + } + } + }; + + const handleZoomInButtonClick = () => { + persistScrollPosition(); + + const newWidth = Math.round(chartWidth * ZOOM_FACTOR); + + if (newWidth <= containerInitialWidth) { + setChartWidth(containerInitialWidth); + setIsZoomed(false); + } else { + setChartWidth(newWidth); + setIsZoomed(true); + } + }; + + const handleAreaMouseMove = (props: any, e: React.MouseEvent) => { + setAreaTooltip({ x: e.clientX, y: e.clientY }); + }; + + const handleAreaMouseLeave = () => { + setAreaTooltip(undefined); + }; + + const handleDotMouseOver = ( + props: any, + e: React.MouseEvent + ) => { + setDotTooltip({ + coordinates: { x: e.clientX, y: e.clientY }, + // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access + data: props.payload as ExtendedLiveDataRecord + }); + }; + + const handleDotMouseLeave = () => { + setDotTooltip(undefined); + }; + + const percentiles = PERCENTILES.map((percentile) => ({ + ...percentile, + duration: props.data.durationData.percentiles.find( + (x) => x.percentile === percentile.percentile + )?.currentDuration + })).filter(Boolean) as PercentileInfo[]; + + const p50 = percentiles.find((p) => p.percentile === 0.5); + const p95 = percentiles.find((p) => p.percentile === 0.95); + + const spanName = props.data.durationData.displayName; + + const data: ExtendedLiveDataRecord[] = [...props.data.liveDataRecords].map( + (x) => ({ + ...x, + dateTimeValue: new Date(x.dateTime).valueOf() + }) + ); + + const maxDuration = getMaxDuration(data)?.duration; + const maxDurationUnit = maxDuration?.unit || "ms"; + const maxDurationDigitCount = maxDuration + ? String(Math.ceil(maxDuration.value)).length + : 0; + const YAxisTickMargin = Math.round(maxDurationDigitCount * 5.5); + const YAxisWidth = Math.round(12 + maxDurationDigitCount * 7.5); + + const scrollbarOffset = + containerRef.current && + containerRef.current.scrollWidth > containerRef.current.clientWidth + ? scrollbar.width + : 0; + + return ( + + + + + + + {spanName} + + + + Live + + + + + + {data.length > 0 ? ( + <> + + + + + + + + + + + + + x.duration.raw} + tickLine={false} + tickFormatter={(x: number) => + String(roundTo(convertTo(x, maxDurationUnit), 0)) + } + tick={{ + fill: tickLabelColor, + fontSize: 10, + textAnchor: "start" + }} + stroke={axisColor} + tickMargin={YAxisTickMargin} + width={YAxisWidth} + label={{ + value: maxDurationUnit, + position: "bottom", + fill: tickLabelColor, + fontSize: 10, + offset: 16 + }} + /> + + + + + + + { + if (!props.width || !props.offset.left) { + return []; + } + + let linesCount = 5; + + const lines = []; + const interval = Math.floor(props.width / linesCount); + let left = props.offset.left + interval; + + while (linesCount) { + lines.push(left); + linesCount--; + left += interval; + } + + return lines; + }} + /> + {data.length > 1 && ( + + p50 && p95 ? [p50.duration.raw, p95.duration.raw] : [] + } + stroke={areaColor} + fill={areaColor} + fillOpacity={0.2} + isAnimationActive={false} + activeDot={false} + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + onMouseMove={handleAreaMouseMove} + onMouseLeave={handleAreaMouseLeave} + /> + )} + + + x.duration.raw + } + stroke={lineColor} + dot={{ + stroke: lineColor, + fill: lineColor, + r: 2, + onMouseOver: handleDotMouseOver, + onMouseLeave: handleDotMouseLeave + }} + isAnimationActive={false} + activeDot={false} + /> + {areaTooltip && ( + + {([p50, p95].filter(Boolean) as PercentileInfo[]).map( + (x) => ( + + {x.label}: {x.duration.value} {x.duration.unit} + + ) + )} + + } + cursor={false} + isAnimationActive={false} + /> + )} + {dotToolTip && ( + + + {dotToolTip.data.duration.value}{" "} + {dotToolTip.data.duration.unit} + + + {format( + new Date(dotToolTip.data.dateTime), + "HH:mm:ss.SSS MM/dd/yyyy" + )} + + + } + isAnimationActive={false} + cursor={false} + /> + )} + + + + + + + Slowest 5% - Median + + + ) : ( + + + No data yet + + Trigger some actions to follow the performance. + + + )} + + ); +}; diff --git a/src/components/RecentActivity/LiveView/mockData.ts b/src/components/RecentActivity/LiveView/mockData.ts new file mode 100644 index 000000000..df2b82a51 --- /dev/null +++ b/src/components/RecentActivity/LiveView/mockData.ts @@ -0,0 +1,833 @@ +import { LiveData } from "./types"; + +export const mockData: LiveData = { + liveDataRecords: [ + { + duration: { + value: 5.42, + unit: "ms", + raw: 5424000.0 + }, + dateTime: "2022-09-30T20:55:08.491486Z" + }, + { + duration: { + value: 6.18, + unit: "ms", + raw: 6181000.0 + }, + dateTime: "2023-05-15T20:55:08.484144Z" + }, + { + duration: { + value: 6.7, + unit: "ms", + raw: 6705000.0 + }, + dateTime: "2023-05-15T20:55:08.476678Z" + }, + { + duration: { + value: 6.23, + unit: "ms", + raw: 6232000.0 + }, + dateTime: "2023-05-15T20:55:08.469399Z" + }, + { + duration: { + value: 5.36, + unit: "ms", + raw: 5364000.0 + }, + dateTime: "2023-05-15T20:55:08.463486Z" + }, + { + duration: { + value: 6.25, + unit: "ms", + raw: 6252000.0 + }, + dateTime: "2023-05-15T20:55:08.45649Z" + }, + { + duration: { + value: 7.09, + unit: "ms", + raw: 7086000.0 + }, + dateTime: "2023-05-15T20:55:08.448348Z" + }, + { + duration: { + value: 5.51, + unit: "ms", + raw: 5514000.0 + }, + dateTime: "2023-05-15T20:55:08.441418Z" + }, + { + duration: { + value: 5.92, + unit: "ms", + raw: 5922000.0 + }, + dateTime: "2023-05-15T20:55:08.433369Z" + }, + { + duration: { + value: 5.66, + unit: "ms", + raw: 5655000.0 + }, + dateTime: "2023-05-15T20:55:08.427183Z" + }, + { + duration: { + value: 5.49, + unit: "ms", + raw: 5492000.0 + }, + dateTime: "2023-05-15T20:55:08.420245Z" + }, + { + duration: { + value: 5.58, + unit: "ms", + raw: 5578000.0 + }, + dateTime: "2023-05-15T20:55:08.414009Z" + }, + { + duration: { + value: 5.54, + unit: "ms", + raw: 5542000.0 + }, + dateTime: "2023-05-15T20:55:08.407718Z" + }, + { + duration: { + value: 6.06, + unit: "ms", + raw: 6062000.0 + }, + dateTime: "2023-05-15T20:55:08.400629Z" + }, + { + duration: { + value: 6.42, + unit: "ms", + raw: 6419000.0 + }, + dateTime: "2023-05-15T20:55:08.393341Z" + }, + { + duration: { + value: 5.77, + unit: "ms", + raw: 5766000.0 + }, + dateTime: "2023-05-15T20:55:08.386845Z" + }, + { + duration: { + value: 5.78, + unit: "ms", + raw: 5783000.0 + }, + dateTime: "2023-05-15T20:55:08.380328Z" + }, + { + duration: { + value: 5.7, + unit: "ms", + raw: 5703000.0 + }, + dateTime: "2023-05-15T20:55:08.373934Z" + }, + { + duration: { + value: 6.92, + unit: "ms", + raw: 6920000.0 + }, + dateTime: "2023-05-15T20:55:08.365194Z" + }, + { + duration: { + value: 5.52, + unit: "ms", + raw: 5515000.0 + }, + dateTime: "2023-05-15T20:55:08.358688Z" + }, + { + duration: { + value: 8.5, + unit: "ms", + raw: 8504000.0 + }, + dateTime: "2023-05-15T20:55:08.348849Z" + }, + { + duration: { + value: 6.11, + unit: "ms", + raw: 6108000.0 + }, + dateTime: "2023-05-15T20:55:08.341788Z" + }, + { + duration: { + value: 6.42, + unit: "ms", + raw: 6421000.0 + }, + dateTime: "2023-05-15T20:55:08.33408Z" + }, + { + duration: { + value: 6.78, + unit: "ms", + raw: 6785000.0 + }, + dateTime: "2023-05-15T20:55:08.326452Z" + }, + { + duration: { + value: 10.5, + unit: "ms", + raw: 10496000.0 + }, + dateTime: "2023-05-15T20:55:08.314516Z" + }, + { + duration: { + value: 7.64, + unit: "ms", + raw: 7643000.0 + }, + dateTime: "2023-05-15T20:55:08.303768Z" + }, + { + duration: { + value: 6.85, + unit: "ms", + raw: 6848000.0 + }, + dateTime: "2023-05-15T20:55:08.294392Z" + }, + { + duration: { + value: 5.7, + unit: "ms", + raw: 5705000.0 + }, + dateTime: "2023-05-15T20:55:08.287545Z" + }, + { + duration: { + value: 5.96, + unit: "ms", + raw: 5962000.0 + }, + dateTime: "2023-05-15T20:55:08.280037Z" + }, + { + duration: { + value: 7.12, + unit: "ms", + raw: 7123000.0 + }, + dateTime: "2023-05-15T20:55:08.265721Z" + }, + { + duration: { + value: 6.19, + unit: "ms", + raw: 6190000.0 + }, + dateTime: "2023-05-15T20:55:08.257448Z" + }, + { + duration: { + value: 7.33, + unit: "ms", + raw: 7334000.0 + }, + dateTime: "2023-05-15T20:55:08.248898Z" + }, + { + duration: { + value: 5.88, + unit: "ms", + raw: 5882000.0 + }, + dateTime: "2023-05-15T20:55:08.237502Z" + }, + { + duration: { + value: 6.0, + unit: "ms", + raw: 6003000.0 + }, + dateTime: "2023-05-15T20:55:08.230341Z" + }, + { + duration: { + value: 5.88, + unit: "ms", + raw: 5885000.0 + }, + dateTime: "2023-05-15T20:55:08.223674Z" + }, + { + duration: { + value: 5.47, + unit: "ms", + raw: 5469000.0 + }, + dateTime: "2023-05-15T20:55:08.214741Z" + }, + { + duration: { + value: 5.44, + unit: "ms", + raw: 5438000.0 + }, + dateTime: "2023-05-15T20:55:08.20848Z" + }, + { + duration: { + value: 6.69, + unit: "ms", + raw: 6687000.0 + }, + dateTime: "2023-05-15T20:55:08.200079Z" + }, + { + duration: { + value: 5.65, + unit: "ms", + raw: 5652000.0 + }, + dateTime: "2023-05-15T20:55:08.193541Z" + }, + { + duration: { + value: 5.86, + unit: "ms", + raw: 5857000.0 + }, + dateTime: "2023-05-15T20:55:08.186328Z" + }, + { + duration: { + value: 5.52, + unit: "ms", + raw: 5515000.0 + }, + dateTime: "2023-05-15T20:55:08.180081Z" + }, + { + duration: { + value: 5.56, + unit: "ms", + raw: 5555000.0 + }, + dateTime: "2023-05-15T20:55:08.173781Z" + }, + { + duration: { + value: 5.58, + unit: "ms", + raw: 5583000.0 + }, + dateTime: "2023-05-15T20:55:08.167073Z" + }, + { + duration: { + value: 5.5, + unit: "ms", + raw: 5497000.0 + }, + dateTime: "2023-05-15T20:55:08.160823Z" + }, + { + duration: { + value: 6.88, + unit: "ms", + raw: 6883000.0 + }, + dateTime: "2023-05-15T20:55:08.152939Z" + }, + { + duration: { + value: 5.49, + unit: "ms", + raw: 5490000.0 + }, + dateTime: "2023-05-15T20:55:08.145679Z" + }, + { + duration: { + value: 6.33, + unit: "ms", + raw: 6326000.0 + }, + dateTime: "2023-05-15T20:55:08.136397Z" + }, + { + duration: { + value: 6.12, + unit: "ms", + raw: 6116000.0 + }, + dateTime: "2023-05-15T20:55:08.128706Z" + }, + { + duration: { + value: 5.57, + unit: "ms", + raw: 5574000.0 + }, + dateTime: "2023-05-15T20:55:08.113816Z" + }, + { + duration: { + value: 16.52, + unit: "ms", + raw: 16517000.0 + }, + dateTime: "2023-05-15T20:55:08.093456Z" + }, + { + duration: { + value: 5.27, + unit: "ms", + raw: 5266000.0 + }, + dateTime: "2023-05-15T20:32:22.854431Z" + }, + { + duration: { + value: 5.42, + unit: "ms", + raw: 5418000.0 + }, + dateTime: "2023-05-15T20:32:22.848743Z" + }, + { + duration: { + value: 6.27, + unit: "ms", + raw: 6273000.0 + }, + dateTime: "2023-05-15T20:32:22.842156Z" + }, + { + duration: { + value: 5.52, + unit: "ms", + raw: 5524000.0 + }, + dateTime: "2023-05-15T20:32:22.836188Z" + }, + { + duration: { + value: 5.78, + unit: "ms", + raw: 5781000.0 + }, + dateTime: "2023-05-15T20:32:22.830112Z" + }, + { + duration: { + value: 5.78, + unit: "ms", + raw: 5778000.0 + }, + dateTime: "2023-05-15T20:32:22.824025Z" + }, + { + duration: { + value: 5.77, + unit: "ms", + raw: 5770000.0 + }, + dateTime: "2023-05-15T20:32:22.817972Z" + }, + { + duration: { + value: 6.52, + unit: "ms", + raw: 6520000.0 + }, + dateTime: "2023-05-15T20:32:22.811146Z" + }, + { + duration: { + value: 5.43, + unit: "ms", + raw: 5429000.0 + }, + dateTime: "2023-05-15T20:32:22.805435Z" + }, + { + duration: { + value: 5.62, + unit: "ms", + raw: 5625000.0 + }, + dateTime: "2023-05-15T20:32:22.79945Z" + }, + { + duration: { + value: 5.32, + unit: "ms", + raw: 5316000.0 + }, + dateTime: "2023-05-15T20:32:22.793818Z" + }, + { + duration: { + value: 5.43, + unit: "ms", + raw: 5426000.0 + }, + dateTime: "2023-05-15T20:32:22.787967Z" + }, + { + duration: { + value: 5.7, + unit: "ms", + raw: 5700000.0 + }, + dateTime: "2023-05-15T20:32:22.782014Z" + }, + { + duration: { + value: 5.83, + unit: "ms", + raw: 5827000.0 + }, + dateTime: "2023-05-15T20:32:22.775849Z" + }, + { + duration: { + value: 6.21, + unit: "ms", + raw: 6206000.0 + }, + dateTime: "2023-05-15T20:32:22.769162Z" + }, + { + duration: { + value: 5.59, + unit: "ms", + raw: 5594000.0 + }, + dateTime: "2023-05-15T20:32:22.763282Z" + }, + { + duration: { + value: 5.3, + unit: "ms", + raw: 5298000.0 + }, + dateTime: "2023-05-15T20:32:22.757595Z" + }, + { + duration: { + value: 5.93, + unit: "ms", + raw: 5930000.0 + }, + dateTime: "2023-05-15T20:32:22.751328Z" + }, + { + duration: { + value: 6.48, + unit: "ms", + raw: 6477000.0 + }, + dateTime: "2023-05-15T20:32:22.744535Z" + }, + { + duration: { + value: 5.22, + unit: "ms", + raw: 5219000.0 + }, + dateTime: "2023-05-15T20:32:22.739025Z" + }, + { + duration: { + value: 5.26, + unit: "ms", + raw: 5261000.0 + }, + dateTime: "2023-05-15T20:32:22.733472Z" + }, + { + duration: { + value: 5.7, + unit: "ms", + raw: 5696000.0 + }, + dateTime: "2023-05-15T20:32:22.727498Z" + }, + { + duration: { + value: 5.3, + unit: "ms", + raw: 5302000.0 + }, + dateTime: "2023-05-15T20:32:22.721696Z" + }, + { + duration: { + value: 5.28, + unit: "ms", + raw: 5282000.0 + }, + dateTime: "2023-05-15T20:32:22.716047Z" + }, + { + duration: { + value: 5.29, + unit: "ms", + raw: 5294000.0 + }, + dateTime: "2023-05-15T20:32:22.710412Z" + }, + { + duration: { + value: 5.33, + unit: "ms", + raw: 5326000.0 + }, + dateTime: "2023-05-15T20:32:22.704619Z" + }, + { + duration: { + value: 5.53, + unit: "ms", + raw: 5532000.0 + }, + dateTime: "2023-05-15T20:32:22.698742Z" + }, + { + duration: { + value: 5.24, + unit: "ms", + raw: 5241000.0 + }, + dateTime: "2023-05-15T20:32:22.693142Z" + }, + { + duration: { + value: 5.41, + unit: "ms", + raw: 5408000.0 + }, + dateTime: "2023-05-15T20:32:22.687328Z" + }, + { + duration: { + value: 6.38, + unit: "ms", + raw: 6377000.0 + }, + dateTime: "2023-05-15T20:32:22.680668Z" + }, + { + duration: { + value: 6.12, + unit: "ms", + raw: 6123000.0 + }, + dateTime: "2023-05-15T20:32:22.674225Z" + }, + { + duration: { + value: 6.09, + unit: "ms", + raw: 6091000.0 + }, + dateTime: "2023-05-15T20:32:22.667707Z" + }, + { + duration: { + value: 6.48, + unit: "ms", + raw: 6478000.0 + }, + dateTime: "2023-05-15T20:32:22.660967Z" + }, + { + duration: { + value: 5.36, + unit: "ms", + raw: 5361000.0 + }, + dateTime: "2023-05-15T20:32:22.655061Z" + }, + { + duration: { + value: 5.26, + unit: "ms", + raw: 5261000.0 + }, + dateTime: "2023-05-15T20:32:22.649463Z" + }, + { + duration: { + value: 5.56, + unit: "ms", + raw: 5558000.0 + }, + dateTime: "2023-05-15T20:32:22.643544Z" + }, + { + duration: { + value: 5.41, + unit: "ms", + raw: 5411000.0 + }, + dateTime: "2023-05-15T20:32:22.637717Z" + }, + { + duration: { + value: 5.27, + unit: "ms", + raw: 5267000.0 + }, + dateTime: "2023-05-15T20:32:22.63204Z" + }, + { + duration: { + value: 5.52, + unit: "ms", + raw: 5522000.0 + }, + dateTime: "2023-05-15T20:32:22.626062Z" + }, + { + duration: { + value: 6.46, + unit: "ms", + raw: 6456000.0 + }, + dateTime: "2023-05-15T20:32:22.618915Z" + }, + { + duration: { + value: 5.37, + unit: "ms", + raw: 5367000.0 + }, + dateTime: "2023-05-15T20:32:22.613257Z" + }, + { + duration: { + value: 5.56, + unit: "ms", + raw: 5561000.0 + }, + dateTime: "2023-05-15T20:32:22.607334Z" + }, + { + duration: { + value: 6.49, + unit: "ms", + raw: 6491000.0 + }, + dateTime: "2023-05-15T20:32:22.600531Z" + }, + { + duration: { + value: 5.37, + unit: "ms", + raw: 5367000.0 + }, + dateTime: "2023-05-15T20:32:22.594809Z" + }, + { + duration: { + value: 6.24, + unit: "ms", + raw: 6243000.0 + }, + dateTime: "2023-05-15T20:32:22.588216Z" + }, + { + duration: { + value: 5.16, + unit: "ms", + raw: 5158000.0 + }, + dateTime: "2023-05-15T20:32:22.582783Z" + }, + { + duration: { + value: 5.87, + unit: "ms", + raw: 5872000.0 + }, + dateTime: "2023-05-15T20:32:22.576547Z" + }, + { + duration: { + value: 5.46, + unit: "ms", + raw: 5464000.0 + }, + dateTime: "2023-05-15T20:32:22.570563Z" + }, + { + duration: { + value: 5.29, + unit: "ms", + raw: 5287000.0 + }, + dateTime: "2023-05-15T20:32:22.564778Z" + }, + { + duration: { + value: 8.29, + unit: "ms", + raw: 8287000.0 + }, + dateTime: "2023-05-15T20:32:22.556144Z" + } + ], + durationData: { + percentiles: [ + { + percentile: 0.5, + currentDuration: { + value: 5.7, + unit: "ms", + raw: 5704000.0 + }, + previousDuration: null, + changeVerified: null + }, + { + percentile: 0.95, + currentDuration: { + value: 7.49, + unit: "ms", + raw: 7488500.0 + }, + previousDuration: null, + changeVerified: null + } + ], + displayName: "HTTP GET SampleInsights/NormalUsage", + codeObjectId: + "method:Sample.MoneyTransfer.API.Controllers.SampleInsightsController$_$NormalUsage" + } +}; diff --git a/src/components/RecentActivity/LiveView/styles.ts b/src/components/RecentActivity/LiveView/styles.ts new file mode 100644 index 000000000..09eddd0b3 --- /dev/null +++ b/src/components/RecentActivity/LiveView/styles.ts @@ -0,0 +1,234 @@ +import styled from "styled-components"; +import { AreaLegendIllustrationProps, AxisChartContainerProps } from "./types"; + +export const Container = styled.div` + display: flex; + flex-direction: column; + height: 100%; + gap: 12px; + + border: 1px solid + ${({ theme }) => { + switch (theme.mode) { + case "light": + return "#d1d1d1"; + case "dark": + case "dark-jetbrains": + return "#323232"; + } + }}; + + background: ${({ theme }) => { + switch (theme.mode) { + case "light": + return "#fbfdff"; + case "dark": + case "dark-jetbrains": + return "#383838"; + } + }}; +`; + +export const Header = styled.div` + display: flex; + align-items: center; + padding: 4px 8px; + gap: 12px; + font-weight: 500; + font-size: 10px; + line-height: 12px; + + border-bottom: ${({ theme }) => { + switch (theme.mode) { + case "light": + return "1px solid #d1d1d1"; + case "dark": + case "dark-jetbrains": + return "none"; + } + }}; + + background: ${({ theme }) => { + switch (theme.mode) { + case "light": + return "#e3e7ed"; + case "dark": + case "dark-jetbrains": + return "#3c3f41"; + } + }}; +`; + +export const Title = styled.span` + display: flex; + align-items: center; + gap: 4px; + overflow: hidden; +`; + +export const SpanIconContainer = styled.span` + display: flex; +`; + +export const SpanName = styled.span` + font-size: 12px; + line-height: 14px; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + + color: ${({ theme }) => { + switch (theme.mode) { + case "light": + return "#4d668a"; + case "dark": + case "dark-jetbrains": + return "#7891d0"; + } + }}; +`; + +export const LiveBadge = styled.span` + display: flex; + align-items: center; + gap: 2px; + margin-left: auto; + padding: 2px 4px; + + color: ${({ theme }) => { + switch (theme.mode) { + case "light": + return "#426dda"; + case "dark": + case "dark-jetbrains": + return "#b9c2eb"; + } + }}; +`; + +export const CloseButton = styled.button` + display: flex; + padding: 0; + background: none; + border: none; + cursor: pointer; +`; + +export const ChartsContainer = styled.div` + display: flex; + height: calc(100% - 124px); + padding-right: 12px; +`; + +export const AxisChartContainer = styled.div` + width: ${({ width }) => width + 1}px; + height: ${({ scrollbarOffset }) => + scrollbarOffset ? `calc(100% - ${scrollbarOffset}px)` : "100%"}; + flex-shrink: 0; +`; + +export const ChartContainer = styled.div` + width: 100%; + overflow-x: auto; + overflow-y: hidden; +`; + +export const LegendContainer = styled.div` + display: flex; + gap: 4px; + padding: 4px 0 16px 12px; + font-size: 10px; + line-height: 12px; + + color: ${({ theme }) => { + switch (theme.mode) { + case "light": + return "#828797"; + case "dark": + case "dark-jetbrains": + return "#9b9b9b"; + } + }}; +`; + +export const AreaLegendIllustration = styled.div` + background: ${({ color }) => color}33; // 20% opacity + border-color: ${({ color }) => color}; + border-style: solid none; + border-width: 1px; + width: 10px; + box-sizing: border-box; + height: 100%; +`; + +export const ZoomButtonsContainer = styled.div` + display: flex; + justify-content: flex-end; + gap: 4px; + padding: 0 12px; +`; + +export const ZoomButton = styled.button` + box-sizing: border-box; + display: flex; + padding: 4px; + border-radius: 4px; + cursor: pointer; + + background: ${({ theme }) => { + switch (theme.mode) { + case "light": + return "#f1f5fa"; + case "dark": + case "dark-jetbrains": + return "#2e2e2e"; + } + }}; + + border: 1px solid + ${({ theme }) => { + switch (theme.mode) { + case "light": + return "#fbfdff"; + case "dark": + case "dark-jetbrains": + return "#383838"; + } + }}; + + box-shadow: ${({ theme }) => { + switch (theme.mode) { + case "light": + return "1px 1px 3px rgba(0, 0, 0, 0.15)"; + case "dark": + case "dark-jetbrains": + return "1px 1px 4px rgba(0, 0, 0, 0.25)"; + } + }}; +`; + +export const NoDataContainer = styled.div` + display: flex; + flex-direction: column; + flex-grow: 1; + align-items: center; + justify-content: center; + gap: 4px; + font-weight: 500; + font-size: 12px; + line-height: 14px; + text-align: center; + color: #7c7c94; +`; + +export const NoDataTitle = styled.span` + font-weight: 500; + font-size: 14px; + line-height: 17px; + text-transform: capitalize; + color: #dadada; +`; + +export const NoDataText = styled.span` + width: 208px; +`; diff --git a/src/components/RecentActivity/LiveView/types.ts b/src/components/RecentActivity/LiveView/types.ts new file mode 100644 index 000000000..af64ded60 --- /dev/null +++ b/src/components/RecentActivity/LiveView/types.ts @@ -0,0 +1,58 @@ +import { Duration } from "../../../globals"; + +export interface LiveViewProps { + data: LiveData; + onClose: (codeObjectId: string) => void; +} + +interface LiveDataRecord { + dateTime: string; + duration: Duration; +} + +interface LiveDataDurationPercentile { + percentile: number; + currentDuration: Duration; + previousDuration: Duration | null; + changeVerified: boolean | null; +} + +interface LiveDataDuration { + percentiles: LiveDataDurationPercentile[]; + codeObjectId: string; + displayName: string; +} + +export interface LiveData { + liveDataRecords: LiveDataRecord[]; + durationData: LiveDataDuration; +} + +export interface PercentileInfo { + duration: Duration; + label: string; + percentile: number; +} + +export interface ExtendedLiveDataRecord extends LiveDataRecord { + dateTimeValue: number; +} + +export interface Coordinates { + x: number; + y: number; +} + +export interface DotTooltipProps { + coordinates: Coordinates; + data: ExtendedLiveDataRecord; +} + +export interface AxisChartContainerProps { + scrollbarOffset: number; + width: number; +} + +export interface AreaLegendIllustrationProps { + color: string; +} diff --git a/src/components/RecentActivity/RecentActivity.stories.tsx b/src/components/RecentActivity/RecentActivity.stories.tsx index b96441f1e..2d7f599d1 100644 --- a/src/components/RecentActivity/RecentActivity.stories.tsx +++ b/src/components/RecentActivity/RecentActivity.stories.tsx @@ -1,5 +1,6 @@ import { Meta, StoryObj } from "@storybook/react"; import { RecentActivity } from "."; +import { mockData as liveData } from "./LiveView/mockData"; // More on how to set up stories at: https://storybook.js.org/docs/react/writing-stories/introduction const meta: Meta = { @@ -18,534 +19,550 @@ type Story = StoryObj; // More on writing stories with args: https://storybook.js.org/docs/react/writing-stories/args export const Empty: Story = {}; -export const WithData: Story = { - args: { - data: { - environments: [ - "ENV_RENDER", - "UNSET_ENV", - "UNSET_ENV1", - "UNSET_ENV2", - "UNSET_ENV3", - "UNSET_ENV4", - "UNSET_ENV5", - "UNSET_ENV6", - "UNSET_ENV7", - "VERY-VERY-VERY-VERY-VERY-VERY-VERY-VERY-VERY-VERY-VERY-VERY-VERY-VERY-LONG-NAME" - ], - entries: [ - { - environment: "ENV_RENDER", - traceFlowDisplayName: "PetClinicWithAgent:HTTP GET /webjars/**", - firstEntrySpan: { - displayText: "PetClinicWithAgent:HTTP GET /webjars/**", - serviceName: "PetClinicWithAgent", - scopeId: "HTTP GET /webjars/**", - spanCodeObjectId: - "span:io.opentelemetry.tomcat-10.0$_$HTTP GET /webjars/**", - methodCodeObjectId: "" - }, - lastEntrySpan: null, - latestTraceId: "DB80F24773E2BBE574E97960F9CB0D64", - latestTraceTimestamp: "2023-01-07T12:59:21.794Z", - latestTraceDuration: { - value: 3.9, - unit: "ms", - raw: 3902134 - }, - slimAggregatedInsights: [] - }, - { - environment: "ENV_RENDER", - traceFlowDisplayName: "PetClinicWithAgent:HTTP GET /webjars/**", - firstEntrySpan: { - displayText: "PetClinicWithAgent:HTTP GET /webjars/**", - serviceName: "PetClinicWithAgent", - scopeId: "HTTP GET /webjars/**", - spanCodeObjectId: - "span:io.opentelemetry.tomcat-10.0$_$HTTP GET /webjars/**", - methodCodeObjectId: "" - }, - lastEntrySpan: null, - latestTraceId: "DB80F24773E2BBE574E97960F9CB0D64", - latestTraceTimestamp: "2023-02-07T12:59:21.794Z", - latestTraceDuration: { - value: 3.9, - unit: "ms", - raw: 3902134 - }, - slimAggregatedInsights: [] - }, - { - environment: "ENV_RENDER", - traceFlowDisplayName: "PetClinicWithAgent:HTTP GET /owners/{ownerId}", - firstEntrySpan: { - displayText: "PetClinicWithAgent:HTTP GET /owners/{ownerId}", - serviceName: "PetClinicWithAgent", - scopeId: "HTTP GET /owners/{ownerId}", - spanCodeObjectId: - "span:io.opentelemetry.tomcat-10.0$_$HTTP GET /owners/{ownerId}", - methodCodeObjectId: - "org.springframework.samples.petclinic.owner.OwnerController$_$showOwner" - }, - lastEntrySpan: null, - latestTraceId: "64CFA03CF78919C46ECBC96B9EC07E35", - latestTraceTimestamp: "2023-01-30T09:39:43.03647Z", - latestTraceDuration: { - value: 9.96, - unit: "ms", - raw: 9964194 - }, - slimAggregatedInsights: [ - { - type: "HotSpot", - codeObjectIds: [ - "org.springframework.samples.petclinic.owner.OwnerController$_$showOwner" - ], - importance: 2 - } - ] - }, - { - environment: "ENV_RENDER", - traceFlowDisplayName: "PetClinicWithAgent:HTTP GET /owners", - firstEntrySpan: { - displayText: "PetClinicWithAgent:HTTP GET /owners", - serviceName: "PetClinicWithAgent", - scopeId: "HTTP GET /owners", - spanCodeObjectId: - "span:io.opentelemetry.tomcat-10.0$_$HTTP GET /owners", - methodCodeObjectId: - "org.springframework.samples.petclinic.owner.OwnerController$_$processFindForm" - }, - lastEntrySpan: null, - latestTraceId: "BD9F4E2166D527AD168672E4A7DEB4FA", - latestTraceTimestamp: "2023-01-30T09:39:42.140086Z", - latestTraceDuration: { - value: 13, - unit: "ms", - raw: 12998642 - }, - slimAggregatedInsights: [ - { - type: "SlowestSpans", - codeObjectIds: [ - "org.springframework.samples.petclinic.owner.OwnerController$_$processFindForm" - ], - importance: 2 - }, - { - type: "SpanEndpointBottleneck", - codeObjectIds: [ - "org.springframework.samples.petclinic.owner.OwnerRepository$_$findByLastName" - ], - importance: 2 - } - ] - }, - { - environment: "ENV_RENDER", - traceFlowDisplayName: "PetClinicWithAgent:HTTP GET /owners/find", - firstEntrySpan: { - displayText: "PetClinicWithAgent:HTTP GET /owners/find", - serviceName: "PetClinicWithAgent", - scopeId: "HTTP GET /owners/find", - spanCodeObjectId: - "span:io.opentelemetry.tomcat-10.0$_$HTTP GET /owners/find", - methodCodeObjectId: - "org.springframework.samples.petclinic.owner.OwnerController$_$initFindForm" - }, - lastEntrySpan: null, - latestTraceId: "8A2E125F7165A13B08D62E0EDC0A62FF", - latestTraceTimestamp: "2023-01-30T09:39:40.214951Z", - latestTraceDuration: { - value: 4.45, - unit: "ms", - raw: 4446862 - }, - slimAggregatedInsights: [] - }, - { - environment: "ENV_RENDER", - traceFlowDisplayName: "PetClinicWithAgent:HTTP GET /", - firstEntrySpan: { - displayText: "PetClinicWithAgent:HTTP GET /", - serviceName: "PetClinicWithAgent", - scopeId: "HTTP GET /", - spanCodeObjectId: "span:io.opentelemetry.tomcat-10.0$_$HTTP GET /", - methodCodeObjectId: - "org.springframework.samples.petclinic.system.WelcomeController$_$welcome" - }, - lastEntrySpan: null, - latestTraceId: "4A819C438C6A18E669180D08729DC48F", - latestTraceTimestamp: "2023-01-30T09:39:39.669249Z", - latestTraceDuration: { - value: 4.55, - unit: "ms", - raw: 4549011 - }, - slimAggregatedInsights: [] - }, +const data = { + environments: [ + "ENV_RENDER", + "UNSET_ENV", + "UNSET_ENV1", + "UNSET_ENV2", + "UNSET_ENV3", + "UNSET_ENV4", + "UNSET_ENV5", + "UNSET_ENV6", + "UNSET_ENV7", + "VERY-VERY-VERY-VERY-VERY-VERY-VERY-VERY-VERY-VERY-VERY-VERY-VERY-VERY-LONG-NAME" + ], + entries: [ + { + environment: "ENV_RENDER", + traceFlowDisplayName: "PetClinicWithAgent:HTTP GET /webjars/**", + firstEntrySpan: { + displayText: "PetClinicWithAgent:HTTP GET /webjars/**", + serviceName: "PetClinicWithAgent", + scopeId: "HTTP GET /webjars/**", + spanCodeObjectId: + "span:io.opentelemetry.tomcat-10.0$_$HTTP GET /webjars/**", + methodCodeObjectId: "" + }, + lastEntrySpan: null, + latestTraceId: "DB80F24773E2BBE574E97960F9CB0D64", + latestTraceTimestamp: "2023-01-07T12:59:21.794Z", + latestTraceDuration: { + value: 3.9, + unit: "ms", + raw: 3902134 + }, + slimAggregatedInsights: [] + }, + { + environment: "ENV_RENDER", + traceFlowDisplayName: "PetClinicWithAgent:HTTP GET /webjars/**", + firstEntrySpan: { + displayText: "PetClinicWithAgent:HTTP GET /webjars/**", + serviceName: "PetClinicWithAgent", + scopeId: "HTTP GET /webjars/**", + spanCodeObjectId: + "span:io.opentelemetry.tomcat-10.0$_$HTTP GET /webjars/**", + methodCodeObjectId: "" + }, + lastEntrySpan: null, + latestTraceId: "DB80F24773E2BBE574E97960F9CB0D64", + latestTraceTimestamp: "2023-02-07T12:59:21.794Z", + latestTraceDuration: { + value: 3.9, + unit: "ms", + raw: 3902134 + }, + slimAggregatedInsights: [] + }, + { + environment: "ENV_RENDER", + traceFlowDisplayName: "PetClinicWithAgent:HTTP GET /owners/{ownerId}", + firstEntrySpan: { + displayText: "PetClinicWithAgent:HTTP GET /owners/{ownerId}", + serviceName: "PetClinicWithAgent", + scopeId: "HTTP GET /owners/{ownerId}", + spanCodeObjectId: + "span:io.opentelemetry.tomcat-10.0$_$HTTP GET /owners/{ownerId}", + methodCodeObjectId: + "org.springframework.samples.petclinic.owner.OwnerController$_$showOwner" + }, + lastEntrySpan: null, + latestTraceId: "64CFA03CF78919C46ECBC96B9EC07E35", + latestTraceTimestamp: "2023-01-30T09:39:43.03647Z", + latestTraceDuration: { + value: 9.96, + unit: "ms", + raw: 9964194 + }, + slimAggregatedInsights: [ { - environment: "ENV_RENDER", - traceFlowDisplayName: "PetClinicWithAgent:HTTP GET /vets.html", - firstEntrySpan: { - displayText: "PetClinicWithAgent:HTTP GET /vets.html", - serviceName: "PetClinicWithAgent", - scopeId: "HTTP GET /vets.html", - spanCodeObjectId: - "span:io.opentelemetry.tomcat-10.0$_$HTTP GET /vets.html", - methodCodeObjectId: - "org.springframework.samples.petclinic.vet.VetController$_$showVetList" - }, - lastEntrySpan: null, - latestTraceId: "66CE458E5FF9F54E930DE5DB48B07E65", - latestTraceTimestamp: "2023-01-30T09:39:37.650562Z", - latestTraceDuration: { - value: 58.49, - unit: "ms", - raw: 58492826 - }, - slimAggregatedInsights: [ - { - type: "SlowestSpans", - codeObjectIds: [ - "org.springframework.samples.petclinic.vet.VetController$_$showVetList" - ], - importance: 2 - }, - { - type: "SpanEndpointBottleneck", - codeObjectIds: [ - "io.opentelemetry.spring-webmvc-6.0$_$Render vets/vetList" - ], - importance: 2 - } - ] - }, - { - environment: "ENV_RENDER", - traceFlowDisplayName: - "PetClinicWithAgent:HTTP POST /owners/{ownerId}/pets/new", - firstEntrySpan: { - displayText: - "PetClinicWithAgent:HTTP POST /owners/{ownerId}/pets/new", - serviceName: "PetClinicWithAgent", - scopeId: "HTTP POST /owners/{ownerId}/pets/new", - spanCodeObjectId: - "span:io.opentelemetry.tomcat-10.0$_$HTTP POST /owners/{ownerId}/pets/new", - methodCodeObjectId: - "org.springframework.samples.petclinic.owner.PetController$_$processCreationForm" - }, - lastEntrySpan: null, - latestTraceId: "3E72B5F5AF009AE6141C616DAB4999DB", - latestTraceTimestamp: "2023-01-30T09:29:23.259494Z", - latestTraceDuration: { - value: 39.86, - unit: "ms", - raw: 39863411 - }, - slimAggregatedInsights: [ - { - type: "SpanEndpointBottleneck", - codeObjectIds: [ - "org.springframework.samples.petclinic.owner.OwnerRepository$_$save" - ], - importance: 2 - } - ] - }, - { - environment: "ENV_RENDER", - traceFlowDisplayName: - "PetClinicWithAgent:HTTP GET /owners/{ownerId}/pets/new", - firstEntrySpan: { - displayText: - "PetClinicWithAgent:HTTP GET /owners/{ownerId}/pets/new", - serviceName: "PetClinicWithAgent", - scopeId: "HTTP GET /owners/{ownerId}/pets/new", - spanCodeObjectId: - "span:io.opentelemetry.tomcat-10.0$_$HTTP GET /owners/{ownerId}/pets/new", - methodCodeObjectId: - "org.springframework.samples.petclinic.owner.PetController$_$initCreationForm" - }, - lastEntrySpan: null, - latestTraceId: "5D2B5A3CE1E127A5BFBFD953573DE985", - latestTraceTimestamp: "2023-01-30T09:29:16.681077Z", - latestTraceDuration: { - value: 37.3, - unit: "ms", - raw: 37296736 - }, - slimAggregatedInsights: [] - }, - { - environment: "ENV_RENDER", - traceFlowDisplayName: - "PetClinicWithAgent:HTTP POST /owners/{ownerId}/edit", - firstEntrySpan: { - displayText: "PetClinicWithAgent:HTTP POST /owners/{ownerId}/edit", - serviceName: "PetClinicWithAgent", - scopeId: "HTTP POST /owners/{ownerId}/edit", - spanCodeObjectId: - "span:io.opentelemetry.tomcat-10.0$_$HTTP POST /owners/{ownerId}/edit", - methodCodeObjectId: - "org.springframework.samples.petclinic.owner.OwnerController$_$processUpdateOwnerForm" - }, - lastEntrySpan: null, - latestTraceId: "C6C9545A395BAFB37D2FF42EB96183EB", - latestTraceTimestamp: "2023-01-30T09:25:57.35119Z", - latestTraceDuration: { - value: 124.8, - unit: "ms", - raw: 124800876 - }, - slimAggregatedInsights: [ - { - type: "SlowestSpans", - codeObjectIds: [ - "org.springframework.samples.petclinic.owner.OwnerController$_$processUpdateOwnerForm" - ], - importance: 2 - }, - { - type: "SpanEndpointBottleneck", - codeObjectIds: [ - "org.springframework.samples.petclinic.owner.OwnerRepository$_$save" - ], - importance: 2 - } - ] - }, - { - environment: "ENV_RENDER", - traceFlowDisplayName: - "PetClinicWithAgent:HTTP GET /owners/{ownerId}/edit", - firstEntrySpan: { - displayText: "PetClinicWithAgent:HTTP GET /owners/{ownerId}/edit", - serviceName: "PetClinicWithAgent", - scopeId: "HTTP GET /owners/{ownerId}/edit", - spanCodeObjectId: - "span:io.opentelemetry.tomcat-10.0$_$HTTP GET /owners/{ownerId}/edit", - methodCodeObjectId: - "org.springframework.samples.petclinic.owner.OwnerController$_$initUpdateOwnerForm" - }, - lastEntrySpan: null, - latestTraceId: "B6CFB46AE4F090D43B38CF3A51291A14", - latestTraceTimestamp: "2023-01-30T09:25:49.164936Z", - latestTraceDuration: { - value: 32.85, - unit: "ms", - raw: 32848772 - }, - slimAggregatedInsights: [] - }, - { - environment: "UNSET_ENV", - traceFlowDisplayName: "my-first-mn-app:HTTP GET /whiskey/get/{name}", - firstEntrySpan: { - displayText: "na:na", - serviceName: "na", - scopeId: "na", - spanCodeObjectId: "na", - methodCodeObjectId: null - }, - lastEntrySpan: null, - latestTraceId: "ED0C93D748A2035AD021377A6F3C2F86", - latestTraceTimestamp: "2023-01-29T13:09:13.341318Z", - latestTraceDuration: { - value: 1.09, - unit: "ms", - raw: 1087700 - }, - slimAggregatedInsights: [] - }, - { - environment: "UNSET_ENV", - traceFlowDisplayName: "my-first-mn-app:HTTP GET /book/get/{id}", - firstEntrySpan: { - displayText: "na:na", - serviceName: "na", - scopeId: "na", - spanCodeObjectId: "na", - methodCodeObjectId: null - }, - lastEntrySpan: null, - latestTraceId: "7A2EF4162A16FE7836841DD60C1C610A", - latestTraceTimestamp: "2023-01-29T12:53:17.361202Z", - latestTraceDuration: { - value: 38.24, - unit: "ms", - raw: 38239200 - }, - slimAggregatedInsights: [] - }, - { - environment: "UNSET_ENV", - traceFlowDisplayName: "my-first-mn-app:HTTP GET /book/id/{id}", - firstEntrySpan: { - displayText: "na:na", - serviceName: "na", - scopeId: "na", - spanCodeObjectId: "na", - methodCodeObjectId: null - }, - lastEntrySpan: null, - latestTraceId: "C0AAEAE7D9B8929CEA1F3D1464A80C99", - latestTraceTimestamp: "2023-01-29T12:53:02.290752Z", - latestTraceDuration: { - value: 7.62, - unit: "ms", - raw: 7619500 - }, - slimAggregatedInsights: [] - }, + type: "HotSpot", + codeObjectIds: [ + "org.springframework.samples.petclinic.owner.OwnerController$_$showOwner" + ], + importance: 2 + } + ] + }, + { + environment: "ENV_RENDER", + traceFlowDisplayName: "PetClinicWithAgent:HTTP GET /owners", + firstEntrySpan: { + displayText: "PetClinicWithAgent:HTTP GET /owners", + serviceName: "PetClinicWithAgent", + scopeId: "HTTP GET /owners", + spanCodeObjectId: + "span:io.opentelemetry.tomcat-10.0$_$HTTP GET /owners", + methodCodeObjectId: + "org.springframework.samples.petclinic.owner.OwnerController$_$processFindForm" + }, + lastEntrySpan: null, + latestTraceId: "BD9F4E2166D527AD168672E4A7DEB4FA", + latestTraceTimestamp: "2023-01-30T09:39:42.140086Z", + latestTraceDuration: { + value: 13, + unit: "ms", + raw: 12998642 + }, + slimAggregatedInsights: [ { - environment: "UNSET_ENV", - traceFlowDisplayName: "my-first-mn-app:HTTP GET GET - /", - firstEntrySpan: { - displayText: "na:na", - serviceName: "na", - scopeId: "na", - spanCodeObjectId: "na", - methodCodeObjectId: null - }, - lastEntrySpan: null, - latestTraceId: "D7EFB24B92C2EFEB6EB4E9F9181BD737", - latestTraceTimestamp: "2023-01-29T12:52:53.9949Z", - latestTraceDuration: { - value: 238.79, - unit: "ms", - raw: 238791600 - }, - slimAggregatedInsights: [] + type: "SlowestSpans", + codeObjectIds: [ + "org.springframework.samples.petclinic.owner.OwnerController$_$processFindForm" + ], + importance: 2 }, { - environment: "UNSET_ENV", - traceFlowDisplayName: - "my-first-mn-app:HTTP GET /users/get/{username}", - firstEntrySpan: { - displayText: "na:na", - serviceName: "na", - scopeId: "na", - spanCodeObjectId: "na", - methodCodeObjectId: null - }, - lastEntrySpan: null, - latestTraceId: "FB593F5978A19F9FD09B71ECD49C22B4", - latestTraceTimestamp: "2023-01-29T12:33:05.89872Z", - latestTraceDuration: { - value: 1.32, - unit: "ms", - raw: 1321000 - }, - slimAggregatedInsights: [] - }, + type: "SpanEndpointBottleneck", + codeObjectIds: [ + "org.springframework.samples.petclinic.owner.OwnerRepository$_$findByLastName" + ], + importance: 2 + } + ] + }, + { + environment: "ENV_RENDER", + traceFlowDisplayName: "PetClinicWithAgent:HTTP GET /owners/find", + firstEntrySpan: { + displayText: "PetClinicWithAgent:HTTP GET /owners/find", + serviceName: "PetClinicWithAgent", + scopeId: "HTTP GET /owners/find", + spanCodeObjectId: + "span:io.opentelemetry.tomcat-10.0$_$HTTP GET /owners/find", + methodCodeObjectId: + "org.springframework.samples.petclinic.owner.OwnerController$_$initFindForm" + }, + lastEntrySpan: null, + latestTraceId: "8A2E125F7165A13B08D62E0EDC0A62FF", + latestTraceTimestamp: "2023-01-30T09:39:40.214951Z", + latestTraceDuration: { + value: 4.45, + unit: "ms", + raw: 4446862 + }, + slimAggregatedInsights: [] + }, + { + environment: "ENV_RENDER", + traceFlowDisplayName: "PetClinicWithAgent:HTTP GET /", + firstEntrySpan: { + displayText: "PetClinicWithAgent:HTTP GET /", + serviceName: "PetClinicWithAgent", + scopeId: "HTTP GET /", + spanCodeObjectId: "span:io.opentelemetry.tomcat-10.0$_$HTTP GET /", + methodCodeObjectId: + "org.springframework.samples.petclinic.system.WelcomeController$_$welcome" + }, + lastEntrySpan: null, + latestTraceId: "4A819C438C6A18E669180D08729DC48F", + latestTraceTimestamp: "2023-01-30T09:39:39.669249Z", + latestTraceDuration: { + value: 4.55, + unit: "ms", + raw: 4549011 + }, + slimAggregatedInsights: [] + }, + { + environment: "ENV_RENDER", + traceFlowDisplayName: "PetClinicWithAgent:HTTP GET /vets.html", + firstEntrySpan: { + displayText: "PetClinicWithAgent:HTTP GET /vets.html", + serviceName: "PetClinicWithAgent", + scopeId: "HTTP GET /vets.html", + spanCodeObjectId: + "span:io.opentelemetry.tomcat-10.0$_$HTTP GET /vets.html", + methodCodeObjectId: + "org.springframework.samples.petclinic.vet.VetController$_$showVetList" + }, + lastEntrySpan: null, + latestTraceId: "66CE458E5FF9F54E930DE5DB48B07E65", + latestTraceTimestamp: "2023-01-30T09:39:37.650562Z", + latestTraceDuration: { + value: 58.49, + unit: "ms", + raw: 58492826 + }, + slimAggregatedInsights: [ { - environment: "UNSET_ENV", - traceFlowDisplayName: "my-first-mn-app:HTTP GET /book", - firstEntrySpan: { - displayText: "na:na", - serviceName: "na", - scopeId: "na", - spanCodeObjectId: "na", - methodCodeObjectId: null - }, - lastEntrySpan: null, - latestTraceId: "77284D368A3122C01B4ACCC18C83AEFF", - latestTraceTimestamp: "2023-01-29T12:32:35.333618Z", - latestTraceDuration: { - value: 1.11, - unit: "ms", - raw: 1113600 - }, - slimAggregatedInsights: [] + type: "SlowestSpans", + codeObjectIds: [ + "org.springframework.samples.petclinic.vet.VetController$_$showVetList" + ], + importance: 2 }, { - environment: "UNSET_ENV", - traceFlowDisplayName: - "PetClinicWithAgent:HTTP GET /SampleInsights/HighUsage", - firstEntrySpan: { - displayText: "na:na", - serviceName: "na", - scopeId: "na", - spanCodeObjectId: "na", - methodCodeObjectId: null - }, - lastEntrySpan: null, - latestTraceId: "5AA14D37EFC218878A45E341DF4ACE2E", - latestTraceTimestamp: "2023-01-26T15:27:03.009074Z", - latestTraceDuration: { - value: 6, - unit: "ms", - raw: 6004792 - }, - slimAggregatedInsights: [] - }, + type: "SpanEndpointBottleneck", + codeObjectIds: [ + "io.opentelemetry.spring-webmvc-6.0$_$Render vets/vetList" + ], + importance: 2 + } + ] + }, + { + environment: "ENV_RENDER", + traceFlowDisplayName: + "PetClinicWithAgent:HTTP POST /owners/{ownerId}/pets/new", + firstEntrySpan: { + displayText: "PetClinicWithAgent:HTTP POST /owners/{ownerId}/pets/new", + serviceName: "PetClinicWithAgent", + scopeId: "HTTP POST /owners/{ownerId}/pets/new", + spanCodeObjectId: + "span:io.opentelemetry.tomcat-10.0$_$HTTP POST /owners/{ownerId}/pets/new", + methodCodeObjectId: + "org.springframework.samples.petclinic.owner.PetController$_$processCreationForm" + }, + lastEntrySpan: null, + latestTraceId: "3E72B5F5AF009AE6141C616DAB4999DB", + latestTraceTimestamp: "2023-01-30T09:29:23.259494Z", + latestTraceDuration: { + value: 39.86, + unit: "ms", + raw: 39863411 + }, + slimAggregatedInsights: [ { - environment: "UNSET_ENV", - traceFlowDisplayName: - "PetClinicWithAgent:HTTP GET /SampleInsights/ErrorHotspot", - firstEntrySpan: { - displayText: "na:na", - serviceName: "na", - scopeId: "na", - spanCodeObjectId: "na", - methodCodeObjectId: null - }, - lastEntrySpan: null, - latestTraceId: "3B692DFE97379C119BB4960DDFA51268", - latestTraceTimestamp: "2023-01-26T15:27:01.035306Z", - latestTraceDuration: { - value: 1.98, - unit: "ms", - raw: 1979067 - }, - slimAggregatedInsights: [] - }, + type: "SpanEndpointBottleneck", + codeObjectIds: [ + "org.springframework.samples.petclinic.owner.OwnerRepository$_$save" + ], + importance: 2 + } + ] + }, + { + environment: "ENV_RENDER", + traceFlowDisplayName: + "PetClinicWithAgent:HTTP GET /owners/{ownerId}/pets/new", + firstEntrySpan: { + displayText: "PetClinicWithAgent:HTTP GET /owners/{ownerId}/pets/new", + serviceName: "PetClinicWithAgent", + scopeId: "HTTP GET /owners/{ownerId}/pets/new", + spanCodeObjectId: + "span:io.opentelemetry.tomcat-10.0$_$HTTP GET /owners/{ownerId}/pets/new", + methodCodeObjectId: + "org.springframework.samples.petclinic.owner.PetController$_$initCreationForm" + }, + lastEntrySpan: null, + latestTraceId: "5D2B5A3CE1E127A5BFBFD953573DE985", + latestTraceTimestamp: "2023-01-30T09:29:16.681077Z", + latestTraceDuration: { + value: 37.3, + unit: "ms", + raw: 37296736 + }, + slimAggregatedInsights: [] + }, + { + environment: "ENV_RENDER", + traceFlowDisplayName: + "PetClinicWithAgent:HTTP POST /owners/{ownerId}/edit", + firstEntrySpan: { + displayText: "PetClinicWithAgent:HTTP POST /owners/{ownerId}/edit", + serviceName: "PetClinicWithAgent", + scopeId: "HTTP POST /owners/{ownerId}/edit", + spanCodeObjectId: + "span:io.opentelemetry.tomcat-10.0$_$HTTP POST /owners/{ownerId}/edit", + methodCodeObjectId: + "org.springframework.samples.petclinic.owner.OwnerController$_$processUpdateOwnerForm" + }, + lastEntrySpan: null, + latestTraceId: "C6C9545A395BAFB37D2FF42EB96183EB", + latestTraceTimestamp: "2023-01-30T09:25:57.35119Z", + latestTraceDuration: { + value: 124.8, + unit: "ms", + raw: 124800876 + }, + slimAggregatedInsights: [ { - environment: "UNSET_ENV", - traceFlowDisplayName: - "PetClinicWithAgent:HTTP GET /SampleInsights/SpanBottleneck", - firstEntrySpan: { - displayText: "na:na", - serviceName: "na", - scopeId: "na", - spanCodeObjectId: "na", - methodCodeObjectId: null - }, - lastEntrySpan: null, - latestTraceId: "CEB1C28473439717AA7F212B0991B312", - latestTraceTimestamp: "2023-01-26T15:27:00.776231Z", - latestTraceDuration: { - value: 250.87, - unit: "ms", - raw: 250869118 - }, - slimAggregatedInsights: [] + type: "SlowestSpans", + codeObjectIds: [ + "org.springframework.samples.petclinic.owner.OwnerController$_$processUpdateOwnerForm" + ], + importance: 2 }, { - environment: "UNSET_ENV", - traceFlowDisplayName: - "PetClinicWithAgent:HTTP GET /SampleInsights/SlowEndpoint", - firstEntrySpan: { - displayText: "na:na", - serviceName: "na", - scopeId: "na", - spanCodeObjectId: "na", - methodCodeObjectId: null - }, - lastEntrySpan: null, - latestTraceId: "ABA784C0D7BA52F4FBF529FB17B91DBC", - latestTraceTimestamp: "2023-01-26T15:26:58.01489Z", - latestTraceDuration: { - value: 2.51, - unit: "sec", - raw: 2505131829 - }, - slimAggregatedInsights: [] + type: "SpanEndpointBottleneck", + codeObjectIds: [ + "org.springframework.samples.petclinic.owner.OwnerRepository$_$save" + ], + importance: 2 } ] + }, + { + environment: "ENV_RENDER", + traceFlowDisplayName: + "PetClinicWithAgent:HTTP GET /owners/{ownerId}/edit", + firstEntrySpan: { + displayText: "PetClinicWithAgent:HTTP GET /owners/{ownerId}/edit", + serviceName: "PetClinicWithAgent", + scopeId: "HTTP GET /owners/{ownerId}/edit", + spanCodeObjectId: + "span:io.opentelemetry.tomcat-10.0$_$HTTP GET /owners/{ownerId}/edit", + methodCodeObjectId: + "org.springframework.samples.petclinic.owner.OwnerController$_$initUpdateOwnerForm" + }, + lastEntrySpan: null, + latestTraceId: "B6CFB46AE4F090D43B38CF3A51291A14", + latestTraceTimestamp: "2023-01-30T09:25:49.164936Z", + latestTraceDuration: { + value: 32.85, + unit: "ms", + raw: 32848772 + }, + slimAggregatedInsights: [] + }, + { + environment: "UNSET_ENV", + traceFlowDisplayName: "my-first-mn-app:HTTP GET /whiskey/get/{name}", + firstEntrySpan: { + displayText: "na:na", + serviceName: "na", + scopeId: "na", + spanCodeObjectId: "na", + methodCodeObjectId: null + }, + lastEntrySpan: null, + latestTraceId: "ED0C93D748A2035AD021377A6F3C2F86", + latestTraceTimestamp: "2023-01-29T13:09:13.341318Z", + latestTraceDuration: { + value: 1.09, + unit: "ms", + raw: 1087700 + }, + slimAggregatedInsights: [] + }, + { + environment: "UNSET_ENV", + traceFlowDisplayName: "my-first-mn-app:HTTP GET /book/get/{id}", + firstEntrySpan: { + displayText: "na:na", + serviceName: "na", + scopeId: "na", + spanCodeObjectId: "na", + methodCodeObjectId: null + }, + lastEntrySpan: null, + latestTraceId: "7A2EF4162A16FE7836841DD60C1C610A", + latestTraceTimestamp: "2023-01-29T12:53:17.361202Z", + latestTraceDuration: { + value: 38.24, + unit: "ms", + raw: 38239200 + }, + slimAggregatedInsights: [] + }, + { + environment: "UNSET_ENV", + traceFlowDisplayName: "my-first-mn-app:HTTP GET /book/id/{id}", + firstEntrySpan: { + displayText: "na:na", + serviceName: "na", + scopeId: "na", + spanCodeObjectId: "na", + methodCodeObjectId: null + }, + lastEntrySpan: null, + latestTraceId: "C0AAEAE7D9B8929CEA1F3D1464A80C99", + latestTraceTimestamp: "2023-01-29T12:53:02.290752Z", + latestTraceDuration: { + value: 7.62, + unit: "ms", + raw: 7619500 + }, + slimAggregatedInsights: [] + }, + { + environment: "UNSET_ENV", + traceFlowDisplayName: "my-first-mn-app:HTTP GET GET - /", + firstEntrySpan: { + displayText: "na:na", + serviceName: "na", + scopeId: "na", + spanCodeObjectId: "na", + methodCodeObjectId: null + }, + lastEntrySpan: null, + latestTraceId: "D7EFB24B92C2EFEB6EB4E9F9181BD737", + latestTraceTimestamp: "2023-01-29T12:52:53.9949Z", + latestTraceDuration: { + value: 238.79, + unit: "ms", + raw: 238791600 + }, + slimAggregatedInsights: [] + }, + { + environment: "UNSET_ENV", + traceFlowDisplayName: "my-first-mn-app:HTTP GET /users/get/{username}", + firstEntrySpan: { + displayText: "na:na", + serviceName: "na", + scopeId: "na", + spanCodeObjectId: "na", + methodCodeObjectId: null + }, + lastEntrySpan: null, + latestTraceId: "FB593F5978A19F9FD09B71ECD49C22B4", + latestTraceTimestamp: "2023-01-29T12:33:05.89872Z", + latestTraceDuration: { + value: 1.32, + unit: "ms", + raw: 1321000 + }, + slimAggregatedInsights: [] + }, + { + environment: "UNSET_ENV", + traceFlowDisplayName: "my-first-mn-app:HTTP GET /book", + firstEntrySpan: { + displayText: "na:na", + serviceName: "na", + scopeId: "na", + spanCodeObjectId: "na", + methodCodeObjectId: null + }, + lastEntrySpan: null, + latestTraceId: "77284D368A3122C01B4ACCC18C83AEFF", + latestTraceTimestamp: "2023-01-29T12:32:35.333618Z", + latestTraceDuration: { + value: 1.11, + unit: "ms", + raw: 1113600 + }, + slimAggregatedInsights: [] + }, + { + environment: "UNSET_ENV", + traceFlowDisplayName: + "PetClinicWithAgent:HTTP GET /SampleInsights/HighUsage", + firstEntrySpan: { + displayText: "na:na", + serviceName: "na", + scopeId: "na", + spanCodeObjectId: "na", + methodCodeObjectId: null + }, + lastEntrySpan: null, + latestTraceId: "5AA14D37EFC218878A45E341DF4ACE2E", + latestTraceTimestamp: "2023-01-26T15:27:03.009074Z", + latestTraceDuration: { + value: 6, + unit: "ms", + raw: 6004792 + }, + slimAggregatedInsights: [] + }, + { + environment: "UNSET_ENV", + traceFlowDisplayName: + "PetClinicWithAgent:HTTP GET /SampleInsights/ErrorHotspot", + firstEntrySpan: { + displayText: "na:na", + serviceName: "na", + scopeId: "na", + spanCodeObjectId: "na", + methodCodeObjectId: null + }, + lastEntrySpan: null, + latestTraceId: "3B692DFE97379C119BB4960DDFA51268", + latestTraceTimestamp: "2023-01-26T15:27:01.035306Z", + latestTraceDuration: { + value: 1.98, + unit: "ms", + raw: 1979067 + }, + slimAggregatedInsights: [] + }, + { + environment: "UNSET_ENV", + traceFlowDisplayName: + "PetClinicWithAgent:HTTP GET /SampleInsights/SpanBottleneck", + firstEntrySpan: { + displayText: "na:na", + serviceName: "na", + scopeId: "na", + spanCodeObjectId: "na", + methodCodeObjectId: null + }, + lastEntrySpan: null, + latestTraceId: "CEB1C28473439717AA7F212B0991B312", + latestTraceTimestamp: "2023-01-26T15:27:00.776231Z", + latestTraceDuration: { + value: 250.87, + unit: "ms", + raw: 250869118 + }, + slimAggregatedInsights: [] + }, + { + environment: "UNSET_ENV", + traceFlowDisplayName: + "PetClinicWithAgent:HTTP GET /SampleInsights/SlowEndpoint", + firstEntrySpan: { + displayText: "na:na", + serviceName: "na", + scopeId: "na", + spanCodeObjectId: "na", + methodCodeObjectId: null + }, + lastEntrySpan: null, + latestTraceId: "ABA784C0D7BA52F4FBF529FB17B91DBC", + latestTraceTimestamp: "2023-01-26T15:26:58.01489Z", + latestTraceDuration: { + value: 2.51, + unit: "sec", + raw: 2505131829 + }, + slimAggregatedInsights: [] + } + ] +}; + +export const WithData: Story = { + args: { + data + } +}; + +export const WithLiveData: Story = { + args: { + data, + liveData + } +}; + +export const WithEmptyLiveData: Story = { + args: { + data, + liveData: { + ...liveData, + liveDataRecords: [] } } }; diff --git a/src/components/RecentActivity/RecentActivityTable/index.tsx b/src/components/RecentActivity/RecentActivityTable/index.tsx index 8da56b26a..a2ffd1228 100644 --- a/src/components/RecentActivity/RecentActivityTable/index.tsx +++ b/src/components/RecentActivity/RecentActivityTable/index.tsx @@ -7,6 +7,7 @@ import { import { useMemo } from "react"; import { useTheme } from "styled-components"; import { Duration } from "../../../globals"; +import { isNumber } from "../../../typeGuards/isNumber"; import { getInsightImportanceColor } from "../../../utils/getInsightImportanceColor"; import { getInsightTypeInfo } from "../../../utils/getInsightTypeInfo"; import { timeAgo } from "../../../utils/timeAgo"; @@ -21,10 +22,9 @@ import { RecentActivityTableProps } from "./types"; const columnHelper = createColumnHelper(); export const isRecent = (entry: ActivityEntry): boolean => { - const MAX_DISTANCE = - typeof window.recentActivityExpirationLimit === "number" - ? window.recentActivityExpirationLimit - : 10 * 60 * 1000; // in milliseconds + const MAX_DISTANCE = isNumber(window.recentActivityExpirationLimit) + ? window.recentActivityExpirationLimit + : 10 * 60 * 1000; // in milliseconds const now = new Date(); return ( now.valueOf() - new Date(entry.latestTraceTimestamp).valueOf() <= @@ -222,7 +222,9 @@ export const RecentActivityTable = (props: RecentActivityTableProps) => { {renderTimeDistance(entry.latestTraceTimestamp, props.viewMode)} {renderSpanLinks(entry)} {renderDuration(entry.latestTraceDuration, props.viewMode)} - {renderInsights(entry.slimAggregatedInsights)} + + {renderInsights(entry.slimAggregatedInsights)} + {props.isTraceButtonVisible && renderTraceButton(entry)} ))} diff --git a/src/components/RecentActivity/RecentActivityTable/styles.ts b/src/components/RecentActivity/RecentActivityTable/styles.ts index 8466cff05..22a0317b4 100644 --- a/src/components/RecentActivity/RecentActivityTable/styles.ts +++ b/src/components/RecentActivity/RecentActivityTable/styles.ts @@ -4,9 +4,8 @@ import { Link } from "../../common/Link"; export const Table = styled.table` width: 100%; - border-spacing: 0 3px; - font-weight: 400; - font-size: 14px; + border-spacing: 0 4px; + font-size: 12px; color: ${({ theme }) => { switch (theme.mode) { @@ -22,7 +21,7 @@ export const Table = styled.table` export const TableHead = styled.thead` font-size: 10px; - height: 26px; + height: 28px; color: ${({ theme }) => { switch (theme.mode) { @@ -37,6 +36,7 @@ export const TableHead = styled.thead` `; export const TableHeaderRow = styled.th` + padding-top: 4px; font-weight: 400; text-align: start; @@ -76,7 +76,7 @@ export const TableBody = styled.tbody` export const TableBodyRow = styled.tr` position: relative; - height: 36px; + height: 42px; `; export const TableBodyCell = styled.td` @@ -98,11 +98,13 @@ export const BadgeContainer = styled.div` export const TimeDistanceContainer = styled.span` display: flex; align-items: center; + font-weight: 500; `; export const DurationContainer = styled.span` display: flex; align-items: center; + font-weight: 500; `; export const InsightsContainer = styled.span` @@ -114,8 +116,8 @@ export const InsightIconContainer = styled.span` display: flex; justify-content: center; align-items: center; - width: 20px; - height: 20px; + width: 24px; + height: 24px; flex-shrink: 0; border-radius: 4px; @@ -133,12 +135,12 @@ export const InsightIconContainer = styled.span` export const TraceButtonContainer = styled.div` display: flex; justify-content: flex-end; - margin-left: auto; `; export const Suffix = styled.span` margin-left: 2px; font-size: 10px; + line-height: 12px; color: ${({ theme }) => { switch (theme.mode) { @@ -158,10 +160,9 @@ export const ListContainer = styled.div` `; export const ListHeader = styled.div` - margin: 8px 0; + margin: 12px 0 8px; padding-left: 12px; line-height: 16px; - font-weight: 400; font-size: 10px; color: ${({ theme }) => { @@ -179,12 +180,10 @@ export const ListHeader = styled.div` export const List = styled.ul` border-radius: 12px; margin: 0; - padding: 9px 0 6px; + padding: 0; list-style-type: none; display: flex; flex-direction: column; - gap: 22px; - font-weight: 400; color: ${({ theme }) => { switch (theme.mode) { @@ -211,11 +210,13 @@ export const List = styled.ul` export const ListItem = styled.li` position: relative; font-size: 12px; - padding: 0 18px 0 12px; - height: 18px; + line-height: 14px; + padding: 0 12px; + height: 42px; display: flex; align-items: center; gap: 8px; + font-weight: 500; `; export const ListBadgeContainer = styled.div` @@ -228,8 +229,14 @@ export const ListBadgeContainer = styled.div` height: fit-content; `; +export const ListInsightsContainer = styled.div` + margin-left: auto; + margin-right: 16px; +`; + export const ListSuffix = styled(Suffix)` font-size: 12px; + line-height: 14px; `; // postcss-styled-components-disable-next-line diff --git a/src/components/RecentActivity/index.tsx b/src/components/RecentActivity/index.tsx index 7b0848d4d..c074b9848 100644 --- a/src/components/RecentActivity/index.tsx +++ b/src/components/RecentActivity/index.tsx @@ -1,3 +1,5 @@ +import { Allotment } from "allotment"; +import "allotment/dist/style.css"; import { useEffect, useMemo, useState } from "react"; import { dispatcher } from "../../dispatcher"; import { usePrevious } from "../../hooks/usePrevious"; @@ -8,8 +10,12 @@ import { CursorFollower } from "../common/CursorFollower"; import { DigmaLogoFlatIcon } from "../common/icons/DigmaLogoFlatIcon"; import { EnvironmentPanel } from "./EnvironmentPanel"; import { ViewMode } from "./EnvironmentPanel/types"; +import { LiveView } from "./LiveView"; +import { LiveData } from "./LiveView/types"; import { RecentActivityTable, isRecent } from "./RecentActivityTable"; import * as s from "./styles"; + +import { isString } from "../../typeGuards/isString"; import { EntrySpan, RecentActivityData, @@ -17,18 +23,19 @@ import { SetIsJaegerData } from "./types"; -const documentationURL = - typeof window.recentActivityDocumentationURL === "string" - ? window.recentActivityDocumentationURL - : null; +const documentationURL = isString(window.recentActivityDocumentationURL) + ? window.recentActivityDocumentationURL + : null; const ACTION_PREFIX = "RECENT_ACTIVITY"; const actions = addPrefix(ACTION_PREFIX, { INITIALIZE: "INITIALIZE", SET_DATA: "SET_DATA", + SET_LIVE_DATA: "SET_LIVE_DATA", GO_TO_SPAN: "GO_TO_SPAN", - GO_TO_TRACE: "GO_TO_TRACE" + GO_TO_TRACE: "GO_TO_TRACE", + CLOSE_LIVE_VIEW: "CLOSE_LIVE_VIEW" }); const renderNoData = () => { @@ -65,6 +72,7 @@ export const RecentActivity = (props: RecentActivityProps) => { const [isJaegerEnabled, setIsJaegerEnabled] = useState( window.isJaegerEnabled === true ); + const [liveData, setLiveData] = useState(); useEffect(() => { window.sendMessageToDigma({ @@ -79,11 +87,16 @@ export const RecentActivity = (props: RecentActivityProps) => { setIsJaegerEnabled((data as SetIsJaegerData).isJaegerEnabled); }; + const handleLiveData = (data: unknown) => { + setLiveData(data as LiveData); + }; + dispatcher.addActionListener(actions.SET_DATA, handleRecentActivityData); dispatcher.addActionListener( globalActions.SET_IS_JAEGER_ENABLED, handleSetIsJaegerEnabledData ); + dispatcher.addActionListener(actions.SET_LIVE_DATA, handleLiveData); return () => { dispatcher.removeActionListener( @@ -105,6 +118,14 @@ export const RecentActivity = (props: RecentActivityProps) => { setData(props.data); }, [props.data]); + useEffect(() => { + if (!props.liveData) { + return; + } + + setLiveData(props.liveData); + }, [props.liveData]); + useEffect(() => { if (!previousSelectedEnvironment && data && data.environments.length) { setSelectedEnvironment(data.environments[0]); @@ -139,6 +160,16 @@ export const RecentActivity = (props: RecentActivityProps) => { setViewMode(mode); }; + const handleCloseLiveView = (codeObjectId: string) => { + setLiveData(undefined); + window.sendMessageToDigma({ + action: actions.CLOSE_LIVE_VIEW, + payload: { + codeObjectId + } + }); + }; + const environmentActivities = useMemo( () => (data ? groupBy(data.entries, "environment") : {}), [data] @@ -159,29 +190,40 @@ export const RecentActivity = (props: RecentActivityProps) => { return ( - - {!selectedEnvironment || - !environmentActivities[selectedEnvironment] || - !environmentActivities[selectedEnvironment].length ? ( - <> - Recent Activity - {renderNoData()} - - ) : ( - - )} + + + + {!selectedEnvironment || + !environmentActivities[selectedEnvironment] || + !environmentActivities[selectedEnvironment].length ? ( + <> + Recent Activity + {renderNoData()} + + ) : ( + + )} + + + {liveData && ( + + + + )} + + ); }; diff --git a/src/components/RecentActivity/styles.ts b/src/components/RecentActivity/styles.ts index d1c11348f..7d64bd1f0 100644 --- a/src/components/RecentActivity/styles.ts +++ b/src/components/RecentActivity/styles.ts @@ -2,9 +2,8 @@ import styled from "styled-components"; import { Link } from "../common/Link"; export const Container = styled.div` - min-height: 100vh; - box-sizing: border-box; - padding: 12px; + height: 100%; + background: ${({ theme }) => { switch (theme.mode) { case "light": @@ -15,10 +14,41 @@ export const Container = styled.div` return "#383838"; } }}; + + .sash { + --sash-size: 12px; + } + + .sash-hover { + --focus-border: none; + } + + &&&&& .split-view-view::before { + margin: 12px 0; + height: calc(100% - 24px); + + --separator-border: ${({ theme }) => { + switch (theme.mode) { + case "light": + return "#b9c0d4"; + case "dark": + case "dark-jetbrains": + return "#9b9b9b"; + } + }}; + } +`; + +export const RecentActivityContainer = styled.div` + padding: 12px; + height: 100%; + overflow: auto; + box-sizing: border-box; + padding-right: 24px; `; export const Header = styled.div` - margin: 8px 0; + margin: 12px 0 8px; padding-left: 12px; line-height: 16px; font-weight: 400; @@ -100,3 +130,10 @@ export const DocumentationLink = styled(Link)` } }}; `; + +export const LiveViewContainer = styled.div` + padding: 12px; + padding-left: 24px; + height: 100%; + box-sizing: border-box; +`; diff --git a/src/components/RecentActivity/types.ts b/src/components/RecentActivity/types.ts index 1c3fb1b88..857a545c7 100644 --- a/src/components/RecentActivity/types.ts +++ b/src/components/RecentActivity/types.ts @@ -1,4 +1,5 @@ import { Duration } from "../../globals"; +import { LiveData } from "./LiveView/types"; export interface EntrySpan { displayText: string; @@ -32,6 +33,7 @@ export interface RecentActivityData { export interface RecentActivityProps { data?: RecentActivityData; + liveData?: LiveData; } export interface SetIsJaegerData { diff --git a/src/components/common/App/index.tsx b/src/components/common/App/index.tsx index e96585cdb..c381430fb 100644 --- a/src/components/common/App/index.tsx +++ b/src/components/common/App/index.tsx @@ -11,7 +11,7 @@ import { AppProps } from "./types"; export const THEMES = ["light", "dark", "dark-jetbrains"]; const isMode = (mode: unknown): mode is Mode => { - return typeof mode === "string" && THEMES.includes(mode); + return isString(mode) && THEMES.includes(mode); }; const getMode = (): Mode => { diff --git a/src/components/common/icons/ChartIcon.tsx b/src/components/common/icons/ChartIcon.tsx new file mode 100644 index 000000000..6b7b906a5 --- /dev/null +++ b/src/components/common/icons/ChartIcon.tsx @@ -0,0 +1,44 @@ +import React from "react"; +import { useIconProps } from "./hooks"; +import { IconProps } from "./types"; + +const ChartIconComponent = (props: IconProps) => { + const { size } = useIconProps(props); + + return ( + + + + + + + + + + + + + + ); +}; + +export const ChartIcon = React.memo(ChartIconComponent); diff --git a/src/components/common/icons/ClockWithTicksIcon.tsx b/src/components/common/icons/ClockWithTicksIcon.tsx new file mode 100644 index 000000000..7fb0215ef --- /dev/null +++ b/src/components/common/icons/ClockWithTicksIcon.tsx @@ -0,0 +1,24 @@ +import React from "react"; +import { useIconProps } from "./hooks"; +import { IconProps } from "./types"; + +const ClockWithTicksIconComponent = (props: IconProps) => { + const { size, color } = useIconProps(props); + + return ( + + + + ); +}; + +export const ClockWithTicksIcon = React.memo(ClockWithTicksIconComponent); diff --git a/src/components/common/icons/CrossCircleIcon.tsx b/src/components/common/icons/CrossCircleIcon.tsx deleted file mode 100644 index ab7f9d0c7..000000000 --- a/src/components/common/icons/CrossCircleIcon.tsx +++ /dev/null @@ -1,30 +0,0 @@ -import React from "react"; -import { useIconProps } from "./hooks"; -import { IconProps } from "./types"; - -const CrossCircleIconComponent = (props: IconProps) => { - const { size, color } = useIconProps(props); - - return ( - - - - - ); -}; - -export const CrossCircleIcon = React.memo(CrossCircleIconComponent); diff --git a/src/components/common/icons/CrossIcon.tsx b/src/components/common/icons/CrossIcon.tsx new file mode 100644 index 000000000..a28e5740e --- /dev/null +++ b/src/components/common/icons/CrossIcon.tsx @@ -0,0 +1,24 @@ +import React from "react"; +import { useIconProps } from "./hooks"; +import { IconProps } from "./types"; + +const CrossIconComponent = (props: IconProps) => { + const { size, color } = useIconProps(props); + + return ( + + + + ); +}; + +export const CrossIcon = React.memo(CrossIconComponent); diff --git a/src/components/common/icons/CrosshairIcon.tsx b/src/components/common/icons/CrosshairIcon.tsx index 7c26d7a5d..ef88fbebf 100644 --- a/src/components/common/icons/CrosshairIcon.tsx +++ b/src/components/common/icons/CrosshairIcon.tsx @@ -17,7 +17,7 @@ const CrosshairIconComponent = (props: IconProps) => { stroke={color} strokeLinecap="round" strokeLinejoin="round" - d="M10 6H8.4M3.6 6H2m4-2.4V2m0 8V8.4M9.2 6a3.2 3.2 0 1 1-6.4 0 3.2 3.2 0 0 1 6.4 0Z" + d="M10 6a4 4 0 0 1-4 4m4-4a4 4 0 0 0-4-4m4 4H7.87M6 10a4 4 0 0 1-4-4m4 4V7.88M2 6a4 4 0 0 1 4-4M2 6h2.13M6 2v2.13" /> ); diff --git a/src/components/common/icons/DataIcon.tsx b/src/components/common/icons/DataIcon.tsx deleted file mode 100644 index 51b43221b..000000000 --- a/src/components/common/icons/DataIcon.tsx +++ /dev/null @@ -1,34 +0,0 @@ -import React from "react"; -import { useIconProps } from "./hooks"; -import { IconProps } from "./types"; - -const DataIconComponent = (props: IconProps) => { - const { size, color } = useIconProps(props); - - return ( - - - - - - - - - - - ); -}; - -export const DataIcon = React.memo(DataIconComponent); diff --git a/src/components/common/icons/DoubleCircleIcon.tsx b/src/components/common/icons/DoubleCircleIcon.tsx new file mode 100644 index 000000000..5d853cdab --- /dev/null +++ b/src/components/common/icons/DoubleCircleIcon.tsx @@ -0,0 +1,31 @@ +import React from "react"; +import { useIconProps } from "./hooks"; +import { IconProps } from "./types"; + +const DoubleCircleIconComponent = (props: IconProps) => { + const { size, color } = useIconProps(props); + + return ( + + + + + + + + + + + ); +}; + +export const DoubleCircleIcon = React.memo(DoubleCircleIconComponent); diff --git a/src/components/common/icons/CheckmarkCircleIcon.tsx b/src/components/common/icons/MinusIcon.tsx similarity index 50% rename from src/components/common/icons/CheckmarkCircleIcon.tsx rename to src/components/common/icons/MinusIcon.tsx index 7b25649ce..b33fbe5cf 100644 --- a/src/components/common/icons/CheckmarkCircleIcon.tsx +++ b/src/components/common/icons/MinusIcon.tsx @@ -2,7 +2,7 @@ import React from "react"; import { useIconProps } from "./hooks"; import { IconProps } from "./types"; -const CheckmarkCircleIconComponent = (props: IconProps) => { +const MinusIconComponent = (props: IconProps) => { const { size, color } = useIconProps(props); return ( @@ -13,15 +13,12 @@ const CheckmarkCircleIconComponent = (props: IconProps) => { fill="none" viewBox="0 0 12 12" > - ); }; -export const CheckmarkCircleIcon = React.memo(CheckmarkCircleIconComponent); +export const MinusIcon = React.memo(MinusIconComponent); diff --git a/src/components/common/icons/PlusIcon.tsx b/src/components/common/icons/PlusIcon.tsx new file mode 100644 index 000000000..250409194 --- /dev/null +++ b/src/components/common/icons/PlusIcon.tsx @@ -0,0 +1,24 @@ +import React from "react"; +import { useIconProps } from "./hooks"; +import { IconProps } from "./types"; + +const PlusIconComponent = (props: IconProps) => { + const { size, color } = useIconProps(props); + + return ( + + + + ); +}; + +export const PlusIcon = React.memo(PlusIconComponent); diff --git a/src/components/common/icons/WarningTriangleIcon.tsx b/src/components/common/icons/WarningTriangleIcon.tsx deleted file mode 100644 index 3e005259b..000000000 --- a/src/components/common/icons/WarningTriangleIcon.tsx +++ /dev/null @@ -1,26 +0,0 @@ -import React from "react"; -import { useIconProps } from "./hooks"; -import { IconProps } from "./types"; - -const WarningTriangleIconComponent = (props: IconProps) => { - const { size, color } = useIconProps(props); - - return ( - - - - ); -}; - -export const WarningTriangleIcon = React.memo(WarningTriangleIconComponent); diff --git a/src/platform.ts b/src/platform.ts index 01773e85b..53dc57660 100644 --- a/src/platform.ts +++ b/src/platform.ts @@ -1,9 +1,10 @@ import { IDE, Platform } from "./globals"; +import { isString } from "./typeGuards/isString"; const PLATFORMS = ["JetBrains", "VS Code", "Other"]; const isPlatform = (platform: unknown): platform is Platform => - typeof platform === "string" && PLATFORMS.includes(platform); + isString(platform) && PLATFORMS.includes(platform); export const getPlatform = (platform: unknown): Platform => isPlatform(platform) ? platform : "Other"; @@ -12,8 +13,7 @@ export const platform = getPlatform(window.platform); const IDES = ["IDEA", "Rider", "PyCharm"]; -const isIDE = (ide: unknown): ide is IDE => - typeof ide === "string" && IDES.includes(ide); +const isIDE = (ide: unknown): ide is IDE => isString(ide) && IDES.includes(ide); export const getIDE = (ide: unknown): IDE | undefined => isIDE(ide) ? ide : undefined; diff --git a/src/typeGuards/isNumber.ts b/src/typeGuards/isNumber.ts new file mode 100644 index 000000000..14f80c301 --- /dev/null +++ b/src/typeGuards/isNumber.ts @@ -0,0 +1 @@ +export const isNumber = (x: unknown): x is number => typeof x === "number"; diff --git a/src/types.ts b/src/types.ts index d974f042c..3109722c6 100644 --- a/src/types.ts +++ b/src/types.ts @@ -1,3 +1,5 @@ +import { Duration } from "./globals"; + export enum InsightType { TopErrorFlows = "TopErrorFlows", SpanDurationChange = "SpanDurationChange", @@ -18,3 +20,45 @@ export enum InsightType { SpanScalingRootCause = "SpanScalingRootCause", SpanDurationBreakdown = "SpanDurationBreakdown" } + +export enum InsightImportance { + Spam = 9, + Clutter = 8, + NotInteresting = 7, + Info = 6, + Interesting = 5, + Important = 4, + HighlyImportant = 3, + Critical = 2, + ShowStopper = 1 +} + +export interface SpanInfo { + name: string; + displayName: string; + instrumentationLibrary: string; + spanCodeObjectId: string; + methodCodeObjectId: string | null; + kind: string; + + /** + * @deprecated + */ + codeObjectId: string | null; +} + +export interface DurationPercentileWithChange { + percentile: number; + currentDuration: Duration; + previousDuration: Duration | null; + changeTime: string | null; + changeVerified: boolean | null; + traceIds: string[]; +} + +export interface SpanInstanceInfo { + traceId: string; + spanId: string; + startTime: string; + duration: Duration; +} diff --git a/src/utils/addPrefix.ts b/src/utils/addPrefix.ts index 42da52a54..88a6bce31 100644 --- a/src/utils/addPrefix.ts +++ b/src/utils/addPrefix.ts @@ -1,3 +1,5 @@ +import { isString } from "../typeGuards/isString"; + type PrefixedMap = Record; export const addPrefix = >( @@ -10,9 +12,7 @@ export const addPrefix = >( for (const [key, value] of Object.entries(actions)) { const prop = key as keyof T; - res[prop] = `${prefix}${ - typeof separator === "string" ? separator : "/" - }${value}`; + res[prop] = `${prefix}${isString(separator) ? separator : "/"}${value}`; } return res; diff --git a/src/utils/getInsightImportanceColor.ts b/src/utils/getInsightImportanceColor.ts index ad99f37fe..6fe762416 100644 --- a/src/utils/getInsightImportanceColor.ts +++ b/src/utils/getInsightImportanceColor.ts @@ -11,10 +11,10 @@ export const getInsightImportanceColor = ( if (importance < 3) { switch (theme.mode) { case "light": - return "#f93967"; + return "#e00036"; case "dark": case "dark-jetbrains": - return "#e00036"; + return "#f93967"; } } if (importance < 5) { diff --git a/src/utils/getInsightTypeInfo.ts b/src/utils/getInsightTypeInfo.ts index 7e277100a..53ef06b6a 100644 --- a/src/utils/getInsightTypeInfo.ts +++ b/src/utils/getInsightTypeInfo.ts @@ -1,6 +1,7 @@ import { MemoExoticComponent } from "react"; import { AlarmClockIcon } from "../components/common/icons/AlarmClockIcon"; import { BottleneckIcon } from "../components/common/icons/BottleneckIcon"; +import { ClockWithTicksIcon } from "../components/common/icons/ClockWithTicksIcon"; import { MeterHighIcon } from "../components/common/icons/MeterHighIcon"; import { MeterLowIcon } from "../components/common/icons/MeterLowIcon"; import { SQLDatabaseIcon } from "../components/common/icons/SQLDatabaseIcon"; @@ -80,7 +81,7 @@ export const getInsightTypeInfo = ( label: "Duration" }, [InsightType.SpanDurationBreakdown]: { - icon: AlarmClockIcon, + icon: ClockWithTicksIcon, label: "Duration Breakdown" } }; diff --git a/src/utils/roundTo.ts b/src/utils/roundTo.ts new file mode 100644 index 000000000..6478f9cf9 --- /dev/null +++ b/src/utils/roundTo.ts @@ -0,0 +1,4 @@ +export const roundTo = (number: number, n: number): number => + Number.isInteger(n) && n >= 0 + ? Math.round((number + Number.EPSILON) * Math.pow(10, n)) / Math.pow(10, n) + : Math.round(number);