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);