From 394bda0ac83b2f48c00e7eca42ecca970d076773 Mon Sep 17 00:00:00 2001 From: Kyrylo Shmidt Date: Fri, 10 Jan 2025 18:41:22 +0100 Subject: [PATCH 1/2] Reuse IssuesReport in Dashboard Fix data normalization Add code generator for Swagger --- .github/workflows/update-dependencies.yml | 2 +- eslint.config.mjs | 12 +- openapi-config.ts | 57 +++ package-lock.json | 468 ++++++++++++++++++ package.json | 2 + .../Admin/Reports/CodeIssues/Header/styles.ts | 86 ---- .../Admin/Reports/CodeIssues/Header/types.ts | 6 - .../Admin/Reports/CodeIssues/index.tsx | 283 +++-------- .../Admin/Reports/CodeIssues/styles.ts | 59 --- .../Dashboard/MetricsReport/Chart/types.ts | 15 - .../Header/ReportHeader.stories.tsx | 50 -- .../Dashboard/MetricsReport/Header/index.tsx | 395 --------------- .../Dashboard/MetricsReport/Header/types.ts | 6 - .../MetricsReport/MetricsReport.stories.tsx | 2 +- .../Dashboard/MetricsReport/index.tsx | 307 +++++------- .../Dashboard/MetricsReport/styles.ts | 52 +- .../Dashboard/MetricsReport/types.ts | 105 ---- .../MetricsReport/useEndpointsIssuesData.ts | 28 -- .../MetricsReport/useServicesIssuesData.ts | 72 --- .../Dashboard/Report/ReportHeader/index.tsx | 2 +- .../insightCards/common/InsightCard/index.tsx | 2 +- src/components/InstallationWizard/index.tsx | 2 +- .../IssuesReport}/Chart/Chart.stories.tsx | 2 +- .../ReportTile/TooltipKeyValue/index.tsx | 0 .../ReportTile/TooltipKeyValue/styles.ts | 2 +- .../Chart/ReportTile/TooltipKeyValue/types.ts | 0 .../IssuesReport}/Chart/ReportTile/index.tsx | 14 +- .../IssuesReport}/Chart/ReportTile/styles.ts | 2 +- .../IssuesReport}/Chart/ReportTile/types.ts | 5 +- .../IssuesReport}/Chart/index.tsx | 9 +- .../IssuesReport}/Chart/styles.ts | 0 .../common/IssuesReport/Chart/types.ts | 14 + .../EmptyState/EmptyState.stories.tsx | 2 +- .../IssuesReport}/EmptyState/index.tsx | 10 +- .../IssuesReport}/EmptyState/styles.ts | 2 +- .../IssuesReport}/EmptyState/types.ts | 2 +- .../IssuesReport}/Header/Header.stories.tsx | 8 +- .../IssuesReport}/Header/index.tsx | 147 +++--- .../IssuesReport}/Header/styles.ts | 8 +- .../common/IssuesReport/Header/types.ts | 27 + .../IssuesReport/IssuesReport.stories.tsx | 19 + .../IssuesReport}/Table/Table.stories.tsx | 2 +- .../IssuesReport}/Table/index.tsx | 10 +- .../IssuesReport}/Table/mockData.ts | 2 +- .../IssuesReport}/Table/styles.ts | 2 +- .../IssuesReport}/Table/types.ts | 13 +- src/components/common/IssuesReport/index.tsx | 267 ++++++++++ src/components/common/IssuesReport/styles.ts | 12 + src/components/common/IssuesReport/types.ts | 44 ++ .../IssuesReport}/utils.ts | 7 +- src/components/common/TreeMap/Tile/types.ts | 2 +- .../common/TreeMap/TreeMap.stories.tsx | 12 +- src/components/common/TreeMap/index.tsx | 35 +- src/components/common/TreeMap/types.ts | 1 + src/containers/Admin/store.ts | 4 +- src/containers/Dashboard/hooks.ts | 5 + src/containers/Dashboard/index.tsx | 12 +- src/containers/Dashboard/store.ts | 18 + src/redux/services/digma.ts | 26 +- src/redux/services/digmaEmpty.ts | 13 + src/redux/services/types.ts | 89 +++- ...uesReportSlice.ts => issuesReportSlice.ts} | 44 +- src/store/metricsReport/metricsReportSlice.ts | 89 ---- .../metricsReport/useMetricsReportSelector.ts | 4 - src/store/useStore.ts | 9 +- 65 files changed, 1445 insertions(+), 1563 deletions(-) create mode 100644 openapi-config.ts delete mode 100644 src/components/Admin/Reports/CodeIssues/Header/styles.ts delete mode 100644 src/components/Admin/Reports/CodeIssues/Header/types.ts delete mode 100644 src/components/Admin/Reports/CodeIssues/styles.ts delete mode 100644 src/components/Dashboard/MetricsReport/Chart/types.ts delete mode 100644 src/components/Dashboard/MetricsReport/Header/ReportHeader.stories.tsx delete mode 100644 src/components/Dashboard/MetricsReport/Header/index.tsx delete mode 100644 src/components/Dashboard/MetricsReport/Header/types.ts delete mode 100644 src/components/Dashboard/MetricsReport/types.ts delete mode 100644 src/components/Dashboard/MetricsReport/useEndpointsIssuesData.ts delete mode 100644 src/components/Dashboard/MetricsReport/useServicesIssuesData.ts rename src/components/{Dashboard/MetricsReport => common/IssuesReport}/Chart/Chart.stories.tsx (95%) rename src/components/{Dashboard/MetricsReport => common/IssuesReport}/Chart/ReportTile/TooltipKeyValue/index.tsx (100%) rename src/components/{Dashboard/MetricsReport => common/IssuesReport}/Chart/ReportTile/TooltipKeyValue/styles.ts (79%) rename src/components/{Dashboard/MetricsReport => common/IssuesReport}/Chart/ReportTile/TooltipKeyValue/types.ts (100%) rename src/components/{Dashboard/MetricsReport => common/IssuesReport}/Chart/ReportTile/index.tsx (78%) rename src/components/{Dashboard/MetricsReport => common/IssuesReport}/Chart/ReportTile/styles.ts (81%) rename src/components/{Dashboard/MetricsReport => common/IssuesReport}/Chart/ReportTile/types.ts (60%) rename src/components/{Dashboard/MetricsReport => common/IssuesReport}/Chart/index.tsx (92%) rename src/components/{Dashboard/MetricsReport => common/IssuesReport}/Chart/styles.ts (100%) create mode 100644 src/components/common/IssuesReport/Chart/types.ts rename src/components/{Dashboard/MetricsReport => common/IssuesReport}/EmptyState/EmptyState.stories.tsx (94%) rename src/components/{Dashboard/MetricsReport => common/IssuesReport}/EmptyState/index.tsx (83%) rename src/components/{Dashboard/MetricsReport => common/IssuesReport}/EmptyState/styles.ts (67%) rename src/components/{Dashboard/MetricsReport => common/IssuesReport}/EmptyState/types.ts (87%) rename src/components/{Admin/Reports/CodeIssues => common/IssuesReport}/Header/Header.stories.tsx (82%) rename src/components/{Admin/Reports/CodeIssues => common/IssuesReport}/Header/index.tsx (70%) rename src/components/{Dashboard/MetricsReport => common/IssuesReport}/Header/styles.ts (87%) create mode 100644 src/components/common/IssuesReport/Header/types.ts create mode 100644 src/components/common/IssuesReport/IssuesReport.stories.tsx rename src/components/{Dashboard/MetricsReport => common/IssuesReport}/Table/Table.stories.tsx (95%) rename src/components/{Dashboard/MetricsReport => common/IssuesReport}/Table/index.tsx (96%) rename src/components/{Dashboard/MetricsReport => common/IssuesReport}/Table/mockData.ts (93%) rename src/components/{Dashboard/MetricsReport => common/IssuesReport}/Table/styles.ts (98%) rename src/components/{Dashboard/MetricsReport => common/IssuesReport}/Table/types.ts (71%) create mode 100644 src/components/common/IssuesReport/index.tsx create mode 100644 src/components/common/IssuesReport/styles.ts create mode 100644 src/components/common/IssuesReport/types.ts rename src/components/{Dashboard/MetricsReport => common/IssuesReport}/utils.ts (93%) create mode 100644 src/containers/Dashboard/hooks.ts create mode 100644 src/containers/Dashboard/store.ts create mode 100644 src/redux/services/digmaEmpty.ts rename src/redux/slices/{codeIssuesReportSlice.ts => issuesReportSlice.ts} (62%) delete mode 100644 src/store/metricsReport/metricsReportSlice.ts delete mode 100644 src/store/metricsReport/useMetricsReportSelector.ts diff --git a/.github/workflows/update-dependencies.yml b/.github/workflows/update-dependencies.yml index 49832b1af..02b26b460 100644 --- a/.github/workflows/update-dependencies.yml +++ b/.github/workflows/update-dependencies.yml @@ -34,4 +34,4 @@ jobs: git add dependencies.json git commit -m "Update dependencies" git push origin ${{ env.BRANCH_NAME }} - gh pr create -f -B main + gh pr create -f -B main --head ${{ env.BRANCH_NAME }} diff --git a/eslint.config.mjs b/eslint.config.mjs index 64332d716..971ae0ac8 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -37,7 +37,7 @@ export default tseslint.config( files: ["**/*.tsx"], ...reactPlugin.configs.flat["jsx-runtime"] }, - // TODO: Fix types by updating "eslint-plugin-react-hooks" once the following issue is resolved + // TODO: fix types by updating "eslint-plugin-react-hooks" once the following issue is resolved // https://github.com/facebook/react/issues/28313 // https://github.com/facebook/react/pull/30774 { @@ -74,5 +74,15 @@ export default tseslint.config( rules: { "react/jsx-curly-brace-presence": "off" } + }, + { + files: ["src/redux/services/digmaCodeGen.ts"], + rules: { + "@typescript-eslint/dot-notation": "warn", + "@typescript-eslint/no-redundant-type-constituents": "warn", + "@typescript-eslint/no-explicit-any": "warn", + "@typescript-eslint/consistent-indexed-object-style": "warn", + "@typescript-eslint/consistent-type-definitions": "warn" + } } ); diff --git a/openapi-config.ts b/openapi-config.ts new file mode 100644 index 000000000..8c2d5455d --- /dev/null +++ b/openapi-config.ts @@ -0,0 +1,57 @@ +import type { ConfigFile } from "@rtk-query/codegen-openapi"; + +const config: ConfigFile = { + schemaFile: "./swagger.json", + apiFile: "./src/redux/services/digmaEmpty.ts", + apiImport: "digmaEmptyApi", + outputFile: "./src/redux/services/digmaCodeGen.ts", + exportName: "digmaCodeGenApi", + endpointOverrides: [ + "postCodeAnalyticsErrorsCodeobjectSummary", + "postCodeAnalyticsCodeObjectsRecentActivity", + "postCodeAnalyticsCodeObjectsLens", + "postCodeAnalyticsCodeobjectsStatus", + "postCodeAnalyticsCodeObjectsSpanNavigation", + "postCodeAnalyticsEventsLatest", + "postCodeAnalyticsUserUsageStats", + "postErrors", + "postErrorsFilters", + "postGraphsGraphForSpanScaling", + "postGraphsGraphForSpanPercentiles", + "postHighlightsPerformance", + "postHighlightsTopInsights", + "postInsightsGetMethodUsage", + "postInsightsTypesForJaeger", + "postInsightsIssues", + "postHighlightsImpact", + "postHighlightsScaling", + "postLiveDataLiveData", + "postReportsServicesIssues", + "postReportsEndpointsIssues", + "postTestingGetLatestTestsOfSpan" + ].map((pattern) => ({ + pattern, + type: "query" + })), + filterEndpoints: [ + /About/, + /Assets/, + /Authentication/, + /CodeAnalytics/, + /Dashboard/, + /Environments/, + /Errors/, + /Graphs/, + /Highlights/, + /Insights/, + /InsightsActions/, + /LiveData/, + /Reports/, + /Services/, + /Spans/, + /Testing/ + ], + hooks: true +}; + +export default config; diff --git a/package-lock.json b/package-lock.json index 06ad01ba8..884e1de10 100644 --- a/package-lock.json +++ b/package-lock.json @@ -44,6 +44,7 @@ "@babel/preset-typescript": "^7.23.2", "@eslint/js": "^9.16.0", "@octokit/rest": "^21.0.2", + "@rtk-query/codegen-openapi": "^2.0.0", "@storybook/addon-a11y": "^8.4.7", "@storybook/addon-designs": "^8.0.4", "@storybook/addon-essentials": "^8.4.7", @@ -124,6 +125,99 @@ "node": ">=6.0.0" } }, + "node_modules/@apidevtools/json-schema-ref-parser": { + "version": "11.7.2", + "resolved": "https://registry.npmjs.org/@apidevtools/json-schema-ref-parser/-/json-schema-ref-parser-11.7.2.tgz", + "integrity": "sha512-4gY54eEGEstClvEkGnwVkTkrx0sqwemEFG5OSRRn3tD91XH0+Q8XIkYIfo7IwEWPpJZwILb9GUXeShtplRc/eA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jsdevtools/ono": "^7.1.3", + "@types/json-schema": "^7.0.15", + "js-yaml": "^4.1.0" + }, + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://github.com/sponsors/philsturgeon" + } + }, + "node_modules/@apidevtools/openapi-schemas": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@apidevtools/openapi-schemas/-/openapi-schemas-2.1.0.tgz", + "integrity": "sha512-Zc1AlqrJlX3SlpupFGpiLi2EbteyP7fXmUOGup6/DnkRgjP9bgMM/ag+n91rsv0U1Gpz0H3VILA/o3bW7Ua6BQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/@apidevtools/swagger-methods": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@apidevtools/swagger-methods/-/swagger-methods-3.0.2.tgz", + "integrity": "sha512-QAkD5kK2b1WfjDS/UQn/qQkbwF31uqRjPTrsCs5ZG9BQGAkjwvqGFjjPqAuzac/IYzpPtRzjCP1WrTuAIjMrXg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@apidevtools/swagger-parser": { + "version": "10.1.1", + "resolved": "https://registry.npmjs.org/@apidevtools/swagger-parser/-/swagger-parser-10.1.1.tgz", + "integrity": "sha512-u/kozRnsPO/x8QtKYJOqoGtC4kH6yg1lfYkB9Au0WhYB0FNLpyFusttQtvhlwjtG3rOwiRz4D8DnnXa8iEpIKA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@apidevtools/json-schema-ref-parser": "11.7.2", + "@apidevtools/openapi-schemas": "^2.1.0", + "@apidevtools/swagger-methods": "^3.0.2", + "@jsdevtools/ono": "^7.1.3", + "ajv": "^8.17.1", + "ajv-draft-04": "^1.0.0", + "call-me-maybe": "^1.0.2" + }, + "peerDependencies": { + "openapi-types": ">=7" + } + }, + "node_modules/@apidevtools/swagger-parser/node_modules/ajv": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/@apidevtools/swagger-parser/node_modules/ajv-draft-04": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/ajv-draft-04/-/ajv-draft-04-1.0.0.tgz", + "integrity": "sha512-mv00Te6nmYbRp5DCwclxtt7yV/joXJPGS7nM+97GdxvuttCOfgI3K4U25zboyeX0O+myI8ERluxQe5wljMmVIw==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "ajv": "^8.5.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, + "node_modules/@apidevtools/swagger-parser/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true, + "license": "MIT" + }, "node_modules/@babel/code-frame": { "version": "7.25.7", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.25.7.tgz", @@ -2652,6 +2746,13 @@ "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, + "node_modules/@exodus/schemasafe": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@exodus/schemasafe/-/schemasafe-1.3.0.tgz", + "integrity": "sha512-5Aap/GaRupgNx/feGBwLLTVv8OQFfv3pq2lPRzPg9R+IOBnDgghTGW7l7EuVXOvg5cc/xSAlRW8rBrjIC3Nvqw==", + "dev": true, + "license": "MIT" + }, "node_modules/@figspec/components": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/@figspec/components/-/components-1.0.3.tgz", @@ -3730,6 +3831,13 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, + "node_modules/@jsdevtools/ono": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/@jsdevtools/ono/-/ono-7.1.3.tgz", + "integrity": "sha512-4JQNk+3mVzK3xh2rqd6RB4J46qUR19azEHBneZyTZM+c456qOrbbM/5xcR8huNCCcbVt7+UmizG6GuUvPvKUYg==", + "dev": true, + "license": "MIT" + }, "node_modules/@jsonjoy.com/base64": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/@jsonjoy.com/base64/-/base64-1.1.2.tgz", @@ -3872,6 +3980,14 @@ "node": ">= 8" } }, + "node_modules/@oazapfts/runtime": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@oazapfts/runtime/-/runtime-1.0.3.tgz", + "integrity": "sha512-8tKiYffhwTGHSHYGnZ3oneLGCjX0po/XAXQ5Ng9fqKkvIdl/xz8+Vh8i+6xjzZqvZ2pLVpUcuSfnvNI/x67L0g==", + "dev": true, + "license": "MIT", + "peer": true + }, "node_modules/@octokit/auth-token": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-5.1.1.tgz", @@ -4069,6 +4185,52 @@ "node": ">=14.0.0" } }, + "node_modules/@rtk-query/codegen-openapi": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@rtk-query/codegen-openapi/-/codegen-openapi-2.0.0.tgz", + "integrity": "sha512-uIOshfqX6bcsMpiwUMKAC+oFEw2fUxICMruhXunB6wq7tHpUg2b+gz+qGjiWAWw1Ly6g6jjvb3N4HRxWy9Yqew==", + "dev": true, + "license": "MIT", + "dependencies": { + "@apidevtools/swagger-parser": "^10.0.2", + "commander": "^6.2.0", + "lodash.camelcase": "^4.3.0", + "oazapfts": "^6.1.0", + "prettier": "^3.2.5", + "semver": "^7.3.5", + "swagger2openapi": "^7.0.4", + "typescript": "^5.5.4" + }, + "bin": { + "rtk-query-codegen-openapi": "lib/bin/cli.mjs" + } + }, + "node_modules/@rtk-query/codegen-openapi/node_modules/commander": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-6.2.1.tgz", + "integrity": "sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/@rtk-query/codegen-openapi/node_modules/prettier": { + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.4.2.tgz", + "integrity": "sha512-e9MewbtFo+Fevyuxn/4rrcDAaq0IYxPGLvObpQjiZBMAzB9IGmzlnG9RZy3FFas+eBMu2vA0CszMeduow5dIuQ==", + "dev": true, + "license": "MIT", + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, "node_modules/@sinclair/typebox": { "version": "0.27.8", "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", @@ -7528,6 +7690,13 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/call-me-maybe": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-me-maybe/-/call-me-maybe-1.0.2.tgz", + "integrity": "sha512-HpX65o1Hnr9HH25ojC1YGs7HCQLq0GCOibSaWER0eNpgJ/Z1MZv2mTc7+xh6WOPxbRVcmgbv4hGU+uSQ/2xFZQ==", + "dev": true, + "license": "MIT" + }, "node_modules/callsites": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", @@ -9591,6 +9760,13 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/es6-promise": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-3.3.1.tgz", + "integrity": "sha512-SOp9Phqvqn7jtEUxPWdWfWoLmyt2VaJ6MpvP9Comy1MceMXqE6bxvaTu4iaxpYYPzhny28Lc+M87/c2cPK6lDg==", + "dev": true, + "license": "MIT" + }, "node_modules/esbuild": { "version": "0.24.0", "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.24.0.tgz", @@ -10264,6 +10440,13 @@ "dev": true, "license": "MIT" }, + "node_modules/fast-safe-stringify": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz", + "integrity": "sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==", + "dev": true, + "license": "MIT" + }, "node_modules/fast-uri": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.0.3.tgz", @@ -11486,6 +11669,13 @@ "dev": true, "license": "MIT" }, + "node_modules/http2-client": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/http2-client/-/http2-client-1.3.5.tgz", + "integrity": "sha512-EC2utToWl4RKfs5zd36Mxq7nzHHBuomZboI0yYL6Y0RmBgT7Sgkq4rQ0ezFTYoIsSs7Tm9SJe+o2FcAg6GBhGA==", + "dev": true, + "license": "MIT" + }, "node_modules/https-proxy-agent": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", @@ -14987,6 +15177,13 @@ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" }, + "node_modules/lodash.camelcase": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", + "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==", + "dev": true, + "license": "MIT" + }, "node_modules/lodash.clamp": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/lodash.clamp/-/lodash.clamp-4.0.3.tgz", @@ -15596,6 +15793,65 @@ "dev": true, "license": "MIT" }, + "node_modules/node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/node-fetch-h2": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/node-fetch-h2/-/node-fetch-h2-2.3.0.tgz", + "integrity": "sha512-ofRW94Ab0T4AOh5Fk8t0h8OBWrmjb0SSB20xh1H8YnPV9EJ+f5AMoYSUQ2zgJ4Iq2HAK0I2l5/Nequ8YzFS3Hg==", + "dev": true, + "license": "MIT", + "dependencies": { + "http2-client": "^1.2.5" + }, + "engines": { + "node": "4.x || >=6.0.0" + } + }, + "node_modules/node-fetch/node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", + "dev": true, + "license": "MIT" + }, + "node_modules/node-fetch/node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", + "dev": true, + "license": "BSD-2-Clause" + }, + "node_modules/node-fetch/node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "dev": true, + "license": "MIT", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, "node_modules/node-forge": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz", @@ -15613,6 +15869,16 @@ "dev": true, "license": "MIT" }, + "node_modules/node-readfiles": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/node-readfiles/-/node-readfiles-0.2.0.tgz", + "integrity": "sha512-SU00ZarexNlE4Rjdm83vglt5Y9yiQ+XI1XpflWlb7q7UTN1JUItm69xMeiQCTxtTfnzt+83T8Cx+vI2ED++VDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es6-promise": "^3.2.1" + } + }, "node_modules/node-releases": { "version": "2.0.18", "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.18.tgz", @@ -15659,6 +15925,102 @@ "integrity": "sha512-cTGB9ptp9dY9A5VbMSe7fQBcl/tt22Vcqdq8+eN93rblOuE0aCFu4aZ2vMwct/2t+lFnosm8RkQW1I0Omb1UtQ==", "dev": true }, + "node_modules/oas-kit-common": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/oas-kit-common/-/oas-kit-common-1.0.8.tgz", + "integrity": "sha512-pJTS2+T0oGIwgjGpw7sIRU8RQMcUoKCDWFLdBqKB2BNmGpbBMH2sdqAaOXUg8OzonZHU0L7vfJu1mJFEiYDWOQ==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "fast-safe-stringify": "^2.0.7" + } + }, + "node_modules/oas-linter": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/oas-linter/-/oas-linter-3.2.2.tgz", + "integrity": "sha512-KEGjPDVoU5K6swgo9hJVA/qYGlwfbFx+Kg2QB/kd7rzV5N8N5Mg6PlsoCMohVnQmo+pzJap/F610qTodKzecGQ==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@exodus/schemasafe": "^1.0.0-rc.2", + "should": "^13.2.1", + "yaml": "^1.10.0" + }, + "funding": { + "url": "https://github.com/Mermade/oas-kit?sponsor=1" + } + }, + "node_modules/oas-resolver": { + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/oas-resolver/-/oas-resolver-2.5.6.tgz", + "integrity": "sha512-Yx5PWQNZomfEhPPOphFbZKi9W93CocQj18NlD2Pa4GWZzdZpSJvYwoiuurRI7m3SpcChrnO08hkuQDL3FGsVFQ==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "node-fetch-h2": "^2.3.0", + "oas-kit-common": "^1.0.8", + "reftools": "^1.1.9", + "yaml": "^1.10.0", + "yargs": "^17.0.1" + }, + "bin": { + "resolve": "resolve.js" + }, + "funding": { + "url": "https://github.com/Mermade/oas-kit?sponsor=1" + } + }, + "node_modules/oas-schema-walker": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/oas-schema-walker/-/oas-schema-walker-1.1.5.tgz", + "integrity": "sha512-2yucenq1a9YPmeNExoUa9Qwrt9RFkjqaMAA1X+U7sbb0AqBeTIdMHky9SQQ6iN94bO5NW0W4TRYXerG+BdAvAQ==", + "dev": true, + "license": "BSD-3-Clause", + "funding": { + "url": "https://github.com/Mermade/oas-kit?sponsor=1" + } + }, + "node_modules/oas-validator": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/oas-validator/-/oas-validator-5.0.8.tgz", + "integrity": "sha512-cu20/HE5N5HKqVygs3dt94eYJfBi0TsZvPVXDhbXQHiEityDN+RROTleefoKRKKJ9dFAF2JBkDHgvWj0sjKGmw==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "call-me-maybe": "^1.0.1", + "oas-kit-common": "^1.0.8", + "oas-linter": "^3.2.2", + "oas-resolver": "^2.5.6", + "oas-schema-walker": "^1.1.5", + "reftools": "^1.1.9", + "should": "^13.2.1", + "yaml": "^1.10.0" + }, + "funding": { + "url": "https://github.com/Mermade/oas-kit?sponsor=1" + } + }, + "node_modules/oazapfts": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/oazapfts/-/oazapfts-6.1.0.tgz", + "integrity": "sha512-+E0db72jn0AMJ36ZzEXF3qCZ+T4pnOem/tmiJVql1Kx4qkWBE4YGeiUicp3gkzLJ/OrmpvSyAufI8eZ7sODCYg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@apidevtools/swagger-parser": "^10.1.0", + "lodash": "^4.17.21", + "minimist": "^1.2.8", + "swagger2openapi": "^7.0.8", + "tapable": "^2.2.1", + "typescript": "^5.4.5" + }, + "bin": { + "oazapfts": "cli.js" + }, + "peerDependencies": { + "@oazapfts/runtime": "*" + } + }, "node_modules/object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", @@ -15839,6 +16201,14 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/openapi-types": { + "version": "12.1.3", + "resolved": "https://registry.npmjs.org/openapi-types/-/openapi-types-12.1.3.tgz", + "integrity": "sha512-N4YtSYJqghVu4iek2ZUvcN/0aqH1kRDuNqzcycDxhOUpg7GdvLa2F3DgS6yBNhInhv2r/6I0Flkn7CqL8+nIcw==", + "dev": true, + "license": "MIT", + "peer": true + }, "node_modules/optionator": { "version": "0.9.4", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", @@ -17190,6 +17560,16 @@ "node": ">=6" } }, + "node_modules/reftools": { + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/reftools/-/reftools-1.1.9.tgz", + "integrity": "sha512-OVede/NQE13xBQ+ob5CKd5KyeJYU2YInb1bmV4nRoOfquZPkAkxuOXicSe1PvqIuZZ4kD13sPKBbR7UFDmli6w==", + "dev": true, + "license": "BSD-3-Clause", + "funding": { + "url": "https://github.com/Mermade/oas-kit?sponsor=1" + } + }, "node_modules/regenerate": { "version": "1.4.2", "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", @@ -17993,6 +18373,66 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/should": { + "version": "13.2.3", + "resolved": "https://registry.npmjs.org/should/-/should-13.2.3.tgz", + "integrity": "sha512-ggLesLtu2xp+ZxI+ysJTmNjh2U0TsC+rQ/pfED9bUZZ4DKefP27D+7YJVVTvKsmjLpIi9jAa7itwDGkDDmt1GQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "should-equal": "^2.0.0", + "should-format": "^3.0.3", + "should-type": "^1.4.0", + "should-type-adaptors": "^1.0.1", + "should-util": "^1.0.0" + } + }, + "node_modules/should-equal": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/should-equal/-/should-equal-2.0.0.tgz", + "integrity": "sha512-ZP36TMrK9euEuWQYBig9W55WPC7uo37qzAEmbjHz4gfyuXrEUgF8cUvQVO+w+d3OMfPvSRQJ22lSm8MQJ43LTA==", + "dev": true, + "license": "MIT", + "dependencies": { + "should-type": "^1.4.0" + } + }, + "node_modules/should-format": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/should-format/-/should-format-3.0.3.tgz", + "integrity": "sha512-hZ58adtulAk0gKtua7QxevgUaXTTXxIi8t41L3zo9AHvjXO1/7sdLECuHeIN2SRtYXpNkmhoUP2pdeWgricQ+Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "should-type": "^1.3.0", + "should-type-adaptors": "^1.0.1" + } + }, + "node_modules/should-type": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/should-type/-/should-type-1.4.0.tgz", + "integrity": "sha512-MdAsTu3n25yDbIe1NeN69G4n6mUnJGtSJHygX3+oN0ZbO3DTiATnf7XnYJdGT42JCXurTb1JI0qOBR65shvhPQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/should-type-adaptors": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/should-type-adaptors/-/should-type-adaptors-1.1.0.tgz", + "integrity": "sha512-JA4hdoLnN+kebEp2Vs8eBe9g7uy0zbRo+RMcU0EsNy+R+k049Ki+N5tT5Jagst2g7EAja+euFuoXFCa8vIklfA==", + "dev": true, + "license": "MIT", + "dependencies": { + "should-type": "^1.3.0", + "should-util": "^1.0.0" + } + }, + "node_modules/should-util": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/should-util/-/should-util-1.0.1.tgz", + "integrity": "sha512-oXF8tfxx5cDk8r2kYqlkUJzZpDBqVY/II2WhvU0n9Y3XYvAYRmeaf1PvvIvTgPnv4KJ+ES5M0PyDq5Jp+Ygy2g==", + "dev": true, + "license": "MIT" + }, "node_modules/side-channel": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz", @@ -18980,6 +19420,34 @@ "integrity": "sha512-ovssysQTa+luh7A5Weu3Rta6FJlFBBbInjOh722LIt6klpU2/HtdUbszju/G4devcvk8PGt7FCLv5wftu3THUA==", "dev": true }, + "node_modules/swagger2openapi": { + "version": "7.0.8", + "resolved": "https://registry.npmjs.org/swagger2openapi/-/swagger2openapi-7.0.8.tgz", + "integrity": "sha512-upi/0ZGkYgEcLeGieoz8gT74oWHA0E7JivX7aN9mAf+Tc7BQoRBvnIGHoPDw+f9TXTW4s6kGYCZJtauP6OYp7g==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "call-me-maybe": "^1.0.1", + "node-fetch": "^2.6.1", + "node-fetch-h2": "^2.3.0", + "node-readfiles": "^0.2.0", + "oas-kit-common": "^1.0.8", + "oas-resolver": "^2.5.6", + "oas-schema-walker": "^1.1.5", + "oas-validator": "^5.0.8", + "reftools": "^1.1.9", + "yaml": "^1.10.0", + "yargs": "^17.0.1" + }, + "bin": { + "boast": "boast.js", + "oas-validate": "oas-validate.js", + "swagger2openapi": "swagger2openapi.js" + }, + "funding": { + "url": "https://github.com/Mermade/oas-kit?sponsor=1" + } + }, "node_modules/symbol-tree": { "version": "3.2.4", "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", diff --git a/package.json b/package.json index 02e8e5caa..b69c3d2a1 100644 --- a/package.json +++ b/package.json @@ -13,6 +13,7 @@ "start": "webpack serve --config webpack.dev.ts --env PLATFORM=Web", "storybook": "storybook dev -p 6006", "build-storybook": "storybook build", + "generate-types": "npx @rtk-query/codegen-openapi openapi-config.ts", "get-jaeger-ui": "node --experimental-transform-types ./scripts/get-jaeger-ui.mts", "update-dependencies": "node --experimental-transform-types ./scripts/update-dependencies.mts", "prebuild": "rimraf dist && npm run get-jaeger-ui", @@ -54,6 +55,7 @@ "@babel/preset-typescript": "^7.23.2", "@eslint/js": "^9.16.0", "@octokit/rest": "^21.0.2", + "@rtk-query/codegen-openapi": "^2.0.0", "@storybook/addon-a11y": "^8.4.7", "@storybook/addon-designs": "^8.0.4", "@storybook/addon-essentials": "^8.4.7", diff --git a/src/components/Admin/Reports/CodeIssues/Header/styles.ts b/src/components/Admin/Reports/CodeIssues/Header/styles.ts deleted file mode 100644 index afb878773..000000000 --- a/src/components/Admin/Reports/CodeIssues/Header/styles.ts +++ /dev/null @@ -1,86 +0,0 @@ -import styled from "styled-components"; -import { - subheading2BoldTypography, - subheading2RegularTypography -} from "../../../../common/App/typographies"; -import { Select } from "../../../../common/v3/Select"; -import { Toggle } from "../../../../common/v3/Toggle"; -import { OptionButton } from "../../../../common/v3/Toggle/styles"; - -export const Container = styled.div` - display: flex; - gap: 24px; - flex-direction: column; -`; - -export const TitleContainer = styled.div` - display: flex; - gap: 8px; - overflow: hidden; -`; - -export const Title = styled.span` - ${subheading2BoldTypography} - - overflow: hidden; - white-space: nowrap; - text-overflow: ellipsis; - color: ${({ theme }) => theme.colors.v3.text.primary}; -`; - -export const TitleSuffix = styled.span` - ${subheading2RegularTypography} - - color: ${({ theme }) => theme.colors.v3.text.secondary}; -`; - -export const Row = styled.div` - display: flex; - justify-content: space-between; - align-items: center; - gap: 18px; -`; - -export const FilterSelect = styled(Select)` - height: 36px; - min-width: 190px; - border-radius: 8px; -`; - -export const Filters = styled(Row)` - display: flex; - gap: 18px; -`; - -const StyledToggle = styled(Toggle)` - align-items: center; - background-color: transparent; - border-radius: 8px; - border-color: ${({ theme }) => theme.colors.v3.stroke.primaryLight}; -`; - -export const ViewModeToggle = styled(StyledToggle)` - padding: 6px; - - ${OptionButton} { - padding: 6px; - } -`; - -export const TimeModeToggle = styled(StyledToggle)` - padding: 0; - - ${OptionButton} { - padding: 10px 16px; - - &:first-child { - border-bottom-right-radius: 0; - border-top-right-radius: 0; - } - - &:last-child { - border-bottom-left-radius: 0; - border-top-left-radius: 0; - } - } -`; diff --git a/src/components/Admin/Reports/CodeIssues/Header/types.ts b/src/components/Admin/Reports/CodeIssues/Header/types.ts deleted file mode 100644 index 4374b2435..000000000 --- a/src/components/Admin/Reports/CodeIssues/Header/types.ts +++ /dev/null @@ -1,6 +0,0 @@ -export interface HeaderProps { - onGoBack: () => void; -} -export interface GetServicesPayload { - environment: string | null; -} diff --git a/src/components/Admin/Reports/CodeIssues/index.tsx b/src/components/Admin/Reports/CodeIssues/index.tsx index 48e0153b0..0cdc5c76c 100644 --- a/src/components/Admin/Reports/CodeIssues/index.tsx +++ b/src/components/Admin/Reports/CodeIssues/index.tsx @@ -1,41 +1,25 @@ -import { useEffect, useMemo } from "react"; import { useAdminDispatch, useAdminSelector } from "../../../../containers/Admin/hooks"; -import { getFeatureFlagValue } from "../../../../featureFlags"; -import { - useGetAboutQuery, - useGetEndpointsIssuesQuery, - useGetEnvironmentServicesQuery, - useGetEnvironmentsQuery, - useGetServicesIssuesQuery -} from "../../../../redux/services/digma"; +import type { IssueCriticality } from "../../../../redux/services/types"; import { + setCriticalityLevels, + setPeriodInDays, + setSelectedEndpoints, setSelectedEnvironmentId, setSelectedService, - setViewLevel -} from "../../../../redux/slices/codeIssuesReportSlice"; -import { FeatureFlag } from "../../../../types"; -import { Chart } from "../../../Dashboard/MetricsReport/Chart"; -import { EmptyState } from "../../../Dashboard/MetricsReport/EmptyState"; -import { Table } from "../../../Dashboard/MetricsReport/Table"; -import type { - EndpointIssuesData, - GetMetricsReportDataPayloadV1, - GetMetricsReportDataPayloadV2, - ScoreCriterion, - ServiceIssuesData -} from "../../../Dashboard/MetricsReport/types"; -import { - transformEndpointsData, - transformServicesData -} from "../../../Dashboard/MetricsReport/utils"; -import { Header } from "./Header"; -import * as s from "./styles"; + setSelectedServices, + setTimeMode, + setViewLevel, + setViewMode, + type IssuesReportTimeMode, + type IssuesReportViewLevel, + type IssuesReportViewMode +} from "../../../../redux/slices/issuesReportSlice"; +import { IssuesReport } from "../../../common/IssuesReport"; export const CodeIssues = () => { - // TODO: create selectors const selectedEnvironmentId = useAdminSelector( (state) => state.codeIssuesReport.selectedEnvironmentId ); @@ -62,208 +46,79 @@ export const CodeIssues = () => { const dispatch = useAdminDispatch(); - const { data: about } = useGetAboutQuery(); - - const { data: environments } = useGetEnvironmentsQuery(); - - useEffect(() => { - if (environments && environments.length > 0 && !selectedEnvironmentId) { - dispatch(setSelectedEnvironmentId(environments[0].id)); - } - }, [environments, selectedEnvironmentId, dispatch]); - - const isInitialized = Boolean(environments && about); - - const { data: services } = useGetEnvironmentServicesQuery( - { - environment: selectedEnvironmentId ?? null - }, - { - skip: !selectedEnvironmentId || viewLevel !== "services" - } - ); - - const isDataFilterEnabled = Boolean( - about && - getFeatureFlagValue( - about, - FeatureFlag.IS_METRICS_REPORT_DATA_FILTER_ENABLED - ) - ); - - const getServicesIssuesPayloadV1: GetMetricsReportDataPayloadV1 = - useMemo(() => { - const servicesArray = - selectedServices.length > 0 ? selectedServices : services ?? []; - - return { - keys: servicesArray.map((x) => ({ - environment: selectedEnvironmentId ?? "", - service: x, - lastDays: timeMode === "baseline" ? null : periodInDays - })) - }; - }, [ - selectedServices, - services, - selectedEnvironmentId, - timeMode, - periodInDays - ]); - - const getServicesIssuesPayloadV2: GetMetricsReportDataPayloadV2 = - useMemo(() => { - const servicesArray = - selectedServices.length > 0 ? selectedServices : services ?? [null]; - - return { - criticalities: criticalityLevels, - keys: servicesArray.map((x) => ({ - environment: selectedEnvironmentId ?? "", - service: x, - lastDays: timeMode === "baseline" ? null : periodInDays - })) - }; - }, [ - selectedServices, - services, - selectedEnvironmentId, - criticalityLevels, - timeMode, - periodInDays - ]); - - const getServicesIssuesPayload = isDataFilterEnabled - ? getServicesIssuesPayloadV2 - : getServicesIssuesPayloadV1; - - const { data: servicesIssues } = useGetServicesIssuesQuery( - getServicesIssuesPayload, + const handleTileTitleClick = () => + // viewLevel: IssuesReportViewLevel, + // value: string { - skip: - !isInitialized || - !selectedEnvironmentId || - !services || - viewLevel !== "services" - } - ); + // TODO: implement + }; - const { data: endpointsIssues } = useGetEndpointsIssuesQuery( + const handleTileIssuesStatsClick = () => + // viewLevel: IssuesReportViewLevel, + // value: string { - environment: selectedEnvironmentId ?? "", - service: selectedService ?? "", - endpoints: selectedEndpoints, - criticalities: criticalityLevels, - lastDays: timeMode === "baseline" ? null : periodInDays - }, - { - skip: - !isInitialized || - !selectedEnvironmentId || - !selectedService || - viewLevel !== "endpoints" - } - ); - - const handleTitleClick = (value: string) => { - if (viewLevel === "services") { - dispatch(setSelectedService(value)); - dispatch(setViewLevel("endpoints")); - } + // TODO: implement + }; - // TODO: make optional for endpoints + const handleSelectedEnvironmentIdChange = (environmentId: string) => { + dispatch(setSelectedEnvironmentId(environmentId)); }; - const handleIssuesStatsClick = () => { - // TODO: remove and make optional + const handleSelectedServicesChange = (services: string[]) => { + dispatch(setSelectedServices(services)); }; - const handleGoBack = () => { - dispatch(setViewLevel("services")); - dispatch(setSelectedService(null)); + const handleSelectedEndpointsChange = (endpoints: string[]) => { + dispatch(setSelectedEndpoints(endpoints)); }; - const isCriticalityEnabled = Boolean( - about && - getFeatureFlagValue( - about, - FeatureFlag.IS_METRICS_REPORT_CRITICALITY_ENABLED - ) - ); - - const scoreCriterion: ScoreCriterion = isCriticalityEnabled - ? "criticality" - : "impact"; + const handleCriticalityLevelsChange = (criticalities: IssueCriticality[]) => { + dispatch(setCriticalityLevels(criticalities)); + }; - const data = - (viewLevel === "services" - ? servicesIssues?.reports - : endpointsIssues?.reports) ?? []; - const transformedData = - viewLevel === "services" - ? transformServicesData(data as ServiceIssuesData[], scoreCriterion) - : transformEndpointsData(data as EndpointIssuesData[], scoreCriterion); + const handlePeriodInDaysChange = (periodInDays: number) => { + dispatch(setPeriodInDays(periodInDays)); + }; - const renderContent = () => { - if ( - (viewLevel === "services" && !servicesIssues) || - (viewLevel === "endpoints" && !endpointsIssues) - ) { - return ; - } + const handleTimeModeChange = (timeMode: IssuesReportTimeMode) => { + dispatch(setTimeMode(timeMode)); + }; - if (data.length === 0) { - if (viewLevel === "services") { - return ; - } + const handleViewModeChange = (viewMode: IssuesReportViewMode) => { + dispatch(setViewMode(viewMode)); + }; - if (viewLevel === "endpoints") { - return ; - } - } + const handleViewLevelChange = (viewLevel: IssuesReportViewLevel) => { + dispatch(setViewLevel(viewLevel)); + }; - return ( - <> - {viewMode === "table" && ( - - )} - {viewMode === "treemap" && ( - - )} - - ); + const handleSelectedServiceChange = (service: string | null) => { + dispatch(setSelectedService(service)); }; return ( - - - {isInitialized ? ( - environments && environments.length > 0 ? ( - <> -
- {renderContent()} - - ) : ( - - ) - ) : ( - - )} - - + ); }; diff --git a/src/components/Admin/Reports/CodeIssues/styles.ts b/src/components/Admin/Reports/CodeIssues/styles.ts deleted file mode 100644 index 938ce95a4..000000000 --- a/src/components/Admin/Reports/CodeIssues/styles.ts +++ /dev/null @@ -1,59 +0,0 @@ -import styled from "styled-components"; -import { bodyRegularTypography } from "../../../common/App/typographies"; - -export const Container = styled.div` - display: flex; - flex-direction: column; - height: 100%; - width: 100%; - padding: 24px 24px 16px; - gap: 24px; - box-sizing: border-box; - overflow: auto; -`; - -export const Footer = styled.div` - align-items: center; - display: flex; - justify-content: start; - gap: 8px; - margin-top: auto; - color: ${({ theme }) => theme.colors.v3.text.disabled}; - ${bodyRegularTypography} -`; - -export const Section = styled.div` - display: flex; - height: 100%; - width: 100%; - flex-direction: column; - position: relative; - overflow: auto; -`; - -// export const ContainerBackgroundGradient = styled.div` -// z-index: -1; -// position: absolute; -// margin: auto; -// right: -24%; -// bottom: -117%; -// height: 160%; -// width: 146%; -// border-radius: 100%; -// opacity: 0.7; -// background: radial-gradient( -// 50% 50% at 50% 50%, -// rgb(79 93 163 / 60%) 0%, -// rgb(79 93 163 / 0%) 100% -// ); -// filter: blur(5px); -// `; - -// export const SectionBackground = styled.div` -// z-index: -1; -// position: absolute; -// inset: 0; -// height: 100%; -// width: 100%; -// background: ${({ theme }) => theme.colors.v3.surface.secondary}; -// `; diff --git a/src/components/Dashboard/MetricsReport/Chart/types.ts b/src/components/Dashboard/MetricsReport/Chart/types.ts deleted file mode 100644 index 15bec0be9..000000000 --- a/src/components/Dashboard/MetricsReport/Chart/types.ts +++ /dev/null @@ -1,15 +0,0 @@ -import type { - PresentationalReportData, - ReportTimeMode, - ReportViewLevel, - ScoreCriterion -} from "../types"; - -export interface ChartProps { - data: PresentationalReportData[]; - onTitleClick: (value: string) => void; - onIssuesStatsClick: (value: string) => void; - scoreCriterion: ScoreCriterion; - viewLevel: ReportViewLevel; - timeMode: ReportTimeMode; -} diff --git a/src/components/Dashboard/MetricsReport/Header/ReportHeader.stories.tsx b/src/components/Dashboard/MetricsReport/Header/ReportHeader.stories.tsx deleted file mode 100644 index c3363437b..000000000 --- a/src/components/Dashboard/MetricsReport/Header/ReportHeader.stories.tsx +++ /dev/null @@ -1,50 +0,0 @@ -import type { Meta, StoryObj } from "@storybook/react"; - -import { Header } from "."; -import { actions as globalActions } from "../../../../actions"; -import { actions } from "../../actions"; - -// More on how to set up stories at: https://storybook.js.org/docs/react/writing-stories/introduction -const meta: Meta = { - title: "Dashboard/MetricsReport/Header", - component: Header, - 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 Default: Story = { - play: () => { - window.setTimeout(() => { - window.postMessage({ - type: "digma", - action: globalActions.SET_ENVIRONMENTS, - payload: [ - { - id: "test1", - name: "test1", - type: "Public" - }, - { - id: "test2", - name: "test2", - type: "Public" - } - ] - }); - }, 500); - window.setTimeout(() => { - window.postMessage({ - type: "digma", - action: actions.SET_SERVICES, - payload: ["service 3", "service 1", "service 2", "service 4"] - }); - }, 500); - } -}; diff --git a/src/components/Dashboard/MetricsReport/Header/index.tsx b/src/components/Dashboard/MetricsReport/Header/index.tsx deleted file mode 100644 index 2320392fd..000000000 --- a/src/components/Dashboard/MetricsReport/Header/index.tsx +++ /dev/null @@ -1,395 +0,0 @@ -import { useEffect, useMemo } from "react"; -import { getFeatureFlagValue } from "../../../../featureFlags"; -import type { DataFetcherConfiguration } from "../../../../hooks/useFetchData"; -import { useFetchData } from "../../../../hooks/useFetchData"; -import { useConfigSelector } from "../../../../store/config/useConfigSelector"; -import { useMetricsReportSelector } from "../../../../store/metricsReport/useMetricsReportSelector"; -import { useStore } from "../../../../store/useStore"; -import { FeatureFlag } from "../../../../types"; -import { sendUserActionTrackingEvent } from "../../../../utils/actions/sendUserActionTrackingEvent"; -import { formatUnit } from "../../../../utils/formatUnit"; -import { CodeIcon } from "../../../common/icons/12px/CodeIcon"; -import { DurationBreakdownIcon } from "../../../common/icons/12px/DurationBreakdownIcon"; -import { WrenchIcon } from "../../../common/icons/12px/WrenchIcon"; -import { InfinityIcon } from "../../../common/icons/16px/InfinityIcon"; -import { TableIcon } from "../../../common/icons/16px/TableIcon"; -import { TreemapIcon } from "../../../common/icons/16px/TreemapIcon"; -import { ChevronIcon } from "../../../common/icons/20px/ChevronIcon"; -import { DatabaseIcon } from "../../../common/icons/DatabaseIcon"; -import { Direction } from "../../../common/icons/types"; -import type { ToggleValue } from "../../../common/Toggle/types"; -import { NewIconButton } from "../../../common/v3/NewIconButton"; -import { Tooltip } from "../../../common/v3/Tooltip"; -import { actions } from "../../actions"; -import { trackingEvents } from "../tracking"; -import type { - Criticality, - GetServiceEndpointsPayload, - GetServiceEnvironmentsPayload, - ReportTimeMode, - ReportViewMode, - SetServiceEndpointsPayload, - SetServiceEnvironmentsPayload -} from "../types"; -import * as s from "./styles"; -import type { GetServicesPayload, HeaderProps } from "./types"; - -const criticalityOptions: { id: Criticality; label: string }[] = [ - { - id: "High", - label: "Critical" - }, - { - id: "Medium", - label: "Medium" - }, - { - id: "Low", - label: "Low" - } -]; - -const DEFAULT_PERIOD = 7; - -export const Header = ({ onGoBack }: HeaderProps) => { - const { environments, backendInfo } = useConfigSelector(); - const { - metricsReportSelectedPeriodInDays: periodInDays, - metricsReportViewLevel: viewLevel, - metricsReportTimeMode: timeMode, - metricsReportSelectedServices: selectedServices, - metricsReportSelectedEndpoints: selectedEndpoints, - metricsReportSelectedEnvironmentId: selectedEnvironmentId, - metricsReportSelectedCriticalityLevels: selectedCriticalityLevels, - metricsReportSelectedService: selectedService, - metricsReportServiceEnvironments: serviceEnvironments, - metricsReportViewMode: viewMode - } = useMetricsReportSelector(); - - const { - setMetricsReportSelectedEnvironmentId: setSelectedEnvironmentId, - setMetricsReportSelectedServices: setSelectedServices, - setMetricsReportSelectedEndpoints: setSelectedEndpoints, - setMetricsReportSelectedCriticalityLevels: setSelectedCriticalityLevels, - setMetricsReportSelectedPeriodInDays: setPeriodInDays, - setMetricsReportServiceEnvironments: setServiceEnvironments, - setMetricsReportServiceEndpoints: setServiceEndpoints, - setMetricsReportViewMode: setViewMode, - setMetricsReportTimeMode: setTimeMode, - setMetricsReportServices: setServices, - setMetricsReportServicesIssuesData: setServicesIssuesData, - setMetricsReportEndpointsIssuesData: setEndpointIssuesData - } = useStore.getState(); - - const environmentsToSelect = useMemo( - () => (viewLevel === "services" ? environments : serviceEnvironments) ?? [], - [viewLevel, environments, serviceEnvironments] - ); - - const selectedEnvironment = useMemo( - () => - environmentsToSelect?.find((x) => x.id === selectedEnvironmentId) ?? null, - [selectedEnvironmentId, environmentsToSelect] - ); - - const isDataFilterEnabled = Boolean( - getFeatureFlagValue( - backendInfo, - FeatureFlag.IS_METRICS_REPORT_DATA_FILTER_ENABLED - ) - ); - - const dataFetcherServicesConfiguration: DataFetcherConfiguration = { - requestAction: actions.GET_SERVICES, - responseAction: actions.SET_SERVICES, - refreshOnPayloadChange: true, - isEnabled: Boolean(selectedEnvironmentId && viewLevel === "services") - }; - - const getServicesPayload: GetServicesPayload = useMemo( - () => ({ environment: selectedEnvironmentId ?? null }), - [selectedEnvironmentId] - ); - - const { data: services } = useFetchData( - dataFetcherServicesConfiguration, - getServicesPayload - ); - - useEffect(() => { - if (services) { - setServices(services); - } - }, [services, setServices]); - - const dataFetcherEnvironmentsConfiguration: DataFetcherConfiguration = { - requestAction: actions.GET_SERVICE_ENVIRONMENTS, - responseAction: actions.SET_SERVICE_ENVIRONMENTS, - refreshOnPayloadChange: true, - isEnabled: Boolean( - selectedEnvironmentId && selectedService && viewLevel === "endpoints" - ) - }; - - const getEnvironmentsPayload: GetServiceEnvironmentsPayload = useMemo( - () => ({ - service: selectedService ?? "" - }), - [selectedService] - ); - - const { data: serviceEnvironmentsData } = useFetchData< - GetServiceEnvironmentsPayload, - SetServiceEnvironmentsPayload - >(dataFetcherEnvironmentsConfiguration, getEnvironmentsPayload); - - const dataFetcherEndpointsConfiguration: DataFetcherConfiguration = { - requestAction: actions.GET_SERVICE_ENDPOINTS_DATA, - responseAction: actions.SET_SERVICE_ENDPOINTS_DATA, - refreshOnPayloadChange: true, - isEnabled: Boolean(selectedEnvironmentId && selectedService) - }; - - useEffect(() => { - if (serviceEnvironmentsData?.environments) { - setServiceEnvironments(serviceEnvironmentsData?.environments); - } - }, [serviceEnvironmentsData, setServiceEnvironments]); - - const getEndpointsPayload: GetServiceEndpointsPayload = useMemo( - () => ({ - environment: selectedEnvironmentId ?? "", - service: selectedService ?? "" - }), - [selectedEnvironmentId, selectedService] - ); - - const { data: endpointsData } = useFetchData< - GetServiceEndpointsPayload, - SetServiceEndpointsPayload - >(dataFetcherEndpointsConfiguration, getEndpointsPayload); - - useEffect(() => { - if (endpointsData?.endpoints) { - setServiceEndpoints(endpointsData.endpoints); - } - }, [endpointsData, setServiceEndpoints]); - - const handleGoBack = () => { - onGoBack(); - }; - - const handleSelectedEnvironmentChanged = (option: string | string[]) => { - sendUserActionTrackingEvent(trackingEvents.ENVIRONMENT_FILTER_SELECTED); - const newItem = Array.isArray(option) ? option[0] : option; - setSelectedEnvironmentId(newItem); - setSelectedServices([]); - setSelectedEndpoints([]); - }; - - const handleSelectedServicesChanged = (option: string | string[]) => { - sendUserActionTrackingEvent(trackingEvents.SERVICE_FILTER_SELECTED); - const newItem = Array.isArray(option) ? option : [option]; - setSelectedServices(newItem); - }; - - const handleSelectedEndpointsChanged = (option: string | string[]) => { - sendUserActionTrackingEvent(trackingEvents.ENDPOINT_FILTER_SELECTED); - const newItem = Array.isArray(option) ? option : [option]; - setSelectedEndpoints(newItem); - }; - - const handleDataChanged = (option: string | string[]) => { - sendUserActionTrackingEvent(trackingEvents.DATA_FILTER_SELECTED); - const newItem = Array.isArray(option) ? option : [option]; - setSelectedCriticalityLevels(newItem as Criticality[]); - }; - - const handlePeriodChanged = (option: string | string[]) => { - sendUserActionTrackingEvent(trackingEvents.PERIOD_FILTER_CHANGED); - const newItem = Array.isArray(option) ? option : [option]; - if (newItem.length === 0) { - setPeriodInDays(DEFAULT_PERIOD); - return; - } - - const value = newItem[0]; - const newValue = Number(value); - setPeriodInDays(newValue); - }; - - const handleViewModeChanged = (value: ToggleValue) => { - sendUserActionTrackingEvent(trackingEvents.VIEW_MODE_CHANGED, { value }); - const newViewMode = value as ReportViewMode; - setViewMode(newViewMode); - }; - - const handleTimeModeChanged = (value: ToggleValue) => { - sendUserActionTrackingEvent(trackingEvents.TIME_MODE_CHANGED, { value }); - const newTimeMode = value as ReportTimeMode; - setTimeMode(newTimeMode); - if (viewLevel === "services") { - setServicesIssuesData(null); - } - if (viewLevel === "endpoints") { - setEndpointIssuesData(null); - } - }; - - const title = - viewLevel === "endpoints" - ? `${selectedService ?? ""} Service` - : "Issues Map"; - const titleSuffix = viewLevel === "endpoints" ? " Endpoints" : ""; - const tooltipTitle = `${title} ${titleSuffix}`; - - return ( - - - - {viewLevel === "endpoints" ? ( - <> - ( - - )} - size={"small"} - buttonType={"secondaryBorderless"} - onClick={handleGoBack} - /> - - - {title} - {titleSuffix} - - - - ) : ( - {title} - )} - - - - - - a.name.localeCompare(b.name)) - .map((x) => ({ - label: x.name, - value: x.id, - enabled: true, - selected: x.id === selectedEnvironmentId - }))} - showSelectedState={true} - icon={(props) => - selectedEnvironment?.type === "Public" ? ( - - ) : ( - - ) - } - onChange={handleSelectedEnvironmentChanged} - placeholder={selectedEnvironment?.name ?? "Select Environments"} - disabled={environmentsToSelect.length === 0} - /> - {viewLevel === "endpoints" ? ( - ({ - label: x.displayName, - value: x.spanCodeObjectId, - enabled: true, - selected: selectedEndpoints.includes(x.spanCodeObjectId) - })) ?? [] - } - useShift={false} - sameWidth={false} - showSelectedState={true} - multiselect={true} - icon={WrenchIcon} - onChange={handleSelectedEndpointsChanged} - searchable={true} - placeholder={ - selectedEndpoints.length > 0 ? "Endpoints" : "All Endpoints" - } - disabled={!endpointsData || endpointsData.endpoints.length === 0} - /> - ) : ( - ({ - label: service, - value: service, - enabled: true, - selected: selectedServices.includes(service) - })) ?? [] - } - showSelectedState={true} - multiselect={true} - icon={WrenchIcon} - onChange={handleSelectedServicesChanged} - placeholder={ - selectedServices.length > 0 ? "Services" : "All Services" - } - disabled={!services || services.length === 0} - /> - )} - {timeMode === "changes" && ( - ({ - value: x.toString(), - label: `${x} ${formatUnit(x, "Day")}`, - selected: x === periodInDays, - enabled: true - }))} - showSelectedState={false} - icon={DurationBreakdownIcon} - onChange={handlePeriodChanged} - placeholder={`Period: ${periodInDays} ${formatUnit( - periodInDays, - "day" - )}`} - /> - )} - {isDataFilterEnabled && ( - ({ - label: item.label, - value: item.id, - enabled: true, - selected: selectedCriticalityLevels.includes(item.id) - }))} - multiselect={true} - icon={DatabaseIcon} - onChange={handleDataChanged} - placeholder={"Data"} - /> - )} - - - }, - { - value: "table", - icon: (props) => - } - ]} - value={viewMode} - onValueChange={handleViewModeChanged} - /> - - - ); -}; diff --git a/src/components/Dashboard/MetricsReport/Header/types.ts b/src/components/Dashboard/MetricsReport/Header/types.ts deleted file mode 100644 index 4374b2435..000000000 --- a/src/components/Dashboard/MetricsReport/Header/types.ts +++ /dev/null @@ -1,6 +0,0 @@ -export interface HeaderProps { - onGoBack: () => void; -} -export interface GetServicesPayload { - environment: string | null; -} diff --git a/src/components/Dashboard/MetricsReport/MetricsReport.stories.tsx b/src/components/Dashboard/MetricsReport/MetricsReport.stories.tsx index b5b84909c..569533b7c 100644 --- a/src/components/Dashboard/MetricsReport/MetricsReport.stories.tsx +++ b/src/components/Dashboard/MetricsReport/MetricsReport.stories.tsx @@ -1,8 +1,8 @@ import type { Meta, StoryObj } from "@storybook/react"; import { MetricsReport } from "."; +import { mockedReport } from "../../common/IssuesReport/Table/mockData"; import { actions } from "../actions"; -import { mockedReport } from "./Table/mockData"; // More on how to set up stories at: https://storybook.js.org/docs/react/writing-stories/introduction const meta: Meta = { diff --git a/src/components/Dashboard/MetricsReport/index.tsx b/src/components/Dashboard/MetricsReport/index.tsx index 16280f1b7..dcbc72905 100644 --- a/src/components/Dashboard/MetricsReport/index.tsx +++ b/src/components/Dashboard/MetricsReport/index.tsx @@ -1,68 +1,62 @@ -import { useEffect, useLayoutEffect, useMemo } from "react"; -import { getFeatureFlagValue } from "../../../featureFlags"; +import { useLayoutEffect } from "react"; +import { + useDashboardDispatch, + useDashboardSelector +} from "../../../containers/Dashboard/hooks"; import { useMount } from "../../../hooks/useMount"; -import { useConfigSelector } from "../../../store/config/useConfigSelector"; -import { useMetricsReportSelector } from "../../../store/metricsReport/useMetricsReportSelector"; -import { useStore } from "../../../store/useStore"; +import { type IssueCriticality } from "../../../redux/services/types"; +import { + setCriticalityLevels, + setPeriodInDays, + setSelectedEndpoints, + setSelectedEnvironmentId, + setSelectedService, + setSelectedServices, + setTimeMode, + setViewLevel, + setViewMode, + type IssuesReportTimeMode, + type IssuesReportViewLevel, + type IssuesReportViewMode +} from "../../../redux/slices/issuesReportSlice"; import { isString } from "../../../typeGuards/isString"; -import { FeatureFlag, SCOPE_CHANGE_EVENTS } from "../../../types"; +import { SCOPE_CHANGE_EVENTS } from "../../../types"; import { changeScope } from "../../../utils/actions/changeScope"; +import { IssuesReport } from "../../common/IssuesReport"; import { DigmaLogoIcon } from "../../common/icons/16px/DigmaLogoIcon"; import { actions } from "../actions"; -import { Chart } from "./Chart"; -import { EmptyState } from "./EmptyState"; -import { Header } from "./Header"; import * as s from "./styles"; -import { Table } from "./Table"; -import type { - EndpointIssuesData, - GetEndpointsIssuesPayload, - ScoreCriterion, - ServiceIssuesData, - UseServicesIssuesDataProps -} from "./types"; -import { useEndpointsIssuesData } from "./useEndpointsIssuesData"; -import { useServicesIssuesData } from "./useServicesIssuesData"; -import { transformEndpointsData, transformServicesData } from "./utils"; export const MetricsReport = () => { - const { environments, backendInfo } = useConfigSelector(); - const { - metricsReportViewLevel: viewLevel, - metricsReportViewMode: viewMode, - metricsReportTimeMode: timeMode, - metricsReportSelectedEnvironmentId: selectedEnvironmentId, - metricsReportSelectedService: selectedService, - metricsReportSelectedServices: selectedServices, - metricsReportSelectedCriticalityLevels: selectedCriticalityLevels, - metricsReportSelectedPeriodInDays: selectedPeriodInDays, - metricsReportSelectedEndpoints: selectedEndpoints, - metricsReportServices: services, - metricsReportServicesIssuesData: servicesIssuesData, - metricsReportEndpointsIssuesData: endpointsIssuesData - } = useMetricsReportSelector(); - - const { - setMetricsReportSelectedService: setSelectedService, - setMetricsReportSelectedEnvironmentId: setSelectedEnvironmentId, - setMetricsReportViewLevel: setViewLevel, - setMetricsReportServicesIssuesData: setServicesIssuesData, - setMetricsReportEndpointsIssuesData: setEndpointsIssuesData - } = useStore.getState(); - - const isCriticalityEnabled = Boolean( - backendInfo && - getFeatureFlagValue( - backendInfo, - FeatureFlag.IS_METRICS_REPORT_CRITICALITY_ENABLED - ) + const selectedEnvironmentId = useDashboardSelector( + (state) => state.metricsReport.selectedEnvironmentId + ); + const criticalityLevels = useDashboardSelector( + (state) => state.metricsReport.criticalityLevels + ); + const periodInDays = useDashboardSelector( + (state) => state.metricsReport.periodInDays + ); + const selectedService = useDashboardSelector( + (state) => state.metricsReport.selectedService + ); + const selectedServices = useDashboardSelector( + (state) => state.metricsReport.selectedServices + ); + const viewLevel = useDashboardSelector( + (state) => state.metricsReport.viewLevel + ); + const viewMode = useDashboardSelector( + (state) => state.metricsReport.viewMode + ); + const timeMode = useDashboardSelector( + (state) => state.metricsReport.timeMode + ); + const selectedEndpoints = useDashboardSelector( + (state) => state.metricsReport.selectedEndpoints ); - const scoreCriterion: ScoreCriterion = isCriticalityEnabled - ? "criticality" - : "impact"; - - const isInitialized = environments && backendInfo; + const dispatch = useDashboardDispatch(); useLayoutEffect(() => { window.sendMessageToDigma({ @@ -76,73 +70,6 @@ export const MetricsReport = () => { } }); - const useServiceIssuesDataPayload: UseServicesIssuesDataProps = - useMemo(() => { - return { - environmentId: selectedEnvironmentId, - services: - selectedServices.length > 0 ? selectedServices : services ?? [], - criticalities: selectedCriticalityLevels, - lastDays: timeMode === "baseline" ? null : selectedPeriodInDays - }; - }, [ - selectedEnvironmentId, - selectedServices, - selectedCriticalityLevels, - selectedPeriodInDays, - services, - timeMode - ]); - - const { data: servicesData } = useServicesIssuesData( - useServiceIssuesDataPayload, - Boolean( - isInitialized && - selectedEnvironmentId && - services && - viewLevel === "services" - ) - ); - - useEffect(() => { - if (servicesData) { - setServicesIssuesData(servicesData.reports); - } - }, [servicesData, setServicesIssuesData]); - - const endpointsIssuesPayload: GetEndpointsIssuesPayload = useMemo( - () => ({ - environment: selectedEnvironmentId ?? "", - service: selectedService ?? "", - endpoints: selectedEndpoints, - criticalities: selectedCriticalityLevels, - lastDays: timeMode === "baseline" ? null : selectedPeriodInDays - }), - [ - selectedEnvironmentId, - selectedService, - selectedEndpoints, - selectedCriticalityLevels, - selectedPeriodInDays, - timeMode - ] - ); - const { data: endpointsData } = useEndpointsIssuesData( - endpointsIssuesPayload, - Boolean( - isInitialized && - selectedEnvironmentId && - selectedService && - viewLevel === "endpoints" - ) - ); - - useEffect(() => { - if (endpointsData) { - setEndpointsIssuesData(endpointsData.reports); - } - }, [endpointsData, setEndpointsIssuesData]); - const goToEndpointIssues = ({ spanCodeObjectId, service, @@ -167,12 +94,10 @@ export const MetricsReport = () => { }); }; - const handleTitleClick = (value: string) => { - if (viewLevel === "services") { - setSelectedService(value); - setViewLevel("endpoints"); - } - + const handleTileTitleClick = ( + viewLevel: IssuesReportViewLevel, + value: string + ) => { if (viewLevel === "endpoints" && selectedEnvironmentId && selectedService) { goToEndpointIssues({ spanCodeObjectId: value, @@ -182,7 +107,10 @@ export const MetricsReport = () => { } }; - const handleIssuesStatsClick = (value: string) => { + const handleIssuesStatsClick = ( + viewLevel: IssuesReportViewLevel, + value: string + ) => { if (viewLevel === "services") { changeScope({ span: null, @@ -206,84 +134,75 @@ export const MetricsReport = () => { } }; - const handleGoBack = () => { - setViewLevel("services"); - setSelectedService(null); + const handleSelectedEnvironmentIdChange = (environmentId: string) => { + dispatch(setSelectedEnvironmentId(environmentId)); }; - const data = - (viewLevel === "services" ? servicesIssuesData : endpointsIssuesData) ?? []; - const transformedData = - viewLevel === "services" - ? transformServicesData(data as ServiceIssuesData[], scoreCriterion) - : transformEndpointsData(data as EndpointIssuesData[], scoreCriterion); + const handleSelectedServicesChange = (services: string[]) => { + dispatch(setSelectedServices(services)); + }; - const renderContent = () => { - if ( - (viewLevel === "services" && !servicesIssuesData) || - (viewLevel === "endpoints" && !endpointsIssuesData) - ) { - return ; - } + const handleSelectedEndpointsChange = (endpoints: string[]) => { + dispatch(setSelectedEndpoints(endpoints)); + }; - if (data.length === 0) { - if (viewLevel === "services") { - return ; - } + const handleCriticalityLevelsChange = (criticalities: IssueCriticality[]) => { + dispatch(setCriticalityLevels(criticalities)); + }; - if (viewLevel === "endpoints") { - return ; - } - } + const handlePeriodInDaysChange = (periodInDays: number) => { + dispatch(setPeriodInDays(periodInDays)); + }; + + const handleTimeModeChange = (timeMode: IssuesReportTimeMode) => { + dispatch(setTimeMode(timeMode)); + }; + + const handleViewModeChange = (viewMode: IssuesReportViewMode) => { + dispatch(setViewMode(viewMode)); + }; + + const handleViewLevelChange = (viewLevel: IssuesReportViewLevel) => { + dispatch(setViewLevel(viewLevel)); + }; - return ( - <> - {viewMode === "table" && ( -
- )} - {viewMode === "treemap" && ( - - )} - - ); + const handleSelectedServiceChange = (service: string | null) => { + dispatch(setSelectedService(service)); }; return ( - - + + - - {isInitialized ? ( - environments.length > 0 ? ( - <> -
- {renderContent()} - - ) : ( - - ) - ) : ( - - )} + + © {new Date().getFullYear()} digma.ai - - + + ); }; diff --git a/src/components/Dashboard/MetricsReport/styles.ts b/src/components/Dashboard/MetricsReport/styles.ts index a3a1c2896..463ba9163 100644 --- a/src/components/Dashboard/MetricsReport/styles.ts +++ b/src/components/Dashboard/MetricsReport/styles.ts @@ -2,27 +2,6 @@ import styled from "styled-components"; import { bodyRegularTypography } from "../../common/App/typographies"; export const Container = styled.div` - display: flex; - flex-direction: column; - height: 100%; - width: 100%; - padding: 24px 24px 16px; - gap: 24px; - box-sizing: border-box; - overflow: auto; -`; - -export const Footer = styled.div` - align-items: center; - display: flex; - justify-content: start; - gap: 8px; - margin-top: auto; - color: ${({ theme }) => theme.colors.v3.text.disabled}; - ${bodyRegularTypography} -`; - -export const Section = styled.div` display: flex; height: 100%; flex-direction: column; @@ -30,6 +9,15 @@ export const Section = styled.div` overflow: hidden; `; +export const ContainerBackground = styled.div` + z-index: -1; + position: absolute; + inset: 0; + height: 100%; + width: 100%; + background: ${({ theme }) => theme.colors.v3.surface.secondary}; +`; + export const ContainerBackgroundGradient = styled.div` z-index: -1; position: absolute; @@ -48,11 +36,23 @@ export const ContainerBackgroundGradient = styled.div` filter: blur(5px); `; -export const SectionBackground = styled.div` - z-index: -1; - position: absolute; - inset: 0; +export const IssuesReportContainer = styled.div` + display: flex; + flex-direction: column; height: 100%; width: 100%; - background: ${({ theme }) => theme.colors.v3.surface.secondary}; + padding: 24px 24px 16px; + gap: 24px; + box-sizing: border-box; + overflow: auto; +`; + +export const Footer = styled.div` + align-items: center; + display: flex; + justify-content: start; + gap: 8px; + margin-top: auto; + color: ${({ theme }) => theme.colors.v3.text.disabled}; + ${bodyRegularTypography} `; diff --git a/src/components/Dashboard/MetricsReport/types.ts b/src/components/Dashboard/MetricsReport/types.ts deleted file mode 100644 index 8e19e46aa..000000000 --- a/src/components/Dashboard/MetricsReport/types.ts +++ /dev/null @@ -1,105 +0,0 @@ -import type { EnvironmentType } from "../../common/App/types"; -import type { Severity } from "./Table/types"; - -export type Criticality = "Low" | "Medium" | "High"; - -export type ReportViewMode = "treemap" | "table"; - -export type ReportTimeMode = "baseline" | "changes"; - -export type ScoreCriterion = "impact" | "criticality"; - -export type ReportViewLevel = "services" | "endpoints"; - -export interface UseServicesIssuesDataProps { - environmentId: string | null; - services: string[]; - criticalities: Criticality[]; - lastDays: number | null; -} - -export interface GetMetricsReportDataPayloadV1 { - keys: { - environment: string; - service: string; - lastDays: number | null; - }[]; -} - -export interface GetMetricsReportDataPayloadV2 { - criticalities: Criticality[]; - keys: { - environment: string; - service: string | null; - lastDays: number | null; - }[]; -} - -export interface ServiceIssuesData { - key: { - environment: string; - service: string; - lastDays: number | null; - }; - issues: number; - impact: number; - criticality: number; -} - -export interface SetMetricsReportDataPayload { - reports: ServiceIssuesData[]; -} - -export interface GetServiceEndpointsPayload { - environment: string; - service: string; -} - -export interface EndpointData { - displayName: string; - spanCodeObjectId: string; -} - -export interface SetServiceEndpointsPayload { - endpoints: EndpointData[]; -} - -export interface GetEndpointsIssuesPayload { - environment: string; - service: string; - endpoints: string[]; - criticalities: Criticality[]; - lastDays: number | null; -} - -export interface EndpointIssuesData { - displayName: string; - spanCodeObjectId: string; - issues: number; - impact: number; - criticality: number; -} - -export interface SetEndpointsIssuesPayload { - reports: EndpointIssuesData[]; -} - -export interface GetServiceEnvironmentsPayload { - service: string; -} - -export interface SetServiceEnvironmentsPayload { - environments: { - id: string; - name: string; - type: EnvironmentType; - }[]; -} - -export interface PresentationalReportData { - id: string; - name: string; - score: number; - criticalIssuesCount: number; - severity: Severity; -} diff --git a/src/components/Dashboard/MetricsReport/useEndpointsIssuesData.ts b/src/components/Dashboard/MetricsReport/useEndpointsIssuesData.ts deleted file mode 100644 index 065244bff..000000000 --- a/src/components/Dashboard/MetricsReport/useEndpointsIssuesData.ts +++ /dev/null @@ -1,28 +0,0 @@ -import type { DataFetcherConfiguration } from "../../../hooks/useFetchData"; -import { useFetchData } from "../../../hooks/useFetchData"; -import { actions } from "../actions"; -import type { - GetEndpointsIssuesPayload, - SetEndpointsIssuesPayload -} from "./types"; - -export const useEndpointsIssuesData = ( - payload: GetEndpointsIssuesPayload, - isEnabled: boolean -) => { - const dataFetcherServiceDataConfiguration: DataFetcherConfiguration = { - requestAction: actions.GET_ENDPOINTS_ISSUES, - responseAction: actions.SET_ENDPOINTS_ISSUES, - refreshOnPayloadChange: true, - isEnabled - }; - - const { data } = useFetchData< - GetEndpointsIssuesPayload, - SetEndpointsIssuesPayload - >(dataFetcherServiceDataConfiguration, payload); - - return { - data - }; -}; diff --git a/src/components/Dashboard/MetricsReport/useServicesIssuesData.ts b/src/components/Dashboard/MetricsReport/useServicesIssuesData.ts deleted file mode 100644 index 1e3b499cc..000000000 --- a/src/components/Dashboard/MetricsReport/useServicesIssuesData.ts +++ /dev/null @@ -1,72 +0,0 @@ -import { useMemo } from "react"; -import { getFeatureFlagValue } from "../../../featureFlags"; -import type { DataFetcherConfiguration } from "../../../hooks/useFetchData"; -import { useFetchData } from "../../../hooks/useFetchData"; -import { useConfigSelector } from "../../../store/config/useConfigSelector"; -import { FeatureFlag } from "../../../types"; -import { actions } from "../actions"; -import type { - GetMetricsReportDataPayloadV1, - GetMetricsReportDataPayloadV2, - SetMetricsReportDataPayload, - UseServicesIssuesDataProps -} from "./types"; - -export const useServicesIssuesData = ( - { - services, - environmentId, - criticalities, - lastDays - }: UseServicesIssuesDataProps, - isEnabled: boolean -) => { - const { backendInfo } = useConfigSelector(); - const isDataFilterEnabled = Boolean( - getFeatureFlagValue( - backendInfo, - FeatureFlag.IS_METRICS_REPORT_DATA_FILTER_ENABLED - ) - ); - - const dataFetcherServiceDataConfiguration: DataFetcherConfiguration = { - requestAction: actions.GET_METRICS_REPORT_DATA, - responseAction: actions.SET_METRICS_REPORT_DATA, - refreshOnPayloadChange: true, - isEnabled - }; - - const payloadV1: GetMetricsReportDataPayloadV1 = useMemo(() => { - return { - keys: services.map((x) => ({ - environment: environmentId ?? "", - service: x, - lastDays - })) - }; - }, [services, environmentId, lastDays]); - - const payloadV2: GetMetricsReportDataPayloadV2 = useMemo(() => { - const servicesArray = services.length > 0 ? services : [null]; - - return { - criticalities, - keys: servicesArray.map((x) => ({ - environment: environmentId ?? "", - service: x, - lastDays: lastDays - })) - }; - }, [services, environmentId, criticalities, lastDays]); - - const payload = isDataFilterEnabled ? payloadV2 : payloadV1; - - const { data } = useFetchData< - GetMetricsReportDataPayloadV1 | GetMetricsReportDataPayloadV2, - SetMetricsReportDataPayload - >(dataFetcherServiceDataConfiguration, payload); - - return { - data - }; -}; diff --git a/src/components/Dashboard/Report/ReportHeader/index.tsx b/src/components/Dashboard/Report/ReportHeader/index.tsx index e57bfc49b..0c22ad8e6 100644 --- a/src/components/Dashboard/Report/ReportHeader/index.tsx +++ b/src/components/Dashboard/Report/ReportHeader/index.tsx @@ -78,7 +78,7 @@ export const ReportHeader = ({ { - // TODO + // TODO: implement }} onRefresh={() => { handleRefresh(); diff --git a/src/components/Insights/InsightsCatalog/InsightsPage/insightCards/common/InsightCard/index.tsx b/src/components/Insights/InsightsCatalog/InsightsPage/insightCards/common/InsightCard/index.tsx index c6cb96df6..a8b4c7e04 100644 --- a/src/components/Insights/InsightsCatalog/InsightsPage/insightCards/common/InsightCard/index.tsx +++ b/src/components/Insights/InsightsCatalog/InsightsPage/insightCards/common/InsightCard/index.tsx @@ -328,7 +328,7 @@ export const InsightCard = ({ label={"Pin"} title={"Pin"} onClick={() => - //TODO: implement + // TODO: implement { return undefined; } diff --git a/src/components/InstallationWizard/index.tsx b/src/components/InstallationWizard/index.tsx index 4c8a786b7..6bd61c9ee 100644 --- a/src/components/InstallationWizard/index.tsx +++ b/src/components/InstallationWizard/index.tsx @@ -77,7 +77,7 @@ export const InstallationWizard = () => { // const [userEmail, setUserEmail] = useState(""); // const [isUserEmailCaptured, setIsUserEmailCaptured] = useState(false); - // TO DO: + // TODO: // add environment variable for presetting the correct installation type // if Digma already installed // const preselectedInstallationType = diff --git a/src/components/Dashboard/MetricsReport/Chart/Chart.stories.tsx b/src/components/common/IssuesReport/Chart/Chart.stories.tsx similarity index 95% rename from src/components/Dashboard/MetricsReport/Chart/Chart.stories.tsx rename to src/components/common/IssuesReport/Chart/Chart.stories.tsx index e6508384f..c558c4359 100644 --- a/src/components/Dashboard/MetricsReport/Chart/Chart.stories.tsx +++ b/src/components/common/IssuesReport/Chart/Chart.stories.tsx @@ -7,7 +7,7 @@ import { transformServicesData } from "../utils"; // More on how to set up stories at: https://storybook.js.org/docs/react/writing-stories/introduction const meta: Meta = { - title: "Dashboard/MetricsReport/Chart", + title: "common/IssuesReport/Chart", component: Chart, parameters: { // More on how to position stories at: https://storybook.js.org/docs/react/configure/story-layout diff --git a/src/components/Dashboard/MetricsReport/Chart/ReportTile/TooltipKeyValue/index.tsx b/src/components/common/IssuesReport/Chart/ReportTile/TooltipKeyValue/index.tsx similarity index 100% rename from src/components/Dashboard/MetricsReport/Chart/ReportTile/TooltipKeyValue/index.tsx rename to src/components/common/IssuesReport/Chart/ReportTile/TooltipKeyValue/index.tsx diff --git a/src/components/Dashboard/MetricsReport/Chart/ReportTile/TooltipKeyValue/styles.ts b/src/components/common/IssuesReport/Chart/ReportTile/TooltipKeyValue/styles.ts similarity index 79% rename from src/components/Dashboard/MetricsReport/Chart/ReportTile/TooltipKeyValue/styles.ts rename to src/components/common/IssuesReport/Chart/ReportTile/TooltipKeyValue/styles.ts index 9b62cd90c..4dbda53c1 100644 --- a/src/components/Dashboard/MetricsReport/Chart/ReportTile/TooltipKeyValue/styles.ts +++ b/src/components/common/IssuesReport/Chart/ReportTile/TooltipKeyValue/styles.ts @@ -1,5 +1,5 @@ import styled from "styled-components"; -import { footnoteRegularTypography } from "../../../../../common/App/typographies"; +import { footnoteRegularTypography } from "../../../../App/typographies"; export const Container = styled.div` ${footnoteRegularTypography} diff --git a/src/components/Dashboard/MetricsReport/Chart/ReportTile/TooltipKeyValue/types.ts b/src/components/common/IssuesReport/Chart/ReportTile/TooltipKeyValue/types.ts similarity index 100% rename from src/components/Dashboard/MetricsReport/Chart/ReportTile/TooltipKeyValue/types.ts rename to src/components/common/IssuesReport/Chart/ReportTile/TooltipKeyValue/types.ts diff --git a/src/components/Dashboard/MetricsReport/Chart/ReportTile/index.tsx b/src/components/common/IssuesReport/Chart/ReportTile/index.tsx similarity index 78% rename from src/components/Dashboard/MetricsReport/Chart/ReportTile/index.tsx rename to src/components/common/IssuesReport/Chart/ReportTile/index.tsx index babf3e831..8d3295618 100644 --- a/src/components/Dashboard/MetricsReport/Chart/ReportTile/index.tsx +++ b/src/components/common/IssuesReport/Chart/ReportTile/index.tsx @@ -1,12 +1,12 @@ import type { MouseEvent } from "react"; -import { Tile } from "../../../../common/TreeMap/Tile"; -import type { ReportTimeMode } from "../../types"; +import type { IssuesReportTimeMode } from "../../../../../redux/slices/issuesReportSlice"; +import { Tile } from "../../../TreeMap/Tile"; import * as s from "./styles"; import { TooltipKeyValue } from "./TooltipKeyValue"; import type { ReportTileProps } from "./types"; -const getFormattedNumber = (viewMode: ReportTimeMode, value: number) => - `${viewMode === "changes" && value > 0 ? "+" : ""}${value}`; +const getFormattedNumber = (timeMode: IssuesReportTimeMode, value: number) => + `${timeMode === "changes" && value > 0 ? "+" : ""}${value}`; export const ReportTile = ({ name, @@ -14,15 +14,15 @@ export const ReportTile = ({ scoreCriterion, score, severity, - viewMode, + timeMode, onTitleClick, onIssuesClick }: ReportTileProps) => { const formattedCriticalIssuesCount = getFormattedNumber( - viewMode, + timeMode, criticalIssuesCount ); - const formattedScore = getFormattedNumber(viewMode, score); + const formattedScore = getFormattedNumber(timeMode, score); const handleTitleClick = () => { if (onTitleClick) { diff --git a/src/components/Dashboard/MetricsReport/Chart/ReportTile/styles.ts b/src/components/common/IssuesReport/Chart/ReportTile/styles.ts similarity index 81% rename from src/components/Dashboard/MetricsReport/Chart/ReportTile/styles.ts rename to src/components/common/IssuesReport/Chart/ReportTile/styles.ts index 2c7a7b6a0..7ec68962c 100644 --- a/src/components/Dashboard/MetricsReport/Chart/ReportTile/styles.ts +++ b/src/components/common/IssuesReport/Chart/ReportTile/styles.ts @@ -1,5 +1,5 @@ import styled from "styled-components"; -import { subheading2BoldTypography } from "../../../../common/App/typographies"; +import { subheading2BoldTypography } from "../../../App/typographies"; export const TooltipContent = styled.div` color: ${({ theme }) => theme.colors.v3.text.primary}; diff --git a/src/components/Dashboard/MetricsReport/Chart/ReportTile/types.ts b/src/components/common/IssuesReport/Chart/ReportTile/types.ts similarity index 60% rename from src/components/Dashboard/MetricsReport/Chart/ReportTile/types.ts rename to src/components/common/IssuesReport/Chart/ReportTile/types.ts index 73440388c..25c59897e 100644 --- a/src/components/Dashboard/MetricsReport/Chart/ReportTile/types.ts +++ b/src/components/common/IssuesReport/Chart/ReportTile/types.ts @@ -1,5 +1,6 @@ +import type { IssuesReportTimeMode } from "../../../../../redux/slices/issuesReportSlice"; import type { Severity } from "../../Table/types"; -import type { ReportTimeMode, ScoreCriterion } from "../../types"; +import type { ScoreCriterion } from "../../types"; export interface ReportTileProps { name: string; @@ -7,7 +8,7 @@ export interface ReportTileProps { scoreCriterion: ScoreCriterion; score: number; severity: Severity; - viewMode: ReportTimeMode; + timeMode: IssuesReportTimeMode; onTitleClick?: () => void; onIssuesClick?: () => void; } diff --git a/src/components/Dashboard/MetricsReport/Chart/index.tsx b/src/components/common/IssuesReport/Chart/index.tsx similarity index 92% rename from src/components/Dashboard/MetricsReport/Chart/index.tsx rename to src/components/common/IssuesReport/Chart/index.tsx index 743eac382..18cbda43f 100644 --- a/src/components/Dashboard/MetricsReport/Chart/index.tsx +++ b/src/components/common/IssuesReport/Chart/index.tsx @@ -4,9 +4,9 @@ import useDimensions from "react-cool-dimensions"; import useScrollbarSize from "react-scrollbar-size"; import type { Input } from "squarify"; import { sendUserActionTrackingEvent } from "../../../../utils/actions/sendUserActionTrackingEvent"; -import { TreeMap } from "../../../common/TreeMap"; -import type { TileData } from "../../../common/TreeMap/types"; -import { trackingEvents } from "../tracking"; +import { trackingEvents } from "../../../Dashboard/MetricsReport/tracking"; +import { TreeMap } from "../../TreeMap"; +import type { TileData } from "../../TreeMap/types"; import { ReportTile } from "./ReportTile"; import * as s from "./styles"; import type { ChartProps } from "./types"; @@ -65,7 +65,7 @@ export const Chart = ({ scoreCriterion={scoreCriterion} score={score} severity={x.severity} - viewMode={timeMode} + timeMode={timeMode} onIssuesClick={handleSeeIssuesClick} onTitleClick={viewLevel === "services" ? handleTitleClick : undefined} /> @@ -86,6 +86,7 @@ export const Chart = ({ void; + onIssuesStatsClick: (value: string) => void; + scoreCriterion: ScoreCriterion; + viewLevel: IssuesReportViewLevel; + timeMode: IssuesReportTimeMode; +} diff --git a/src/components/Dashboard/MetricsReport/EmptyState/EmptyState.stories.tsx b/src/components/common/IssuesReport/EmptyState/EmptyState.stories.tsx similarity index 94% rename from src/components/Dashboard/MetricsReport/EmptyState/EmptyState.stories.tsx rename to src/components/common/IssuesReport/EmptyState/EmptyState.stories.tsx index 2e25176db..d7bed4a71 100644 --- a/src/components/Dashboard/MetricsReport/EmptyState/EmptyState.stories.tsx +++ b/src/components/common/IssuesReport/EmptyState/EmptyState.stories.tsx @@ -4,7 +4,7 @@ import { EmptyState } from "."; // More on how to set up stories at: https://storybook.js.org/docs/react/writing-stories/introduction const meta: Meta = { - title: "Dashboard/MetricsReport/EmptyState", + title: "common/IssuesReport/EmptyState", component: EmptyState, parameters: { // More on how to position stories at: https://storybook.js.org/docs/react/configure/story-layout diff --git a/src/components/Dashboard/MetricsReport/EmptyState/index.tsx b/src/components/common/IssuesReport/EmptyState/index.tsx similarity index 83% rename from src/components/Dashboard/MetricsReport/EmptyState/index.tsx rename to src/components/common/IssuesReport/EmptyState/index.tsx index 818327a82..e206e54c1 100644 --- a/src/components/Dashboard/MetricsReport/EmptyState/index.tsx +++ b/src/components/common/IssuesReport/EmptyState/index.tsx @@ -1,8 +1,8 @@ -import { CrossCircleIcon } from "../../../common/icons/20px/CrossCircleIcon"; -import { CardsColoredIcon } from "../../../common/icons/CardsColoredIcon"; -import { EmptyState as CommonEmptyState } from "../../../common/v3/EmptyState"; -import type { EmptyStateProps as CommonEmptyStateProps } from "../../../common/v3/EmptyState/types"; -import { NewButton } from "../../../common/v3/NewButton"; +import { CrossCircleIcon } from "../../icons/20px/CrossCircleIcon"; +import { CardsColoredIcon } from "../../icons/CardsColoredIcon"; +import { EmptyState as CommonEmptyState } from "../../v3/EmptyState"; +import type { EmptyStateProps as CommonEmptyStateProps } from "../../v3/EmptyState/types"; +import { NewButton } from "../../v3/NewButton"; import * as s from "./styles"; import type { EmptyStatePreset, EmptyStateProps } from "./types"; diff --git a/src/components/Dashboard/MetricsReport/EmptyState/styles.ts b/src/components/common/IssuesReport/EmptyState/styles.ts similarity index 67% rename from src/components/Dashboard/MetricsReport/EmptyState/styles.ts rename to src/components/common/IssuesReport/EmptyState/styles.ts index 7884e2909..ff237b7ba 100644 --- a/src/components/Dashboard/MetricsReport/EmptyState/styles.ts +++ b/src/components/common/IssuesReport/EmptyState/styles.ts @@ -1,5 +1,5 @@ import styled from "styled-components"; -import { Spinner as CommonSpinner } from "../../../common/v3/Spinner"; +import { Spinner as CommonSpinner } from "../../v3/Spinner"; export const Spinner = styled(CommonSpinner)` color: ${({ theme }) => theme.colors.v3.surface.gray}; diff --git a/src/components/Dashboard/MetricsReport/EmptyState/types.ts b/src/components/common/IssuesReport/EmptyState/types.ts similarity index 87% rename from src/components/Dashboard/MetricsReport/EmptyState/types.ts rename to src/components/common/IssuesReport/EmptyState/types.ts index dd74a7022..21bf7310e 100644 --- a/src/components/Dashboard/MetricsReport/EmptyState/types.ts +++ b/src/components/common/IssuesReport/EmptyState/types.ts @@ -1,4 +1,4 @@ -import type { EmptyStateProps as CommonEmptyStateProps } from "../../../common/v3/EmptyState/types"; +import type { EmptyStateProps as CommonEmptyStateProps } from "../../v3/EmptyState/types"; export type EmptyStatePreset = | "noEndpoints" diff --git a/src/components/Admin/Reports/CodeIssues/Header/Header.stories.tsx b/src/components/common/IssuesReport/Header/Header.stories.tsx similarity index 82% rename from src/components/Admin/Reports/CodeIssues/Header/Header.stories.tsx rename to src/components/common/IssuesReport/Header/Header.stories.tsx index f33f66ca7..a98df4866 100644 --- a/src/components/Admin/Reports/CodeIssues/Header/Header.stories.tsx +++ b/src/components/common/IssuesReport/Header/Header.stories.tsx @@ -4,7 +4,7 @@ import { Header } from "."; // More on how to set up stories at: https://storybook.js.org/docs/react/writing-stories/introduction const meta: Meta = { - title: "Admin/Reports/CodeIssues/Header", + title: "common/IssuesReport/Header", component: Header, parameters: { // More on how to position stories at: https://storybook.js.org/docs/react/configure/story-layout @@ -17,4 +17,8 @@ export default meta; type Story = StoryObj; // More on writing stories with args: https://storybook.js.org/docs/react/writing-stories/args -export const Default: Story = {}; +export const Default: Story = { + args: { + defaultTitle: "Code Issues" + } +}; diff --git a/src/components/Admin/Reports/CodeIssues/Header/index.tsx b/src/components/common/IssuesReport/Header/index.tsx similarity index 70% rename from src/components/Admin/Reports/CodeIssues/Header/index.tsx rename to src/components/common/IssuesReport/Header/index.tsx index bb32394bd..be9ecaf6c 100644 --- a/src/components/Admin/Reports/CodeIssues/Header/index.tsx +++ b/src/components/common/IssuesReport/Header/index.tsx @@ -1,50 +1,40 @@ import { useMemo } from "react"; -import { - useAdminDispatch, - useAdminSelector -} from "../../../../../containers/Admin/hooks"; -import { getFeatureFlagValue } from "../../../../../featureFlags"; +import { getFeatureFlagValue } from "../../../../featureFlags"; import { useGetAboutQuery, useGetEnvironmentServicesQuery, useGetEnvironmentsQuery, useGetServiceEndpointsQuery, useGetServiceEnvironmentsQuery -} from "../../../../../redux/services/digma"; -import { - setCriticalityLevels, - setPeriodInDays, - setSelectedEndpoints, - setSelectedEnvironmentId, - setSelectedServices, - setTimeMode, - setViewMode -} from "../../../../../redux/slices/codeIssuesReportSlice"; -import { FeatureFlag } from "../../../../../types"; -import { sendUserActionTrackingEvent } from "../../../../../utils/actions/sendUserActionTrackingEvent"; -import { formatUnit } from "../../../../../utils/formatUnit"; -import { CodeIcon } from "../../../../common/icons/12px/CodeIcon"; -import { DurationBreakdownIcon } from "../../../../common/icons/12px/DurationBreakdownIcon"; -import { WrenchIcon } from "../../../../common/icons/12px/WrenchIcon"; -import { InfinityIcon } from "../../../../common/icons/16px/InfinityIcon"; -import { TableIcon } from "../../../../common/icons/16px/TableIcon"; -import { TreemapIcon } from "../../../../common/icons/16px/TreemapIcon"; -import { ChevronIcon } from "../../../../common/icons/20px/ChevronIcon"; -import { DatabaseIcon } from "../../../../common/icons/DatabaseIcon"; -import { Direction } from "../../../../common/icons/types"; -import type { ToggleValue } from "../../../../common/Toggle/types"; -import { NewIconButton } from "../../../../common/v3/NewIconButton"; -import { Tooltip } from "../../../../common/v3/Tooltip"; -import { trackingEvents } from "../../../../Dashboard/MetricsReport/tracking"; +} from "../../../../redux/services/digma"; +import type { IssueCriticality } from "../../../../redux/services/types"; import type { - Criticality, - ReportTimeMode, - ReportViewMode -} from "../../../../Dashboard/MetricsReport/types"; + IssuesReportTimeMode, + IssuesReportViewMode +} from "../../../../redux/slices/issuesReportSlice"; +import { FeatureFlag } from "../../../../types"; +import { sendUserActionTrackingEvent } from "../../../../utils/actions/sendUserActionTrackingEvent"; +import { formatUnit } from "../../../../utils/formatUnit"; +import { trackingEvents } from "../../../Dashboard/MetricsReport/tracking"; +import { CodeIcon } from "../../icons/12px/CodeIcon"; +import { DurationBreakdownIcon } from "../../icons/12px/DurationBreakdownIcon"; +import { WrenchIcon } from "../../icons/12px/WrenchIcon"; +import { InfinityIcon } from "../../icons/16px/InfinityIcon"; +import { TableIcon } from "../../icons/16px/TableIcon"; +import { TreemapIcon } from "../../icons/16px/TreemapIcon"; +import { ChevronIcon } from "../../icons/20px/ChevronIcon"; +import { DatabaseIcon } from "../../icons/DatabaseIcon"; +import { Direction } from "../../icons/types"; +import type { ToggleValue } from "../../Toggle/types"; +import { NewIconButton } from "../../v3/NewIconButton"; +import { Tooltip } from "../../v3/Tooltip"; import * as s from "./styles"; import type { HeaderProps } from "./types"; -const criticalityOptions: { id: Criticality; label: string }[] = [ +const criticalityOptions: { + id: IssueCriticality; + label: string; +}[] = [ { id: "High", label: "Critical" @@ -61,34 +51,26 @@ const criticalityOptions: { id: Criticality; label: string }[] = [ const DEFAULT_PERIOD = 7; -export const Header = ({ onGoBack }: HeaderProps) => { - // TODO: create selectors - const selectedEnvironmentId = useAdminSelector( - (state) => state.codeIssuesReport.selectedEnvironmentId - ); - const selectedCriticalityLevels = useAdminSelector( - (state) => state.codeIssuesReport.criticalityLevels - ); - const periodInDays = useAdminSelector( - (state) => state.codeIssuesReport.periodInDays - ); - const selectedService = useAdminSelector( - (state) => state.codeIssuesReport.selectedService - ); - const selectedServices = useAdminSelector( - (state) => state.codeIssuesReport.selectedServices - ); - const viewLevel = useAdminSelector( - (state) => state.codeIssuesReport.viewLevel - ); - const viewMode = useAdminSelector((state) => state.codeIssuesReport.viewMode); - const timeMode = useAdminSelector((state) => state.codeIssuesReport.timeMode); - const selectedEndpoints = useAdminSelector( - (state) => state.codeIssuesReport.selectedEndpoints - ); - - const dispatch = useAdminDispatch(); - +export const Header = ({ + viewMode, + viewLevel, + timeMode, + selectedEnvironmentId, + selectedService, + criticalityLevels, + periodInDays, + selectedEndpoints, + selectedServices, + onSelectedEnvironmentIdChange, + onSelectedServicesChange, + onSelectedEndpointsChange, + onCriticalityLevelsChange, + onPeriodInDaysChange, + onTimeModeChange, + onViewModeChange, + defaultTitle, + onGoBack +}: HeaderProps) => { const { data: about } = useGetAboutQuery(); const { data: environments } = useGetEnvironmentsQuery(); @@ -151,67 +133,58 @@ export const Header = ({ onGoBack }: HeaderProps) => { const handleSelectedEnvironmentChanged = (option: string | string[]) => { sendUserActionTrackingEvent(trackingEvents.ENVIRONMENT_FILTER_SELECTED); const newItem = Array.isArray(option) ? option[0] : option; - dispatch(setSelectedEnvironmentId(newItem)); - dispatch(setSelectedServices([])); - dispatch(setSelectedEndpoints([])); + onSelectedEnvironmentIdChange(newItem); + onSelectedServicesChange([]); + onSelectedEndpointsChange([]); }; const handleSelectedServicesChanged = (option: string | string[]) => { sendUserActionTrackingEvent(trackingEvents.SERVICE_FILTER_SELECTED); const newItem = Array.isArray(option) ? option : [option]; - dispatch(setSelectedServices(newItem)); + onSelectedServicesChange(newItem); }; const handleSelectedEndpointsChanged = (option: string | string[]) => { sendUserActionTrackingEvent(trackingEvents.ENDPOINT_FILTER_SELECTED); const newItem = Array.isArray(option) ? option : [option]; - dispatch(setSelectedEndpoints(newItem)); + onSelectedEndpointsChange(newItem); }; const handleDataChanged = (option: string | string[]) => { sendUserActionTrackingEvent(trackingEvents.DATA_FILTER_SELECTED); const newItem = Array.isArray(option) ? option : [option]; - dispatch(setCriticalityLevels(newItem as Criticality[])); + onCriticalityLevelsChange(newItem as IssueCriticality[]); }; const handlePeriodChanged = (option: string | string[]) => { sendUserActionTrackingEvent(trackingEvents.PERIOD_FILTER_CHANGED); const newItem = Array.isArray(option) ? option : [option]; if (newItem.length === 0) { - dispatch(setPeriodInDays(DEFAULT_PERIOD)); + onPeriodInDaysChange(DEFAULT_PERIOD); return; } const value = newItem[0]; const newValue = Number(value); - dispatch(setPeriodInDays(newValue)); + onPeriodInDaysChange(newValue); }; const handleViewModeChanged = (value: ToggleValue) => { sendUserActionTrackingEvent(trackingEvents.VIEW_MODE_CHANGED, { value }); - const newViewMode = value as ReportViewMode; - dispatch(setViewMode(newViewMode)); + const newViewMode = value as IssuesReportViewMode; + onViewModeChange(newViewMode); }; const handleTimeModeChanged = (value: ToggleValue) => { sendUserActionTrackingEvent(trackingEvents.TIME_MODE_CHANGED, { value }); - const newTimeMode = value as ReportTimeMode; - dispatch(setTimeMode(newTimeMode)); - - // TODO: check if still needed - // if (selectedViewLevel === "services") { - // setServicesIssuesData(null); - // } - - // if (selectedViewLevel === "endpoints") { - // setEndpointIssuesData(null); - // } + const newTimeMode = value as IssuesReportTimeMode; + onTimeModeChange(newTimeMode); }; const title = viewLevel === "endpoints" ? `${selectedService ?? ""} Service` - : "Code Issues"; + : defaultTitle; const titleSuffix = viewLevel === "endpoints" ? " Endpoints" : ""; const tooltipTitle = `${title} ${titleSuffix}`; @@ -339,7 +312,7 @@ export const Header = ({ onGoBack }: HeaderProps) => { label: item.label, value: item.id, enabled: true, - selected: selectedCriticalityLevels.includes(item.id) + selected: criticalityLevels.includes(item.id) }))} multiselect={true} icon={DatabaseIcon} diff --git a/src/components/Dashboard/MetricsReport/Header/styles.ts b/src/components/common/IssuesReport/Header/styles.ts similarity index 87% rename from src/components/Dashboard/MetricsReport/Header/styles.ts rename to src/components/common/IssuesReport/Header/styles.ts index 6f7fed821..421a73219 100644 --- a/src/components/Dashboard/MetricsReport/Header/styles.ts +++ b/src/components/common/IssuesReport/Header/styles.ts @@ -2,10 +2,10 @@ import styled from "styled-components"; import { subheading2BoldTypography, subheading2RegularTypography -} from "../../../common/App/typographies"; -import { Select } from "../../../common/v3/Select"; -import { Toggle } from "../../../common/v3/Toggle"; -import { OptionButton } from "../../../common/v3/Toggle/styles"; +} from "../../App/typographies"; +import { Select } from "../../v3/Select"; +import { Toggle } from "../../v3/Toggle"; +import { OptionButton } from "../../v3/Toggle/styles"; export const Container = styled.div` display: flex; diff --git a/src/components/common/IssuesReport/Header/types.ts b/src/components/common/IssuesReport/Header/types.ts new file mode 100644 index 000000000..5c1bca51a --- /dev/null +++ b/src/components/common/IssuesReport/Header/types.ts @@ -0,0 +1,27 @@ +import type { IssueCriticality } from "../../../../redux/services/types"; +import type { + IssuesReportTimeMode, + IssuesReportViewLevel, + IssuesReportViewMode +} from "../../../../redux/slices/issuesReportSlice"; + +export interface HeaderProps { + viewMode: IssuesReportViewMode; + viewLevel: IssuesReportViewLevel; + timeMode: IssuesReportTimeMode; + selectedEnvironmentId: string | null; + selectedService: string | null; + criticalityLevels: IssueCriticality[]; + periodInDays: number; + selectedEndpoints: string[]; + selectedServices: string[]; + onSelectedEnvironmentIdChange: (environmentId: string) => void; + onSelectedServicesChange: (services: string[]) => void; + onSelectedEndpointsChange: (endpoints: string[]) => void; + onCriticalityLevelsChange: (criticalities: IssueCriticality[]) => void; + onPeriodInDaysChange: (periodInDays: number) => void; + onTimeModeChange: (timeMode: IssuesReportTimeMode) => void; + onViewModeChange: (viewMode: IssuesReportViewMode) => void; + onGoBack: () => void; + defaultTitle: string; +} diff --git a/src/components/common/IssuesReport/IssuesReport.stories.tsx b/src/components/common/IssuesReport/IssuesReport.stories.tsx new file mode 100644 index 000000000..3721fc015 --- /dev/null +++ b/src/components/common/IssuesReport/IssuesReport.stories.tsx @@ -0,0 +1,19 @@ +import type { Meta, StoryObj } from "@storybook/react"; +import { IssuesReport } from "."; + +// More on how to set up stories at: https://storybook.js.org/docs/react/writing-stories/introduction +const meta: Meta = { + title: "common/IssuesReport", + component: IssuesReport, + 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 Default: Story = {}; diff --git a/src/components/Dashboard/MetricsReport/Table/Table.stories.tsx b/src/components/common/IssuesReport/Table/Table.stories.tsx similarity index 95% rename from src/components/Dashboard/MetricsReport/Table/Table.stories.tsx rename to src/components/common/IssuesReport/Table/Table.stories.tsx index 3f3fb8ebc..11110a205 100644 --- a/src/components/Dashboard/MetricsReport/Table/Table.stories.tsx +++ b/src/components/common/IssuesReport/Table/Table.stories.tsx @@ -6,7 +6,7 @@ import { mockedReport } from "./mockData"; // More on how to set up stories at: https://storybook.js.org/docs/react/writing-stories/introduction const meta: Meta = { - title: "Dashboard/MetricsReport/Table", + title: "common/IssuesReport/Table", component: Table, parameters: { // More on how to position stories at: https://storybook.js.org/docs/react/configure/story-layout diff --git a/src/components/Dashboard/MetricsReport/Table/index.tsx b/src/components/common/IssuesReport/Table/index.tsx similarity index 96% rename from src/components/Dashboard/MetricsReport/Table/index.tsx rename to src/components/common/IssuesReport/Table/index.tsx index 876ce94f0..2d587f996 100644 --- a/src/components/Dashboard/MetricsReport/Table/index.tsx +++ b/src/components/common/IssuesReport/Table/index.tsx @@ -10,11 +10,11 @@ import { import type { ReactNode } from "react"; import { useState } from "react"; import { sendUserActionTrackingEvent } from "../../../../utils/actions/sendUserActionTrackingEvent"; -import { SortIcon } from "../../../common/icons/16px/SortIcon"; -import { ChevronIcon } from "../../../common/icons/20px/ChevronIcon"; -import { Direction } from "../../../common/icons/types"; -import { Tooltip } from "../../../common/v3/Tooltip"; -import { trackingEvents } from "../tracking"; +import { trackingEvents } from "../../../Dashboard/MetricsReport/tracking"; +import { SortIcon } from "../../icons/16px/SortIcon"; +import { ChevronIcon } from "../../icons/20px/ChevronIcon"; +import { Direction } from "../../icons/types"; +import { Tooltip } from "../../v3/Tooltip"; import type { PresentationalReportData } from "../types"; import * as s from "./styles"; import type { ColumnMeta, Severity, TableProps } from "./types"; diff --git a/src/components/Dashboard/MetricsReport/Table/mockData.ts b/src/components/common/IssuesReport/Table/mockData.ts similarity index 93% rename from src/components/Dashboard/MetricsReport/Table/mockData.ts rename to src/components/common/IssuesReport/Table/mockData.ts index 91ad17221..5cd9f2657 100644 --- a/src/components/Dashboard/MetricsReport/Table/mockData.ts +++ b/src/components/common/IssuesReport/Table/mockData.ts @@ -1,4 +1,4 @@ -import type { SetMetricsReportDataPayload } from "../types"; +import type { SetMetricsReportDataPayload } from "../../../../redux/services/types"; export const mockedReport: SetMetricsReportDataPayload = { reports: [ diff --git a/src/components/Dashboard/MetricsReport/Table/styles.ts b/src/components/common/IssuesReport/Table/styles.ts similarity index 98% rename from src/components/Dashboard/MetricsReport/Table/styles.ts rename to src/components/common/IssuesReport/Table/styles.ts index 17867b644..eae6bec39 100644 --- a/src/components/Dashboard/MetricsReport/Table/styles.ts +++ b/src/components/common/IssuesReport/Table/styles.ts @@ -2,7 +2,7 @@ import styled from "styled-components"; import { subheading1BoldTypography, subheading1RegularTypography -} from "../../../common/App/typographies"; +} from "../../App/typographies"; import type { TableBodyCellProps, TableCellContentProps } from "./types"; export const Table = styled.table` diff --git a/src/components/Dashboard/MetricsReport/Table/types.ts b/src/components/common/IssuesReport/Table/types.ts similarity index 71% rename from src/components/Dashboard/MetricsReport/Table/types.ts rename to src/components/common/IssuesReport/Table/types.ts index a2a9d5464..4250179c3 100644 --- a/src/components/Dashboard/MetricsReport/Table/types.ts +++ b/src/components/common/IssuesReport/Table/types.ts @@ -1,17 +1,16 @@ import type { - PresentationalReportData, - ReportTimeMode, - ReportViewLevel, - ScoreCriterion -} from "../types"; + IssuesReportTimeMode, + IssuesReportViewLevel +} from "../../../../redux/slices/issuesReportSlice"; +import type { PresentationalReportData, ScoreCriterion } from "../types"; export interface TableProps { data: PresentationalReportData[]; - timeMode: ReportTimeMode; + timeMode: IssuesReportTimeMode; onTitleClick: (value: string) => void; onIssuesStatsClick: (value: string) => void; scoreCriterion: ScoreCriterion; - viewLevel: ReportViewLevel; + viewLevel: IssuesReportViewLevel; } export type ContentAlignment = "left" | "center" | "right"; diff --git a/src/components/common/IssuesReport/index.tsx b/src/components/common/IssuesReport/index.tsx new file mode 100644 index 000000000..926b8d771 --- /dev/null +++ b/src/components/common/IssuesReport/index.tsx @@ -0,0 +1,267 @@ +import { useEffect, useMemo } from "react"; +import { getFeatureFlagValue } from "../../../featureFlags"; +import { + useGetAboutQuery, + useGetEndpointsIssuesQuery, + useGetEnvironmentServicesQuery, + useGetEnvironmentsQuery, + useGetServicesIssuesQuery +} from "../../../redux/services/digma"; +import type { + EndpointIssuesData, + GetMetricsReportDataPayloadV1, + GetMetricsReportDataPayloadV2, + ServiceIssuesData +} from "../../../redux/services/types"; +import { FeatureFlag } from "../../../types"; +import { Chart } from "./Chart"; +import { EmptyState } from "./EmptyState"; +import { Header } from "./Header"; +import * as s from "./styles"; +import { Table } from "./Table"; +import type { IssuesReportProps, ScoreCriterion } from "./types"; +import { transformEndpointsData, transformServicesData } from "./utils"; + +export const IssuesReport = ({ + viewMode, + viewLevel, + timeMode, + selectedEnvironmentId, + selectedService, + criticalityLevels, + periodInDays, + selectedEndpoints, + selectedServices, + defaultHeaderTitle, + onSelectedEnvironmentIdChange, + onSelectedServicesChange, + onSelectedEndpointsChange, + onCriticalityLevelsChange, + onPeriodInDaysChange, + onTimeModeChange, + onViewModeChange, + onViewLevelChange, + onTileTitleClick, + onTileIssuesStatsClick, + onSelectedServiceChange +}: IssuesReportProps) => { + const { data: about } = useGetAboutQuery(); + const { data: environments } = useGetEnvironmentsQuery(); + + useEffect(() => { + if (environments && environments.length > 0 && !selectedEnvironmentId) { + onSelectedEnvironmentIdChange(environments[0].id); + } + }, [environments, selectedEnvironmentId, onSelectedEnvironmentIdChange]); + + const isInitialized = Boolean(environments && about); + + const { data: services } = useGetEnvironmentServicesQuery( + { + environment: selectedEnvironmentId ?? null + }, + { + skip: !selectedEnvironmentId || viewLevel !== "services" + } + ); + + const isDataFilterEnabled = Boolean( + about && + getFeatureFlagValue( + about, + FeatureFlag.IS_METRICS_REPORT_DATA_FILTER_ENABLED + ) + ); + + const getServicesIssuesPayloadV1: GetMetricsReportDataPayloadV1 = + useMemo(() => { + const servicesArray = + selectedServices.length > 0 ? selectedServices : services ?? []; + + return { + keys: servicesArray.map((x) => ({ + environment: selectedEnvironmentId ?? "", + service: x, + lastDays: timeMode === "baseline" ? null : periodInDays + })) + }; + }, [ + selectedServices, + services, + selectedEnvironmentId, + timeMode, + periodInDays + ]); + + const getServicesIssuesPayloadV2: GetMetricsReportDataPayloadV2 = + useMemo(() => { + const servicesArray = + selectedServices.length > 0 ? selectedServices : services ?? [null]; + + return { + criticalities: criticalityLevels, + keys: servicesArray.map((x) => ({ + environment: selectedEnvironmentId ?? "", + service: x, + lastDays: timeMode === "baseline" ? null : periodInDays + })) + }; + }, [ + selectedServices, + services, + selectedEnvironmentId, + criticalityLevels, + timeMode, + periodInDays + ]); + + const getServicesIssuesPayload = isDataFilterEnabled + ? getServicesIssuesPayloadV2 + : getServicesIssuesPayloadV1; + + const { data: servicesIssues } = useGetServicesIssuesQuery( + getServicesIssuesPayload, + { + skip: + !isInitialized || + !selectedEnvironmentId || + !services || + viewLevel !== "services" + } + ); + + const { data: endpointsIssues } = useGetEndpointsIssuesQuery( + { + environment: selectedEnvironmentId ?? "", + service: selectedService ?? "", + endpoints: selectedEndpoints, + criticalities: criticalityLevels, + lastDays: timeMode === "baseline" ? null : periodInDays + }, + { + skip: + !isInitialized || + !selectedEnvironmentId || + !selectedService || + viewLevel !== "endpoints" + } + ); + + const handleTitleClick = (value: string) => { + if (viewLevel === "services") { + onSelectedServiceChange(value); + onViewLevelChange("endpoints"); + } + onTileTitleClick(viewLevel, value); + }; + + const handleIssuesStatsClick = (value: string) => { + onTileIssuesStatsClick(viewLevel, value); + }; + + const handleGoBack = () => { + onViewLevelChange("services"); + onSelectedServiceChange(null); + }; + + const isCriticalityEnabled = Boolean( + about && + getFeatureFlagValue( + about, + FeatureFlag.IS_METRICS_REPORT_CRITICALITY_ENABLED + ) + ); + + const scoreCriterion: ScoreCriterion = isCriticalityEnabled + ? "criticality" + : "impact"; + + const data = + (viewLevel === "services" + ? servicesIssues?.reports + : endpointsIssues?.reports) ?? []; + const transformedData = + viewLevel === "services" + ? transformServicesData(data as ServiceIssuesData[], scoreCriterion) + : transformEndpointsData(data as EndpointIssuesData[], scoreCriterion); + + const renderContent = () => { + if ( + (viewLevel === "services" && !servicesIssues) || + (viewLevel === "endpoints" && !endpointsIssues) + ) { + return ; + } + + if (data.length === 0) { + if (viewLevel === "services") { + return ; + } + + if (viewLevel === "endpoints") { + return ; + } + } + + return ( + <> + {viewMode === "table" && ( +
+ )} + {viewMode === "treemap" && ( + + )} + + ); + }; + + return ( + + {isInitialized ? ( + environments && environments.length > 0 ? ( + <> +
+ {renderContent()} + + ) : ( + + ) + ) : ( + + )} + + ); +}; diff --git a/src/components/common/IssuesReport/styles.ts b/src/components/common/IssuesReport/styles.ts new file mode 100644 index 000000000..74951f36a --- /dev/null +++ b/src/components/common/IssuesReport/styles.ts @@ -0,0 +1,12 @@ +import styled from "styled-components"; + +export const Container = styled.div` + display: flex; + flex-direction: column; + height: 100%; + width: 100%; + padding: 24px 24px 16px; + gap: 24px; + box-sizing: border-box; + overflow: auto; +`; diff --git a/src/components/common/IssuesReport/types.ts b/src/components/common/IssuesReport/types.ts new file mode 100644 index 000000000..ff1367515 --- /dev/null +++ b/src/components/common/IssuesReport/types.ts @@ -0,0 +1,44 @@ +import type { IssueCriticality } from "../../../redux/services/types"; +import type { + IssuesReportTimeMode, + IssuesReportViewLevel, + IssuesReportViewMode +} from "../../../redux/slices/issuesReportSlice"; +import type { Severity } from "../../common/IssuesReport/Table/types"; + +export interface IssuesReportProps { + viewMode: IssuesReportViewMode; + viewLevel: IssuesReportViewLevel; + timeMode: IssuesReportTimeMode; + selectedEnvironmentId: string | null; + selectedService: string | null; + criticalityLevels: IssueCriticality[]; + periodInDays: number; + selectedEndpoints: string[]; + selectedServices: string[]; + defaultHeaderTitle: string; + onSelectedEnvironmentIdChange: (environmentId: string) => void; + onSelectedServicesChange: (services: string[]) => void; + onSelectedEndpointsChange: (endpoints: string[]) => void; + onCriticalityLevelsChange: (criticalities: IssueCriticality[]) => void; + onPeriodInDaysChange: (periodInDays: number) => void; + onTimeModeChange: (timeMode: IssuesReportTimeMode) => void; + onViewModeChange: (viewMode: IssuesReportViewMode) => void; + onTileTitleClick: (viewLevel: IssuesReportViewLevel, value: string) => void; + onTileIssuesStatsClick: ( + viewLevel: IssuesReportViewLevel, + value: string + ) => void; + onViewLevelChange: (viewLevel: IssuesReportViewLevel) => void; + onSelectedServiceChange: (service: string | null) => void; +} + +export type ScoreCriterion = "impact" | "criticality"; + +export interface PresentationalReportData { + id: string; + name: string; + score: number; + criticalIssuesCount: number; + severity: Severity; +} diff --git a/src/components/Dashboard/MetricsReport/utils.ts b/src/components/common/IssuesReport/utils.ts similarity index 93% rename from src/components/Dashboard/MetricsReport/utils.ts rename to src/components/common/IssuesReport/utils.ts index 5c787dfe1..1f9106434 100644 --- a/src/components/Dashboard/MetricsReport/utils.ts +++ b/src/components/common/IssuesReport/utils.ts @@ -1,10 +1,9 @@ -import type { Severity } from "./Table/types"; import type { EndpointIssuesData, - PresentationalReportData, - ScoreCriterion, ServiceIssuesData -} from "./types"; +} from "../../../redux/services/types"; +import type { Severity } from "./Table/types"; +import type { PresentationalReportData, ScoreCriterion } from "./types"; const getSeverity = (min: number, max: number, value: number): Severity => { const normalizedMin = Math.max(min, 0); diff --git a/src/components/common/TreeMap/Tile/types.ts b/src/components/common/TreeMap/Tile/types.ts index 598a6db3e..2b1ec0557 100644 --- a/src/components/common/TreeMap/Tile/types.ts +++ b/src/components/common/TreeMap/Tile/types.ts @@ -1,5 +1,5 @@ import type { ReactNode } from "react"; -import type { Severity } from "../../../Dashboard/MetricsReport/Table/types"; +import type { Severity } from "../../IssuesReport/Table/types"; export interface TileProps { title: string; diff --git a/src/components/common/TreeMap/TreeMap.stories.tsx b/src/components/common/TreeMap/TreeMap.stories.tsx index 7f5c47115..aeea599e4 100644 --- a/src/components/common/TreeMap/TreeMap.stories.tsx +++ b/src/components/common/TreeMap/TreeMap.stories.tsx @@ -1,6 +1,6 @@ import type { Meta, StoryObj } from "@storybook/react"; import { TreeMap } from "."; -import { ReportTile } from "../../Dashboard/MetricsReport/Chart/ReportTile"; +import { ReportTile } from "../IssuesReport/Chart/ReportTile"; // More on how to set up stories at: https://storybook.js.org/docs/react/writing-stories/introduction const meta: Meta = { @@ -36,7 +36,7 @@ export const Default: Story = { score={1500} scoreCriterion={"criticality"} severity={"Top"} - viewMode={"baseline"} + timeMode={"baseline"} /> ) }, @@ -50,7 +50,7 @@ export const Default: Story = { score={710} scoreCriterion={"criticality"} severity={"High"} - viewMode={"baseline"} + timeMode={"baseline"} /> ) }, @@ -64,7 +64,7 @@ export const Default: Story = { score={530} scoreCriterion={"criticality"} severity={"Medium"} - viewMode={"baseline"} + timeMode={"baseline"} /> ) }, @@ -78,7 +78,7 @@ export const Default: Story = { score={100} scoreCriterion={"criticality"} severity={"Low"} - viewMode={"baseline"} + timeMode={"baseline"} /> ) }, @@ -92,7 +92,7 @@ export const Default: Story = { score={100} scoreCriterion={"criticality"} severity={"Low"} - viewMode={"baseline"} + timeMode={"baseline"} /> ) } diff --git a/src/components/common/TreeMap/index.tsx b/src/components/common/TreeMap/index.tsx index be3a535f9..3cc2e170b 100644 --- a/src/components/common/TreeMap/index.tsx +++ b/src/components/common/TreeMap/index.tsx @@ -4,12 +4,17 @@ import { isNull } from "../../../typeGuards/isNull"; import type { TileData, TreeMapProps } from "./types"; const normalizeData = (data: Input[]) => { + if (data.length === 0) { + return data; + } + const MIN_MAX_RATIO = 5; const NORMALIZED_MIN = 1; const NORMALIZED_MAX = MIN_MAX_RATIO; - const dataMin = Math.min(...data.map((item) => item.value)) || NORMALIZED_MIN; - const dataMax = Math.max(...data.map((item) => item.value)) || NORMALIZED_MIN; + const values = data.map((item) => item.value); + const dataMin = Math.min(...values); + const dataMax = Math.max(...values); if (dataMin === dataMax) { return data.map((item) => ({ @@ -18,18 +23,15 @@ const normalizeData = (data: Input[]) => { })); } - const dataMinMaxRatio = dataMax / dataMin; - - return dataMinMaxRatio > MIN_MAX_RATIO - ? data.map((item) => ({ - id: item.id, - value: - ((item.value - dataMin) * (NORMALIZED_MAX - NORMALIZED_MIN)) / - (dataMax - dataMin) + - NORMALIZED_MIN, // min-max normalization - content: item.content - })) - : data; + return data.map((item) => ({ + id: item.id, + value: + ((Math.max(0, item.value) - dataMin) * + (NORMALIZED_MAX - NORMALIZED_MIN)) / + (dataMax - dataMin) + + NORMALIZED_MIN, // min-max normalization + content: item.content + })); }; const calculateTiles = ( @@ -73,10 +75,11 @@ export const TreeMap = ({ data, width, height, - minTileDimensions + minTileDimensions, + normalize }: TreeMapProps) => { const container = { x0: 0, y0: 0, x1: width ?? 0, y1: height ?? 0 }; - const normalizedData = normalizeData(data); + const normalizedData = normalize ? normalizeData(data) : data; const sortedData = [...normalizedData].sort((a, b) => b.value - a.value); const tilesData = calculateTiles(sortedData, container, minTileDimensions); diff --git a/src/components/common/TreeMap/types.ts b/src/components/common/TreeMap/types.ts index bcacf80cd..fa4c1f560 100644 --- a/src/components/common/TreeMap/types.ts +++ b/src/components/common/TreeMap/types.ts @@ -7,6 +7,7 @@ export interface TileData { } export interface TreeMapProps { + normalize?: boolean; padding?: number; data: Input[]; width: number | null; diff --git a/src/containers/Admin/store.ts b/src/containers/Admin/store.ts index 72c37458a..3daeaa3bf 100644 --- a/src/containers/Admin/store.ts +++ b/src/containers/Admin/store.ts @@ -2,11 +2,11 @@ import { configureStore } from "@reduxjs/toolkit"; import { setupListeners } from "@reduxjs/toolkit/query"; import { authApi } from "../../redux/services/auth"; import { digmaApi } from "../../redux/services/digma"; -import codeIssuesReportSlice from "../../redux/slices/codeIssuesReportSlice"; +import issuesReportSlice from "../../redux/slices/issuesReportSlice"; export const store = configureStore({ reducer: { - codeIssuesReport: codeIssuesReportSlice, + codeIssuesReport: issuesReportSlice, [authApi.reducerPath]: authApi.reducer, [digmaApi.reducerPath]: digmaApi.reducer }, diff --git a/src/containers/Dashboard/hooks.ts b/src/containers/Dashboard/hooks.ts new file mode 100644 index 000000000..7f9e9b4ef --- /dev/null +++ b/src/containers/Dashboard/hooks.ts @@ -0,0 +1,5 @@ +import { useDispatch, useSelector } from "react-redux"; +import type { DashboardDispatch, DashboardRootState } from "./store"; + +export const useDashboardDispatch = useDispatch.withTypes(); +export const useDashboardSelector = useSelector.withTypes(); diff --git a/src/containers/Dashboard/index.tsx b/src/containers/Dashboard/index.tsx index e6a171636..e913c27ae 100644 --- a/src/containers/Dashboard/index.tsx +++ b/src/containers/Dashboard/index.tsx @@ -1,5 +1,6 @@ import { StrictMode } from "react"; import { createRoot } from "react-dom/client"; +import { Provider } from "react-redux"; import { cancelMessage, initializeDigmaMessageListener, @@ -11,6 +12,7 @@ import { App } from "../../components/common/App"; import { dispatcher } from "../../dispatcher"; import { isString } from "../../typeGuards/isString"; import { handleUncaughtError } from "../../utils/handleUncaughtError"; +import { store } from "../Dashboard/store"; import { GlobalStyle } from "./styles"; const APP_ID = "dashboard"; @@ -44,10 +46,12 @@ if (rootElement) { const root = createRoot(rootElement); root.render( - - - {getView()} - + + + + {getView()} + + ); } diff --git a/src/containers/Dashboard/store.ts b/src/containers/Dashboard/store.ts new file mode 100644 index 000000000..38814dd68 --- /dev/null +++ b/src/containers/Dashboard/store.ts @@ -0,0 +1,18 @@ +import { configureStore } from "@reduxjs/toolkit"; +import { setupListeners } from "@reduxjs/toolkit/query"; +import { digmaApi } from "../../redux/services/digma"; +import issuesReportSlice from "../../redux/slices/issuesReportSlice"; + +export const store = configureStore({ + reducer: { + metricsReport: issuesReportSlice, + [digmaApi.reducerPath]: digmaApi.reducer + }, + middleware: (getDefaultMiddleware) => + getDefaultMiddleware().concat(digmaApi.middleware) +}); + +setupListeners(store.dispatch); + +export type DashboardRootState = ReturnType; +export type DashboardDispatch = typeof store.dispatch; diff --git a/src/redux/services/digma.ts b/src/redux/services/digma.ts index f8381b622..4f2dcab03 100644 --- a/src/redux/services/digma.ts +++ b/src/redux/services/digma.ts @@ -1,22 +1,20 @@ import { createApi, fetchBaseQuery } from "@reduxjs/toolkit/query/react"; -import type { GetServicesPayload } from "../../components/Dashboard/MetricsReport/Header/types"; -import { - type GetEndpointsIssuesPayload, - type GetMetricsReportDataPayloadV1, - type GetMetricsReportDataPayloadV2, - type GetServiceEndpointsPayload, - type GetServiceEnvironmentsPayload, - type SetEndpointsIssuesPayload, - type SetMetricsReportDataPayload, - type SetServiceEndpointsPayload, - type SetServiceEnvironmentsPayload -} from "../../components/Dashboard/MetricsReport/types"; import { isString } from "../../typeGuards/isString"; import type { GetAboutResponse, + GetEndpointsIssuesPayload, + GetEnvironmentServicesPayload, GetEnvironmentServicesResponse, GetEnvironmentsResponse, - GetUserProfileResponse + GetMetricsReportDataPayloadV1, + GetMetricsReportDataPayloadV2, + GetServiceEndpointsPayload, + GetServiceEnvironmentsPayload, + GetUserProfileResponse, + SetEndpointsIssuesPayload, + SetMetricsReportDataPayload, + SetServiceEndpointsPayload, + SetServiceEnvironmentsPayload } from "./types"; export const digmaApi = createApi({ @@ -59,7 +57,7 @@ export const digmaApi = createApi({ }), getEnvironmentServices: builder.query< GetEnvironmentServicesResponse, - GetServicesPayload + GetEnvironmentServicesPayload >({ query: (data) => ({ url: "services/getServices", diff --git a/src/redux/services/digmaEmpty.ts b/src/redux/services/digmaEmpty.ts new file mode 100644 index 000000000..03b1a3c36 --- /dev/null +++ b/src/redux/services/digmaEmpty.ts @@ -0,0 +1,13 @@ +import { createApi, fetchBaseQuery } from "@reduxjs/toolkit/query/react"; +import { isString } from "../../typeGuards/isString"; + +export const digmaEmptyApi = createApi({ + reducerPath: "digmaApi", + baseQuery: fetchBaseQuery({ + baseUrl: isString(window.digmaApiProxyPrefix) + ? window.digmaApiProxyPrefix + : "/api/", + credentials: "same-origin" + }), + endpoints: () => ({}) +}); diff --git a/src/redux/services/types.ts b/src/redux/services/types.ts index 117a63774..93a642e6a 100644 --- a/src/redux/services/types.ts +++ b/src/redux/services/types.ts @@ -1,4 +1,9 @@ -import type { DeploymentType } from "../../components/common/App/types"; +import type { + DeploymentType, + EnvironmentType +} from "../../components/common/App/types"; + +export type IssueCriticality = "Low" | "Medium" | "High"; export interface GetAboutResponse { applicationVersion: string; @@ -11,10 +16,92 @@ export interface GetUserProfileResponse { email: string; } +export interface GetMetricsReportDataPayloadV1 { + keys: { + environment: string; + service: string; + lastDays: number | null; + }[]; +} + +export interface GetMetricsReportDataPayloadV2 { + criticalities: IssueCriticality[]; + keys: { + environment: string; + service: string | null; + lastDays: number | null; + }[]; +} + +export interface ServiceIssuesData { + key: { + environment: string; + service: string; + lastDays: number | null; + }; + issues: number; + impact: number; + criticality: number; +} + +export interface SetMetricsReportDataPayload { + reports: ServiceIssuesData[]; +} + +export interface GetServiceEndpointsPayload { + environment: string; + service: string; +} + +export interface EndpointData { + displayName: string; + spanCodeObjectId: string; +} + +export interface SetServiceEndpointsPayload { + endpoints: EndpointData[]; +} + +export interface GetEndpointsIssuesPayload { + environment: string; + service: string; + endpoints: string[]; + criticalities: IssueCriticality[]; + lastDays: number | null; +} + +export interface EndpointIssuesData { + displayName: string; + spanCodeObjectId: string; + issues: number; + impact: number; + criticality: number; +} + +export interface SetEndpointsIssuesPayload { + reports: EndpointIssuesData[]; +} + +export interface GetServiceEnvironmentsPayload { + service: string; +} + +export interface SetServiceEnvironmentsPayload { + environments: { + id: string; + name: string; + type: EnvironmentType; + }[]; +} + export type GetEnvironmentsResponse = { type: "Private" | "Public"; id: string; name: string; }[]; +export interface GetEnvironmentServicesPayload { + environment: string | null; +} + export type GetEnvironmentServicesResponse = string[]; diff --git a/src/redux/slices/codeIssuesReportSlice.ts b/src/redux/slices/issuesReportSlice.ts similarity index 62% rename from src/redux/slices/codeIssuesReportSlice.ts rename to src/redux/slices/issuesReportSlice.ts index 9f2ef34d9..899c25ef5 100644 --- a/src/redux/slices/codeIssuesReportSlice.ts +++ b/src/redux/slices/issuesReportSlice.ts @@ -1,25 +1,26 @@ import type { PayloadAction } from "@reduxjs/toolkit"; import { createSlice } from "@reduxjs/toolkit"; -import type { - Criticality, - ReportTimeMode, - ReportViewLevel, - ReportViewMode -} from "../../components/Dashboard/MetricsReport/types"; +import type { IssueCriticality } from "../services/types"; -export interface CodeIssuesReportState { - viewMode: ReportViewMode; - viewLevel: ReportViewLevel; - timeMode: ReportTimeMode; +export type IssuesReportViewMode = "treemap" | "table"; + +export type IssuesReportTimeMode = "baseline" | "changes"; + +export type IssuesReportViewLevel = "services" | "endpoints"; + +export interface IssuesReportState { + viewMode: IssuesReportViewMode; + viewLevel: IssuesReportViewLevel; + timeMode: IssuesReportTimeMode; selectedEnvironmentId: string | null; selectedService: string | null; - criticalityLevels: Criticality[]; + criticalityLevels: IssueCriticality[]; periodInDays: number; selectedEndpoints: string[]; selectedServices: string[]; } -const initialState: CodeIssuesReportState = { +const initialState: IssuesReportState = { viewMode: "treemap", viewLevel: "services", timeMode: "baseline", @@ -31,17 +32,17 @@ const initialState: CodeIssuesReportState = { selectedServices: [] }; -export const codeIssuesReportSlice = createSlice({ - name: "codeIssuesReport", +export const issuesReportSlice = createSlice({ + name: "issuesReport", initialState, reducers: { - setViewMode: (state, action: PayloadAction) => { + setViewMode: (state, action: PayloadAction) => { state.viewMode = action.payload; }, - setViewLevel: (state, action: PayloadAction) => { + setViewLevel: (state, action: PayloadAction) => { state.viewLevel = action.payload; }, - setTimeMode: (state, action: PayloadAction) => { + setTimeMode: (state, action: PayloadAction) => { state.timeMode = action.payload; }, setSelectedEnvironmentId: (state, action: PayloadAction) => { @@ -50,7 +51,10 @@ export const codeIssuesReportSlice = createSlice({ setSelectedService: (state, action: PayloadAction) => { state.selectedService = action.payload; }, - setCriticalityLevels: (state, action: PayloadAction) => { + setCriticalityLevels: ( + state, + action: PayloadAction + ) => { state.criticalityLevels = action.payload; }, setPeriodInDays: (state, action: PayloadAction) => { @@ -75,6 +79,6 @@ export const { setPeriodInDays, setSelectedEndpoints, setSelectedServices -} = codeIssuesReportSlice.actions; +} = issuesReportSlice.actions; -export default codeIssuesReportSlice.reducer; +export default issuesReportSlice.reducer; diff --git a/src/store/metricsReport/metricsReportSlice.ts b/src/store/metricsReport/metricsReportSlice.ts deleted file mode 100644 index 20d18b77b..000000000 --- a/src/store/metricsReport/metricsReportSlice.ts +++ /dev/null @@ -1,89 +0,0 @@ -import { createSlice } from "zustand-slices"; -import type { - Criticality, - EndpointData, - EndpointIssuesData, - ReportTimeMode, - ReportViewLevel, - ReportViewMode, - ServiceIssuesData -} from "../../components/Dashboard/MetricsReport/types"; -import type { Environment } from "../../components/common/App/types"; - -export interface MetricsReportState { - metricsReportViewMode: ReportViewMode; - metricsReportViewLevel: ReportViewLevel; - metricsReportTimeMode: ReportTimeMode; - metricsReportSelectedEnvironmentId: string | null; - metricsReportSelectedService: string | null; - metricsReportSelectedCriticalityLevels: Criticality[]; - metricsReportSelectedPeriodInDays: number; - metricsReportSelectedEndpoints: string[]; - metricsReportSelectedServices: string[]; - metricsReportServices: string[] | null; - metricsReportServicesIssuesData: ServiceIssuesData[] | null; - metricsReportServiceEnvironments: Environment[] | null; - metricsReportServiceEndpoints: EndpointData[] | null; - metricsReportEndpointsIssuesData: EndpointIssuesData[] | null; -} - -const initialState: MetricsReportState = { - metricsReportViewMode: "treemap", - metricsReportViewLevel: "services", - metricsReportTimeMode: "baseline", - metricsReportSelectedEnvironmentId: null, - metricsReportSelectedService: null, - metricsReportSelectedCriticalityLevels: ["Medium", "High"], - metricsReportSelectedPeriodInDays: 7, - metricsReportSelectedEndpoints: [], - metricsReportSelectedServices: [], - metricsReportServices: null, - metricsReportServicesIssuesData: null, - metricsReportServiceEnvironments: null, - metricsReportServiceEndpoints: null, - metricsReportEndpointsIssuesData: null -}; - -const set = - (update: Partial) => (state: MetricsReportState) => ({ - ...state, - ...update - }); - -export const metricsReportSlice = createSlice({ - name: "metricsReport", - value: initialState, - actions: { - setMetricsReportViewMode: (viewMode: ReportViewMode) => - set({ metricsReportViewMode: viewMode }), - setMetricsReportViewLevel: (viewLevel: ReportViewLevel) => - set({ metricsReportViewLevel: viewLevel }), - setMetricsReportTimeMode: (timeMode: ReportTimeMode) => - set({ metricsReportTimeMode: timeMode }), - setMetricsReportSelectedEnvironmentId: (environmentId: string) => - set({ metricsReportSelectedEnvironmentId: environmentId }), - setMetricsReportSelectedService: (selectedService: string | null) => - set({ metricsReportSelectedService: selectedService }), - setMetricsReportSelectedCriticalityLevels: ( - criticalityLevels: Criticality[] - ) => set({ metricsReportSelectedCriticalityLevels: criticalityLevels }), - setMetricsReportSelectedPeriodInDays: (periodInDays: number) => - set({ metricsReportSelectedPeriodInDays: periodInDays }), - setMetricsReportSelectedEndpoints: (endpoints: string[]) => - set({ metricsReportSelectedEndpoints: endpoints }), - setMetricsReportSelectedServices: (services: string[]) => - set({ metricsReportSelectedServices: services }), - setMetricsReportServices: (services: string[] | null) => - set({ metricsReportServices: services }), - setMetricsReportServicesIssuesData: ( - servicesData: ServiceIssuesData[] | null - ) => set({ metricsReportServicesIssuesData: servicesData }), - setMetricsReportServiceEnvironments: (environments: Environment[] | null) => - set({ metricsReportServiceEnvironments: environments }), - setMetricsReportServiceEndpoints: (endpoints: EndpointData[] | null) => - set({ metricsReportServiceEndpoints: endpoints }), - setMetricsReportEndpointsIssuesData: (data: EndpointIssuesData[] | null) => - set({ metricsReportEndpointsIssuesData: data }), - resetMetricsReport: () => set(initialState) - } -}); diff --git a/src/store/metricsReport/useMetricsReportSelector.ts b/src/store/metricsReport/useMetricsReportSelector.ts deleted file mode 100644 index ae674852c..000000000 --- a/src/store/metricsReport/useMetricsReportSelector.ts +++ /dev/null @@ -1,4 +0,0 @@ -import { useStore } from "../useStore"; - -export const useMetricsReportSelector = () => - useStore((state) => state.metricsReport); diff --git a/src/store/useStore.ts b/src/store/useStore.ts index 912f71813..471f4d308 100644 --- a/src/store/useStore.ts +++ b/src/store/useStore.ts @@ -5,18 +5,11 @@ import { assetsSlice } from "./assets/assetsSlice"; import { configSlice } from "./config/configSlice"; import { errorsSlice } from "./errors/errorsSlice"; import { insightsSlice } from "./insights/insightsSlice"; -import { metricsReportSlice } from "./metricsReport/metricsReportSlice"; import { withMutableActions } from "./withMutableActions"; export const useStore = create( withMutableActions( - withSlices( - configSlice, - insightsSlice, - assetsSlice, - metricsReportSlice, - errorsSlice - ), + withSlices(configSlice, insightsSlice, assetsSlice, errorsSlice), { setScope: (scope: Scope) => (_, set) => { set((state) => From 7169eb7fd16e862e2a37f26f9f1050053e84f35b Mon Sep 17 00:00:00 2001 From: Kyrylo Shmidt Date: Fri, 10 Jan 2025 20:17:56 +0100 Subject: [PATCH 2/2] Fix styles --- .../Admin/Reports/CodeIssues/index.tsx | 57 +++++++++++-------- .../Admin/Reports/CodeIssues/styles.ts | 7 +++ src/components/Admin/styles.ts | 3 +- .../Dashboard/MetricsReport/index.tsx | 4 +- .../Dashboard/MetricsReport/styles.ts | 3 +- src/components/common/IssuesReport/styles.ts | 3 - src/redux/slices/issuesReportSlice.ts | 6 +- 7 files changed, 50 insertions(+), 33 deletions(-) create mode 100644 src/components/Admin/Reports/CodeIssues/styles.ts diff --git a/src/components/Admin/Reports/CodeIssues/index.tsx b/src/components/Admin/Reports/CodeIssues/index.tsx index 0cdc5c76c..87210cac5 100644 --- a/src/components/Admin/Reports/CodeIssues/index.tsx +++ b/src/components/Admin/Reports/CodeIssues/index.tsx @@ -2,8 +2,10 @@ import { useAdminDispatch, useAdminSelector } from "../../../../containers/Admin/hooks"; +import { useMount } from "../../../../hooks/useMount"; import type { IssueCriticality } from "../../../../redux/services/types"; import { + clear, setCriticalityLevels, setPeriodInDays, setSelectedEndpoints, @@ -18,6 +20,7 @@ import { type IssuesReportViewMode } from "../../../../redux/slices/issuesReportSlice"; import { IssuesReport } from "../../../common/IssuesReport"; +import * as s from "./styles"; export const CodeIssues = () => { const selectedEnvironmentId = useAdminSelector( @@ -46,6 +49,12 @@ export const CodeIssues = () => { const dispatch = useAdminDispatch(); + useMount(() => { + return () => { + dispatch(clear()); + }; + }); + const handleTileTitleClick = () => // viewLevel: IssuesReportViewLevel, // value: string @@ -97,28 +106,30 @@ export const CodeIssues = () => { }; return ( - + + + ); }; diff --git a/src/components/Admin/Reports/CodeIssues/styles.ts b/src/components/Admin/Reports/CodeIssues/styles.ts new file mode 100644 index 000000000..6bf807248 --- /dev/null +++ b/src/components/Admin/Reports/CodeIssues/styles.ts @@ -0,0 +1,7 @@ +import styled from "styled-components"; + +export const Container = styled.div` + height: 100%; + padding: 24px; + box-sizing: border-box; +`; diff --git a/src/components/Admin/styles.ts b/src/components/Admin/styles.ts index 11d6337c6..b803fb4c8 100644 --- a/src/components/Admin/styles.ts +++ b/src/components/Admin/styles.ts @@ -10,10 +10,11 @@ export const ContentContainer = styled.div` display: flex; flex-direction: column; flex-grow: 1; + height: 100%; overflow: hidden; `; export const MainContainer = styled.main` + height: 100%; overflow: auto; - flex-grow: 1; `; diff --git a/src/components/Dashboard/MetricsReport/index.tsx b/src/components/Dashboard/MetricsReport/index.tsx index dcbc72905..0b428aec3 100644 --- a/src/components/Dashboard/MetricsReport/index.tsx +++ b/src/components/Dashboard/MetricsReport/index.tsx @@ -174,7 +174,7 @@ export const MetricsReport = () => { - + { © {new Date().getFullYear()} digma.ai - + ); }; diff --git a/src/components/Dashboard/MetricsReport/styles.ts b/src/components/Dashboard/MetricsReport/styles.ts index 463ba9163..952c87ed9 100644 --- a/src/components/Dashboard/MetricsReport/styles.ts +++ b/src/components/Dashboard/MetricsReport/styles.ts @@ -36,7 +36,7 @@ export const ContainerBackgroundGradient = styled.div` filter: blur(5px); `; -export const IssuesReportContainer = styled.div` +export const ContentContainer = styled.div` display: flex; flex-direction: column; height: 100%; @@ -44,7 +44,6 @@ export const IssuesReportContainer = styled.div` padding: 24px 24px 16px; gap: 24px; box-sizing: border-box; - overflow: auto; `; export const Footer = styled.div` diff --git a/src/components/common/IssuesReport/styles.ts b/src/components/common/IssuesReport/styles.ts index 74951f36a..25d294026 100644 --- a/src/components/common/IssuesReport/styles.ts +++ b/src/components/common/IssuesReport/styles.ts @@ -5,8 +5,5 @@ export const Container = styled.div` flex-direction: column; height: 100%; width: 100%; - padding: 24px 24px 16px; gap: 24px; - box-sizing: border-box; - overflow: auto; `; diff --git a/src/redux/slices/issuesReportSlice.ts b/src/redux/slices/issuesReportSlice.ts index 899c25ef5..6144f8b4e 100644 --- a/src/redux/slices/issuesReportSlice.ts +++ b/src/redux/slices/issuesReportSlice.ts @@ -65,7 +65,8 @@ export const issuesReportSlice = createSlice({ }, setSelectedServices: (state, action: PayloadAction) => { state.selectedServices = action.payload; - } + }, + clear: () => initialState } }); @@ -78,7 +79,8 @@ export const { setCriticalityLevels, setPeriodInDays, setSelectedEndpoints, - setSelectedServices + setSelectedServices, + clear } = issuesReportSlice.actions; export default issuesReportSlice.reducer;